at 23.11-pre 6.3 kB view raw
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 13in 14 15{ 16 17 options = { 18 19 networking.nat.enable = mkOption { 20 type = types.bool; 21 default = false; 22 description = lib.mdDoc '' 23 Whether to enable Network Address Translation (NAT). 24 ''; 25 }; 26 27 networking.nat.enableIPv6 = mkOption { 28 type = types.bool; 29 default = false; 30 description = lib.mdDoc '' 31 Whether to enable IPv6 NAT. 32 ''; 33 }; 34 35 networking.nat.internalInterfaces = mkOption { 36 type = types.listOf types.str; 37 default = [ ]; 38 example = [ "eth0" ]; 39 description = lib.mdDoc '' 40 The interfaces for which to perform NAT. Packets coming from 41 these interface and destined for the external interface will 42 be rewritten. 43 ''; 44 }; 45 46 networking.nat.internalIPs = mkOption { 47 type = types.listOf types.str; 48 default = [ ]; 49 example = [ "192.168.1.0/24" ]; 50 description = lib.mdDoc '' 51 The IP address ranges for which to perform NAT. Packets 52 coming from these addresses (on any interface) and destined 53 for the external interface will be rewritten. 54 ''; 55 }; 56 57 networking.nat.internalIPv6s = mkOption { 58 type = types.listOf types.str; 59 default = [ ]; 60 example = [ "fc00::/64" ]; 61 description = lib.mdDoc '' 62 The IPv6 address ranges for which to perform NAT. Packets 63 coming from these addresses (on any interface) and destined 64 for the external interface will be rewritten. 65 ''; 66 }; 67 68 networking.nat.externalInterface = mkOption { 69 type = types.nullOr types.str; 70 default = null; 71 example = "eth1"; 72 description = lib.mdDoc '' 73 The name of the external network interface. 74 ''; 75 }; 76 77 networking.nat.externalIP = mkOption { 78 type = types.nullOr types.str; 79 default = null; 80 example = "203.0.113.123"; 81 description = lib.mdDoc '' 82 The public IP address to which packets from the local 83 network are to be rewritten. If this is left empty, the 84 IP address associated with the external interface will be 85 used. 86 ''; 87 }; 88 89 networking.nat.externalIPv6 = mkOption { 90 type = types.nullOr types.str; 91 default = null; 92 example = "2001:dc0:2001:11::175"; 93 description = lib.mdDoc '' 94 The public IPv6 address to which packets from the local 95 network are to be rewritten. If this is left empty, the 96 IP address associated with the external interface will be 97 used. 98 ''; 99 }; 100 101 networking.nat.forwardPorts = mkOption { 102 type = with types; listOf (submodule { 103 options = { 104 sourcePort = mkOption { 105 type = types.either types.int (types.strMatching "[[:digit:]]+:[[:digit:]]+"); 106 example = 8080; 107 description = lib.mdDoc "Source port of the external interface; to specify a port range, use a string with a colon (e.g. \"60000:61000\")"; 108 }; 109 110 destination = mkOption { 111 type = types.str; 112 example = "10.0.0.1:80"; 113 description = lib.mdDoc "Forward connection to destination ip:port (or [ipv6]:port); to specify a port range, use ip:start-end"; 114 }; 115 116 proto = mkOption { 117 type = types.str; 118 default = "tcp"; 119 example = "udp"; 120 description = lib.mdDoc "Protocol of forwarded connection"; 121 }; 122 123 loopbackIPs = mkOption { 124 type = types.listOf types.str; 125 default = [ ]; 126 example = literalExpression ''[ "55.1.2.3" ]''; 127 description = lib.mdDoc "Public IPs for NAT reflection; for connections to `loopbackip:sourcePort` from the host itself and from other hosts behind NAT"; 128 }; 129 }; 130 }); 131 default = [ ]; 132 example = [ 133 { sourcePort = 8080; destination = "10.0.0.1:80"; proto = "tcp"; } 134 { sourcePort = 8080; destination = "[fc00::2]:80"; proto = "tcp"; } 135 ]; 136 description = lib.mdDoc '' 137 List of forwarded ports from the external interface to 138 internal destinations by using DNAT. Destination can be 139 IPv6 if IPv6 NAT is enabled. 140 ''; 141 }; 142 143 networking.nat.dmzHost = mkOption { 144 type = types.nullOr types.str; 145 default = null; 146 example = "10.0.0.1"; 147 description = lib.mdDoc '' 148 The local IP address to which all traffic that does not match any 149 forwarding rule is forwarded. 150 ''; 151 }; 152 153 }; 154 155 156 config = mkIf config.networking.nat.enable { 157 158 assertions = [ 159 { 160 assertion = cfg.enableIPv6 -> config.networking.enableIPv6; 161 message = "networking.nat.enableIPv6 requires networking.enableIPv6"; 162 } 163 { 164 assertion = (cfg.dmzHost != null) -> (cfg.externalInterface != null); 165 message = "networking.nat.dmzHost requires networking.nat.externalInterface"; 166 } 167 { 168 assertion = (cfg.forwardPorts != [ ]) -> (cfg.externalInterface != null); 169 message = "networking.nat.forwardPorts requires networking.nat.externalInterface"; 170 } 171 ]; 172 173 # Use the same iptables package as in config.networking.firewall. 174 # When the firewall is enabled, this should be deduplicated without any 175 # error. 176 environment.systemPackages = [ config.networking.firewall.package ]; 177 178 boot = { 179 kernelModules = [ "nf_nat_ftp" ]; 180 kernel.sysctl = { 181 "net.ipv4.conf.all.forwarding" = mkOverride 99 true; 182 "net.ipv4.conf.default.forwarding" = mkOverride 99 true; 183 } // optionalAttrs cfg.enableIPv6 { 184 # Do not prevent IPv6 autoconfiguration. 185 # See <http://strugglers.net/~andy/blog/2011/09/04/linux-ipv6-router-advertisements-and-forwarding/>. 186 "net.ipv6.conf.all.accept_ra" = mkOverride 99 2; 187 "net.ipv6.conf.default.accept_ra" = mkOverride 99 2; 188 189 # Forward IPv6 packets. 190 "net.ipv6.conf.all.forwarding" = mkOverride 99 true; 191 "net.ipv6.conf.default.forwarding" = mkOverride 99 true; 192 }; 193 }; 194 195 }; 196}