···
cfg = config.networking.nat;
12
-
dest = if cfg.externalIP == null then "-j MASQUERADE" else "-j SNAT --to-source ${cfg.externalIP}";
12
+
mkDest = externalIP: if externalIP == null
13
+
then "-j MASQUERADE"
14
+
else "-j SNAT --to-source ${externalIP}";
15
+
dest = mkDest cfg.externalIP;
16
+
destIPv6 = mkDest cfg.externalIPv6;
18
+
# Whether given IP (plus optional port) is an IPv6.
19
+
isIPv6 = ip: builtins.length (lib.splitString ":" ip) > 2;
helpers = import ./helpers.nix { inherit config lib; };
···
33
-
# Create subchain where we store rules
34
-
ip46tables -w -t nat -N nixos-nat-pre
35
-
ip46tables -w -t nat -N nixos-nat-post
36
-
ip46tables -w -t nat -N nixos-nat-out
38
+
mkSetupNat = { iptables, dest, internalIPs, forwardPorts }: ''
# We can't match on incoming interface in POSTROUTING, so
# mark packets coming from the internal interfaces.
${concatMapStrings (iface: ''
41
-
iptables -w -t nat -A nixos-nat-pre \
42
+
${iptables} -w -t nat -A nixos-nat-pre \
-i '${iface}' -j MARK --set-mark 1
'') cfg.internalInterfaces}
# NAT the marked packets.
${optionalString (cfg.internalInterfaces != []) ''
47
-
iptables -w -t nat -A nixos-nat-post -m mark --mark 1 \
48
+
${iptables} -w -t nat -A nixos-nat-post -m mark --mark 1 \
${optionalString (cfg.externalInterface != null) "-o ${cfg.externalInterface}"} ${dest}
# NAT packets coming from the internal IPs.
${concatMapStrings (range: ''
53
-
iptables -w -t nat -A nixos-nat-post \
54
+
${iptables} -w -t nat -A nixos-nat-post \
-s '${range}' ${optionalString (cfg.externalInterface != null) "-o ${cfg.externalInterface}"} ${dest}
55
-
'') cfg.internalIPs}
# NAT from external ports to internal ports.
${concatMapStrings (fwd: ''
59
-
iptables -w -t nat -A nixos-nat-pre \
60
+
${iptables} -w -t nat -A nixos-nat-pre \
-i ${toString cfg.externalInterface} -p ${fwd.proto} \
--dport ${builtins.toString fwd.sourcePort} \
-j DNAT --to-destination ${fwd.destination}
${concatMapStrings (loopbackip:
66
-
m = builtins.match "([0-9.]+):([0-9-]+)" fwd.destination;
67
-
destinationIP = if (m == null) then throw "bad ip:ports `${fwd.destination}'" else elemAt m 0;
68
-
destinationPorts = if (m == null) then throw "bad ip:ports `${fwd.destination}'" else builtins.replaceStrings ["-"] [":"] (elemAt m 1);
67
+
matchIP = if isIPv6 fwd.destination then "[[]([0-9a-fA-F:]+)[]]" else "([0-9.]+)";
68
+
m = builtins.match "${matchIP}:([0-9-]+)" fwd.destination;
69
+
destinationIP = if m == null then throw "bad ip:ports `${fwd.destination}'" else elemAt m 0;
70
+
destinationPorts = if m == null then throw "bad ip:ports `${fwd.destination}'" else builtins.replaceStrings ["-"] [":"] (elemAt m 1);
# Allow connections to ${loopbackip}:${toString fwd.sourcePort} from the host itself
71
-
iptables -w -t nat -A nixos-nat-out \
73
+
${iptables} -w -t nat -A nixos-nat-out \
-d ${loopbackip} -p ${fwd.proto} \
--dport ${builtins.toString fwd.sourcePort} \
-j DNAT --to-destination ${fwd.destination}
# Allow connections to ${loopbackip}:${toString fwd.sourcePort} from other hosts behind NAT
77
-
iptables -w -t nat -A nixos-nat-pre \
79
+
${iptables} -w -t nat -A nixos-nat-pre \
-d ${loopbackip} -p ${fwd.proto} \
--dport ${builtins.toString fwd.sourcePort} \
-j DNAT --to-destination ${fwd.destination}
82
-
iptables -w -t nat -A nixos-nat-post \
84
+
${iptables} -w -t nat -A nixos-nat-post \
-d ${destinationIP} -p ${fwd.proto} \
--dport ${destinationPorts} \
-j SNAT --to-source ${loopbackip}
87
-
'') cfg.forwardPorts}
94
+
# Create subchains where we store rules
95
+
ip46tables -w -t nat -N nixos-nat-pre
96
+
ip46tables -w -t nat -N nixos-nat-post
97
+
ip46tables -w -t nat -N nixos-nat-out
100
+
iptables = "iptables";
102
+
inherit (cfg) internalIPs;
103
+
forwardPorts = filter (x: !(isIPv6 x.destination)) cfg.forwardPorts;
106
+
${optionalString cfg.enableIPv6 (mkSetupNat {
107
+
iptables = "ip6tables";
109
+
internalIPs = cfg.internalIPv6s;
110
+
forwardPorts = filter (x: isIPv6 x.destination) cfg.forwardPorts;
${optionalString (cfg.dmzHost != null) ''
iptables -w -t nat -A nixos-nat-pre \
···
144
+
networking.nat.enableIPv6 = mkOption {
149
+
Whether to enable IPv6 NAT.
networking.nat.internalInterfaces = mkOption {
type = types.listOf types.str;
···
177
+
networking.nat.internalIPv6s = mkOption {
178
+
type = types.listOf types.str;
180
+
example = [ "fc00::/64" ];
183
+
The IPv6 address ranges for which to perform NAT. Packets
184
+
coming from these addresses (on any interface) and destined
185
+
for the external interface will be rewritten.
networking.nat.externalInterface = mkOption {
type = types.nullOr types.str;
···
212
+
networking.nat.externalIPv6 = mkOption {
213
+
type = types.nullOr types.str;
215
+
example = "2001:dc0:2001:11::175";
218
+
The public IPv6 address to which packets from the local
219
+
network are to be rewritten. If this is left empty, the
220
+
IP address associated with the external interface will be
networking.nat.forwardPorts = mkOption {
type = with types; listOf (submodule {
···
179
-
description = "Forward connection to destination ip:port; to specify a port range, use ip:start-end";
237
+
description = "Forward connection to destination ip:port (or [ipv6]:port); to specify a port range, use ip:start-end";
···
198
-
example = [ { sourcePort = 8080; destination = "10.0.0.1:80"; proto = "tcp"; } ];
257
+
{ sourcePort = 8080; destination = "10.0.0.1:80"; proto = "tcp"; }
258
+
{ sourcePort = 8080; destination = "[fc00::2]:80"; proto = "tcp"; }
List of forwarded ports from the external interface to
202
-
internal destinations by using DNAT.
263
+
internal destinations by using DNAT. Destination can be
264
+
IPv6 if IPv6 NAT is enabled.
···
(mkIf config.networking.nat.enable {
311
+
{ assertion = cfg.enableIPv6 -> config.networking.enableIPv6;
312
+
message = "networking.nat.enableIPv6 requires networking.enableIPv6";
{ assertion = (cfg.dmzHost != null) -> (cfg.externalInterface != null);
message = "networking.nat.dmzHost requires networking.nat.externalInterface";
···
"net.ipv4.conf.all.forwarding" = mkOverride 99 true;
"net.ipv4.conf.default.forwarding" = mkOverride 99 true;
329
+
} // optionalAttrs cfg.enableIPv6 {
330
+
# Do not prevent IPv6 autoconfiguration.
331
+
# See <http://strugglers.net/~andy/blog/2011/09/04/linux-ipv6-router-advertisements-and-forwarding/>.
332
+
"net.ipv6.conf.all.accept_ra" = mkOverride 99 2;
333
+
"net.ipv6.conf.default.accept_ra" = mkOverride 99 2;
335
+
# Forward IPv6 packets.
336
+
"net.ipv6.conf.all.forwarding" = mkOverride 99 true;
337
+
"net.ipv6.conf.default.forwarding" = mkOverride 99 true;