nixos/firewall: per-interface port options

This commit is contained in:
gnidorah 2018-05-29 22:10:25 +03:00
parent 0450c7f5f3
commit c60c8aa759

@ -148,38 +148,42 @@ let
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.
@ -254,106 +258,30 @@ let
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 ];
@ -363,7 +291,7 @@ in
'';
};
networking.firewall.allowedUDPPortRanges = mkOption {
allowedUDPPortRanges = mkOption {
type = types.listOf (types.attrsOf types.int);
default = [ ];
example = [ { from = 60000; to = 61000; } ];
@ -372,133 +300,226 @@ in
Range of open UDP ports.
'';
};
};
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.
'';
};
in
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"
'';
};
{
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.
###### interface
If using asymmetric routing or other complicated routing, set
this option to loose mode or disable it and setup your own
counter-measures.
options = {
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.
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.
'';
};
(needs kernel 3.3+)
'';
};
logRefusedConnections = mkOption {
type = types.bool;
default = true;
description =
''
Whether to log rejected or dropped incoming connections.
'';
};
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.
'';
};
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.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.
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.
'';
};
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.
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.
'';
};
Loading of helpers is recommended to be done through the
CT target. More info:
https://home.regit.org/netfilter-en/secure-use-of-helpers/
'';
};
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.autoLoadConntrackHelpers = mkOption {
type = types.bool;
default = false;
description =
''
Whether to auto-load connection-tracking helpers.
See the description at networking.firewall.connectionTrackingModules
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.
'';
};
(needs kernel 3.5+)
'';
};
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.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.
'';
};
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.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.
'';
};
If using asymmetric routing or other complicated routing, set
this option to loose mode or disable it and setup your own
counter-measures.
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.
'';
};
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+)
'';
};
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.
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 =
''
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 =
''
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.
'';
};
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;
};