2023-08-22 20:05:09 +00:00
|
|
|
{ config, pkgs, lib, ... }:
|
|
|
|
|
|
|
|
let
|
|
|
|
cfg = config.networking.jool;
|
|
|
|
|
|
|
|
jool = config.boot.kernelPackages.jool;
|
|
|
|
jool-cli = pkgs.jool-cli;
|
|
|
|
|
|
|
|
hardening = {
|
|
|
|
# Run as unprivileged user
|
|
|
|
User = "jool";
|
|
|
|
Group = "jool";
|
|
|
|
DynamicUser = true;
|
|
|
|
|
|
|
|
# Restrict filesystem to only read the jool module
|
|
|
|
TemporaryFileSystem = [ "/" ];
|
|
|
|
BindReadOnlyPaths = [
|
|
|
|
builtins.storeDir
|
2023-10-09 19:29:22 +00:00
|
|
|
"/run/booted-system/kernel-modules"
|
2023-08-22 20:05:09 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
# Give capabilities to load the module and configure it
|
|
|
|
AmbientCapabilities = [ "CAP_SYS_MODULE" "CAP_NET_ADMIN" ];
|
|
|
|
RestrictAddressFamilies = [ "AF_NETLINK" ];
|
|
|
|
|
|
|
|
# Other restrictions
|
|
|
|
RestrictNamespaces = [ "net" ];
|
|
|
|
SystemCallFilter = [ "@system-service" "@module" ];
|
|
|
|
CapabilityBoundingSet = [ "CAP_SYS_MODULE" "CAP_NET_ADMIN" ];
|
|
|
|
};
|
|
|
|
|
|
|
|
configFormat = pkgs.formats.json {};
|
|
|
|
|
2023-10-09 19:29:22 +00:00
|
|
|
# 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; });
|
|
|
|
|
|
|
|
# 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";
|
2024-04-21 15:54:59 +00:00
|
|
|
description = ''
|
2023-10-09 19:29:22 +00:00
|
|
|
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";
|
2024-04-21 15:54:59 +00:00
|
|
|
description = ''
|
2023-10-09 19:29:22 +00:00
|
|
|
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).
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
};
|
2023-08-22 20:05:09 +00:00
|
|
|
|
2023-10-09 19:29:22 +00:00
|
|
|
# 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;
|
|
|
|
};
|
2023-08-22 20:05:09 +00:00
|
|
|
};
|
2023-10-09 19:29:22 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
};
|
2023-08-22 20:05:09 +00:00
|
|
|
};
|
|
|
|
|
2023-10-09 19:29:22 +00:00
|
|
|
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"
|
|
|
|
'';
|
2023-08-22 20:05:09 +00:00
|
|
|
|
|
|
|
in
|
|
|
|
|
|
|
|
{
|
|
|
|
options = {
|
|
|
|
networking.jool.enable = lib.mkOption {
|
|
|
|
type = lib.types.bool;
|
|
|
|
default = false;
|
|
|
|
relatedPackages = [ "linuxPackages.jool" "jool-cli" ];
|
2024-04-21 15:54:59 +00:00
|
|
|
description = ''
|
2023-08-22 20:05:09 +00:00
|
|
|
Whether to enable Jool, an Open Source implementation of IPv4/IPv6
|
|
|
|
translation on Linux.
|
|
|
|
|
|
|
|
Jool can perform stateless IP/ICMP translation (SIIT) or stateful
|
|
|
|
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.
|
2023-10-09 19:29:22 +00:00
|
|
|
|
|
|
|
Enabling this option will install the Jool kernel module and the
|
|
|
|
command line tools for controlling it.
|
2023-08-22 20:05:09 +00:00
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
2023-10-09 19:29:22 +00:00
|
|
|
networking.jool.nat64 = lib.mkOption {
|
|
|
|
type = lib.types.attrsOf nat64Options;
|
|
|
|
default = { };
|
2023-08-22 20:05:09 +00:00
|
|
|
example = lib.literalExpression ''
|
|
|
|
{
|
2023-10-09 19:29:22 +00:00
|
|
|
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";
|
|
|
|
}
|
|
|
|
];
|
|
|
|
|
|
|
|
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"; }
|
|
|
|
];
|
|
|
|
};
|
2023-08-22 20:05:09 +00:00
|
|
|
}
|
|
|
|
'';
|
2024-04-21 15:54:59 +00:00
|
|
|
description = ''
|
2023-10-09 19:29:22 +00:00
|
|
|
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`.
|
2023-08-22 20:05:09 +00:00
|
|
|
|
|
|
|
::: {.note}
|
2023-10-09 19:29:22 +00:00
|
|
|
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.
|
2023-08-22 20:05:09 +00:00
|
|
|
:::
|
|
|
|
|
|
|
|
::: {.warning}
|
2023-10-09 19:29:22 +00:00
|
|
|
Changes to an instance performed via `jool -i <name>` are applied
|
|
|
|
correctly but will be lost after restarting the respective
|
|
|
|
`jool-nat64-<name>.service`.
|
2023-08-22 20:05:09 +00:00
|
|
|
:::
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
2023-10-09 19:29:22 +00:00
|
|
|
networking.jool.siit = lib.mkOption {
|
|
|
|
type = lib.types.attrsOf siitOptions;
|
|
|
|
default = { };
|
2023-08-22 20:05:09 +00:00
|
|
|
example = lib.literalExpression ''
|
|
|
|
{
|
2023-10-09 19:29:22 +00:00
|
|
|
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"; }
|
|
|
|
];
|
|
|
|
};
|
2023-08-22 20:05:09 +00:00
|
|
|
}
|
|
|
|
'';
|
2024-04-21 15:54:59 +00:00
|
|
|
description = ''
|
2023-10-09 19:29:22 +00:00
|
|
|
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`.
|
2023-08-22 20:05:09 +00:00
|
|
|
|
|
|
|
::: {.note}
|
2023-10-09 19:29:22 +00:00
|
|
|
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.
|
2023-08-22 20:05:09 +00:00
|
|
|
:::
|
|
|
|
|
|
|
|
::: {.warning}
|
2023-10-09 19:29:22 +00:00
|
|
|
Changes to an instance performed via `jool -i <name>` are applied
|
|
|
|
correctly but will be lost after restarting the respective
|
|
|
|
`jool-siit-<name>.service`.
|
2023-08-22 20:05:09 +00:00
|
|
|
:::
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
config = lib.mkIf cfg.enable {
|
2023-10-09 19:29:22 +00:00
|
|
|
# Install kernel module and cli tools
|
2023-08-22 20:05:09 +00:00
|
|
|
boot.extraModulePackages = [ jool ];
|
2023-10-09 19:29:22 +00:00
|
|
|
environment.systemPackages = [ jool-cli ];
|
2023-08-22 20:05:09 +00:00
|
|
|
|
2023-10-09 19:29:22 +00:00
|
|
|
# Install services for each instance
|
|
|
|
systemd.services = lib.mkMerge
|
|
|
|
(lib.mapAttrsToList makeNat64Unit cfg.nat64 ++
|
|
|
|
lib.mapAttrsToList makeSiitUnit cfg.siit);
|
2023-08-22 20:05:09 +00:00
|
|
|
|
2023-10-09 19:29:22 +00:00
|
|
|
# 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)));
|
2023-08-22 20:05:09 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
meta.maintainers = with lib.maintainers; [ rnhmjoj ];
|
|
|
|
|
|
|
|
}
|