Merge pull request #207758 from mkg20001/nftex

authored by

Maciej Krüger and committed by
GitHub
18748c17 18f081cb

+176 -36
+6
nixos/doc/manual/release-notes/rl-2311.section.md
··· 223 223 224 224 - Suricata was upgraded from 6.0 to 7.0 and no longer considers HTTP/2 support as experimental, see [upstream release notes](https://forum.suricata.io/t/suricata-7-0-0-released/3715) for more details. 225 225 226 + - `networking.nftables` now has the option `networking.nftables.table.<table>` to create tables 227 + and have them be updated atomically, instead of flushing the ruleset. 228 + 229 + - `networking.nftables` is no longer flushing all rulesets on every reload. 230 + Use `networking.nftables.flushRuleset = true;` to get back the old behaviour. 231 + 226 232 ## Nixpkgs internals {#sec-release-23.11-nixpkgs-internals} 227 233 228 234 - The use of `sourceRoot = "source";`, `sourceRoot = "source/subdir";`, and similar lines in package derivations using the default `unpackPhase` is deprecated as it requires `unpackPhase` to always produce a directory named "source". Use `sourceRoot = src.name`, `sourceRoot = "${src.name}/subdir";`, or `setSourceRoot = "sourceRoot=$(echo */subdir)";` or similar instead.
+2 -7
nixos/modules/services/networking/firewall-nftables.nix
··· 70 70 } 71 71 ]; 72 72 73 - networking.nftables.ruleset = '' 74 - 75 - table inet nixos-fw { 76 - 73 + networking.nftables.tables."nixos-fw".family = "inet"; 74 + networking.nftables.tables."nixos-fw".content = '' 77 75 ${optionalString (cfg.checkReversePath != false) '' 78 76 chain rpfilter { 79 77 type filter hook prerouting priority mangle + 10; policy drop; ··· 169 167 170 168 } 171 169 ''} 172 - 173 - } 174 - 175 170 ''; 176 171 177 172 };
+18 -18
nixos/modules/services/networking/nat-nftables.nix
··· 145 145 } 146 146 ]; 147 147 148 - networking.nftables.ruleset = '' 149 - table ip nixos-nat { 150 - ${mkTable { 148 + networking.nftables.tables = { 149 + "nixos-nat" = { 150 + family = "ip"; 151 + content = mkTable { 151 152 ipVer = "ip"; 152 153 inherit dest ipSet; 153 154 forwardPorts = filter (x: !(isIPv6 x.destination)) cfg.forwardPorts; 154 155 inherit (cfg) dmzHost; 155 - }} 156 - } 157 - 158 - ${optionalString cfg.enableIPv6 '' 159 - table ip6 nixos-nat { 160 - ${mkTable { 161 - ipVer = "ip6"; 162 - dest = destIPv6; 163 - ipSet = ipv6Set; 164 - forwardPorts = filter (x: isIPv6 x.destination) cfg.forwardPorts; 165 - dmzHost = null; 166 - }} 167 - } 168 - ''} 169 - ''; 156 + }; 157 + }; 158 + "nixos-nat6" = mkIf cfg.enableIPv6 { 159 + family = "ip6"; 160 + name = "nixos-nat"; 161 + content = mkTable { 162 + ipVer = "ip6"; 163 + dest = destIPv6; 164 + ipSet = ipv6Set; 165 + forwardPorts = filter (x: isIPv6 x.destination) cfg.forwardPorts; 166 + dmzHost = null; 167 + }; 168 + }; 169 + }; 170 170 171 171 networking.firewall.extraForwardRules = optionalString config.networking.firewall.filterForward '' 172 172 ${optionalString (ifaceSet != "") ''
+148 -8
nixos/modules/services/networking/nftables.nix
··· 2 2 with lib; 3 3 let 4 4 cfg = config.networking.nftables; 5 + 6 + tableSubmodule = { name, ... }: { 7 + options = { 8 + enable = mkOption { 9 + type = types.bool; 10 + default = true; 11 + description = lib.mdDoc "Enable this table."; 12 + }; 13 + 14 + name = mkOption { 15 + type = types.str; 16 + description = lib.mdDoc "Table name."; 17 + }; 18 + 19 + content = mkOption { 20 + type = types.lines; 21 + description = lib.mdDoc "The table content."; 22 + }; 23 + 24 + family = mkOption { 25 + description = lib.mdDoc "Table family."; 26 + type = types.enum [ "ip" "ip6" "inet" "arp" "bridge" "netdev" ]; 27 + }; 28 + }; 29 + 30 + config = { 31 + name = mkDefault name; 32 + }; 33 + }; 5 34 in 6 35 { 7 36 ###### interface ··· 54 83 ''; 55 84 }; 56 85 86 + networking.nftables.flushRuleset = mkEnableOption (lib.mdDoc "Flush the entire ruleset on each reload."); 87 + 88 + networking.nftables.extraDeletions = mkOption { 89 + type = types.lines; 90 + default = ""; 91 + example = '' 92 + # this makes deleting a non-existing table a no-op instead of an error 93 + table inet some-table; 94 + 95 + delete table inet some-table; 96 + ''; 97 + description = 98 + lib.mdDoc '' 99 + Extra deletion commands to be run on every firewall start, reload 100 + and after stopping the firewall. 101 + ''; 102 + }; 103 + 57 104 networking.nftables.ruleset = mkOption { 58 105 type = types.lines; 59 106 default = ""; ··· 103 150 lib.mdDoc '' 104 151 The ruleset to be used with nftables. Should be in a format that 105 152 can be loaded using "/bin/nft -f". The ruleset is updated atomically. 106 - This option conflicts with rulesetFile. 153 + Note that if the tables should be cleaned first, either: 154 + - networking.nftables.flushRuleset = true; needs to be set (flushes all tables) 155 + - networking.nftables.extraDeletions needs to be set 156 + - or networking.nftables.tables can be used, which will clean up the table automatically 107 157 ''; 108 158 }; 109 159 networking.nftables.rulesetFile = mkOption { ··· 113 163 lib.mdDoc '' 114 164 The ruleset file to be used with nftables. Should be in a format that 115 165 can be loaded using "nft -f". The ruleset is updated atomically. 116 - This option conflicts with ruleset and nftables based firewall. 117 166 ''; 118 167 }; 168 + networking.nftables.tables = mkOption { 169 + type = types.attrsOf (types.submodule tableSubmodule); 170 + 171 + default = {}; 172 + 173 + description = lib.mdDoc '' 174 + Tables to be added to ruleset. 175 + Tables will be added together with delete statements to clean up the table before every update. 176 + ''; 177 + 178 + example = { 179 + filter = { 180 + family = "inet"; 181 + content = '' 182 + # Check out https://wiki.nftables.org/ for better documentation. 183 + # Table for both IPv4 and IPv6. 184 + # Block all incoming connections traffic except SSH and "ping". 185 + chain input { 186 + type filter hook input priority 0; 187 + 188 + # accept any localhost traffic 189 + iifname lo accept 190 + 191 + # accept traffic originated from us 192 + ct state {established, related} accept 193 + 194 + # ICMP 195 + # routers may also want: mld-listener-query, nd-router-solicit 196 + ip6 nexthdr icmpv6 icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept 197 + ip protocol icmp icmp type { destination-unreachable, router-advertisement, time-exceeded, parameter-problem } accept 198 + 199 + # allow "ping" 200 + ip6 nexthdr icmpv6 icmpv6 type echo-request accept 201 + ip protocol icmp icmp type echo-request accept 202 + 203 + # accept SSH connections (required for a server) 204 + tcp dport 22 accept 205 + 206 + # count and drop any other traffic 207 + counter drop 208 + } 209 + 210 + # Allow all outgoing connections. 211 + chain output { 212 + type filter hook output priority 0; 213 + accept 214 + } 215 + 216 + chain forward { 217 + type filter hook forward priority 0; 218 + accept 219 + } 220 + ''; 221 + }; 222 + }; 223 + }; 119 224 }; 120 225 121 226 ###### implementation ··· 124 229 boot.blacklistedKernelModules = [ "ip_tables" ]; 125 230 environment.systemPackages = [ pkgs.nftables ]; 126 231 networking.networkmanager.firewallBackend = mkDefault "nftables"; 232 + # versionOlder for backportability, remove afterwards 233 + networking.nftables.flushRuleset = mkDefault (versionOlder config.system.stateVersion "23.11" || (cfg.rulesetFile != null || cfg.ruleset != "")); 127 234 systemd.services.nftables = { 128 235 description = "nftables firewall"; 129 236 before = [ "network-pre.target" ]; ··· 131 238 wantedBy = [ "multi-user.target" ]; 132 239 reloadIfChanged = true; 133 240 serviceConfig = let 241 + enabledTables = filterAttrs (_: table: table.enable) cfg.tables; 242 + deletionsScript = pkgs.writeScript "nftables-deletions" '' 243 + #! ${pkgs.nftables}/bin/nft -f 244 + ${if cfg.flushRuleset then "flush ruleset" 245 + else concatStringsSep "\n" (mapAttrsToList (_: table: '' 246 + table ${table.family} ${table.name} 247 + delete table ${table.family} ${table.name} 248 + '') enabledTables)} 249 + ${cfg.extraDeletions} 250 + ''; 251 + deletionsScriptVar = "/var/lib/nftables/deletions.nft"; 252 + ensureDeletions = pkgs.writeShellScript "nftables-ensure-deletions" '' 253 + touch ${deletionsScriptVar} 254 + chmod +x ${deletionsScriptVar} 255 + ''; 256 + saveDeletionsScript = pkgs.writeShellScript "nftables-save-deletions" '' 257 + cp ${deletionsScript} ${deletionsScriptVar} 258 + ''; 259 + cleanupDeletionsScript = pkgs.writeShellScript "nftables-cleanup-deletions" '' 260 + rm ${deletionsScriptVar} 261 + ''; 134 262 rulesScript = pkgs.writeTextFile { 135 263 name = "nftables-rules"; 136 264 executable = true; 137 265 text = '' 138 266 #! ${pkgs.nftables}/bin/nft -f 139 - flush ruleset 140 - ${if cfg.rulesetFile != null then '' 267 + # previous deletions, if any 268 + include "${deletionsScriptVar}" 269 + # current deletions 270 + include "${deletionsScript}" 271 + ${concatStringsSep "\n" (mapAttrsToList (_: table: '' 272 + table ${table.family} ${table.name} { 273 + ${table.content} 274 + } 275 + '') enabledTables)} 276 + ${cfg.ruleset} 277 + ${lib.optionalString (cfg.rulesetFile != null) '' 141 278 include "${cfg.rulesetFile}" 142 - '' else cfg.ruleset} 279 + ''} 143 280 ''; 144 281 checkPhase = lib.optionalString cfg.checkRuleset '' 145 282 cp $out ruleset.conf 283 + sed 's|include "${deletionsScriptVar}"||' -i ruleset.conf 146 284 ${cfg.preCheckRuleset} 147 285 export NIX_REDIRECTS=/etc/protocols=${pkgs.buildPackages.iana-etc}/etc/protocols:/etc/services=${pkgs.buildPackages.iana-etc}/etc/services 148 286 LD_PRELOAD="${pkgs.buildPackages.libredirect}/lib/libredirect.so ${pkgs.buildPackages.lklWithFirewall.lib}/lib/liblkl-hijack.so" \ ··· 152 290 in { 153 291 Type = "oneshot"; 154 292 RemainAfterExit = true; 155 - ExecStart = rulesScript; 156 - ExecReload = rulesScript; 157 - ExecStop = "${pkgs.nftables}/bin/nft flush ruleset"; 293 + ExecStart = [ ensureDeletions rulesScript ]; 294 + ExecStartPost = saveDeletionsScript; 295 + ExecReload = [ ensureDeletions rulesScript saveDeletionsScript ]; 296 + ExecStop = [ deletionsScriptVar cleanupDeletionsScript ]; 297 + StateDirectory = "nftables"; 158 298 }; 159 299 }; 160 300 };
+2 -3
nixos/tests/lxd/nftables.nix
··· 20 20 networking = { 21 21 firewall.enable = false; 22 22 nftables.enable = true; 23 - nftables.ruleset = '' 24 - table inet filter { 23 + nftables.tables."filter".family = "inet"; 24 + nftables.tables."filter".content = '' 25 25 chain incoming { 26 26 type filter hook input priority 0; 27 27 policy accept; ··· 36 36 type filter hook output priority 0; 37 37 policy accept; 38 38 } 39 - } 40 39 ''; 41 40 }; 42 41 };