2020-04-24 23:36:52 +00:00
|
|
|
# This module enables Network Address Translation (NAT).
|
|
|
|
# XXX: todo: support multiple upstream links
|
|
|
|
# see http://yesican.chsoft.biz/lartc/MultihomedLinuxNetworking.html
|
|
|
|
|
|
|
|
{ config, lib, pkgs, ... }:
|
|
|
|
|
|
|
|
with lib;
|
|
|
|
|
|
|
|
let
|
|
|
|
|
2022-12-28 21:21:41 +00:00
|
|
|
cfg = config.networking.nat;
|
2020-04-24 23:36:52 +00:00
|
|
|
|
|
|
|
in
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
options = {
|
|
|
|
|
|
|
|
networking.nat.enable = mkOption {
|
|
|
|
type = types.bool;
|
|
|
|
default = false;
|
2022-12-28 21:21:41 +00:00
|
|
|
description = lib.mdDoc ''
|
|
|
|
Whether to enable Network Address Translation (NAT).
|
|
|
|
'';
|
2020-04-24 23:36:52 +00:00
|
|
|
};
|
|
|
|
|
2020-12-03 08:41:04 +00:00
|
|
|
networking.nat.enableIPv6 = mkOption {
|
|
|
|
type = types.bool;
|
|
|
|
default = false;
|
2022-12-28 21:21:41 +00:00
|
|
|
description = lib.mdDoc ''
|
|
|
|
Whether to enable IPv6 NAT.
|
|
|
|
'';
|
2020-12-03 08:41:04 +00:00
|
|
|
};
|
|
|
|
|
2020-04-24 23:36:52 +00:00
|
|
|
networking.nat.internalInterfaces = mkOption {
|
|
|
|
type = types.listOf types.str;
|
2022-12-28 21:21:41 +00:00
|
|
|
default = [ ];
|
2020-04-24 23:36:52 +00:00
|
|
|
example = [ "eth0" ];
|
2022-12-28 21:21:41 +00:00
|
|
|
description = lib.mdDoc ''
|
|
|
|
The interfaces for which to perform NAT. Packets coming from
|
|
|
|
these interface and destined for the external interface will
|
|
|
|
be rewritten.
|
|
|
|
'';
|
2020-04-24 23:36:52 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
networking.nat.internalIPs = mkOption {
|
|
|
|
type = types.listOf types.str;
|
2022-12-28 21:21:41 +00:00
|
|
|
default = [ ];
|
2020-04-24 23:36:52 +00:00
|
|
|
example = [ "192.168.1.0/24" ];
|
2022-12-28 21:21:41 +00:00
|
|
|
description = lib.mdDoc ''
|
|
|
|
The IP address ranges for which to perform NAT. Packets
|
|
|
|
coming from these addresses (on any interface) and destined
|
|
|
|
for the external interface will be rewritten.
|
|
|
|
'';
|
2020-04-24 23:36:52 +00:00
|
|
|
};
|
|
|
|
|
2020-12-03 08:41:04 +00:00
|
|
|
networking.nat.internalIPv6s = mkOption {
|
|
|
|
type = types.listOf types.str;
|
2022-12-28 21:21:41 +00:00
|
|
|
default = [ ];
|
2020-12-03 08:41:04 +00:00
|
|
|
example = [ "fc00::/64" ];
|
2022-12-28 21:21:41 +00:00
|
|
|
description = lib.mdDoc ''
|
|
|
|
The IPv6 address ranges for which to perform NAT. Packets
|
|
|
|
coming from these addresses (on any interface) and destined
|
|
|
|
for the external interface will be rewritten.
|
|
|
|
'';
|
2020-12-03 08:41:04 +00:00
|
|
|
};
|
|
|
|
|
2020-04-24 23:36:52 +00:00
|
|
|
networking.nat.externalInterface = mkOption {
|
|
|
|
type = types.nullOr types.str;
|
|
|
|
default = null;
|
|
|
|
example = "eth1";
|
2022-12-28 21:21:41 +00:00
|
|
|
description = lib.mdDoc ''
|
|
|
|
The name of the external network interface.
|
|
|
|
'';
|
2020-04-24 23:36:52 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
networking.nat.externalIP = mkOption {
|
|
|
|
type = types.nullOr types.str;
|
|
|
|
default = null;
|
|
|
|
example = "203.0.113.123";
|
2022-12-28 21:21:41 +00:00
|
|
|
description = lib.mdDoc ''
|
|
|
|
The public IP address to which packets from the local
|
|
|
|
network are to be rewritten. If this is left empty, the
|
|
|
|
IP address associated with the external interface will be
|
|
|
|
used.
|
|
|
|
'';
|
2020-04-24 23:36:52 +00:00
|
|
|
};
|
|
|
|
|
2020-12-03 08:41:04 +00:00
|
|
|
networking.nat.externalIPv6 = mkOption {
|
|
|
|
type = types.nullOr types.str;
|
|
|
|
default = null;
|
|
|
|
example = "2001:dc0:2001:11::175";
|
2022-12-28 21:21:41 +00:00
|
|
|
description = lib.mdDoc ''
|
|
|
|
The public IPv6 address to which packets from the local
|
|
|
|
network are to be rewritten. If this is left empty, the
|
|
|
|
IP address associated with the external interface will be
|
|
|
|
used.
|
|
|
|
'';
|
2020-12-03 08:41:04 +00:00
|
|
|
};
|
|
|
|
|
2020-04-24 23:36:52 +00:00
|
|
|
networking.nat.forwardPorts = mkOption {
|
|
|
|
type = with types; listOf (submodule {
|
|
|
|
options = {
|
|
|
|
sourcePort = mkOption {
|
|
|
|
type = types.either types.int (types.strMatching "[[:digit:]]+:[[:digit:]]+");
|
|
|
|
example = 8080;
|
2022-08-12 12:06:08 +00:00
|
|
|
description = lib.mdDoc "Source port of the external interface; to specify a port range, use a string with a colon (e.g. \"60000:61000\")";
|
2020-04-24 23:36:52 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
destination = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
example = "10.0.0.1:80";
|
2022-08-12 12:06:08 +00:00
|
|
|
description = lib.mdDoc "Forward connection to destination ip:port (or [ipv6]:port); to specify a port range, use ip:start-end";
|
2020-04-24 23:36:52 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
proto = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
default = "tcp";
|
|
|
|
example = "udp";
|
2022-08-12 12:06:08 +00:00
|
|
|
description = lib.mdDoc "Protocol of forwarded connection";
|
2020-04-24 23:36:52 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
loopbackIPs = mkOption {
|
|
|
|
type = types.listOf types.str;
|
2022-12-28 21:21:41 +00:00
|
|
|
default = [ ];
|
2021-10-06 13:57:05 +00:00
|
|
|
example = literalExpression ''[ "55.1.2.3" ]'';
|
2023-02-02 18:25:31 +00:00
|
|
|
description = lib.mdDoc "Public IPs for NAT reflection; for connections to `loopbackip:sourcePort` from the host itself and from other hosts behind NAT";
|
2020-04-24 23:36:52 +00:00
|
|
|
};
|
|
|
|
};
|
|
|
|
});
|
2022-12-28 21:21:41 +00:00
|
|
|
default = [ ];
|
2020-12-03 08:41:04 +00:00
|
|
|
example = [
|
|
|
|
{ sourcePort = 8080; destination = "10.0.0.1:80"; proto = "tcp"; }
|
|
|
|
{ sourcePort = 8080; destination = "[fc00::2]:80"; proto = "tcp"; }
|
|
|
|
];
|
2022-12-28 21:21:41 +00:00
|
|
|
description = lib.mdDoc ''
|
|
|
|
List of forwarded ports from the external interface to
|
|
|
|
internal destinations by using DNAT. Destination can be
|
|
|
|
IPv6 if IPv6 NAT is enabled.
|
|
|
|
'';
|
2020-04-24 23:36:52 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
networking.nat.dmzHost = mkOption {
|
|
|
|
type = types.nullOr types.str;
|
|
|
|
default = null;
|
|
|
|
example = "10.0.0.1";
|
2022-12-28 21:21:41 +00:00
|
|
|
description = lib.mdDoc ''
|
|
|
|
The local IP address to which all traffic that does not match any
|
|
|
|
forwarding rule is forwarded.
|
|
|
|
'';
|
2020-04-24 23:36:52 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2022-12-28 21:21:41 +00:00
|
|
|
config = mkIf config.networking.nat.enable {
|
|
|
|
|
|
|
|
assertions = [
|
|
|
|
{
|
|
|
|
assertion = cfg.enableIPv6 -> config.networking.enableIPv6;
|
|
|
|
message = "networking.nat.enableIPv6 requires networking.enableIPv6";
|
|
|
|
}
|
|
|
|
{
|
|
|
|
assertion = (cfg.dmzHost != null) -> (cfg.externalInterface != null);
|
|
|
|
message = "networking.nat.dmzHost requires networking.nat.externalInterface";
|
|
|
|
}
|
|
|
|
{
|
|
|
|
assertion = (cfg.forwardPorts != [ ]) -> (cfg.externalInterface != null);
|
|
|
|
message = "networking.nat.forwardPorts requires networking.nat.externalInterface";
|
|
|
|
}
|
|
|
|
];
|
|
|
|
|
|
|
|
# Use the same iptables package as in config.networking.firewall.
|
|
|
|
# When the firewall is enabled, this should be deduplicated without any
|
|
|
|
# error.
|
|
|
|
environment.systemPackages = [ config.networking.firewall.package ];
|
|
|
|
|
|
|
|
boot = {
|
|
|
|
kernelModules = [ "nf_nat_ftp" ];
|
|
|
|
kernel.sysctl = {
|
|
|
|
"net.ipv4.conf.all.forwarding" = mkOverride 99 true;
|
|
|
|
"net.ipv4.conf.default.forwarding" = mkOverride 99 true;
|
|
|
|
} // optionalAttrs cfg.enableIPv6 {
|
|
|
|
# Do not prevent IPv6 autoconfiguration.
|
|
|
|
# See <http://strugglers.net/~andy/blog/2011/09/04/linux-ipv6-router-advertisements-and-forwarding/>.
|
|
|
|
"net.ipv6.conf.all.accept_ra" = mkOverride 99 2;
|
|
|
|
"net.ipv6.conf.default.accept_ra" = mkOverride 99 2;
|
|
|
|
|
|
|
|
# Forward IPv6 packets.
|
|
|
|
"net.ipv6.conf.all.forwarding" = mkOverride 99 true;
|
|
|
|
"net.ipv6.conf.default.forwarding" = mkOverride 99 true;
|
2020-04-24 23:36:52 +00:00
|
|
|
};
|
2022-12-28 21:21:41 +00:00
|
|
|
};
|
2020-04-24 23:36:52 +00:00
|
|
|
|
2022-12-28 21:21:41 +00:00
|
|
|
};
|
2020-04-24 23:36:52 +00:00
|
|
|
}
|