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}