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 tcp \
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 = types.listOf types.optionSet;
126 default = [];
127 example = [ { sourcePort = 8080; destination = "10.0.0.1:80"; } ];
128 options = {
129 sourcePort = mkOption {
130 type = types.int;
131 example = 8080;
132 description = "Source port of the external interface";
133 };
134
135 destination = mkOption {
136 type = types.str;
137 example = "10.0.0.1:80";
138 description = "Forward tcp connection to destination ip:port";
139 };
140 };
141
142 description =
143 ''
144 List of forwarded ports from the external interface to
145 internal destinations by using DNAT.
146 '';
147 };
148
149 };
150
151
152 ###### implementation
153
154 config = mkIf config.networking.nat.enable {
155
156 environment.systemPackages = [ pkgs.iptables ];
157
158 boot = {
159 kernelModules = [ "nf_nat_ftp" ];
160 kernel.sysctl = {
161 "net.ipv4.conf.all.forwarding" = mkOverride 99 true;
162 "net.ipv4.conf.default.forwarding" = mkOverride 99 true;
163 };
164 };
165
166 networking.firewall = mkIf config.networking.firewall.enable {
167 extraCommands = mkMerge [ (mkBefore flushNat) setupNat ];
168 extraStopCommands = flushNat;
169 };
170
171 systemd.services = mkIf (!config.networking.firewall.enable) { nat = {
172 description = "Network Address Translation";
173 wantedBy = [ "network.target" ];
174 after = [ "network-interfaces.target" "systemd-modules-load.service" ];
175 path = [ pkgs.iptables ];
176 unitConfig.ConditionCapability = "CAP_NET_ADMIN";
177
178 serviceConfig = {
179 Type = "oneshot";
180 RemainAfterExit = true;
181 };
182
183 script = flushNat + setupNat;
184
185 postStop = flushNat;
186 }; };
187 };
188}