vuls: init at 0.27.0
[NixPkgs.git] / nixos / modules / services / networking / nat-iptables.nix
blob351ba9ec35542ad09ec76b2314a3417b78081162
1 # This module enables Network Address Translation (NAT).
2 # XXX: todo: support multiple upstream links
3 # see http://yesican.chsoft.biz/lartc/MultihomedLinuxNetworking.html
5 { config, lib, pkgs, ... }:
7 with lib;
9 let
10   cfg = config.networking.nat;
12   mkDest = externalIP:
13     if externalIP == null
14     then "-j MASQUERADE"
15     else "-j SNAT --to-source ${externalIP}";
16   dest = mkDest cfg.externalIP;
17   destIPv6 = mkDest cfg.externalIPv6;
19   # Whether given IP (plus optional port) is an IPv6.
20   isIPv6 = ip: builtins.length (lib.splitString ":" ip) > 2;
22   helpers = import ./helpers.nix { inherit config lib; };
24   flushNat = ''
25     ${helpers}
26     ip46tables -w -t nat -D PREROUTING -j nixos-nat-pre 2>/dev/null|| true
27     ip46tables -w -t nat -F nixos-nat-pre 2>/dev/null || true
28     ip46tables -w -t nat -X nixos-nat-pre 2>/dev/null || true
29     ip46tables -w -t nat -D POSTROUTING -j nixos-nat-post 2>/dev/null || true
30     ip46tables -w -t nat -F nixos-nat-post 2>/dev/null || true
31     ip46tables -w -t nat -X nixos-nat-post 2>/dev/null || true
32     ip46tables -w -t nat -D OUTPUT -j nixos-nat-out 2>/dev/null || true
33     ip46tables -w -t nat -F nixos-nat-out 2>/dev/null || true
34     ip46tables -w -t nat -X nixos-nat-out 2>/dev/null || true
36     ${cfg.extraStopCommands}
37   '';
39   mkSetupNat = { iptables, dest, internalIPs, forwardPorts }: ''
40     # We can't match on incoming interface in POSTROUTING, so
41     # mark packets coming from the internal interfaces.
42     ${concatMapStrings (iface: ''
43       ${iptables} -w -t nat -A nixos-nat-pre \
44         -i '${iface}' -j MARK --set-mark 1
45     '') cfg.internalInterfaces}
47     # NAT the marked packets.
48     ${optionalString (cfg.internalInterfaces != []) ''
49       ${iptables} -w -t nat -A nixos-nat-post -m mark --mark 1 \
50         ${optionalString (cfg.externalInterface != null) "-o ${cfg.externalInterface}"} ${dest}
51     ''}
53     # NAT packets coming from the internal IPs.
54     ${concatMapStrings (range: ''
55       ${iptables} -w -t nat -A nixos-nat-post \
56         -s '${range}' ${optionalString (cfg.externalInterface != null) "-o ${cfg.externalInterface}"} ${dest}
57     '') internalIPs}
59     # NAT from external ports to internal ports.
60     ${concatMapStrings (fwd: ''
61       ${iptables} -w -t nat -A nixos-nat-pre \
62         -i ${toString cfg.externalInterface} -p ${fwd.proto} \
63         --dport ${builtins.toString fwd.sourcePort} \
64         -j DNAT --to-destination ${fwd.destination}
66       ${concatMapStrings (loopbackip:
67         let
68           matchIP          = if isIPv6 fwd.destination then "[[]([0-9a-fA-F:]+)[]]" else "([0-9.]+)";
69           m                = builtins.match "${matchIP}:([0-9-]+)" fwd.destination;
70           destinationIP    = if m == null then throw "bad ip:ports `${fwd.destination}'" else elemAt m 0;
71           destinationPorts = if m == null then throw "bad ip:ports `${fwd.destination}'" else builtins.replaceStrings ["-"] [":"] (elemAt m 1);
72         in ''
73           # Allow connections to ${loopbackip}:${toString fwd.sourcePort} from the host itself
74           ${iptables} -w -t nat -A nixos-nat-out \
75             -d ${loopbackip} -p ${fwd.proto} \
76             --dport ${builtins.toString fwd.sourcePort} \
77             -j DNAT --to-destination ${fwd.destination}
79           # Allow connections to ${loopbackip}:${toString fwd.sourcePort} from other hosts behind NAT
80           ${iptables} -w -t nat -A nixos-nat-pre \
81             -d ${loopbackip} -p ${fwd.proto} \
82             --dport ${builtins.toString fwd.sourcePort} \
83             -j DNAT --to-destination ${fwd.destination}
85           ${iptables} -w -t nat -A nixos-nat-post \
86             -d ${destinationIP} -p ${fwd.proto} \
87             --dport ${destinationPorts} \
88             -j SNAT --to-source ${loopbackip}
89         '') fwd.loopbackIPs}
90     '') forwardPorts}
91   '';
93   setupNat = ''
94     ${helpers}
95     # Create subchains where we store rules
96     ip46tables -w -t nat -N nixos-nat-pre
97     ip46tables -w -t nat -N nixos-nat-post
98     ip46tables -w -t nat -N nixos-nat-out
100     ${mkSetupNat {
101       iptables = "iptables";
102       inherit dest;
103       inherit (cfg) internalIPs;
104       forwardPorts = filter (x: !(isIPv6 x.destination)) cfg.forwardPorts;
105     }}
107     ${optionalString cfg.enableIPv6 (mkSetupNat {
108       iptables = "ip6tables";
109       dest = destIPv6;
110       internalIPs = cfg.internalIPv6s;
111       forwardPorts = filter (x: isIPv6 x.destination) cfg.forwardPorts;
112     })}
114     ${optionalString (cfg.dmzHost != null) ''
115       iptables -w -t nat -A nixos-nat-pre \
116         -i ${toString cfg.externalInterface} -j DNAT \
117         --to-destination ${cfg.dmzHost}
118     ''}
120     ${cfg.extraCommands}
122     # Append our chains to the nat tables
123     ip46tables -w -t nat -A PREROUTING -j nixos-nat-pre
124     ip46tables -w -t nat -A POSTROUTING -j nixos-nat-post
125     ip46tables -w -t nat -A OUTPUT -j nixos-nat-out
126   '';
132   options = {
134     networking.nat.extraCommands = mkOption {
135       type = types.lines;
136       default = "";
137       example = "iptables -A INPUT -p icmp -j ACCEPT";
138       description = ''
139         Additional shell commands executed as part of the nat
140         initialisation script.
142         This option is incompatible with the nftables based nat module.
143       '';
144     };
146     networking.nat.extraStopCommands = mkOption {
147       type = types.lines;
148       default = "";
149       example = "iptables -D INPUT -p icmp -j ACCEPT || true";
150       description = ''
151         Additional shell commands executed as part of the nat
152         teardown script.
154         This option is incompatible with the nftables based nat module.
155       '';
156     };
158   };
161   config = mkIf (!config.networking.nftables.enable)
162     (mkMerge [
163       ({ networking.firewall.extraCommands = mkBefore flushNat; })
164       (mkIf config.networking.nat.enable {
166         networking.firewall = mkIf config.networking.firewall.enable {
167           extraCommands = setupNat;
168           extraStopCommands = flushNat;
169         };
171         systemd.services = mkIf (!config.networking.firewall.enable) {
172           nat = {
173             description = "Network Address Translation";
174             wantedBy = [ "network.target" ];
175             after = [ "network-pre.target" "systemd-modules-load.service" ];
176             path = [ config.networking.firewall.package ];
177             unitConfig.ConditionCapability = "CAP_NET_ADMIN";
179             serviceConfig = {
180               Type = "oneshot";
181               RemainAfterExit = true;
182             };
184             script = flushNat + setupNat;
186             postStop = flushNat;
187           };
188         };
189       })
190     ]);