1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7let
8
9 cfg = config.networking.firewall;
10
11 canonicalizePortList = ports: lib.unique (builtins.sort builtins.lessThan ports);
12
13 commonOptions = {
14 allowedTCPPorts = lib.mkOption {
15 type = lib.types.listOf lib.types.port;
16 default = [ ];
17 apply = canonicalizePortList;
18 example = [
19 22
20 80
21 ];
22 description = ''
23 List of TCP ports on which incoming connections are
24 accepted.
25 '';
26 };
27
28 allowedTCPPortRanges = lib.mkOption {
29 type = lib.types.listOf (lib.types.attrsOf lib.types.port);
30 default = [ ];
31 example = [
32 {
33 from = 8999;
34 to = 9003;
35 }
36 ];
37 description = ''
38 A range of TCP ports on which incoming connections are
39 accepted.
40 '';
41 };
42
43 allowedUDPPorts = lib.mkOption {
44 type = lib.types.listOf lib.types.port;
45 default = [ ];
46 apply = canonicalizePortList;
47 example = [ 53 ];
48 description = ''
49 List of open UDP ports.
50 '';
51 };
52
53 allowedUDPPortRanges = lib.mkOption {
54 type = lib.types.listOf (lib.types.attrsOf lib.types.port);
55 default = [ ];
56 example = [
57 {
58 from = 60000;
59 to = 61000;
60 }
61 ];
62 description = ''
63 Range of open UDP ports.
64 '';
65 };
66 };
67
68in
69
70{
71
72 options = {
73
74 networking.firewall = {
75 enable = lib.mkOption {
76 type = lib.types.bool;
77 default = true;
78 description = ''
79 Whether to enable the firewall. This is a simple stateful
80 firewall that blocks connection attempts to unauthorised TCP
81 or UDP ports on this machine.
82 '';
83 };
84
85 package = lib.mkOption {
86 type = lib.types.package;
87 default = if config.networking.nftables.enable then pkgs.nftables else pkgs.iptables;
88 defaultText = lib.literalExpression ''if config.networking.nftables.enable then "pkgs.nftables" else "pkgs.iptables"'';
89 example = lib.literalExpression "pkgs.iptables-legacy";
90 description = ''
91 The package to use for running the firewall service.
92 '';
93 };
94
95 logRefusedConnections = lib.mkOption {
96 type = lib.types.bool;
97 default = true;
98 description = ''
99 Whether to log rejected or dropped incoming connections.
100 Note: The logs are found in the kernel logs, i.e. dmesg
101 or journalctl -k.
102 '';
103 };
104
105 logRefusedPackets = lib.mkOption {
106 type = lib.types.bool;
107 default = false;
108 description = ''
109 Whether to log all rejected or dropped incoming packets.
110 This tends to give a lot of log messages, so it's mostly
111 useful for debugging.
112 Note: The logs are found in the kernel logs, i.e. dmesg
113 or journalctl -k.
114 '';
115 };
116
117 logRefusedUnicastsOnly = lib.mkOption {
118 type = lib.types.bool;
119 default = true;
120 description = ''
121 If {option}`networking.firewall.logRefusedPackets`
122 and this option are enabled, then only log packets
123 specifically directed at this machine, i.e., not broadcasts
124 or multicasts.
125 '';
126 };
127
128 rejectPackets = lib.mkOption {
129 type = lib.types.bool;
130 default = false;
131 description = ''
132 If set, refused packets are rejected rather than dropped
133 (ignored). This means that an ICMP "port unreachable" error
134 message is sent back to the client (or a TCP RST packet in
135 case of an existing connection). Rejecting packets makes
136 port scanning somewhat easier.
137 '';
138 };
139
140 trustedInterfaces = lib.mkOption {
141 type = lib.types.listOf lib.types.str;
142 default = [ ];
143 example = [ "enp0s2" ];
144 description = ''
145 Traffic coming in from these interfaces will be accepted
146 unconditionally. Traffic from the loopback (lo) interface
147 will always be accepted.
148 '';
149 };
150
151 allowPing = lib.mkOption {
152 type = lib.types.bool;
153 default = true;
154 description = ''
155 Whether to respond to incoming ICMPv4 echo requests
156 ("pings"). ICMPv6 pings are always allowed because the
157 larger address space of IPv6 makes network scanning much
158 less effective.
159 '';
160 };
161
162 pingLimit = lib.mkOption {
163 type = lib.types.nullOr (lib.types.separatedString " ");
164 default = null;
165 example = "--limit 1/minute --limit-burst 5";
166 description = ''
167 If pings are allowed, this allows setting rate limits on them.
168
169 For the iptables based firewall, it should be set like
170 "--limit 1/minute --limit-burst 5".
171
172 For the nftables based firewall, it should be set like
173 "2/second" or "1/minute burst 5 packets".
174 '';
175 };
176
177 checkReversePath = lib.mkOption {
178 type = lib.types.either lib.types.bool (
179 lib.types.enum [
180 "strict"
181 "loose"
182 ]
183 );
184 default = true;
185 defaultText = lib.literalMD "`true` except if the iptables based firewall is in use and the kernel lacks rpfilter support";
186 example = "loose";
187 description = ''
188 Performs a reverse path filter test on a packet. If a reply
189 to the packet would not be sent via the same interface that
190 the packet arrived on, it is refused.
191
192 If using asymmetric routing or other complicated routing, set
193 this option to loose mode or disable it and setup your own
194 counter-measures.
195
196 This option can be either true (or "strict"), "loose" (only
197 drop the packet if the source address is not reachable via any
198 interface) or false.
199 '';
200 };
201
202 logReversePathDrops = lib.mkOption {
203 type = lib.types.bool;
204 default = false;
205 description = ''
206 Logs dropped packets failing the reverse path filter test if
207 the option networking.firewall.checkReversePath is enabled.
208 '';
209 };
210
211 filterForward = lib.mkOption {
212 type = lib.types.bool;
213 default = false;
214 description = ''
215 Enable filtering in IP forwarding.
216
217 This option only works with the nftables based firewall.
218 '';
219 };
220
221 connectionTrackingModules = lib.mkOption {
222 type = lib.types.listOf lib.types.str;
223 default = [ ];
224 example = [
225 "ftp"
226 "irc"
227 "sane"
228 "sip"
229 "tftp"
230 "amanda"
231 "h323"
232 "netbios_sn"
233 "pptp"
234 "snmp"
235 ];
236 description = ''
237 List of connection-tracking helpers that are auto-loaded.
238 The complete list of possible values is given in the example.
239
240 As helpers can pose as a security risk, it is advised to
241 set this to an empty list and disable the setting
242 networking.firewall.autoLoadConntrackHelpers unless you
243 know what you are doing. Connection tracking is disabled
244 by default.
245
246 Loading of helpers is recommended to be done through the
247 CT target. More info:
248 <https://home.regit.org/netfilter-en/secure-use-of-helpers/>
249 '';
250 };
251
252 autoLoadConntrackHelpers = lib.mkOption {
253 type = lib.types.bool;
254 default = false;
255 description = ''
256 Whether to auto-load connection-tracking helpers.
257 See the description at networking.firewall.connectionTrackingModules
258
259 (needs kernel 3.5+)
260 '';
261 };
262
263 extraPackages = lib.mkOption {
264 type = lib.types.listOf lib.types.package;
265 default = [ ];
266 example = lib.literalExpression "[ pkgs.ipset ]";
267 description = ''
268 Additional packages to be included in the environment of the system
269 as well as the path of networking.firewall.extraCommands.
270 '';
271 };
272
273 interfaces = lib.mkOption {
274 default = { };
275 type = with lib.types; attrsOf (submodule [ { options = commonOptions; } ]);
276 description = ''
277 Interface-specific open ports.
278 '';
279 };
280
281 allInterfaces = lib.mkOption {
282 internal = true;
283 visible = false;
284 default = {
285 default = lib.mapAttrs (name: value: cfg.${name}) commonOptions;
286 } // cfg.interfaces;
287 type = with lib.types; attrsOf (submodule [ { options = commonOptions; } ]);
288 description = ''
289 All open ports.
290 '';
291 };
292 } // commonOptions;
293
294 };
295
296 config = lib.mkIf cfg.enable {
297
298 assertions = [
299 {
300 assertion = cfg.filterForward -> config.networking.nftables.enable;
301 message = "filterForward only works with the nftables based firewall";
302 }
303 {
304 assertion =
305 cfg.autoLoadConntrackHelpers -> lib.versionOlder config.boot.kernelPackages.kernel.version "6";
306 message = "conntrack helper autoloading has been removed from kernel 6.0 and newer";
307 }
308 ];
309
310 networking.firewall.trustedInterfaces = [ "lo" ];
311
312 environment.systemPackages = [
313 cfg.package
314 pkgs.nixos-firewall-tool
315 ] ++ cfg.extraPackages;
316
317 boot.kernelModules =
318 (lib.optional cfg.autoLoadConntrackHelpers "nf_conntrack")
319 ++ map (x: "nf_conntrack_${x}") cfg.connectionTrackingModules;
320 boot.extraModprobeConfig = lib.optionalString cfg.autoLoadConntrackHelpers ''
321 options nf_conntrack nf_conntrack_helper=1
322 '';
323
324 };
325
326}