vuls: init at 0.27.0
[NixPkgs.git] / nixos / modules / services / networking / fastnetmon-advanced.nix
blob26e8ad8b76d97ce718a7a32982cc0fd2548e28a5
1 { config, lib, pkgs, ... }:
3 let
4   # Background information: FastNetMon requires a MongoDB to start. This is because
5   # it uses MongoDB to store its configuration. That is, in a normal setup there is
6   # one collection with one document.
7   # To provide declarative configuration in our NixOS module, this database is
8   # completely emptied and replaced on each boot by the fastnetmon-setup service
9   # using the configuration backup functionality.
11   cfg = config.services.fastnetmon-advanced;
12   settingsFormat = pkgs.formats.yaml { };
14   # obtain the default configs by starting up ferretdb and fcli in a derivation
15   default_configs = pkgs.runCommand "default-configs" {
16     nativeBuildInputs = [
17       pkgs.ferretdb
18       pkgs.fastnetmon-advanced # for fcli
19       pkgs.proot
20     ];
21   } ''
22     mkdir ferretdb fastnetmon $out
23     FERRETDB_TELEMETRY="disable" FERRETDB_HANDLER="sqlite" FERRETDB_STATE_DIR="$PWD/ferretdb" FERRETDB_SQLITE_URL="file:$PWD/ferretdb/" ferretdb &
25     cat << EOF > fastnetmon/fastnetmon.conf
26     ${builtins.toJSON {
27       mongodb_username = "";
28     }}
29     EOF
30     proot -b fastnetmon:/etc/fastnetmon -0 fcli create_configuration
31     proot -b fastnetmon:/etc/fastnetmon -0 fcli set bgp default
32     proot -b fastnetmon:/etc/fastnetmon -0 fcli export_configuration backup.tar
33     tar -C $out --no-same-owner -xvf backup.tar
34   '';
36   # merge the user configs into the default configs
37   config_tar = pkgs.runCommand "fastnetmon-config.tar" {
38     nativeBuildInputs = with pkgs; [ jq ];
39   } ''
40     jq -s add ${default_configs}/main.json ${pkgs.writeText "main-add.json" (builtins.toJSON cfg.settings)} > main.json
41     mkdir hostgroup
42     ${lib.concatImapStringsSep "\n" (pos: hostgroup: ''
43       jq -s add ${default_configs}/hostgroup/0.json ${pkgs.writeText "hostgroup-${toString (pos - 1)}-add.json" (builtins.toJSON hostgroup)} > hostgroup/${toString (pos - 1)}.json
44     '') hostgroups}
45     mkdir bgp
46     ${lib.concatImapStringsSep "\n" (pos: bgp: ''
47       jq -s add ${default_configs}/bgp/0.json ${pkgs.writeText "bgp-${toString (pos - 1)}-add.json" (builtins.toJSON bgp)} > bgp/${toString (pos - 1)}.json
48     '') bgpPeers}
49     tar -cf $out main.json ${lib.concatImapStringsSep " " (pos: _: "hostgroup/${toString (pos - 1)}.json") hostgroups} ${lib.concatImapStringsSep " " (pos: _: "bgp/${toString (pos - 1)}.json") bgpPeers}
50   '';
52   hostgroups = lib.mapAttrsToList (name: hostgroup: { inherit name; } // hostgroup) cfg.hostgroups;
53   bgpPeers = lib.mapAttrsToList (name: bgpPeer: { inherit name; } // bgpPeer) cfg.bgpPeers;
55 in {
56   options.services.fastnetmon-advanced = with lib; {
57     enable = mkEnableOption "the fastnetmon-advanced DDoS Protection daemon";
59     settings = mkOption {
60       description = ''
61         Extra configuration options to declaratively load into FastNetMon Advanced.
63         See the [FastNetMon Advanced Configuration options reference](https://fastnetmon.com/docs-fnm-advanced/fastnetmon-advanced-configuration-options/) for more details.
64       '';
65       type = settingsFormat.type;
66       default = {};
67       example = literalExpression ''
68         {
69           networks_list = [ "192.0.2.0/24" ];
70           gobgp = true;
71           gobgp_flow_spec_announces = true;
72         }
73       '';
74     };
75     hostgroups = mkOption {
76       description = "Hostgroups to declaratively load into FastNetMon Advanced";
77       type = types.attrsOf settingsFormat.type;
78       default = {};
79     };
80     bgpPeers = mkOption {
81       description = "BGP Peers to declaratively load into FastNetMon Advanced";
82       type = types.attrsOf settingsFormat.type;
83       default = {};
84     };
86     enableAdvancedTrafficPersistence = mkOption {
87       description = "Store historical flow data in clickhouse";
88       type = types.bool;
89       default = false;
90     };
92     traffic_db.settings = mkOption {
93       type = settingsFormat.type;
94       description = "Additional settings for /etc/fastnetmon/traffic_db.conf";
95     };
96   };
98   config = lib.mkMerge [ (lib.mkIf cfg.enable {
99     environment.systemPackages = with pkgs; [
100       fastnetmon-advanced # for fcli
101     ];
103     environment.etc."fastnetmon/license.lic".source = "/var/lib/fastnetmon/license.lic";
104     environment.etc."fastnetmon/gobgpd.conf".source = "/run/fastnetmon/gobgpd.conf";
105     environment.etc."fastnetmon/fastnetmon.conf".source = pkgs.writeText "fastnetmon.conf" (builtins.toJSON {
106       mongodb_username = "";
107     });
109     services.ferretdb.enable = true;
111     systemd.services.fastnetmon-setup = {
112       wantedBy = [ "multi-user.target" ];
113       after = [ "ferretdb.service" ];
114       path = with pkgs; [ fastnetmon-advanced config.systemd.package ];
115       script = ''
116         fcli create_configuration
117         fcli delete hostgroup global
118         fcli import_configuration ${config_tar}
119         systemctl --no-block try-restart fastnetmon
120       '';
121       serviceConfig.Type = "oneshot";
122     };
124     systemd.services.fastnetmon = {
125       wantedBy = [ "multi-user.target" ];
126       after = [ "ferretdb.service" "fastnetmon-setup.service" "polkit.service" ];
127       path = with pkgs; [ iproute2 ];
128       unitConfig = {
129         # Disable logic which shuts service when we do too many restarts
130         # We do restarts from sudo fcli commit and it's expected that we may have many restarts
131         # Details: https://github.com/systemd/systemd/issues/2416
132         StartLimitInterval = 0;
133       };
134       serviceConfig = {
135         ExecStart = "${pkgs.fastnetmon-advanced}/bin/fastnetmon --log_to_console";
137         LimitNOFILE = 65535;
138         # Restart service when it fails due to any reasons, we need to keep processing traffic no matter what happened
139         Restart= "on-failure";
140         RestartSec= "5s";
142         DynamicUser = true;
143         CacheDirectory = "fastnetmon";
144         RuntimeDirectory = "fastnetmon"; # for gobgpd config
145         StateDirectory = "fastnetmon"; # for license file
146       };
147     };
149     security.polkit.enable = true;
150     security.polkit.extraConfig = ''
151       polkit.addRule(function(action, subject) {
152         if (action.id == "org.freedesktop.systemd1.manage-units" &&
153           subject.isInGroup("fastnetmon")) {
154           if (action.lookup("unit") == "gobgp.service") {
155             var verb = action.lookup("verb");
156             if (verb == "start" || verb == "stop" || verb == "restart") {
157               return polkit.Result.YES;
158             }
159           }
160         }
161       });
162     '';
164     # We don't use the existing gobgp NixOS module and package, because the gobgp
165     # version might not be compatible with fastnetmon. Also, the service name
166     # _must_ be 'gobgp' and not 'gobgpd', so that fastnetmon can reload the config.
167     systemd.services.gobgp = {
168       wantedBy = [ "multi-user.target" ];
169       after = [ "network.target" ];
170       description = "GoBGP Routing Daemon";
171       unitConfig = {
172         ConditionPathExists = "/run/fastnetmon/gobgpd.conf";
173       };
174       serviceConfig = {
175         Type = "notify";
176         ExecStartPre = "${pkgs.fastnetmon-advanced}/bin/fnm-gobgpd -f /run/fastnetmon/gobgpd.conf -d";
177         SupplementaryGroups = [ "fastnetmon" ];
178         ExecStart = "${pkgs.fastnetmon-advanced}/bin/fnm-gobgpd -f /run/fastnetmon/gobgpd.conf --sdnotify";
179         ExecReload = "${pkgs.fastnetmon-advanced}/bin/fnm-gobgpd -r";
180         DynamicUser = true;
181         AmbientCapabilities = "cap_net_bind_service";
182       };
183     };
184   })
186   (lib.mkIf (cfg.enable && cfg.enableAdvancedTrafficPersistence) {
187     ## Advanced Traffic persistence
188     ## https://fastnetmon.com/docs-fnm-advanced/fastnetmon-advanced-traffic-persistency/
190     services.clickhouse.enable = true;
192     services.fastnetmon-advanced.settings.traffic_db = true;
194     services.fastnetmon-advanced.traffic_db.settings = {
195       clickhouse_batch_size = lib.mkDefault 1000;
196       clickhouse_batch_delay = lib.mkDefault 1;
197       traffic_db_host = lib.mkDefault "127.0.0.1";
198       traffic_db_port = lib.mkDefault 8100;
199       clickhouse_host = lib.mkDefault "127.0.0.1";
200       clickhouse_port = lib.mkDefault 9000;
201       clickhouse_user = lib.mkDefault "default";
202       clickhouse_password = lib.mkDefault "";
203     };
204     environment.etc."fastnetmon/traffic_db.conf".text = builtins.toJSON cfg.traffic_db.settings;
206     systemd.services.traffic_db = {
207       wantedBy = [ "multi-user.target" ];
208       after = [ "network.target" ];
209       serviceConfig = {
210         ExecStart = "${pkgs.fastnetmon-advanced}/bin/traffic_db";
211         # Restart service when it fails due to any reasons, we need to keep processing traffic no matter what happened
212         Restart= "on-failure";
213         RestartSec= "5s";
215         DynamicUser = true;
216       };
217     };
219   }) ];
221   meta.maintainers = lib.teams.wdz.members;