8 cfg = config.networking.nftables;
14 enable = lib.mkOption {
15 type = lib.types.bool;
17 description = "Enable this table.";
22 description = "Table name.";
25 content = lib.mkOption {
26 type = lib.types.lines;
27 description = "The table content.";
30 family = lib.mkOption {
31 description = "Table family.";
32 type = lib.types.enum [
44 name = lib.mkDefault name;
52 networking.nftables.enable = lib.mkOption {
53 type = lib.types.bool;
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>
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>.
73 networking.nftables.checkRuleset = lib.mkOption {
74 type = lib.types.bool;
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.
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)
90 "/etc/hosts" = config.environment.etc.hosts.source;
91 "/etc/protocols" = config.environment.etc.protocols.source;
92 "/etc/services" = config.environment.etc.services.source;
94 defaultText = lib.literalExpression ''
96 "/etc/hosts" = config.environment.etc.hosts.source;
97 "/etc/protocols" = config.environment.etc.protocols.source;
98 "/etc/services" = config.environment.etc.services.source;
102 Set of paths that should be intercepted and rewritten while checking the ruleset
103 using `pkgs.buildPackages.libredirect`.
107 networking.nftables.preCheckRuleset = lib.mkOption {
108 type = lib.types.lines;
110 example = lib.literalExpression ''
111 sed 's/skgid meadow/skgid nogroup/g' -i ruleset.conf
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.
120 networking.nftables.flushRuleset = lib.mkEnableOption "flushing the entire ruleset on each reload";
122 networking.nftables.extraDeletions = lib.mkOption {
123 type = lib.types.lines;
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;
132 Extra deletion commands to be run on every firewall start, reload
133 and after stopping the firewall.
137 networking.nftables.ruleset = lib.mkOption {
138 type = lib.types.lines;
141 # Check out https://wiki.nftables.org/ for better documentation.
142 # Table for both IPv4 and IPv6.
144 # Block all incoming connections traffic except SSH and "ping".
146 type filter hook input priority 0;
148 # accept any localhost traffic
151 # accept traffic originated from us
152 ct state {established, related} accept
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
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)
166 # count and drop any other traffic
170 # Allow all outgoing connections.
172 type filter hook output priority 0;
177 type filter hook forward priority 0;
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
191 networking.nftables.rulesetFile = lib.mkOption {
192 type = lib.types.nullOr lib.types.path;
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.
200 networking.nftables.flattenRulesetFile = lib.mkOption {
201 type = lib.types.bool;
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`.
207 It is expected that {option}`networking.nftables.rulesetFile` can be accessed from the build sandbox.
212 networking.nftables.tables = lib.mkOption {
213 type = lib.types.attrsOf (lib.types.submodule tableSubmodule);
218 Tables to be added to ruleset.
219 Tables will be added together with delete statements to clean up the table before every update.
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".
230 type filter hook input priority 0;
232 # accept any localhost traffic
235 # accept traffic originated from us
236 ct state {established, related} accept
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
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)
250 # count and drop any other traffic
254 # Allow all outgoing connections.
256 type filter hook output priority 0;
261 type filter hook forward priority 0;
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 != "")
280 systemd.services.nftables = {
281 description = "nftables firewall";
282 after = [ "sysinit.target" ];
287 conflicts = [ "shutdown.target" ];
292 wantedBy = [ "multi-user.target" ];
293 reloadIfChanged = true;
296 enabledTables = lib.filterAttrs (_: table: table.enable) cfg.tables;
297 deletionsScript = pkgs.writeScript "nftables-deletions" ''
298 #! ${pkgs.nftables}/bin/nft -f
300 if cfg.flushRuleset then
303 lib.concatStringsSep "\n" (
304 lib.mapAttrsToList (_: table: ''
305 table ${table.family} ${table.name}
306 delete table ${table.family} ${table.name}
310 ${cfg.extraDeletions}
312 deletionsScriptVar = "/var/lib/nftables/deletions.nft";
313 ensureDeletions = pkgs.writeShellScript "nftables-ensure-deletions" ''
314 touch ${deletionsScriptVar}
315 chmod +x ${deletionsScriptVar}
317 saveDeletionsScript = pkgs.writeShellScript "nftables-save-deletions" ''
318 cp ${deletionsScript} ${deletionsScriptVar}
320 cleanupDeletionsScript = pkgs.writeShellScript "nftables-cleanup-deletions" ''
321 rm ${deletionsScriptVar}
323 rulesScript = pkgs.writeTextFile {
324 name = "nftables-rules";
327 #! ${pkgs.nftables}/bin/nft -f
328 # previous deletions, if any
329 include "${deletionsScriptVar}"
331 include "${deletionsScript}"
332 ${lib.concatStringsSep "\n" (
333 lib.mapAttrsToList (_: table: ''
334 table ${table.family} ${table.name} {
341 if cfg.rulesetFile != null then
342 if cfg.flattenRulesetFile then
343 builtins.readFile cfg.rulesetFile
346 include "${cfg.rulesetFile}"
352 checkPhase = lib.optionalString cfg.checkRuleset ''
354 sed 's|include "${deletionsScriptVar}"||' -i ruleset.conf
355 ${cfg.preCheckRuleset}
356 export NIX_REDIRECTS=${
358 lib.concatStringsSep ":" (lib.mapAttrsToList (n: v: "${n}=${v}") cfg.checkRulesetRedirects)
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
368 RemainAfterExit = true;
373 ExecStartPost = saveDeletionsScript;
381 cleanupDeletionsScript
383 StateDirectory = "nftables";
385 unitConfig.DefaultDependencies = false;