1# This module enables Network Address Translation (NAT).
2# XXX: todo: support multiple upstream links
3# see http://yesican.chsoft.biz/lartc/MultihomedLinuxNetworking.html
4
5{ config, lib, pkgs, ... }:
6
7with lib;
8
9let
10
11 cfg = config.networking.nat;
12
13 dest = if cfg.externalIP == null then "-j MASQUERADE" else "-j SNAT --to-source ${cfg.externalIP}";
14
15 flushNat = ''
16 iptables -w -t nat -D PREROUTING -j nixos-nat-pre 2>/dev/null|| true
17 iptables -w -t nat -F nixos-nat-pre 2>/dev/null || true
18 iptables -w -t nat -X nixos-nat-pre 2>/dev/null || true
19 iptables -w -t nat -D POSTROUTING -j nixos-nat-post 2>/dev/null || true
20 iptables -w -t nat -F nixos-nat-post 2>/dev/null || true
21 iptables -w -t nat -X nixos-nat-post 2>/dev/null || true
22 '';
23
24 setupNat = ''
25 # Create subchain where we store rules
26 iptables -w -t nat -N nixos-nat-pre
27 iptables -w -t nat -N nixos-nat-post
28
29 # We can't match on incoming interface in POSTROUTING, so
30 # mark packets coming from the external interfaces.
31 ${concatMapStrings (iface: ''
32 iptables -w -t nat -A nixos-nat-pre \
33 -i '${iface}' -j MARK --set-mark 1
34 '') cfg.internalInterfaces}
35
36 # NAT the marked packets.
37 ${optionalString (cfg.internalInterfaces != []) ''
38 iptables -w -t nat -A nixos-nat-post -m mark --mark 1 \
39 -o ${cfg.externalInterface} ${dest}
40 ''}
41
42 # NAT packets coming from the internal IPs.
43 ${concatMapStrings (range: ''
44 iptables -w -t nat -A nixos-nat-post \
45 -s '${range}' -o ${cfg.externalInterface} ${dest}
46 '') cfg.internalIPs}
47
48 # NAT from external ports to internal ports.
49 ${concatMapStrings (fwd: ''
50 iptables -w -t nat -A nixos-nat-pre \
51 -i ${cfg.externalInterface} -p ${fwd.proto} \
52 --dport ${builtins.toString fwd.sourcePort} \
53 -j DNAT --to-destination ${fwd.destination}
54 '') cfg.forwardPorts}
55
56 # Append our chains to the nat tables
57 iptables -w -t nat -A PREROUTING -j nixos-nat-pre
58 iptables -w -t nat -A POSTROUTING -j nixos-nat-post
59 '';
60
61in
62
63{
64
65 ###### interface
66
67 options = {
68
69 networking.nat.enable = mkOption {
70 type = types.bool;
71 default = false;
72 description =
73 ''
74 Whether to enable Network Address Translation (NAT).
75 '';
76 };
77
78 networking.nat.internalInterfaces = mkOption {
79 type = types.listOf types.str;
80 default = [];
81 example = [ "eth0" ];
82 description =
83 ''
84 The interfaces for which to perform NAT. Packets coming from
85 these interface and destined for the external interface will
86 be rewritten.
87 '';
88 };
89
90 networking.nat.internalIPs = mkOption {
91 type = types.listOf types.str;
92 default = [];
93 example = [ "192.168.1.0/24" ];
94 description =
95 ''
96 The IP address ranges for which to perform NAT. Packets
97 coming from these addresses (on any interface) and destined
98 for the external interface will be rewritten.
99 '';
100 };
101
102 networking.nat.externalInterface = mkOption {
103 type = types.str;
104 example = "eth1";
105 description =
106 ''
107 The name of the external network interface.
108 '';
109 };
110
111 networking.nat.externalIP = mkOption {
112 type = types.nullOr types.str;
113 default = null;
114 example = "203.0.113.123";
115 description =
116 ''
117 The public IP address to which packets from the local
118 network are to be rewritten. If this is left empty, the
119 IP address associated with the external interface will be
120 used.
121 '';
122 };
123
124 networking.nat.forwardPorts = mkOption {
125 type = with types; listOf (submodule {
126 options = {
127 sourcePort = mkOption {
128 type = types.int;
129 example = 8080;
130 description = "Source port of the external interface";
131 };
132
133 destination = mkOption {
134 type = types.str;
135 example = "10.0.0.1:80";
136 description = "Forward connection to destination ip:port";
137 };
138
139 proto = mkOption {
140 type = types.str;
141 default = "tcp";
142 example = "udp";
143 description = "Protocol of forwarded connection";
144 };
145 };
146 });
147 default = [];
148 example = [ { sourcePort = 8080; destination = "10.0.0.1:80"; proto = "tcp"; } ];
149 description =
150 ''
151 List of forwarded ports from the external interface to
152 internal destinations by using DNAT.
153 '';
154 };
155
156 };
157
158
159 ###### implementation
160
161 config = mkMerge [
162 { networking.firewall.extraCommands = mkBefore flushNat; }
163 (mkIf config.networking.nat.enable {
164
165 environment.systemPackages = [ pkgs.iptables ];
166
167 boot = {
168 kernelModules = [ "nf_nat_ftp" ];
169 kernel.sysctl = {
170 "net.ipv4.conf.all.forwarding" = mkOverride 99 true;
171 "net.ipv4.conf.default.forwarding" = mkOverride 99 true;
172 };
173 };
174
175 networking.firewall = mkIf config.networking.firewall.enable {
176 extraCommands = setupNat;
177 extraStopCommands = flushNat;
178 };
179
180 systemd.services = mkIf (!config.networking.firewall.enable) { nat = {
181 description = "Network Address Translation";
182 wantedBy = [ "network.target" ];
183 after = [ "network-pre.target" "systemd-modules-load.service" ];
184 path = [ pkgs.iptables ];
185 unitConfig.ConditionCapability = "CAP_NET_ADMIN";
186
187 serviceConfig = {
188 Type = "oneshot";
189 RemainAfterExit = true;
190 };
191
192 script = flushNat + setupNat;
193
194 postStop = flushNat;
195 }; };
196 })
197 ];
198}