commit
fcf38c9bac
@ -38,7 +38,7 @@
|
||||
|
||||
- [stalwart-mail](https://stalw.art), an all-in-one email server (SMTP, IMAP, JMAP). Available as [services.stalwart-mail](#opt-services.stalwart-mail.enable).
|
||||
|
||||
- [Jool](https://nicmx.github.io/Jool/en/index.html), an Open Source implementation of IPv4/IPv6 translation on Linux. Available as [networking.jool.enable](#opt-networking.jool.enable).
|
||||
- [Jool](https://nicmx.github.io/Jool/en/index.html), a kernelspace NAT64 and SIIT implementation, providing translation between IPv4 and IPv6. Available as [networking.jool.enable](#opt-networking.jool.enable).
|
||||
|
||||
- [Apache Guacamole](https://guacamole.apache.org/), a cross-platform, clientless remote desktop gateway. Available as [services.guacamole-server](#opt-services.guacamole-server.enable) and [services.guacamole-client](#opt-services.guacamole-client.enable) services.
|
||||
|
||||
|
@ -16,7 +16,7 @@ let
|
||||
TemporaryFileSystem = [ "/" ];
|
||||
BindReadOnlyPaths = [
|
||||
builtins.storeDir
|
||||
"/run/current-system/kernel-modules"
|
||||
"/run/booted-system/kernel-modules"
|
||||
];
|
||||
|
||||
# Give capabilities to load the module and configure it
|
||||
@ -31,26 +31,96 @@ let
|
||||
|
||||
configFormat = pkgs.formats.json {};
|
||||
|
||||
mkDefaultAttrs = lib.mapAttrs (n: v: lib.mkDefault v);
|
||||
# Generate the config file of instance `name`
|
||||
nat64Conf = name:
|
||||
configFormat.generate "jool-nat64-${name}.conf"
|
||||
(cfg.nat64.${name} // { instance = name; });
|
||||
siitConf = name:
|
||||
configFormat.generate "jool-siit-${name}.conf"
|
||||
(cfg.siit.${name} // { instance = name; });
|
||||
|
||||
defaultNat64 = {
|
||||
instance = "default";
|
||||
framework = "netfilter";
|
||||
global.pool6 = "64:ff9b::/96";
|
||||
};
|
||||
defaultSiit = {
|
||||
instance = "default";
|
||||
framework = "netfilter";
|
||||
# NAT64 config type
|
||||
nat64Options = lib.types.submodule {
|
||||
# The format is plain JSON
|
||||
freeformType = configFormat.type;
|
||||
# Some options with a default value
|
||||
options.framework = lib.mkOption {
|
||||
type = lib.types.enum [ "netfilter" "iptables" ];
|
||||
default = "netfilter";
|
||||
description = lib.mdDoc ''
|
||||
The framework to use for attaching Jool's translation to the exist
|
||||
kernel packet processing rules. See the
|
||||
[documentation](https://nicmx.github.io/Jool/en/intro-jool.html#design)
|
||||
for the differences between the two options.
|
||||
'';
|
||||
};
|
||||
options.global.pool6 = lib.mkOption {
|
||||
type = lib.types.strMatching "[[:xdigit:]:]+/[[:digit:]]+"
|
||||
// { description = "Network prefix in CIDR notation"; };
|
||||
default = "64:ff9b::/96";
|
||||
description = lib.mdDoc ''
|
||||
The prefix used for embedding IPv4 into IPv6 addresses.
|
||||
Defaults to the well-known NAT64 prefix, defined by
|
||||
[RFC 6052](https://datatracker.ietf.org/doc/html/rfc6052).
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
nat64Conf = configFormat.generate "jool-nat64.conf" cfg.nat64.config;
|
||||
siitConf = configFormat.generate "jool-siit.conf" cfg.siit.config;
|
||||
# SIIT config type
|
||||
siitOptions = lib.types.submodule {
|
||||
# The format is, again, plain JSON
|
||||
freeformType = configFormat.type;
|
||||
# Some options with a default value
|
||||
options = { inherit (nat64Options.getSubOptions []) framework; };
|
||||
};
|
||||
|
||||
makeNat64Unit = name: opts: {
|
||||
"jool-nat64-${name}" = {
|
||||
description = "Jool, NAT64 setup of instance ${name}";
|
||||
documentation = [ "https://nicmx.github.io/Jool/en/documentation.html" ];
|
||||
after = [ "network.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
ExecStartPre = "${pkgs.kmod}/bin/modprobe jool";
|
||||
ExecStart = "${jool-cli}/bin/jool file handle ${nat64Conf name}";
|
||||
ExecStop = "${jool-cli}/bin/jool -f ${nat64Conf name} instance remove";
|
||||
} // hardening;
|
||||
};
|
||||
};
|
||||
|
||||
makeSiitUnit = name: opts: {
|
||||
"jool-siit-${name}" = {
|
||||
description = "Jool, SIIT setup of instance ${name}";
|
||||
documentation = [ "https://nicmx.github.io/Jool/en/documentation.html" ];
|
||||
after = [ "network.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
ExecStartPre = "${pkgs.kmod}/bin/modprobe jool_siit";
|
||||
ExecStart = "${jool-cli}/bin/jool_siit file handle ${siitConf name}";
|
||||
ExecStop = "${jool-cli}/bin/jool_siit -f ${siitConf name} instance remove";
|
||||
} // hardening;
|
||||
};
|
||||
};
|
||||
|
||||
checkNat64 = name: _: ''
|
||||
printf 'Validating Jool configuration for NAT64 instance "${name}"... '
|
||||
jool file check ${nat64Conf name}
|
||||
printf 'Ok.\n'; touch "$out"
|
||||
'';
|
||||
|
||||
checkSiit = name: _: ''
|
||||
printf 'Validating Jool configuration for SIIT instance "${name}"... '
|
||||
jool_siit file check ${siitConf name}
|
||||
printf 'Ok.\n'; touch "$out"
|
||||
'';
|
||||
|
||||
in
|
||||
|
||||
{
|
||||
###### interface
|
||||
|
||||
options = {
|
||||
networking.jool.enable = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
@ -64,157 +134,146 @@ in
|
||||
NAT64, analogous to the IPv4 NAPT. Refer to the upstream
|
||||
[documentation](https://nicmx.github.io/Jool/en/intro-xlat.html) for
|
||||
the supported modes of translation and how to configure them.
|
||||
|
||||
Enabling this option will install the Jool kernel module and the
|
||||
command line tools for controlling it.
|
||||
'';
|
||||
};
|
||||
|
||||
networking.jool.nat64.enable = lib.mkEnableOption (lib.mdDoc "a NAT64 instance of Jool.");
|
||||
networking.jool.nat64.config = lib.mkOption {
|
||||
type = configFormat.type;
|
||||
default = defaultNat64;
|
||||
networking.jool.nat64 = lib.mkOption {
|
||||
type = lib.types.attrsOf nat64Options;
|
||||
default = { };
|
||||
example = lib.literalExpression ''
|
||||
{
|
||||
# custom NAT64 prefix
|
||||
global.pool6 = "2001:db8:64::/96";
|
||||
default = {
|
||||
# custom NAT64 prefix
|
||||
global.pool6 = "2001:db8:64::/96";
|
||||
|
||||
# Port forwarding
|
||||
bib = [
|
||||
{ # SSH 192.0.2.16 → 2001:db8:a::1
|
||||
"protocol" = "TCP";
|
||||
"ipv4 address" = "192.0.2.16#22";
|
||||
"ipv6 address" = "2001:db8:a::1#22";
|
||||
}
|
||||
{ # DNS (TCP) 192.0.2.16 → 2001:db8:a::2
|
||||
"protocol" = "TCP";
|
||||
"ipv4 address" = "192.0.2.16#53";
|
||||
"ipv6 address" = "2001:db8:a::2#53";
|
||||
}
|
||||
{ # DNS (UDP) 192.0.2.16 → 2001:db8:a::2
|
||||
"protocol" = "UDP";
|
||||
"ipv4 address" = "192.0.2.16#53";
|
||||
"ipv6 address" = "2001:db8:a::2#53";
|
||||
}
|
||||
];
|
||||
# Port forwarding
|
||||
bib = [
|
||||
{ # SSH 192.0.2.16 → 2001:db8:a::1
|
||||
"protocol" = "TCP";
|
||||
"ipv4 address" = "192.0.2.16#22";
|
||||
"ipv6 address" = "2001:db8:a::1#22";
|
||||
}
|
||||
{ # DNS (TCP) 192.0.2.16 → 2001:db8:a::2
|
||||
"protocol" = "TCP";
|
||||
"ipv4 address" = "192.0.2.16#53";
|
||||
"ipv6 address" = "2001:db8:a::2#53";
|
||||
}
|
||||
{ # DNS (UDP) 192.0.2.16 → 2001:db8:a::2
|
||||
"protocol" = "UDP";
|
||||
"ipv4 address" = "192.0.2.16#53";
|
||||
"ipv6 address" = "2001:db8:a::2#53";
|
||||
}
|
||||
];
|
||||
|
||||
pool4 = [
|
||||
# Ports for dynamic translation
|
||||
{ protocol = "TCP"; prefix = "192.0.2.16/32"; "port range" = "40001-65535"; }
|
||||
{ protocol = "UDP"; prefix = "192.0.2.16/32"; "port range" = "40001-65535"; }
|
||||
{ protocol = "ICMP"; prefix = "192.0.2.16/32"; "port range" = "40001-65535"; }
|
||||
pool4 = [
|
||||
# Port ranges for dynamic translation
|
||||
{ protocol = "TCP"; prefix = "192.0.2.16/32"; "port range" = "40001-65535"; }
|
||||
{ protocol = "UDP"; prefix = "192.0.2.16/32"; "port range" = "40001-65535"; }
|
||||
{ protocol = "ICMP"; prefix = "192.0.2.16/32"; "port range" = "40001-65535"; }
|
||||
|
||||
# Ports for static BIB entries
|
||||
{ protocol = "TCP"; prefix = "192.0.2.16/32"; "port range" = "22"; }
|
||||
{ protocol = "UDP"; prefix = "192.0.2.16/32"; "port range" = "53"; }
|
||||
];
|
||||
# Ports for static BIB entries
|
||||
{ protocol = "TCP"; prefix = "192.0.2.16/32"; "port range" = "22"; }
|
||||
{ protocol = "UDP"; prefix = "192.0.2.16/32"; "port range" = "53"; }
|
||||
];
|
||||
};
|
||||
}
|
||||
'';
|
||||
description = lib.mdDoc ''
|
||||
The configuration of a stateful NAT64 instance of Jool managed through
|
||||
NixOS. See https://nicmx.github.io/Jool/en/config-atomic.html for the
|
||||
available options.
|
||||
Definitions of NAT64 instances of Jool.
|
||||
See the
|
||||
[documentation](https://nicmx.github.io/Jool/en/config-atomic.html) for
|
||||
the available options. Also check out the
|
||||
[tutorial](https://nicmx.github.io/Jool/en/run-nat64.html) for an
|
||||
introduction to NAT64 and how to troubleshoot the setup.
|
||||
|
||||
The attribute name defines the name of the instance, with the main one
|
||||
being `default`: this can be accessed from the command line without
|
||||
specifying the name with `-i`.
|
||||
|
||||
::: {.note}
|
||||
Existing or more instances created manually will not interfere with the
|
||||
NixOS instance, provided the respective `pool4` addresses and port
|
||||
ranges are not overlapping.
|
||||
Instances created imperatively from the command line will not interfere
|
||||
with the NixOS instances, provided the respective `pool4` addresses and
|
||||
port ranges are not overlapping.
|
||||
:::
|
||||
|
||||
::: {.warning}
|
||||
Changes to the NixOS instance performed via `jool instance nixos-nat64`
|
||||
are applied correctly but will be lost after restarting
|
||||
`jool-nat64.service`.
|
||||
Changes to an instance performed via `jool -i <name>` are applied
|
||||
correctly but will be lost after restarting the respective
|
||||
`jool-nat64-<name>.service`.
|
||||
:::
|
||||
'';
|
||||
};
|
||||
|
||||
networking.jool.siit.enable = lib.mkEnableOption (lib.mdDoc "a SIIT instance of Jool.");
|
||||
networking.jool.siit.config = lib.mkOption {
|
||||
type = configFormat.type;
|
||||
default = defaultSiit;
|
||||
networking.jool.siit = lib.mkOption {
|
||||
type = lib.types.attrsOf siitOptions;
|
||||
default = { };
|
||||
example = lib.literalExpression ''
|
||||
{
|
||||
# Maps any IPv4 address x.y.z.t to 2001:db8::x.y.z.t and v.v.
|
||||
pool6 = "2001:db8::/96";
|
||||
default = {
|
||||
# Maps any IPv4 address x.y.z.t to 2001:db8::x.y.z.t and v.v.
|
||||
global.pool6 = "2001:db8::/96";
|
||||
|
||||
# Explicit address mappings
|
||||
eamt = [
|
||||
# 2001:db8:1:: ←→ 192.0.2.0
|
||||
{ "ipv6 prefix": "2001:db8:1::/128", "ipv4 prefix": "192.0.2.0" }
|
||||
# 2001:db8:1::x ←→ 198.51.100.x
|
||||
{ "ipv6 prefix": "2001:db8:2::/120", "ipv4 prefix": "198.51.100.0/24" }
|
||||
]
|
||||
# Explicit address mappings
|
||||
eamt = [
|
||||
# 2001:db8:1:: ←→ 192.0.2.0
|
||||
{ "ipv6 prefix" = "2001:db8:1::/128"; "ipv4 prefix" = "192.0.2.0"; }
|
||||
# 2001:db8:1::x ←→ 198.51.100.x
|
||||
{ "ipv6 prefix" = "2001:db8:2::/120"; "ipv4 prefix" = "198.51.100.0/24"; }
|
||||
];
|
||||
};
|
||||
}
|
||||
'';
|
||||
description = lib.mdDoc ''
|
||||
The configuration of a SIIT instance of Jool managed through
|
||||
NixOS. See https://nicmx.github.io/Jool/en/config-atomic.html for the
|
||||
available options.
|
||||
Definitions of SIIT instances of Jool.
|
||||
See the
|
||||
[documentation](https://nicmx.github.io/Jool/en/config-atomic.html) for
|
||||
the available options. Also check out the
|
||||
[tutorial](https://nicmx.github.io/Jool/en/run-vanilla.html) for an
|
||||
introduction to SIIT and how to troubleshoot the setup.
|
||||
|
||||
The attribute name defines the name of the instance, with the main one
|
||||
being `default`: this can be accessed from the command line without
|
||||
specifying the name with `-i`.
|
||||
|
||||
::: {.note}
|
||||
Existing or more instances created manually will not interfere with the
|
||||
NixOS instance, provided the respective `EAMT` address mappings are not
|
||||
overlapping.
|
||||
Instances created imperatively from the command line will not interfere
|
||||
with the NixOS instances, provided the respective EAMT addresses and
|
||||
port ranges are not overlapping.
|
||||
:::
|
||||
|
||||
::: {.warning}
|
||||
Changes to the NixOS instance performed via `jool instance nixos-siit`
|
||||
are applied correctly but will be lost after restarting
|
||||
`jool-siit.service`.
|
||||
Changes to an instance performed via `jool -i <name>` are applied
|
||||
correctly but will be lost after restarting the respective
|
||||
`jool-siit-<name>.service`.
|
||||
:::
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
###### implementation
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
environment.systemPackages = [ jool-cli ];
|
||||
# Install kernel module and cli tools
|
||||
boot.extraModulePackages = [ jool ];
|
||||
environment.systemPackages = [ jool-cli ];
|
||||
|
||||
systemd.services.jool-nat64 = lib.mkIf cfg.nat64.enable {
|
||||
description = "Jool, NAT64 setup";
|
||||
documentation = [ "https://nicmx.github.io/Jool/en/documentation.html" ];
|
||||
after = [ "network.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
reloadIfChanged = true;
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
ExecStartPre = "${pkgs.kmod}/bin/modprobe jool";
|
||||
ExecStart = "${jool-cli}/bin/jool file handle ${nat64Conf}";
|
||||
ExecStop = "${jool-cli}/bin/jool -f ${nat64Conf} instance remove";
|
||||
} // hardening;
|
||||
};
|
||||
|
||||
systemd.services.jool-siit = lib.mkIf cfg.siit.enable {
|
||||
description = "Jool, SIIT setup";
|
||||
documentation = [ "https://nicmx.github.io/Jool/en/documentation.html" ];
|
||||
after = [ "network.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
reloadIfChanged = true;
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
ExecStartPre = "${pkgs.kmod}/bin/modprobe jool_siit";
|
||||
ExecStart = "${jool-cli}/bin/jool_siit file handle ${siitConf}";
|
||||
ExecStop = "${jool-cli}/bin/jool_siit -f ${siitConf} instance remove";
|
||||
} // hardening;
|
||||
};
|
||||
|
||||
system.checks = lib.singleton (pkgs.runCommand "jool-validated" {
|
||||
nativeBuildInputs = [ pkgs.buildPackages.jool-cli ];
|
||||
preferLocalBuild = true;
|
||||
} ''
|
||||
printf 'Validating Jool configuration... '
|
||||
${lib.optionalString cfg.siit.enable "jool_siit file check ${siitConf}"}
|
||||
${lib.optionalString cfg.nat64.enable "jool file check ${nat64Conf}"}
|
||||
printf 'ok\n'
|
||||
touch "$out"
|
||||
'');
|
||||
|
||||
networking.jool.nat64.config = mkDefaultAttrs defaultNat64;
|
||||
networking.jool.siit.config = mkDefaultAttrs defaultSiit;
|
||||
# Install services for each instance
|
||||
systemd.services = lib.mkMerge
|
||||
(lib.mapAttrsToList makeNat64Unit cfg.nat64 ++
|
||||
lib.mapAttrsToList makeSiitUnit cfg.siit);
|
||||
|
||||
# Check the configuration of each instance
|
||||
system.checks = lib.optional (cfg.nat64 != {} || cfg.siit != {})
|
||||
(pkgs.runCommand "jool-validated"
|
||||
{
|
||||
nativeBuildInputs = with pkgs.buildPackages; [ jool-cli ];
|
||||
preferLocalBuild = true;
|
||||
}
|
||||
(lib.concatStrings
|
||||
(lib.mapAttrsToList checkNat64 cfg.nat64 ++
|
||||
lib.mapAttrsToList checkSiit cfg.siit)));
|
||||
};
|
||||
|
||||
meta.maintainers = with lib.maintainers; [ rnhmjoj ];
|
||||
|
@ -395,7 +395,7 @@ in {
|
||||
jibri = handleTest ./jibri.nix {};
|
||||
jirafeau = handleTest ./jirafeau.nix {};
|
||||
jitsi-meet = handleTest ./jitsi-meet.nix {};
|
||||
jool = handleTest ./jool.nix {};
|
||||
jool = import ./jool.nix { inherit pkgs runTest; };
|
||||
k3s = handleTest ./k3s {};
|
||||
kafka = handleTest ./kafka.nix {};
|
||||
kanidm = handleTest ./kanidm.nix {};
|
||||
|
@ -1,9 +1,4 @@
|
||||
{ system ? builtins.currentSystem,
|
||||
config ? {},
|
||||
pkgs ? import ../.. { inherit system config; }
|
||||
}:
|
||||
|
||||
with import ../lib/testing-python.nix { inherit system pkgs; };
|
||||
{ pkgs, runTest }:
|
||||
|
||||
let
|
||||
inherit (pkgs) lib;
|
||||
@ -23,7 +18,6 @@ let
|
||||
description = "Mock webserver";
|
||||
wants = [ "network-online.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig.Restart = "always";
|
||||
script = ''
|
||||
while true; do
|
||||
{
|
||||
@ -40,7 +34,7 @@ let
|
||||
in
|
||||
|
||||
{
|
||||
siit = makeTest {
|
||||
siit = runTest {
|
||||
# This test simulates the setup described in [1] with two IPv6 and
|
||||
# IPv4-only devices on different subnets communicating through a border
|
||||
# relay running Jool in SIIT mode.
|
||||
@ -49,8 +43,7 @@ in
|
||||
meta.maintainers = with lib.maintainers; [ rnhmjoj ];
|
||||
|
||||
# Border relay
|
||||
nodes.relay = { ... }: {
|
||||
imports = [ ../modules/profiles/minimal.nix ];
|
||||
nodes.relay = {
|
||||
virtualisation.vlans = [ 1 2 ];
|
||||
|
||||
# Enable packet routing
|
||||
@ -65,20 +58,13 @@ in
|
||||
eth2.ipv4.addresses = [ { address = "192.0.2.1"; prefixLength = 24; } ];
|
||||
};
|
||||
|
||||
networking.jool = {
|
||||
enable = true;
|
||||
siit.enable = true;
|
||||
siit.config.global.pool6 = "fd::/96";
|
||||
};
|
||||
networking.jool.enable = true;
|
||||
networking.jool.siit.default.global.pool6 = "fd::/96";
|
||||
};
|
||||
|
||||
# IPv6 only node
|
||||
nodes.alice = { ... }: {
|
||||
imports = [
|
||||
../modules/profiles/minimal.nix
|
||||
ipv6Only
|
||||
(webserver 6 "Hello, Bob!")
|
||||
];
|
||||
nodes.alice = {
|
||||
imports = [ ipv6Only (webserver 6 "Hello, Bob!") ];
|
||||
|
||||
virtualisation.vlans = [ 1 ];
|
||||
networking.interfaces.eth1.ipv6 = {
|
||||
@ -89,12 +75,8 @@ in
|
||||
};
|
||||
|
||||
# IPv4 only node
|
||||
nodes.bob = { ... }: {
|
||||
imports = [
|
||||
../modules/profiles/minimal.nix
|
||||
ipv4Only
|
||||
(webserver 4 "Hello, Alice!")
|
||||
];
|
||||
nodes.bob = {
|
||||
imports = [ ipv4Only (webserver 4 "Hello, Alice!") ];
|
||||
|
||||
virtualisation.vlans = [ 2 ];
|
||||
networking.interfaces.eth1.ipv4 = {
|
||||
@ -107,17 +89,17 @@ in
|
||||
testScript = ''
|
||||
start_all()
|
||||
|
||||
relay.wait_for_unit("jool-siit.service")
|
||||
relay.wait_for_unit("jool-siit-default.service")
|
||||
alice.wait_for_unit("network-addresses-eth1.service")
|
||||
bob.wait_for_unit("network-addresses-eth1.service")
|
||||
|
||||
with subtest("Alice and Bob can't ping each other"):
|
||||
relay.systemctl("stop jool-siit.service")
|
||||
relay.systemctl("stop jool-siit-default.service")
|
||||
alice.fail("ping -c1 fd::192.0.2.16")
|
||||
bob.fail("ping -c1 198.51.100.8")
|
||||
|
||||
with subtest("Alice and Bob can ping using the relay"):
|
||||
relay.systemctl("start jool-siit.service")
|
||||
relay.systemctl("start jool-siit-default.service")
|
||||
alice.wait_until_succeeds("ping -c1 fd::192.0.2.16")
|
||||
bob.wait_until_succeeds("ping -c1 198.51.100.8")
|
||||
|
||||
@ -132,7 +114,7 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
nat64 = makeTest {
|
||||
nat64 = runTest {
|
||||
# This test simulates the setup described in [1] with two IPv6-only nodes
|
||||
# (a client and a homeserver) on the LAN subnet and an IPv4 node on the WAN.
|
||||
# The router runs Jool in stateful NAT64 mode, masquarading the LAN and
|
||||
@ -142,8 +124,7 @@ in
|
||||
meta.maintainers = with lib.maintainers; [ rnhmjoj ];
|
||||
|
||||
# Router
|
||||
nodes.router = { ... }: {
|
||||
imports = [ ../modules/profiles/minimal.nix ];
|
||||
nodes.router = {
|
||||
virtualisation.vlans = [ 1 2 ];
|
||||
|
||||
# Enable packet routing
|
||||
@ -158,32 +139,29 @@ in
|
||||
eth2.ipv4.addresses = [ { address = "203.0.113.1"; prefixLength = 24; } ];
|
||||
};
|
||||
|
||||
networking.jool = {
|
||||
enable = true;
|
||||
nat64.enable = true;
|
||||
nat64.config = {
|
||||
bib = [
|
||||
{ # forward HTTP 203.0.113.1 (router) → 2001:db8::9 (homeserver)
|
||||
"protocol" = "TCP";
|
||||
"ipv4 address" = "203.0.113.1#80";
|
||||
"ipv6 address" = "2001:db8::9#80";
|
||||
}
|
||||
];
|
||||
pool4 = [
|
||||
# Ports for dynamic translation
|
||||
{ protocol = "TCP"; prefix = "203.0.113.1/32"; "port range" = "40001-65535"; }
|
||||
{ protocol = "UDP"; prefix = "203.0.113.1/32"; "port range" = "40001-65535"; }
|
||||
{ protocol = "ICMP"; prefix = "203.0.113.1/32"; "port range" = "40001-65535"; }
|
||||
# Ports for static BIB entries
|
||||
{ protocol = "TCP"; prefix = "203.0.113.1/32"; "port range" = "80"; }
|
||||
];
|
||||
};
|
||||
networking.jool.enable = true;
|
||||
networking.jool.nat64.default = {
|
||||
bib = [
|
||||
{ # forward HTTP 203.0.113.1 (router) → 2001:db8::9 (homeserver)
|
||||
"protocol" = "TCP";
|
||||
"ipv4 address" = "203.0.113.1#80";
|
||||
"ipv6 address" = "2001:db8::9#80";
|
||||
}
|
||||
];
|
||||
pool4 = [
|
||||
# Ports for dynamic translation
|
||||
{ protocol = "TCP"; prefix = "203.0.113.1/32"; "port range" = "40001-65535"; }
|
||||
{ protocol = "UDP"; prefix = "203.0.113.1/32"; "port range" = "40001-65535"; }
|
||||
{ protocol = "ICMP"; prefix = "203.0.113.1/32"; "port range" = "40001-65535"; }
|
||||
# Ports for static BIB entries
|
||||
{ protocol = "TCP"; prefix = "203.0.113.1/32"; "port range" = "80"; }
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
# LAN client (IPv6 only)
|
||||
nodes.client = { ... }: {
|
||||
imports = [ ../modules/profiles/minimal.nix ipv6Only ];
|
||||
nodes.client = {
|
||||
imports = [ ipv6Only ];
|
||||
virtualisation.vlans = [ 1 ];
|
||||
|
||||
networking.interfaces.eth1.ipv6 = {
|
||||
@ -194,12 +172,8 @@ in
|
||||
};
|
||||
|
||||
# LAN server (IPv6 only)
|
||||
nodes.homeserver = { ... }: {
|
||||
imports = [
|
||||
../modules/profiles/minimal.nix
|
||||
ipv6Only
|
||||
(webserver 6 "Hello from IPv6!")
|
||||
];
|
||||
nodes.homeserver = {
|
||||
imports = [ ipv6Only (webserver 6 "Hello from IPv6!") ];
|
||||
|
||||
virtualisation.vlans = [ 1 ];
|
||||
networking.interfaces.eth1.ipv6 = {
|
||||
@ -210,12 +184,8 @@ in
|
||||
};
|
||||
|
||||
# WAN server (IPv4 only)
|
||||
nodes.server = { ... }: {
|
||||
imports = [
|
||||
../modules/profiles/minimal.nix
|
||||
ipv4Only
|
||||
(webserver 4 "Hello from IPv4!")
|
||||
];
|
||||
nodes.server = {
|
||||
imports = [ ipv4Only (webserver 4 "Hello from IPv4!") ];
|
||||
|
||||
virtualisation.vlans = [ 2 ];
|
||||
networking.interfaces.eth1.ipv4.addresses =
|
||||
@ -229,7 +199,7 @@ in
|
||||
node.wait_for_unit("network-addresses-eth1.service")
|
||||
|
||||
with subtest("Client can ping the WAN server"):
|
||||
router.wait_for_unit("jool-nat64.service")
|
||||
router.wait_for_unit("jool-nat64-default.service")
|
||||
client.succeed("ping -c1 64:ff9b::203.0.113.16")
|
||||
|
||||
with subtest("Client can connect to the WAN webserver"):
|
||||
|
Loading…
Reference in New Issue
Block a user