at 24.11-pre 9.9 kB view raw
1{ config, pkgs, lib, ... }: 2 3let 4 cfg = config.networking.jool; 5 6 jool = config.boot.kernelPackages.jool; 7 jool-cli = pkgs.jool-cli; 8 9 hardening = { 10 # Run as unprivileged user 11 User = "jool"; 12 Group = "jool"; 13 DynamicUser = true; 14 15 # Restrict filesystem to only read the jool module 16 TemporaryFileSystem = [ "/" ]; 17 BindReadOnlyPaths = [ 18 builtins.storeDir 19 "/run/booted-system/kernel-modules" 20 ]; 21 22 # Give capabilities to load the module and configure it 23 AmbientCapabilities = [ "CAP_SYS_MODULE" "CAP_NET_ADMIN" ]; 24 RestrictAddressFamilies = [ "AF_NETLINK" ]; 25 26 # Other restrictions 27 RestrictNamespaces = [ "net" ]; 28 SystemCallFilter = [ "@system-service" "@module" ]; 29 CapabilityBoundingSet = [ "CAP_SYS_MODULE" "CAP_NET_ADMIN" ]; 30 }; 31 32 configFormat = pkgs.formats.json {}; 33 34 # Generate the config file of instance `name` 35 nat64Conf = name: 36 configFormat.generate "jool-nat64-${name}.conf" 37 (cfg.nat64.${name} // { instance = name; }); 38 siitConf = name: 39 configFormat.generate "jool-siit-${name}.conf" 40 (cfg.siit.${name} // { instance = name; }); 41 42 # NAT64 config type 43 nat64Options = lib.types.submodule { 44 # The format is plain JSON 45 freeformType = configFormat.type; 46 # Some options with a default value 47 options.framework = lib.mkOption { 48 type = lib.types.enum [ "netfilter" "iptables" ]; 49 default = "netfilter"; 50 description = '' 51 The framework to use for attaching Jool's translation to the exist 52 kernel packet processing rules. See the 53 [documentation](https://nicmx.github.io/Jool/en/intro-jool.html#design) 54 for the differences between the two options. 55 ''; 56 }; 57 options.global.pool6 = lib.mkOption { 58 type = lib.types.strMatching "[[:xdigit:]:]+/[[:digit:]]+" 59 // { description = "Network prefix in CIDR notation"; }; 60 default = "64:ff9b::/96"; 61 description = '' 62 The prefix used for embedding IPv4 into IPv6 addresses. 63 Defaults to the well-known NAT64 prefix, defined by 64 [RFC 6052](https://datatracker.ietf.org/doc/html/rfc6052). 65 ''; 66 }; 67 }; 68 69 # SIIT config type 70 siitOptions = lib.types.submodule { 71 # The format is, again, plain JSON 72 freeformType = configFormat.type; 73 # Some options with a default value 74 options = { inherit (nat64Options.getSubOptions []) framework; }; 75 }; 76 77 makeNat64Unit = name: opts: { 78 "jool-nat64-${name}" = { 79 description = "Jool, NAT64 setup of instance ${name}"; 80 documentation = [ "https://nicmx.github.io/Jool/en/documentation.html" ]; 81 after = [ "network.target" ]; 82 wantedBy = [ "multi-user.target" ]; 83 serviceConfig = { 84 Type = "oneshot"; 85 RemainAfterExit = true; 86 ExecStartPre = "${pkgs.kmod}/bin/modprobe jool"; 87 ExecStart = "${jool-cli}/bin/jool file handle ${nat64Conf name}"; 88 ExecStop = "${jool-cli}/bin/jool -f ${nat64Conf name} instance remove"; 89 } // hardening; 90 }; 91 }; 92 93 makeSiitUnit = name: opts: { 94 "jool-siit-${name}" = { 95 description = "Jool, SIIT setup of instance ${name}"; 96 documentation = [ "https://nicmx.github.io/Jool/en/documentation.html" ]; 97 after = [ "network.target" ]; 98 wantedBy = [ "multi-user.target" ]; 99 serviceConfig = { 100 Type = "oneshot"; 101 RemainAfterExit = true; 102 ExecStartPre = "${pkgs.kmod}/bin/modprobe jool_siit"; 103 ExecStart = "${jool-cli}/bin/jool_siit file handle ${siitConf name}"; 104 ExecStop = "${jool-cli}/bin/jool_siit -f ${siitConf name} instance remove"; 105 } // hardening; 106 }; 107 }; 108 109 checkNat64 = name: _: '' 110 printf 'Validating Jool configuration for NAT64 instance "${name}"... ' 111 jool file check ${nat64Conf name} 112 printf 'Ok.\n'; touch "$out" 113 ''; 114 115 checkSiit = name: _: '' 116 printf 'Validating Jool configuration for SIIT instance "${name}"... ' 117 jool_siit file check ${siitConf name} 118 printf 'Ok.\n'; touch "$out" 119 ''; 120 121in 122 123{ 124 options = { 125 networking.jool.enable = lib.mkOption { 126 type = lib.types.bool; 127 default = false; 128 relatedPackages = [ "linuxPackages.jool" "jool-cli" ]; 129 description = '' 130 Whether to enable Jool, an Open Source implementation of IPv4/IPv6 131 translation on Linux. 132 133 Jool can perform stateless IP/ICMP translation (SIIT) or stateful 134 NAT64, analogous to the IPv4 NAPT. Refer to the upstream 135 [documentation](https://nicmx.github.io/Jool/en/intro-xlat.html) for 136 the supported modes of translation and how to configure them. 137 138 Enabling this option will install the Jool kernel module and the 139 command line tools for controlling it. 140 ''; 141 }; 142 143 networking.jool.nat64 = lib.mkOption { 144 type = lib.types.attrsOf nat64Options; 145 default = { }; 146 example = lib.literalExpression '' 147 { 148 default = { 149 # custom NAT64 prefix 150 global.pool6 = "2001:db8:64::/96"; 151 152 # Port forwarding 153 bib = [ 154 { # SSH 192.0.2.16 2001:db8:a::1 155 "protocol" = "TCP"; 156 "ipv4 address" = "192.0.2.16#22"; 157 "ipv6 address" = "2001:db8:a::1#22"; 158 } 159 { # DNS (TCP) 192.0.2.16 2001:db8:a::2 160 "protocol" = "TCP"; 161 "ipv4 address" = "192.0.2.16#53"; 162 "ipv6 address" = "2001:db8:a::2#53"; 163 } 164 { # DNS (UDP) 192.0.2.16 2001:db8:a::2 165 "protocol" = "UDP"; 166 "ipv4 address" = "192.0.2.16#53"; 167 "ipv6 address" = "2001:db8:a::2#53"; 168 } 169 ]; 170 171 pool4 = [ 172 # Port ranges for dynamic translation 173 { protocol = "TCP"; prefix = "192.0.2.16/32"; "port range" = "40001-65535"; } 174 { protocol = "UDP"; prefix = "192.0.2.16/32"; "port range" = "40001-65535"; } 175 { protocol = "ICMP"; prefix = "192.0.2.16/32"; "port range" = "40001-65535"; } 176 177 # Ports for static BIB entries 178 { protocol = "TCP"; prefix = "192.0.2.16/32"; "port range" = "22"; } 179 { protocol = "UDP"; prefix = "192.0.2.16/32"; "port range" = "53"; } 180 ]; 181 }; 182 } 183 ''; 184 description = '' 185 Definitions of NAT64 instances of Jool. 186 See the 187 [documentation](https://nicmx.github.io/Jool/en/config-atomic.html) for 188 the available options. Also check out the 189 [tutorial](https://nicmx.github.io/Jool/en/run-nat64.html) for an 190 introduction to NAT64 and how to troubleshoot the setup. 191 192 The attribute name defines the name of the instance, with the main one 193 being `default`: this can be accessed from the command line without 194 specifying the name with `-i`. 195 196 ::: {.note} 197 Instances created imperatively from the command line will not interfere 198 with the NixOS instances, provided the respective `pool4` addresses and 199 port ranges are not overlapping. 200 ::: 201 202 ::: {.warning} 203 Changes to an instance performed via `jool -i <name>` are applied 204 correctly but will be lost after restarting the respective 205 `jool-nat64-<name>.service`. 206 ::: 207 ''; 208 }; 209 210 networking.jool.siit = lib.mkOption { 211 type = lib.types.attrsOf siitOptions; 212 default = { }; 213 example = lib.literalExpression '' 214 { 215 default = { 216 # Maps any IPv4 address x.y.z.t to 2001:db8::x.y.z.t and v.v. 217 global.pool6 = "2001:db8::/96"; 218 219 # Explicit address mappings 220 eamt = [ 221 # 2001:db8:1:: 192.0.2.0 222 { "ipv6 prefix" = "2001:db8:1::/128"; "ipv4 prefix" = "192.0.2.0"; } 223 # 2001:db8:1::x 198.51.100.x 224 { "ipv6 prefix" = "2001:db8:2::/120"; "ipv4 prefix" = "198.51.100.0/24"; } 225 ]; 226 }; 227 } 228 ''; 229 description = '' 230 Definitions of SIIT instances of Jool. 231 See the 232 [documentation](https://nicmx.github.io/Jool/en/config-atomic.html) for 233 the available options. Also check out the 234 [tutorial](https://nicmx.github.io/Jool/en/run-vanilla.html) for an 235 introduction to SIIT and how to troubleshoot the setup. 236 237 The attribute name defines the name of the instance, with the main one 238 being `default`: this can be accessed from the command line without 239 specifying the name with `-i`. 240 241 ::: {.note} 242 Instances created imperatively from the command line will not interfere 243 with the NixOS instances, provided the respective EAMT addresses and 244 port ranges are not overlapping. 245 ::: 246 247 ::: {.warning} 248 Changes to an instance performed via `jool -i <name>` are applied 249 correctly but will be lost after restarting the respective 250 `jool-siit-<name>.service`. 251 ::: 252 ''; 253 }; 254 255 }; 256 257 config = lib.mkIf cfg.enable { 258 # Install kernel module and cli tools 259 boot.extraModulePackages = [ jool ]; 260 environment.systemPackages = [ jool-cli ]; 261 262 # Install services for each instance 263 systemd.services = lib.mkMerge 264 (lib.mapAttrsToList makeNat64Unit cfg.nat64 ++ 265 lib.mapAttrsToList makeSiitUnit cfg.siit); 266 267 # Check the configuration of each instance 268 system.checks = lib.optional (cfg.nat64 != {} || cfg.siit != {}) 269 (pkgs.runCommand "jool-validated" 270 { 271 nativeBuildInputs = with pkgs.buildPackages; [ jool-cli ]; 272 preferLocalBuild = true; 273 } 274 (lib.concatStrings 275 (lib.mapAttrsToList checkNat64 cfg.nat64 ++ 276 lib.mapAttrsToList checkSiit cfg.siit))); 277 }; 278 279 meta.maintainers = with lib.maintainers; [ rnhmjoj ]; 280 281}