Merge pull request #41222 from gnidorah/firewall

nixos/firewall: per-interface port options

Changed files
+228 -207
nixos
modules
services
networking
+228 -207
nixos/modules/services/networking/firewall.nix
···
ip46tables -A nixos-fw -m conntrack --ctstate ESTABLISHED,RELATED -j nixos-fw-accept
# Accept connections to the allowed TCP ports.
-
${concatMapStrings (port:
+
${concatStrings (mapAttrsToList (iface: cfg:
+
concatMapStrings (port:
''
-
ip46tables -A nixos-fw -p tcp --dport ${toString port} -j nixos-fw-accept
+
ip46tables -A nixos-fw -p tcp --dport ${toString port} -j nixos-fw-accept ${optionalString (iface != "default") "-i ${iface}"}
''
) cfg.allowedTCPPorts
-
}
+
) cfg.interfaces)}
# Accept connections to the allowed TCP port ranges.
-
${concatMapStrings (rangeAttr:
+
${concatStrings (mapAttrsToList (iface: cfg:
+
concatMapStrings (rangeAttr:
let range = toString rangeAttr.from + ":" + toString rangeAttr.to; in
''
-
ip46tables -A nixos-fw -p tcp --dport ${range} -j nixos-fw-accept
+
ip46tables -A nixos-fw -p tcp --dport ${range} -j nixos-fw-accept ${optionalString (iface != "default") "-i ${iface}"}
''
) cfg.allowedTCPPortRanges
-
}
+
) cfg.interfaces)}
# Accept packets on the allowed UDP ports.
-
${concatMapStrings (port:
+
${concatStrings (mapAttrsToList (iface: cfg:
+
concatMapStrings (port:
''
-
ip46tables -A nixos-fw -p udp --dport ${toString port} -j nixos-fw-accept
+
ip46tables -A nixos-fw -p udp --dport ${toString port} -j nixos-fw-accept ${optionalString (iface != "default") "-i ${iface}"}
''
) cfg.allowedUDPPorts
-
}
+
) cfg.interfaces)}
# Accept packets on the allowed UDP port ranges.
-
${concatMapStrings (rangeAttr:
+
${concatStrings (mapAttrsToList (iface: cfg:
+
concatMapStrings (rangeAttr:
let range = toString rangeAttr.from + ":" + toString rangeAttr.to; in
''
-
ip46tables -A nixos-fw -p udp --dport ${range} -j nixos-fw-accept
+
ip46tables -A nixos-fw -p udp --dport ${range} -j nixos-fw-accept ${optionalString (iface != "default") "-i ${iface}"}
''
) cfg.allowedUDPPortRanges
-
}
+
) cfg.interfaces)}
# Accept IPv4 multicast. Not a big security risk since
# probably nobody is listening anyway.
···
fi
'';
-
in
-
-
{
-
-
###### interface
-
-
options = {
-
-
networking.firewall.enable = mkOption {
-
type = types.bool;
-
default = true;
-
description =
-
''
-
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.
-
'';
-
};
-
-
networking.firewall.logRefusedConnections = mkOption {
-
type = types.bool;
-
default = true;
-
description =
-
''
-
Whether to log rejected or dropped incoming connections.
-
'';
-
};
-
-
networking.firewall.logRefusedPackets = mkOption {
-
type = types.bool;
-
default = false;
-
description =
-
''
-
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.
-
'';
-
};
-
-
networking.firewall.logRefusedUnicastsOnly = mkOption {
-
type = types.bool;
-
default = true;
-
description =
-
''
-
If <option>networking.firewall.logRefusedPackets</option>
-
and this option are enabled, then only log packets
-
specifically directed at this machine, i.e., not broadcasts
-
or multicasts.
-
'';
-
};
-
-
networking.firewall.rejectPackets = mkOption {
-
type = types.bool;
-
default = false;
-
description =
-
''
-
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.
-
'';
-
};
-
-
networking.firewall.trustedInterfaces = mkOption {
-
type = types.listOf types.str;
-
default = [ ];
-
example = [ "enp0s2" ];
-
description =
-
''
-
Traffic coming in from these interfaces will be accepted
-
unconditionally. Traffic from the loopback (lo) interface
-
will always be accepted.
-
'';
-
};
-
-
networking.firewall.allowedTCPPorts = mkOption {
+
commonOptions = {
+
allowedTCPPorts = mkOption {
type = types.listOf types.int;
default = [ ];
example = [ 22 80 ];
description =
-
''
+
''
List of TCP ports on which incoming connections are
accepted.
'';
};
-
networking.firewall.allowedTCPPortRanges = mkOption {
+
allowedTCPPortRanges = mkOption {
type = types.listOf (types.attrsOf types.int);
default = [ ];
example = [ { from = 8999; to = 9003; } ];
description =
-
''
+
''
A range of TCP ports on which incoming connections are
accepted.
'';
};
-
networking.firewall.allowedUDPPorts = mkOption {
+
allowedUDPPorts = mkOption {
type = types.listOf types.int;
default = [ ];
example = [ 53 ];
···
'';
};
-
networking.firewall.allowedUDPPortRanges = mkOption {
+
allowedUDPPortRanges = mkOption {
type = types.listOf (types.attrsOf types.int);
default = [ ];
example = [ { from = 60000; to = 61000; } ];
···
Range of open UDP ports.
'';
};
+
};
+
+
in
+
+
{
-
networking.firewall.allowPing = mkOption {
-
type = types.bool;
-
default = true;
-
description =
-
''
-
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.
-
'';
-
};
+
###### interface
+
+
options = {
+
+
networking.firewall = {
+
enable = mkOption {
+
type = types.bool;
+
default = true;
+
description =
+
''
+
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.
+
'';
+
};
+
+
logRefusedConnections = mkOption {
+
type = types.bool;
+
default = true;
+
description =
+
''
+
Whether to log rejected or dropped incoming connections.
+
'';
+
};
+
+
logRefusedPackets = mkOption {
+
type = types.bool;
+
default = false;
+
description =
+
''
+
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.
+
'';
+
};
+
+
logRefusedUnicastsOnly = mkOption {
+
type = types.bool;
+
default = true;
+
description =
+
''
+
If <option>networking.firewall.logRefusedPackets</option>
+
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 =
+
''
+
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 =
+
''
+
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 =
+
''
+
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 =
+
''
+
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"
+
'';
+
};
-
networking.firewall.pingLimit = mkOption {
-
type = types.nullOr (types.separatedString " ");
-
default = null;
-
example = "--limit 1/minute --limit-burst 5";
-
description =
-
''
-
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;
+
example = "loose";
+
description =
+
''
+
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.
-
networking.firewall.checkReversePath = mkOption {
-
type = types.either types.bool (types.enum ["strict" "loose"]);
-
default = kernelHasRPFilter;
-
example = "loose";
-
description =
-
''
-
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.
-
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.
-
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.
+
(needs kernel 3.3+)
+
'';
+
};
-
(needs kernel 3.3+)
-
'';
-
};
+
logReversePathDrops = mkOption {
+
type = types.bool;
+
default = false;
+
description =
+
''
+
Logs dropped packets failing the reverse path filter test if
+
the option networking.firewall.checkReversePath is enabled.
+
'';
+
};
-
networking.firewall.logReversePathDrops = mkOption {
-
type = types.bool;
-
default = false;
-
description =
-
''
-
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 =
+
''
+
List of connection-tracking helpers that are auto-loaded.
+
The complete list of possible values is given in the example.
-
networking.firewall.connectionTrackingModules = mkOption {
-
type = types.listOf types.str;
-
default = [ ];
-
example = [ "ftp" "irc" "sane" "sip" "tftp" "amanda" "h323" "netbios_sn" "pptp" "snmp" ];
-
description =
-
''
-
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.
-
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/
+
'';
+
};
-
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 =
+
''
+
Whether to auto-load connection-tracking helpers.
+
See the description at networking.firewall.connectionTrackingModules
-
networking.firewall.autoLoadConntrackHelpers = mkOption {
-
type = types.bool;
-
default = false;
-
description =
-
''
-
Whether to auto-load connection-tracking helpers.
-
See the description at networking.firewall.connectionTrackingModules
+
(needs kernel 3.5+)
+
'';
+
};
-
(needs kernel 3.5+)
-
'';
-
};
+
extraCommands = mkOption {
+
type = types.lines;
+
default = "";
+
example = "iptables -A INPUT -p icmp -j ACCEPT";
+
description =
+
''
+
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.
+
'';
+
};
-
networking.firewall.extraCommands = mkOption {
-
type = types.lines;
-
default = "";
-
example = "iptables -A INPUT -p icmp -j ACCEPT";
-
description =
-
''
-
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 = [ ];
+
example = literalExample "[ pkgs.ipset ]";
+
description =
+
''
+
Additional packages to be included in the environment of the system
+
as well as the path of networking.firewall.extraCommands.
+
'';
+
};
-
networking.firewall.extraPackages = mkOption {
-
type = types.listOf types.package;
-
default = [ ];
-
example = literalExample "[ pkgs.ipset ]";
-
description =
-
''
-
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 =
+
''
+
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.
+
'';
+
};
-
networking.firewall.extraStopCommands = mkOption {
-
type = types.lines;
-
default = "";
-
example = "iptables -P INPUT ACCEPT";
-
description =
-
''
-
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 {
+
default = {
+
default = mapAttrs (name: value: cfg."${name}") commonOptions;
+
};
+
type = with types; attrsOf (submodule [ { options = commonOptions; } ]);
+
description =
+
''
+
Interface-specific open ports. Setting this value will override
+
all values of the <literal>networking.firewall.allowed*</literal>
+
options.
+
'';
+
};
+
} // commonOptions;
};