2011-07-05 13:51:46 +01:00
|
|
|
|
/* This module enables a simple firewall.
|
|
|
|
|
|
|
|
|
|
The firewall can be customised in arbitrary ways by setting
|
|
|
|
|
‘networking.firewall.extraCommands’. For modularity, the firewall
|
|
|
|
|
uses several chains:
|
|
|
|
|
|
2017-01-18 16:18:11 +00:00
|
|
|
|
- ‘nixos-fw’ is the main chain for input packet processing.
|
|
|
|
|
|
|
|
|
|
- ‘nixos-fw-accept’ is called for accepted packets. If you want
|
|
|
|
|
additional logging, or want to reject certain packets anyway, you
|
|
|
|
|
can insert rules at the start of this chain.
|
2011-09-14 19:20:50 +01:00
|
|
|
|
|
2011-07-05 13:51:46 +01:00
|
|
|
|
- ‘nixos-fw-log-refuse’ and ‘nixos-fw-refuse’ are called for
|
|
|
|
|
refused packets. (The former jumps to the latter after logging
|
|
|
|
|
the packet.) If you want additional logging, or want to accept
|
|
|
|
|
certain packets anyway, you can insert rules at the start of
|
2017-01-18 16:18:11 +00:00
|
|
|
|
this chain.
|
2011-07-05 13:51:46 +01:00
|
|
|
|
|
2017-01-18 16:18:11 +00:00
|
|
|
|
- ‘nixos-fw-rpfilter’ is used as the main chain in the raw table,
|
|
|
|
|
called from the built-in ‘PREROUTING’ chain. If the kernel
|
|
|
|
|
supports it and `cfg.checkReversePath` is set this chain will
|
|
|
|
|
perform a reverse path filter test.
|
|
|
|
|
|
|
|
|
|
- ‘nixos-drop’ is used while reloading the firewall in order to drop
|
|
|
|
|
all traffic. Since reloading isn't implemented in an atomic way
|
|
|
|
|
this'll prevent any traffic from leaking through while reloading
|
|
|
|
|
the firewall. However, if the reloading fails, the ‘firewall-stop’
|
|
|
|
|
script will be called which in return will effectively disable the
|
|
|
|
|
complete firewall (in the default configuration).
|
2011-07-05 13:51:46 +01:00
|
|
|
|
|
|
|
|
|
*/
|
2011-09-14 19:20:50 +01:00
|
|
|
|
|
2014-04-14 15:26:48 +01:00
|
|
|
|
{ config, lib, pkgs, ... }:
|
2009-09-29 15:21:56 +01:00
|
|
|
|
|
2014-04-14 15:26:48 +01:00
|
|
|
|
with lib;
|
2009-07-25 00:12:52 +01:00
|
|
|
|
|
|
|
|
|
let
|
|
|
|
|
|
2009-09-29 15:21:56 +01:00
|
|
|
|
cfg = config.networking.firewall;
|
|
|
|
|
|
2017-02-06 21:43:23 +00:00
|
|
|
|
inherit (config.boot.kernelPackages) kernel;
|
2017-01-18 16:18:11 +00:00
|
|
|
|
|
2017-02-06 21:43:23 +00:00
|
|
|
|
kernelHasRPFilter = ((kernel.config.isEnabled or (x: false)) "IP_NF_MATCH_RPFILTER") || (kernel.features.netfilterRPFilter or false);
|
2017-01-18 16:18:11 +00:00
|
|
|
|
|
2019-12-15 04:10:17 +00:00
|
|
|
|
helpers = import ./helpers.nix { inherit config lib; };
|
2011-07-05 13:51:46 +01:00
|
|
|
|
|
2014-09-16 09:15:10 +01:00
|
|
|
|
writeShScript = name: text: let dir = pkgs.writeScriptBin name ''
|
2018-03-01 19:38:53 +00:00
|
|
|
|
#! ${pkgs.runtimeShell} -e
|
2014-09-16 09:15:10 +01:00
|
|
|
|
${text}
|
|
|
|
|
''; in "${dir}/bin/${name}";
|
|
|
|
|
|
2019-08-13 22:52:01 +01:00
|
|
|
|
defaultInterface = { default = mapAttrs (name: value: cfg.${name}) commonOptions; };
|
2018-11-23 00:24:23 +00:00
|
|
|
|
allInterfaces = defaultInterface // cfg.interfaces;
|
2018-11-21 02:29:33 +00:00
|
|
|
|
|
2014-09-16 09:15:10 +01:00
|
|
|
|
startScript = writeShScript "firewall-start" ''
|
|
|
|
|
${helpers}
|
|
|
|
|
|
|
|
|
|
# Flush the old firewall rules. !!! Ideally, updating the
|
|
|
|
|
# firewall would be atomic. Apparently that's possible
|
|
|
|
|
# with iptables-restore.
|
|
|
|
|
ip46tables -D INPUT -j nixos-fw 2> /dev/null || true
|
2017-01-18 16:18:11 +00:00
|
|
|
|
for chain in nixos-fw nixos-fw-accept nixos-fw-log-refuse nixos-fw-refuse; do
|
2014-09-16 09:15:10 +01:00
|
|
|
|
ip46tables -F "$chain" 2> /dev/null || true
|
|
|
|
|
ip46tables -X "$chain" 2> /dev/null || true
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# The "nixos-fw-accept" chain just accepts packets.
|
|
|
|
|
ip46tables -N nixos-fw-accept
|
|
|
|
|
ip46tables -A nixos-fw-accept -j ACCEPT
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# The "nixos-fw-refuse" chain rejects or drops packets.
|
|
|
|
|
ip46tables -N nixos-fw-refuse
|
|
|
|
|
|
|
|
|
|
${if cfg.rejectPackets then ''
|
|
|
|
|
# Send a reset for existing TCP connections that we've
|
|
|
|
|
# somehow forgotten about. Send ICMP "port unreachable"
|
|
|
|
|
# for everything else.
|
|
|
|
|
ip46tables -A nixos-fw-refuse -p tcp ! --syn -j REJECT --reject-with tcp-reset
|
|
|
|
|
ip46tables -A nixos-fw-refuse -j REJECT
|
|
|
|
|
'' else ''
|
|
|
|
|
ip46tables -A nixos-fw-refuse -j DROP
|
|
|
|
|
''}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# The "nixos-fw-log-refuse" chain performs logging, then
|
|
|
|
|
# jumps to the "nixos-fw-refuse" chain.
|
|
|
|
|
ip46tables -N nixos-fw-log-refuse
|
|
|
|
|
|
|
|
|
|
${optionalString cfg.logRefusedConnections ''
|
2017-10-11 19:12:58 +01:00
|
|
|
|
ip46tables -A nixos-fw-log-refuse -p tcp --syn -j LOG --log-level info --log-prefix "refused connection: "
|
2014-09-16 09:15:10 +01:00
|
|
|
|
''}
|
|
|
|
|
${optionalString (cfg.logRefusedPackets && !cfg.logRefusedUnicastsOnly) ''
|
|
|
|
|
ip46tables -A nixos-fw-log-refuse -m pkttype --pkt-type broadcast \
|
2017-10-11 19:12:58 +01:00
|
|
|
|
-j LOG --log-level info --log-prefix "refused broadcast: "
|
2014-09-16 09:15:10 +01:00
|
|
|
|
ip46tables -A nixos-fw-log-refuse -m pkttype --pkt-type multicast \
|
2017-10-11 19:12:58 +01:00
|
|
|
|
-j LOG --log-level info --log-prefix "refused multicast: "
|
2014-09-16 09:15:10 +01:00
|
|
|
|
''}
|
|
|
|
|
ip46tables -A nixos-fw-log-refuse -m pkttype ! --pkt-type unicast -j nixos-fw-refuse
|
|
|
|
|
${optionalString cfg.logRefusedPackets ''
|
|
|
|
|
ip46tables -A nixos-fw-log-refuse \
|
2017-10-11 19:12:58 +01:00
|
|
|
|
-j LOG --log-level info --log-prefix "refused packet: "
|
2014-09-16 09:15:10 +01:00
|
|
|
|
''}
|
|
|
|
|
ip46tables -A nixos-fw-log-refuse -j nixos-fw-refuse
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# The "nixos-fw" chain does the actual work.
|
|
|
|
|
ip46tables -N nixos-fw
|
|
|
|
|
|
2017-05-29 16:24:27 +01:00
|
|
|
|
# Clean up rpfilter rules
|
|
|
|
|
ip46tables -t raw -D PREROUTING -j nixos-fw-rpfilter 2> /dev/null || true
|
|
|
|
|
ip46tables -t raw -F nixos-fw-rpfilter 2> /dev/null || true
|
|
|
|
|
ip46tables -t raw -X nixos-fw-rpfilter 2> /dev/null || true
|
|
|
|
|
|
2016-10-08 13:26:52 +01:00
|
|
|
|
${optionalString (kernelHasRPFilter && (cfg.checkReversePath != false)) ''
|
2017-05-29 16:24:27 +01:00
|
|
|
|
# Perform a reverse-path test to refuse spoofers
|
|
|
|
|
# For now, we just drop, as the raw table doesn't have a log-refuse yet
|
2016-07-31 12:49:24 +01:00
|
|
|
|
ip46tables -t raw -N nixos-fw-rpfilter 2> /dev/null || true
|
2018-08-29 19:50:53 +01:00
|
|
|
|
ip46tables -t raw -A nixos-fw-rpfilter -m rpfilter --validmark ${optionalString (cfg.checkReversePath == "loose") "--loose"} -j RETURN
|
2016-07-31 12:49:24 +01:00
|
|
|
|
|
2017-11-19 21:11:32 +00:00
|
|
|
|
# Allows this host to act as a DHCP4 client without first having to use APIPA
|
|
|
|
|
iptables -t raw -A nixos-fw-rpfilter -p udp --sport 67 --dport 68 -j RETURN
|
|
|
|
|
|
2016-07-31 12:49:24 +01:00
|
|
|
|
# Allows this host to act as a DHCPv4 server
|
|
|
|
|
iptables -t raw -A nixos-fw-rpfilter -s 0.0.0.0 -d 255.255.255.255 -p udp --sport 68 --dport 67 -j RETURN
|
|
|
|
|
|
|
|
|
|
${optionalString cfg.logReversePathDrops ''
|
|
|
|
|
ip46tables -t raw -A nixos-fw-rpfilter -j LOG --log-level info --log-prefix "rpfilter drop: "
|
|
|
|
|
''}
|
|
|
|
|
ip46tables -t raw -A nixos-fw-rpfilter -j DROP
|
|
|
|
|
|
|
|
|
|
ip46tables -t raw -A PREROUTING -j nixos-fw-rpfilter
|
2014-09-16 09:15:10 +01:00
|
|
|
|
''}
|
|
|
|
|
|
|
|
|
|
# Accept all traffic on the trusted interfaces.
|
|
|
|
|
${flip concatMapStrings cfg.trustedInterfaces (iface: ''
|
|
|
|
|
ip46tables -A nixos-fw -i ${iface} -j nixos-fw-accept
|
|
|
|
|
'')}
|
|
|
|
|
|
|
|
|
|
# Accept packets from established or related connections.
|
|
|
|
|
ip46tables -A nixos-fw -m conntrack --ctstate ESTABLISHED,RELATED -j nixos-fw-accept
|
|
|
|
|
|
|
|
|
|
# Accept connections to the allowed TCP ports.
|
2018-05-29 20:10:25 +01:00
|
|
|
|
${concatStrings (mapAttrsToList (iface: cfg:
|
|
|
|
|
concatMapStrings (port:
|
2014-09-16 09:15:10 +01:00
|
|
|
|
''
|
2018-11-23 00:24:23 +00:00
|
|
|
|
ip46tables -A nixos-fw -p tcp --dport ${toString port} -j nixos-fw-accept ${optionalString (iface != "default") "-i ${iface}"}
|
2014-09-16 09:15:10 +01:00
|
|
|
|
''
|
|
|
|
|
) cfg.allowedTCPPorts
|
2018-11-21 02:29:33 +00:00
|
|
|
|
) allInterfaces)}
|
2014-09-16 09:15:10 +01:00
|
|
|
|
|
|
|
|
|
# Accept connections to the allowed TCP port ranges.
|
2018-05-29 20:10:25 +01:00
|
|
|
|
${concatStrings (mapAttrsToList (iface: cfg:
|
|
|
|
|
concatMapStrings (rangeAttr:
|
2014-09-16 09:15:10 +01:00
|
|
|
|
let range = toString rangeAttr.from + ":" + toString rangeAttr.to; in
|
|
|
|
|
''
|
2018-11-23 00:24:23 +00:00
|
|
|
|
ip46tables -A nixos-fw -p tcp --dport ${range} -j nixos-fw-accept ${optionalString (iface != "default") "-i ${iface}"}
|
2014-09-16 09:15:10 +01:00
|
|
|
|
''
|
|
|
|
|
) cfg.allowedTCPPortRanges
|
2018-11-21 02:29:33 +00:00
|
|
|
|
) allInterfaces)}
|
2014-09-16 09:15:10 +01:00
|
|
|
|
|
|
|
|
|
# Accept packets on the allowed UDP ports.
|
2018-05-29 20:10:25 +01:00
|
|
|
|
${concatStrings (mapAttrsToList (iface: cfg:
|
|
|
|
|
concatMapStrings (port:
|
2014-09-16 09:15:10 +01:00
|
|
|
|
''
|
2018-11-23 00:24:23 +00:00
|
|
|
|
ip46tables -A nixos-fw -p udp --dport ${toString port} -j nixos-fw-accept ${optionalString (iface != "default") "-i ${iface}"}
|
2014-09-16 09:15:10 +01:00
|
|
|
|
''
|
|
|
|
|
) cfg.allowedUDPPorts
|
2018-11-21 02:29:33 +00:00
|
|
|
|
) allInterfaces)}
|
2014-09-16 09:15:10 +01:00
|
|
|
|
|
|
|
|
|
# Accept packets on the allowed UDP port ranges.
|
2018-05-29 20:10:25 +01:00
|
|
|
|
${concatStrings (mapAttrsToList (iface: cfg:
|
|
|
|
|
concatMapStrings (rangeAttr:
|
2014-09-16 09:15:10 +01:00
|
|
|
|
let range = toString rangeAttr.from + ":" + toString rangeAttr.to; in
|
|
|
|
|
''
|
2018-11-23 00:24:23 +00:00
|
|
|
|
ip46tables -A nixos-fw -p udp --dport ${range} -j nixos-fw-accept ${optionalString (iface != "default") "-i ${iface}"}
|
2014-09-16 09:15:10 +01:00
|
|
|
|
''
|
|
|
|
|
) cfg.allowedUDPPortRanges
|
2018-11-21 02:29:33 +00:00
|
|
|
|
) allInterfaces)}
|
2014-09-16 09:15:10 +01:00
|
|
|
|
|
|
|
|
|
# Optionally respond to ICMPv4 pings.
|
|
|
|
|
${optionalString cfg.allowPing ''
|
|
|
|
|
iptables -w -A nixos-fw -p icmp --icmp-type echo-request ${optionalString (cfg.pingLimit != null)
|
|
|
|
|
"-m limit ${cfg.pingLimit} "
|
|
|
|
|
}-j nixos-fw-accept
|
|
|
|
|
''}
|
|
|
|
|
|
|
|
|
|
${optionalString config.networking.enableIPv6 ''
|
2017-01-14 16:01:19 +00:00
|
|
|
|
# Accept all ICMPv6 messages except redirects and node
|
|
|
|
|
# information queries (type 139). See RFC 4890, section
|
|
|
|
|
# 4.4.
|
2014-09-16 09:15:10 +01:00
|
|
|
|
ip6tables -A nixos-fw -p icmpv6 --icmpv6-type redirect -j DROP
|
|
|
|
|
ip6tables -A nixos-fw -p icmpv6 --icmpv6-type 139 -j DROP
|
|
|
|
|
ip6tables -A nixos-fw -p icmpv6 -j nixos-fw-accept
|
2017-01-14 16:01:19 +00:00
|
|
|
|
|
|
|
|
|
# Allow this host to act as a DHCPv6 client
|
|
|
|
|
ip6tables -A nixos-fw -d fe80::/64 -p udp --dport 546 -j nixos-fw-accept
|
2014-09-16 09:15:10 +01:00
|
|
|
|
''}
|
|
|
|
|
|
|
|
|
|
${cfg.extraCommands}
|
|
|
|
|
|
|
|
|
|
# Reject/drop everything else.
|
|
|
|
|
ip46tables -A nixos-fw -j nixos-fw-log-refuse
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Enable the firewall.
|
|
|
|
|
ip46tables -A INPUT -j nixos-fw
|
|
|
|
|
'';
|
|
|
|
|
|
|
|
|
|
stopScript = writeShScript "firewall-stop" ''
|
|
|
|
|
${helpers}
|
|
|
|
|
|
|
|
|
|
# Clean up in case reload fails
|
|
|
|
|
ip46tables -D INPUT -j nixos-drop 2>/dev/null || true
|
|
|
|
|
|
|
|
|
|
# Clean up after added ruleset
|
|
|
|
|
ip46tables -D INPUT -j nixos-fw 2>/dev/null || true
|
|
|
|
|
|
2016-10-08 13:26:52 +01:00
|
|
|
|
${optionalString (kernelHasRPFilter && (cfg.checkReversePath != false)) ''
|
2016-07-31 12:49:24 +01:00
|
|
|
|
ip46tables -t raw -D PREROUTING -j nixos-fw-rpfilter 2>/dev/null || true
|
2014-11-14 07:07:18 +00:00
|
|
|
|
''}
|
|
|
|
|
|
2014-09-16 09:15:10 +01:00
|
|
|
|
${cfg.extraStopCommands}
|
|
|
|
|
'';
|
|
|
|
|
|
|
|
|
|
reloadScript = writeShScript "firewall-reload" ''
|
|
|
|
|
${helpers}
|
|
|
|
|
|
|
|
|
|
# Create a unique drop rule
|
|
|
|
|
ip46tables -D INPUT -j nixos-drop 2>/dev/null || true
|
|
|
|
|
ip46tables -F nixos-drop 2>/dev/null || true
|
|
|
|
|
ip46tables -X nixos-drop 2>/dev/null || true
|
|
|
|
|
ip46tables -N nixos-drop
|
|
|
|
|
ip46tables -A nixos-drop -j DROP
|
|
|
|
|
|
|
|
|
|
# Don't allow traffic to leak out until the script has completed
|
|
|
|
|
ip46tables -A INPUT -j nixos-drop
|
2018-04-17 10:40:05 +01:00
|
|
|
|
|
|
|
|
|
${cfg.extraStopCommands}
|
|
|
|
|
|
2014-09-16 09:15:10 +01:00
|
|
|
|
if ${startScript}; then
|
|
|
|
|
ip46tables -D INPUT -j nixos-drop 2>/dev/null || true
|
|
|
|
|
else
|
|
|
|
|
echo "Failed to reload firewall... Stopping"
|
|
|
|
|
${stopScript}
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
'';
|
|
|
|
|
|
2019-03-09 18:59:01 +00:00
|
|
|
|
canonicalizePortList =
|
|
|
|
|
ports: lib.unique (builtins.sort builtins.lessThan ports);
|
|
|
|
|
|
2018-05-29 20:10:25 +01:00
|
|
|
|
commonOptions = {
|
|
|
|
|
allowedTCPPorts = mkOption {
|
2019-03-09 18:45:11 +00:00
|
|
|
|
type = types.listOf types.port;
|
2017-01-18 16:18:11 +00:00
|
|
|
|
default = [ ];
|
2019-03-09 18:59:01 +00:00
|
|
|
|
apply = canonicalizePortList;
|
2017-01-18 16:18:11 +00:00
|
|
|
|
example = [ 22 80 ];
|
2009-07-25 00:12:52 +01:00
|
|
|
|
description =
|
2022-07-28 22:19:15 +01:00
|
|
|
|
lib.mdDoc ''
|
2009-07-25 00:12:52 +01:00
|
|
|
|
List of TCP ports on which incoming connections are
|
|
|
|
|
accepted.
|
|
|
|
|
'';
|
|
|
|
|
};
|
2011-09-14 19:20:50 +01:00
|
|
|
|
|
2018-05-29 20:10:25 +01:00
|
|
|
|
allowedTCPPortRanges = mkOption {
|
2019-03-09 18:45:11 +00:00
|
|
|
|
type = types.listOf (types.attrsOf types.port);
|
2017-01-18 16:18:11 +00:00
|
|
|
|
default = [ ];
|
|
|
|
|
example = [ { from = 8999; to = 9003; } ];
|
2014-02-19 11:19:19 +00:00
|
|
|
|
description =
|
2022-07-28 22:19:15 +01:00
|
|
|
|
lib.mdDoc ''
|
2014-02-19 11:19:19 +00:00
|
|
|
|
A range of TCP ports on which incoming connections are
|
|
|
|
|
accepted.
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
2018-05-29 20:10:25 +01:00
|
|
|
|
allowedUDPPorts = mkOption {
|
2019-03-09 18:45:11 +00:00
|
|
|
|
type = types.listOf types.port;
|
2017-01-18 16:18:11 +00:00
|
|
|
|
default = [ ];
|
2019-03-09 18:59:01 +00:00
|
|
|
|
apply = canonicalizePortList;
|
2017-01-18 16:18:11 +00:00
|
|
|
|
example = [ 53 ];
|
2011-03-09 16:37:16 +00:00
|
|
|
|
description =
|
2022-07-28 22:19:15 +01:00
|
|
|
|
lib.mdDoc ''
|
2011-03-09 16:37:16 +00:00
|
|
|
|
List of open UDP ports.
|
|
|
|
|
'';
|
|
|
|
|
};
|
2011-09-14 19:20:50 +01:00
|
|
|
|
|
2018-05-29 20:10:25 +01:00
|
|
|
|
allowedUDPPortRanges = mkOption {
|
2019-03-09 18:45:11 +00:00
|
|
|
|
type = types.listOf (types.attrsOf types.port);
|
2017-01-18 16:18:11 +00:00
|
|
|
|
default = [ ];
|
|
|
|
|
example = [ { from = 60000; to = 61000; } ];
|
2014-02-19 11:19:19 +00:00
|
|
|
|
description =
|
2022-07-28 22:19:15 +01:00
|
|
|
|
lib.mdDoc ''
|
2014-02-19 11:19:19 +00:00
|
|
|
|
Range of open UDP ports.
|
|
|
|
|
'';
|
|
|
|
|
};
|
2018-05-29 20:10:25 +01:00
|
|
|
|
};
|
2014-02-19 11:19:19 +00:00
|
|
|
|
|
2018-05-29 20:10:25 +01:00
|
|
|
|
in
|
2012-10-12 12:16:33 +01:00
|
|
|
|
|
2018-05-29 20:10:25 +01:00
|
|
|
|
{
|
2012-10-12 12:16:33 +01:00
|
|
|
|
|
2018-05-29 20:10:25 +01:00
|
|
|
|
###### interface
|
2011-09-14 19:20:50 +01:00
|
|
|
|
|
2018-05-29 20:10:25 +01:00
|
|
|
|
options = {
|
2015-07-27 00:32:59 +01:00
|
|
|
|
|
2018-05-29 20:10:25 +01:00
|
|
|
|
networking.firewall = {
|
|
|
|
|
enable = mkOption {
|
|
|
|
|
type = types.bool;
|
|
|
|
|
default = true;
|
|
|
|
|
description =
|
2022-07-28 22:19:15 +01:00
|
|
|
|
lib.mdDoc ''
|
2018-05-29 20:10:25 +01:00
|
|
|
|
Whether to enable the firewall. This is a simple stateful
|
|
|
|
|
firewall that blocks connection attempts to unauthorised TCP
|
|
|
|
|
or UDP ports on this machine. It does not affect packet
|
|
|
|
|
forwarding.
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
2019-08-19 18:21:24 +01:00
|
|
|
|
package = mkOption {
|
|
|
|
|
type = types.package;
|
|
|
|
|
default = pkgs.iptables;
|
2021-10-03 17:06:03 +01:00
|
|
|
|
defaultText = literalExpression "pkgs.iptables";
|
2022-02-08 09:31:09 +00:00
|
|
|
|
example = literalExpression "pkgs.iptables-legacy";
|
2019-08-19 18:21:24 +01:00
|
|
|
|
description =
|
2022-07-28 22:19:15 +01:00
|
|
|
|
lib.mdDoc ''
|
2019-08-19 18:21:24 +01:00
|
|
|
|
The iptables package to use for running the firewall service."
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
2018-05-29 20:10:25 +01:00
|
|
|
|
logRefusedConnections = mkOption {
|
|
|
|
|
type = types.bool;
|
|
|
|
|
default = true;
|
|
|
|
|
description =
|
2022-07-28 22:19:15 +01:00
|
|
|
|
lib.mdDoc ''
|
2018-05-29 20:10:25 +01:00
|
|
|
|
Whether to log rejected or dropped incoming connections.
|
2021-07-04 14:02:13 +01:00
|
|
|
|
Note: The logs are found in the kernel logs, i.e. dmesg
|
|
|
|
|
or journalctl -k.
|
2018-05-29 20:10:25 +01:00
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
logRefusedPackets = mkOption {
|
|
|
|
|
type = types.bool;
|
|
|
|
|
default = false;
|
|
|
|
|
description =
|
2022-07-28 22:19:15 +01:00
|
|
|
|
lib.mdDoc ''
|
2018-05-29 20:10:25 +01:00
|
|
|
|
Whether to log all rejected or dropped incoming packets.
|
|
|
|
|
This tends to give a lot of log messages, so it's mostly
|
|
|
|
|
useful for debugging.
|
2021-07-04 14:02:13 +01:00
|
|
|
|
Note: The logs are found in the kernel logs, i.e. dmesg
|
|
|
|
|
or journalctl -k.
|
2018-05-29 20:10:25 +01:00
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
logRefusedUnicastsOnly = mkOption {
|
|
|
|
|
type = types.bool;
|
|
|
|
|
default = true;
|
|
|
|
|
description =
|
2022-07-28 22:19:15 +01:00
|
|
|
|
lib.mdDoc ''
|
|
|
|
|
If {option}`networking.firewall.logRefusedPackets`
|
2018-05-29 20:10:25 +01:00
|
|
|
|
and this option are enabled, then only log packets
|
|
|
|
|
specifically directed at this machine, i.e., not broadcasts
|
|
|
|
|
or multicasts.
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
rejectPackets = mkOption {
|
|
|
|
|
type = types.bool;
|
|
|
|
|
default = false;
|
|
|
|
|
description =
|
2022-07-28 22:19:15 +01:00
|
|
|
|
lib.mdDoc ''
|
2018-05-29 20:10:25 +01:00
|
|
|
|
If set, refused packets are rejected rather than dropped
|
|
|
|
|
(ignored). This means that an ICMP "port unreachable" error
|
|
|
|
|
message is sent back to the client (or a TCP RST packet in
|
|
|
|
|
case of an existing connection). Rejecting packets makes
|
|
|
|
|
port scanning somewhat easier.
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
trustedInterfaces = mkOption {
|
|
|
|
|
type = types.listOf types.str;
|
|
|
|
|
default = [ ];
|
|
|
|
|
example = [ "enp0s2" ];
|
|
|
|
|
description =
|
2022-07-28 22:19:15 +01:00
|
|
|
|
lib.mdDoc ''
|
2018-05-29 20:10:25 +01:00
|
|
|
|
Traffic coming in from these interfaces will be accepted
|
|
|
|
|
unconditionally. Traffic from the loopback (lo) interface
|
|
|
|
|
will always be accepted.
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
allowPing = mkOption {
|
|
|
|
|
type = types.bool;
|
|
|
|
|
default = true;
|
|
|
|
|
description =
|
2022-07-28 22:19:15 +01:00
|
|
|
|
lib.mdDoc ''
|
2018-05-29 20:10:25 +01:00
|
|
|
|
Whether to respond to incoming ICMPv4 echo requests
|
|
|
|
|
("pings"). ICMPv6 pings are always allowed because the
|
|
|
|
|
larger address space of IPv6 makes network scanning much
|
|
|
|
|
less effective.
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
pingLimit = mkOption {
|
|
|
|
|
type = types.nullOr (types.separatedString " ");
|
|
|
|
|
default = null;
|
|
|
|
|
example = "--limit 1/minute --limit-burst 5";
|
|
|
|
|
description =
|
2022-07-28 22:19:15 +01:00
|
|
|
|
lib.mdDoc ''
|
2018-05-29 20:10:25 +01:00
|
|
|
|
If pings are allowed, this allows setting rate limits
|
|
|
|
|
on them. If non-null, this option should be in the form of
|
|
|
|
|
flags like "--limit 1/minute --limit-burst 5"
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
checkReversePath = mkOption {
|
|
|
|
|
type = types.either types.bool (types.enum ["strict" "loose"]);
|
|
|
|
|
default = kernelHasRPFilter;
|
2022-08-20 21:27:20 +01:00
|
|
|
|
defaultText = literalMD "`true` if supported by the chosen kernel";
|
2018-05-29 20:10:25 +01:00
|
|
|
|
example = "loose";
|
|
|
|
|
description =
|
2022-07-28 22:19:15 +01:00
|
|
|
|
lib.mdDoc ''
|
2018-05-29 20:10:25 +01:00
|
|
|
|
Performs a reverse path filter test on a packet. If a reply
|
|
|
|
|
to the packet would not be sent via the same interface that
|
|
|
|
|
the packet arrived on, it is refused.
|
|
|
|
|
|
|
|
|
|
If using asymmetric routing or other complicated routing, set
|
|
|
|
|
this option to loose mode or disable it and setup your own
|
|
|
|
|
counter-measures.
|
|
|
|
|
|
|
|
|
|
This option can be either true (or "strict"), "loose" (only
|
|
|
|
|
drop the packet if the source address is not reachable via any
|
|
|
|
|
interface) or false. Defaults to the value of
|
|
|
|
|
kernelHasRPFilter.
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
logReversePathDrops = mkOption {
|
|
|
|
|
type = types.bool;
|
|
|
|
|
default = false;
|
|
|
|
|
description =
|
2022-07-28 22:19:15 +01:00
|
|
|
|
lib.mdDoc ''
|
2018-05-29 20:10:25 +01:00
|
|
|
|
Logs dropped packets failing the reverse path filter test if
|
|
|
|
|
the option networking.firewall.checkReversePath is enabled.
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
connectionTrackingModules = mkOption {
|
|
|
|
|
type = types.listOf types.str;
|
|
|
|
|
default = [ ];
|
|
|
|
|
example = [ "ftp" "irc" "sane" "sip" "tftp" "amanda" "h323" "netbios_sn" "pptp" "snmp" ];
|
|
|
|
|
description =
|
2022-07-28 22:19:15 +01:00
|
|
|
|
lib.mdDoc ''
|
2018-05-29 20:10:25 +01:00
|
|
|
|
List of connection-tracking helpers that are auto-loaded.
|
|
|
|
|
The complete list of possible values is given in the example.
|
|
|
|
|
|
|
|
|
|
As helpers can pose as a security risk, it is advised to
|
|
|
|
|
set this to an empty list and disable the setting
|
|
|
|
|
networking.firewall.autoLoadConntrackHelpers unless you
|
|
|
|
|
know what you are doing. Connection tracking is disabled
|
|
|
|
|
by default.
|
|
|
|
|
|
|
|
|
|
Loading of helpers is recommended to be done through the
|
|
|
|
|
CT target. More info:
|
|
|
|
|
https://home.regit.org/netfilter-en/secure-use-of-helpers/
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
autoLoadConntrackHelpers = mkOption {
|
|
|
|
|
type = types.bool;
|
|
|
|
|
default = false;
|
|
|
|
|
description =
|
2022-07-28 22:19:15 +01:00
|
|
|
|
lib.mdDoc ''
|
2018-05-29 20:10:25 +01:00
|
|
|
|
Whether to auto-load connection-tracking helpers.
|
|
|
|
|
See the description at networking.firewall.connectionTrackingModules
|
|
|
|
|
|
|
|
|
|
(needs kernel 3.5+)
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
extraCommands = mkOption {
|
|
|
|
|
type = types.lines;
|
|
|
|
|
default = "";
|
|
|
|
|
example = "iptables -A INPUT -p icmp -j ACCEPT";
|
|
|
|
|
description =
|
2022-07-28 22:19:15 +01:00
|
|
|
|
lib.mdDoc ''
|
2018-05-29 20:10:25 +01:00
|
|
|
|
Additional shell commands executed as part of the firewall
|
|
|
|
|
initialisation script. These are executed just before the
|
|
|
|
|
final "reject" firewall rule is added, so they can be used
|
|
|
|
|
to allow packets that would otherwise be refused.
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
extraPackages = mkOption {
|
|
|
|
|
type = types.listOf types.package;
|
|
|
|
|
default = [ ];
|
2021-10-03 17:06:03 +01:00
|
|
|
|
example = literalExpression "[ pkgs.ipset ]";
|
2018-05-29 20:10:25 +01:00
|
|
|
|
description =
|
2022-07-28 22:19:15 +01:00
|
|
|
|
lib.mdDoc ''
|
2018-05-29 20:10:25 +01:00
|
|
|
|
Additional packages to be included in the environment of the system
|
|
|
|
|
as well as the path of networking.firewall.extraCommands.
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
extraStopCommands = mkOption {
|
|
|
|
|
type = types.lines;
|
|
|
|
|
default = "";
|
|
|
|
|
example = "iptables -P INPUT ACCEPT";
|
|
|
|
|
description =
|
2022-07-28 22:19:15 +01:00
|
|
|
|
lib.mdDoc ''
|
2018-05-29 20:10:25 +01:00
|
|
|
|
Additional shell commands executed as part of the firewall
|
|
|
|
|
shutdown script. These are executed just after the removal
|
|
|
|
|
of the NixOS input rule, or if the service enters a failed
|
|
|
|
|
state.
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
interfaces = mkOption {
|
2018-11-18 20:03:42 +00:00
|
|
|
|
default = { };
|
2018-05-29 20:10:25 +01:00
|
|
|
|
type = with types; attrsOf (submodule [ { options = commonOptions; } ]);
|
|
|
|
|
description =
|
2022-07-28 22:19:15 +01:00
|
|
|
|
lib.mdDoc ''
|
2018-11-18 20:03:42 +00:00
|
|
|
|
Interface-specific open ports.
|
2018-05-29 20:10:25 +01:00
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
} // commonOptions;
|
2014-09-16 05:29:46 +01:00
|
|
|
|
|
2009-07-25 00:12:52 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
###### implementation
|
2009-07-26 22:27:35 +01:00
|
|
|
|
|
2013-10-31 12:26:06 +00:00
|
|
|
|
# FIXME: Maybe if `enable' is false, the firewall should still be
|
|
|
|
|
# built but not started by default?
|
2011-03-10 09:39:17 +00:00
|
|
|
|
config = mkIf cfg.enable {
|
2009-07-25 00:12:52 +01:00
|
|
|
|
|
2012-09-20 22:51:44 +01:00
|
|
|
|
networking.firewall.trustedInterfaces = [ "lo" ];
|
|
|
|
|
|
2019-08-19 18:21:24 +01:00
|
|
|
|
environment.systemPackages = [ cfg.package ] ++ cfg.extraPackages;
|
2009-07-25 00:12:52 +01:00
|
|
|
|
|
2017-01-22 16:29:38 +00:00
|
|
|
|
boot.kernelModules = (optional cfg.autoLoadConntrackHelpers "nf_conntrack")
|
|
|
|
|
++ map (x: "nf_conntrack_${x}") cfg.connectionTrackingModules;
|
|
|
|
|
boot.extraModprobeConfig = optionalString cfg.autoLoadConntrackHelpers ''
|
|
|
|
|
options nf_conntrack nf_conntrack_helper=1
|
2012-10-12 12:16:33 +01:00
|
|
|
|
'';
|
2011-03-11 13:34:17 +00:00
|
|
|
|
|
2020-03-17 01:32:37 +00:00
|
|
|
|
assertions = [
|
|
|
|
|
# This is approximately "checkReversePath -> kernelHasRPFilter",
|
|
|
|
|
# but the checkReversePath option can include non-boolean
|
|
|
|
|
# values.
|
|
|
|
|
{ assertion = cfg.checkReversePath == false || kernelHasRPFilter;
|
|
|
|
|
message = "This kernel does not support rpfilter"; }
|
|
|
|
|
];
|
2011-03-11 13:34:17 +00:00
|
|
|
|
|
2014-09-16 09:15:10 +01:00
|
|
|
|
systemd.services.firewall = {
|
|
|
|
|
description = "Firewall";
|
2016-09-07 13:18:32 +01:00
|
|
|
|
wantedBy = [ "sysinit.target" ];
|
2016-09-07 13:24:54 +01:00
|
|
|
|
wants = [ "network-pre.target" ];
|
2014-12-02 01:19:06 +00:00
|
|
|
|
before = [ "network-pre.target" ];
|
|
|
|
|
after = [ "systemd-modules-load.service" ];
|
2014-09-16 04:04:31 +01:00
|
|
|
|
|
2019-08-19 18:21:24 +01:00
|
|
|
|
path = [ cfg.package ] ++ cfg.extraPackages;
|
2014-09-16 09:15:10 +01:00
|
|
|
|
|
|
|
|
|
# FIXME: this module may also try to load kernel modules, but
|
2017-01-18 16:18:11 +00:00
|
|
|
|
# containers don't have CAP_SYS_MODULE. So the host system had
|
2014-09-16 09:15:10 +01:00
|
|
|
|
# better have all necessary modules already loaded.
|
|
|
|
|
unitConfig.ConditionCapability = "CAP_NET_ADMIN";
|
2016-09-07 13:18:32 +01:00
|
|
|
|
unitConfig.DefaultDependencies = false;
|
2014-09-16 09:15:10 +01:00
|
|
|
|
|
|
|
|
|
reloadIfChanged = true;
|
|
|
|
|
|
|
|
|
|
serviceConfig = {
|
|
|
|
|
Type = "oneshot";
|
|
|
|
|
RemainAfterExit = true;
|
|
|
|
|
ExecStart = "@${startScript} firewall-start";
|
|
|
|
|
ExecReload = "@${reloadScript} firewall-reload";
|
|
|
|
|
ExecStop = "@${stopScript} firewall-stop";
|
2009-07-25 00:12:52 +01:00
|
|
|
|
};
|
2014-09-16 09:15:10 +01:00
|
|
|
|
};
|
2009-07-25 00:12:52 +01:00
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
}
|