1 { config, lib, pkgs, ... }:
4 cfg = config.networking.firewall;
6 ifaceSet = lib.concatStringsSep ", " (
7 map (x: ''"${x}"'') cfg.trustedInterfaces
10 portsToNftSet = ports: portRanges: lib.concatStringsSep ", " (
11 map (x: toString x) ports
12 ++ map (x: "${toString x.from}-${toString x.to}") portRanges
21 networking.firewall = {
22 extraInputRules = lib.mkOption {
23 type = lib.types.lines;
25 example = "ip6 saddr { fc00::/7, fe80::/10 } tcp dport 24800 accept";
27 Additional nftables rules to be appended to the input-allow
30 This option only works with the nftables based firewall.
34 extraForwardRules = lib.mkOption {
35 type = lib.types.lines;
37 example = "iifname wg0 accept";
39 Additional nftables rules to be appended to the forward-allow
42 This option only works with the nftables based firewall.
46 extraReversePathFilterRules = lib.mkOption {
47 type = lib.types.lines;
49 example = "fib daddr . mark . iif type local accept";
51 Additional nftables rules to be appended to the rpfilter-allow
54 This option only works with the nftables based firewall.
61 config = lib.mkIf (cfg.enable && config.networking.nftables.enable) {
65 assertion = cfg.extraCommands == "";
66 message = "extraCommands is incompatible with the nftables based firewall: ${cfg.extraCommands}";
69 assertion = cfg.extraStopCommands == "";
70 message = "extraStopCommands is incompatible with the nftables based firewall: ${cfg.extraStopCommands}";
73 assertion = cfg.pingLimit == null || !(lib.hasPrefix "--" cfg.pingLimit);
74 message = "nftables syntax like \"2/second\" should be used in networking.firewall.pingLimit";
77 assertion = config.networking.nftables.rulesetFile == null;
78 message = "networking.nftables.rulesetFile conflicts with the firewall";
82 networking.nftables.tables."nixos-fw".family = "inet";
83 networking.nftables.tables."nixos-fw".content = ''
84 ${lib.optionalString (cfg.checkReversePath != false) ''
86 type filter hook prerouting priority mangle + 10; policy drop;
88 meta nfproto ipv4 udp sport . udp dport { 67 . 68, 68 . 67 } accept comment "DHCPv4 client/server"
89 fib saddr . mark ${lib.optionalString (cfg.checkReversePath != "loose") ". iif"} oif exists accept
93 ${lib.optionalString cfg.logReversePathDrops ''
94 log level info prefix "rpfilter drop: "
100 chain rpfilter-allow {
101 ${cfg.extraReversePathFilterRules}
105 type filter hook input priority filter; policy drop;
107 ${lib.optionalString (ifaceSet != "") ''iifname { ${ifaceSet} } accept comment "trusted interfaces"''}
109 # Some ICMPv6 types like NDP is untracked
112 established : accept,
114 new : jump input-allow,
115 untracked: jump input-allow,
118 ${lib.optionalString cfg.logRefusedConnections ''
119 tcp flags syn / fin,syn,rst,ack log level info prefix "refused connection: "
121 ${lib.optionalString (cfg.logRefusedPackets && !cfg.logRefusedUnicastsOnly) ''
122 pkttype broadcast log level info prefix "refused broadcast: "
123 pkttype multicast log level info prefix "refused multicast: "
125 ${lib.optionalString cfg.logRefusedPackets ''
126 pkttype host log level info prefix "refused packet: "
129 ${lib.optionalString cfg.rejectPackets ''
130 meta l4proto tcp reject with tcp reset
138 ${lib.concatStrings (lib.mapAttrsToList (iface: cfg:
140 ifaceExpr = lib.optionalString (iface != "default") "iifname ${iface}";
141 tcpSet = portsToNftSet cfg.allowedTCPPorts cfg.allowedTCPPortRanges;
142 udpSet = portsToNftSet cfg.allowedUDPPorts cfg.allowedUDPPortRanges;
145 ${lib.optionalString (tcpSet != "") "${ifaceExpr} tcp dport { ${tcpSet} } accept"}
146 ${lib.optionalString (udpSet != "") "${ifaceExpr} udp dport { ${udpSet} } accept"}
148 ) cfg.allInterfaces)}
150 ${lib.optionalString cfg.allowPing ''
151 icmp type echo-request ${lib.optionalString (cfg.pingLimit != null) "limit rate ${cfg.pingLimit}"} accept comment "allow ping"
154 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."
155 ip6 daddr fe80::/64 udp dport 546 accept comment "DHCPv6 client"
157 ${cfg.extraInputRules}
161 ${lib.optionalString cfg.filterForward ''
163 type filter hook forward priority filter; policy drop;
167 established : accept,
169 new : jump forward-allow,
170 untracked : jump forward-allow,
175 chain forward-allow {
177 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."
179 ct status dnat accept comment "allow port forward"
181 ${cfg.extraForwardRules}