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}