diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index e728dcb3f19..7e7bb93783c 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -911,6 +911,7 @@ ./services/networking/jibri/default.nix ./services/networking/jicofo.nix ./services/networking/jitsi-videobridge.nix + ./services/networking/jool.nix ./services/networking/kea.nix ./services/networking/keepalived/default.nix ./services/networking/keybase.nix diff --git a/nixos/modules/services/networking/jool.nix b/nixos/modules/services/networking/jool.nix new file mode 100644 index 00000000000..8fc08efcb67 --- /dev/null +++ b/nixos/modules/services/networking/jool.nix @@ -0,0 +1,211 @@ +{ 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 + "/run/current-system/kernel-modules" + ]; + + # 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 {}; + + mkDefaultAttrs = lib.mapAttrs (n: v: lib.mkDefault v); + + defaultNat64 = { + instance = "default"; + framework = "netfilter"; + global.pool6 = "64:ff9b::/96"; + }; + defaultSiit = { + instance = "default"; + framework = "netfilter"; + }; + + nat64Conf = configFormat.generate "jool-nat64.conf" cfg.nat64.config; + siitConf = configFormat.generate "jool-siit.conf" cfg.siit.config; + +in + +{ + ###### interface + + options = { + networking.jool.enable = lib.mkOption { + type = lib.types.bool; + default = false; + relatedPackages = [ "linuxPackages.jool" "jool-cli" ]; + description = lib.mdDoc '' + 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. + ''; + }; + + networking.jool.nat64.enable = lib.mkEnableOption (lib.mdDoc "a NAT64 instance of Jool."); + networking.jool.nat64.config = lib.mkOption { + type = configFormat.type; + default = defaultNat64; + example = lib.literalExpression '' + { + # 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 = [ + # 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"; } + + # 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. + + ::: {.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. + ::: + + ::: {.warning} + Changes to the NixOS instance performed via `jool instance nixos-nat64` + are applied correctly but will be lost after restarting + `jool-nat64.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; + 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"; + + # 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. + + ::: {.note} + Existing or more instances created manually will not interfere with the + NixOS instance, provided the respective `EAMT` address mappings 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`. + ::: + ''; + }; + + }; + + ###### implementation + + config = lib.mkIf cfg.enable { + environment.systemPackages = [ jool-cli ]; + boot.extraModulePackages = [ jool ]; + + 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; + }; + + networking.jool.nat64.config = mkDefaultAttrs defaultNat64; + networking.jool.siit.config = mkDefaultAttrs defaultSiit; + + }; + + meta.maintainers = with lib.maintainers; [ rnhmjoj ]; + +}