zfs_unstable: 2.3.0-rc3 -> 2.3.0-rc4 (#365045)
[NixPkgs.git] / nixos / modules / services / networking / nftables.nix
blob7ee1b7caafed1a638395667ed5662bfc03a4ea84
2   config,
3   pkgs,
4   lib,
5   ...
6 }:
7 let
8   cfg = config.networking.nftables;
10   tableSubmodule =
11     { name, ... }:
12     {
13       options = {
14         enable = lib.mkOption {
15           type = lib.types.bool;
16           default = true;
17           description = "Enable this table.";
18         };
20         name = lib.mkOption {
21           type = lib.types.str;
22           description = "Table name.";
23         };
25         content = lib.mkOption {
26           type = lib.types.lines;
27           description = "The table content.";
28         };
30         family = lib.mkOption {
31           description = "Table family.";
32           type = lib.types.enum [
33             "ip"
34             "ip6"
35             "inet"
36             "arp"
37             "bridge"
38             "netdev"
39           ];
40         };
41       };
43       config = {
44         name = lib.mkDefault name;
45       };
46     };
49   ###### interface
51   options = {
52     networking.nftables.enable = lib.mkOption {
53       type = lib.types.bool;
54       default = false;
55       description = ''
56         Whether to enable nftables and use nftables based firewall if enabled.
57         nftables is a Linux-based packet filtering framework intended to
58         replace frameworks like iptables.
60         Note that if you have Docker enabled you will not be able to use
61         nftables without intervention. Docker uses iptables internally to
62         setup NAT for containers. This module disables the ip_tables kernel
63         module, however Docker automatically loads the module. Please see
64         <https://github.com/NixOS/nixpkgs/issues/24318#issuecomment-289216273>
65         for more information.
67         There are other programs that use iptables internally too, such as
68         libvirt. For information on how the two firewalls interact, see
69         <https://wiki.nftables.org/wiki-nftables/index.php/Troubleshooting#Question_4._How_do_nftables_and_iptables_interact_when_used_on_the_same_system.3F>.
70       '';
71     };
73     networking.nftables.checkRuleset = lib.mkOption {
74       type = lib.types.bool;
75       default = true;
76       description = ''
77         Run `nft check` on the ruleset to spot syntax errors during build.
78         Because this is executed in a sandbox, the check might fail if it requires
79         access to any environmental factors or paths outside the Nix store.
80         To circumvent this, the ruleset file can be edited using the preCheckRuleset
81         option to work in the sandbox environment.
82       '';
83     };
85     networking.nftables.checkRulesetRedirects = lib.mkOption {
86       type = lib.types.addCheck (lib.types.attrsOf lib.types.path) (
87         attrs: lib.all lib.types.path.check (lib.attrNames attrs)
88       );
89       default = {
90         "/etc/hosts" = config.environment.etc.hosts.source;
91         "/etc/protocols" = config.environment.etc.protocols.source;
92         "/etc/services" = config.environment.etc.services.source;
93       };
94       defaultText = lib.literalExpression ''
95         {
96           "/etc/hosts" = config.environment.etc.hosts.source;
97           "/etc/protocols" = config.environment.etc.protocols.source;
98           "/etc/services" = config.environment.etc.services.source;
99         }
100       '';
101       description = ''
102         Set of paths that should be intercepted and rewritten while checking the ruleset
103         using `pkgs.buildPackages.libredirect`.
104       '';
105     };
107     networking.nftables.preCheckRuleset = lib.mkOption {
108       type = lib.types.lines;
109       default = "";
110       example = lib.literalExpression ''
111         sed 's/skgid meadow/skgid nogroup/g' -i ruleset.conf
112       '';
113       description = ''
114         This script gets run before the ruleset is checked. It can be used to
115         create additional files needed for the ruleset check to work, or modify
116         the ruleset for cases the build environment cannot cover.
117       '';
118     };
120     networking.nftables.flushRuleset = lib.mkEnableOption "flushing the entire ruleset on each reload";
122     networking.nftables.extraDeletions = lib.mkOption {
123       type = lib.types.lines;
124       default = "";
125       example = ''
126         # this makes deleting a non-existing table a no-op instead of an error
127         table inet some-table;
129         delete table inet some-table;
130       '';
131       description = ''
132         Extra deletion commands to be run on every firewall start, reload
133         and after stopping the firewall.
134       '';
135     };
137     networking.nftables.ruleset = lib.mkOption {
138       type = lib.types.lines;
139       default = "";
140       example = ''
141         # Check out https://wiki.nftables.org/ for better documentation.
142         # Table for both IPv4 and IPv6.
143         table inet filter {
144           # Block all incoming connections traffic except SSH and "ping".
145           chain input {
146             type filter hook input priority 0;
148             # accept any localhost traffic
149             iifname lo accept
151             # accept traffic originated from us
152             ct state {established, related} accept
154             # ICMP
155             # routers may also want: mld-listener-query, nd-router-solicit
156             ip6 nexthdr icmpv6 icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept
157             ip protocol icmp icmp type { destination-unreachable, router-advertisement, time-exceeded, parameter-problem } accept
159             # allow "ping"
160             ip6 nexthdr icmpv6 icmpv6 type echo-request accept
161             ip protocol icmp icmp type echo-request accept
163             # accept SSH connections (required for a server)
164             tcp dport 22 accept
166             # count and drop any other traffic
167             counter drop
168           }
170           # Allow all outgoing connections.
171           chain output {
172             type filter hook output priority 0;
173             accept
174           }
176           chain forward {
177             type filter hook forward priority 0;
178             accept
179           }
180         }
181       '';
182       description = ''
183         The ruleset to be used with nftables.  Should be in a format that
184         can be loaded using "/bin/nft -f".  The ruleset is updated atomically.
185         Note that if the tables should be cleaned first, either:
186         - networking.nftables.flushRuleset = true; needs to be set (flushes all tables)
187         - networking.nftables.extraDeletions needs to be set
188         - or networking.nftables.tables can be used, which will clean up the table automatically
189       '';
190     };
191     networking.nftables.rulesetFile = lib.mkOption {
192       type = lib.types.nullOr lib.types.path;
193       default = null;
194       description = ''
195         The ruleset file to be used with nftables.  Should be in a format that
196         can be loaded using "nft -f".  The ruleset is updated atomically.
197       '';
198     };
200     networking.nftables.flattenRulesetFile = lib.mkOption {
201       type = lib.types.bool;
202       default = false;
203       description = ''
204         Use `builtins.readFile` rather than `include` to handle {option}`networking.nftables.rulesetFile`. It is useful when you want to apply {option}`networking.nftables.preCheckRuleset` to {option}`networking.nftables.rulesetFile`.
206         ::: {.note}
207         It is expected that {option}`networking.nftables.rulesetFile` can be accessed from the build sandbox.
208         :::
209       '';
210     };
212     networking.nftables.tables = lib.mkOption {
213       type = lib.types.attrsOf (lib.types.submodule tableSubmodule);
215       default = { };
217       description = ''
218         Tables to be added to ruleset.
219         Tables will be added together with delete statements to clean up the table before every update.
220       '';
222       example = {
223         filter = {
224           family = "inet";
225           content = ''
226             # Check out https://wiki.nftables.org/ for better documentation.
227             # Table for both IPv4 and IPv6.
228             # Block all incoming connections traffic except SSH and "ping".
229             chain input {
230               type filter hook input priority 0;
232               # accept any localhost traffic
233               iifname lo accept
235               # accept traffic originated from us
236               ct state {established, related} accept
238               # ICMP
239               # routers may also want: mld-listener-query, nd-router-solicit
240               ip6 nexthdr icmpv6 icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept
241               ip protocol icmp icmp type { destination-unreachable, router-advertisement, time-exceeded, parameter-problem } accept
243               # allow "ping"
244               ip6 nexthdr icmpv6 icmpv6 type echo-request accept
245               ip protocol icmp icmp type echo-request accept
247               # accept SSH connections (required for a server)
248               tcp dport 22 accept
250               # count and drop any other traffic
251               counter drop
252             }
254             # Allow all outgoing connections.
255             chain output {
256               type filter hook output priority 0;
257               accept
258             }
260             chain forward {
261               type filter hook forward priority 0;
262               accept
263             }
264           '';
265         };
266       };
267     };
268   };
270   ###### implementation
272   config = lib.mkIf cfg.enable {
273     boot.blacklistedKernelModules = [ "ip_tables" ];
274     environment.systemPackages = [ pkgs.nftables ];
275     # versionOlder for backportability, remove afterwards
276     networking.nftables.flushRuleset = lib.mkDefault (
277       lib.versionOlder config.system.stateVersion "23.11"
278       || (cfg.rulesetFile != null || cfg.ruleset != "")
279     );
280     systemd.services.nftables = {
281       description = "nftables firewall";
282       after = [ "sysinit.target" ];
283       before = [
284         "network-pre.target"
285         "shutdown.target"
286       ];
287       conflicts = [ "shutdown.target" ];
288       wants = [
289         "network-pre.target"
290         "sysinit.target"
291       ];
292       wantedBy = [ "multi-user.target" ];
293       reloadIfChanged = true;
294       serviceConfig =
295         let
296           enabledTables = lib.filterAttrs (_: table: table.enable) cfg.tables;
297           deletionsScript = pkgs.writeScript "nftables-deletions" ''
298             #! ${pkgs.nftables}/bin/nft -f
299             ${
300               if cfg.flushRuleset then
301                 "flush ruleset"
302               else
303                 lib.concatStringsSep "\n" (
304                   lib.mapAttrsToList (_: table: ''
305                     table ${table.family} ${table.name}
306                     delete table ${table.family} ${table.name}
307                   '') enabledTables
308                 )
309             }
310             ${cfg.extraDeletions}
311           '';
312           deletionsScriptVar = "/var/lib/nftables/deletions.nft";
313           ensureDeletions = pkgs.writeShellScript "nftables-ensure-deletions" ''
314             touch ${deletionsScriptVar}
315             chmod +x ${deletionsScriptVar}
316           '';
317           saveDeletionsScript = pkgs.writeShellScript "nftables-save-deletions" ''
318             cp ${deletionsScript} ${deletionsScriptVar}
319           '';
320           cleanupDeletionsScript = pkgs.writeShellScript "nftables-cleanup-deletions" ''
321             rm ${deletionsScriptVar}
322           '';
323           rulesScript = pkgs.writeTextFile {
324             name = "nftables-rules";
325             executable = true;
326             text = ''
327               #! ${pkgs.nftables}/bin/nft -f
328               # previous deletions, if any
329               include "${deletionsScriptVar}"
330               # current deletions
331               include "${deletionsScript}"
332               ${lib.concatStringsSep "\n" (
333                 lib.mapAttrsToList (_: table: ''
334                   table ${table.family} ${table.name} {
335                     ${table.content}
336                   }
337                 '') enabledTables
338               )}
339               ${cfg.ruleset}
340               ${
341                 if cfg.rulesetFile != null then
342                   if cfg.flattenRulesetFile then
343                     builtins.readFile cfg.rulesetFile
344                   else
345                     ''
346                       include "${cfg.rulesetFile}"
347                     ''
348                 else
349                   ""
350               }
351             '';
352             checkPhase = lib.optionalString cfg.checkRuleset ''
353               cp $out ruleset.conf
354               sed 's|include "${deletionsScriptVar}"||' -i ruleset.conf
355               ${cfg.preCheckRuleset}
356               export NIX_REDIRECTS=${
357                 lib.escapeShellArg (
358                   lib.concatStringsSep ":" (lib.mapAttrsToList (n: v: "${n}=${v}") cfg.checkRulesetRedirects)
359                 )
360               }
361               LD_PRELOAD="${pkgs.buildPackages.libredirect}/lib/libredirect.so ${pkgs.buildPackages.lklWithFirewall.lib}/lib/liblkl-hijack.so" \
362                 ${pkgs.buildPackages.nftables}/bin/nft --check --file ruleset.conf
363             '';
364           };
365         in
366         {
367           Type = "oneshot";
368           RemainAfterExit = true;
369           ExecStart = [
370             ensureDeletions
371             rulesScript
372           ];
373           ExecStartPost = saveDeletionsScript;
374           ExecReload = [
375             ensureDeletions
376             rulesScript
377             saveDeletionsScript
378           ];
379           ExecStop = [
380             deletionsScriptVar
381             cleanupDeletionsScript
382           ];
383           StateDirectory = "nftables";
384         };
385       unitConfig.DefaultDependencies = false;
386     };
387   };