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