at master 6.3 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7let 8 9 cfg = config.networking.firewall; 10 11 ifaceSet = lib.concatStringsSep ", " (map (x: ''"${x}"'') cfg.trustedInterfaces); 12 13 portsToNftSet = 14 ports: portRanges: 15 lib.concatStringsSep ", " ( 16 map (x: toString x) ports ++ map (x: "${toString x.from}-${toString x.to}") portRanges 17 ); 18 19in 20 21{ 22 23 options = { 24 25 networking.firewall = { 26 extraInputRules = lib.mkOption { 27 type = lib.types.lines; 28 default = ""; 29 example = "ip6 saddr { fc00::/7, fe80::/10 } tcp dport 24800 accept"; 30 description = '' 31 Additional nftables rules to be appended to the input-allow 32 chain. 33 34 This option only works with the nftables based firewall. 35 ''; 36 }; 37 38 extraForwardRules = lib.mkOption { 39 type = lib.types.lines; 40 default = ""; 41 example = "iifname wg0 accept"; 42 description = '' 43 Additional nftables rules to be appended to the forward-allow 44 chain. 45 46 This option only works with the nftables based firewall. 47 ''; 48 }; 49 50 extraReversePathFilterRules = lib.mkOption { 51 type = lib.types.lines; 52 default = ""; 53 example = "fib daddr . mark . iif type local accept"; 54 description = '' 55 Additional nftables rules to be appended to the rpfilter-allow 56 chain. 57 58 This option only works with the nftables based firewall. 59 ''; 60 }; 61 }; 62 63 }; 64 65 config = lib.mkIf (cfg.enable && config.networking.nftables.enable) { 66 67 assertions = [ 68 { 69 assertion = cfg.extraCommands == ""; 70 message = "extraCommands is incompatible with the nftables based firewall: ${cfg.extraCommands}"; 71 } 72 { 73 assertion = cfg.extraStopCommands == ""; 74 message = "extraStopCommands is incompatible with the nftables based firewall: ${cfg.extraStopCommands}"; 75 } 76 { 77 assertion = cfg.pingLimit == null || !(lib.hasPrefix "--" cfg.pingLimit); 78 message = "nftables syntax like \"2/second\" should be used in networking.firewall.pingLimit"; 79 } 80 { 81 assertion = config.networking.nftables.rulesetFile == null; 82 message = "networking.nftables.rulesetFile conflicts with the firewall"; 83 } 84 ]; 85 86 networking.nftables.tables."nixos-fw".family = "inet"; 87 networking.nftables.tables."nixos-fw".content = '' 88 set temp-ports { 89 comment "Temporarily opened ports" 90 type inet_proto . inet_service 91 flags interval 92 auto-merge 93 } 94 95 ${lib.optionalString (cfg.checkReversePath != false) '' 96 chain rpfilter { 97 type filter hook prerouting priority mangle + 10; policy drop; 98 99 meta nfproto ipv4 udp sport . udp dport { 67 . 68, 68 . 67 } accept comment "DHCPv4 client/server" 100 fib saddr . mark ${lib.optionalString (cfg.checkReversePath != "loose") ". iif"} oif exists accept 101 102 jump rpfilter-allow 103 104 ${lib.optionalString cfg.logReversePathDrops '' 105 log level info prefix "rpfilter drop: " 106 ''} 107 108 } 109 ''} 110 111 chain rpfilter-allow { 112 ${cfg.extraReversePathFilterRules} 113 } 114 115 chain input { 116 type filter hook input priority filter; policy drop; 117 118 ${lib.optionalString ( 119 ifaceSet != "" 120 ) ''iifname { ${ifaceSet} } accept comment "trusted interfaces"''} 121 122 # Some ICMPv6 types like NDP is untracked 123 ct state vmap { 124 invalid : drop, 125 established : accept, 126 related : accept, 127 new : jump input-allow, 128 untracked: jump input-allow, 129 } 130 131 ${lib.optionalString cfg.logRefusedConnections '' 132 tcp flags syn / fin,syn,rst,ack log level info prefix "refused connection: " 133 ''} 134 ${lib.optionalString (cfg.logRefusedPackets && !cfg.logRefusedUnicastsOnly) '' 135 pkttype broadcast log level info prefix "refused broadcast: " 136 pkttype multicast log level info prefix "refused multicast: " 137 ''} 138 ${lib.optionalString cfg.logRefusedPackets '' 139 pkttype host log level info prefix "refused packet: " 140 ''} 141 142 ${lib.optionalString cfg.rejectPackets '' 143 meta l4proto tcp reject with tcp reset 144 reject 145 ''} 146 147 } 148 149 chain input-allow { 150 151 ${lib.concatStrings ( 152 lib.mapAttrsToList ( 153 iface: cfg: 154 let 155 ifaceExpr = lib.optionalString (iface != "default") "iifname ${iface}"; 156 tcpSet = portsToNftSet cfg.allowedTCPPorts cfg.allowedTCPPortRanges; 157 udpSet = portsToNftSet cfg.allowedUDPPorts cfg.allowedUDPPortRanges; 158 in 159 '' 160 ${lib.optionalString (tcpSet != "") "${ifaceExpr} tcp dport { ${tcpSet} } accept"} 161 ${lib.optionalString (udpSet != "") "${ifaceExpr} udp dport { ${udpSet} } accept"} 162 '' 163 ) cfg.allInterfaces 164 )} 165 166 meta l4proto . th dport @temp-ports accept 167 168 ${lib.optionalString cfg.allowPing '' 169 icmp type echo-request ${ 170 lib.optionalString (cfg.pingLimit != null) "limit rate ${cfg.pingLimit}" 171 } accept comment "allow ping" 172 ''} 173 174 icmpv6 type != { nd-redirect, 139 } accept comment "Accept all ICMPv6 messages except redirects and node information queries (type 139). See RFC 4890, section 4.4." 175 ip6 daddr fe80::/64 udp dport 546 accept comment "DHCPv6 client" 176 177 ${cfg.extraInputRules} 178 179 } 180 181 ${lib.optionalString cfg.filterForward '' 182 chain forward { 183 type filter hook forward priority filter; policy drop; 184 185 ct state vmap { 186 invalid : drop, 187 established : accept, 188 related : accept, 189 new : jump forward-allow, 190 untracked : jump forward-allow, 191 } 192 193 } 194 195 chain forward-allow { 196 197 icmpv6 type != { router-renumbering, 139 } accept comment "Accept all ICMPv6 messages except renumbering and node information queries (type 139). See RFC 4890, section 4.3." 198 199 ct status dnat accept comment "allow port forward" 200 201 ${cfg.extraForwardRules} 202 203 } 204 ''} 205 ''; 206 207 }; 208 209}