at master 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 } 106 // hardening; 107 }; 108 }; 109 110 makeSiitUnit = name: opts: { 111 "jool-siit-${name}" = { 112 description = "Jool, SIIT setup of instance ${name}"; 113 documentation = [ "https://nicmx.github.io/Jool/en/documentation.html" ]; 114 after = [ "network.target" ]; 115 wantedBy = [ "multi-user.target" ]; 116 serviceConfig = { 117 Type = "oneshot"; 118 RemainAfterExit = true; 119 ExecStartPre = "${pkgs.kmod}/bin/modprobe jool_siit"; 120 ExecStart = "${jool-cli}/bin/jool_siit file handle ${siitConf name}"; 121 ExecStop = "${jool-cli}/bin/jool_siit -f ${siitConf name} instance remove"; 122 } 123 // hardening; 124 }; 125 }; 126 127 checkNat64 = name: _: '' 128 printf 'Validating Jool configuration for NAT64 instance "${name}"... ' 129 jool file check ${nat64Conf name} 130 printf 'Ok.\n'; touch "$out" 131 ''; 132 133 checkSiit = name: _: '' 134 printf 'Validating Jool configuration for SIIT instance "${name}"... ' 135 jool_siit file check ${siitConf name} 136 printf 'Ok.\n'; touch "$out" 137 ''; 138 139in 140 141{ 142 options = { 143 networking.jool.enable = lib.mkOption { 144 type = lib.types.bool; 145 default = false; 146 relatedPackages = [ 147 "linuxPackages.jool" 148 "jool-cli" 149 ]; 150 description = '' 151 Whether to enable Jool, an Open Source implementation of IPv4/IPv6 152 translation on Linux. 153 154 Jool can perform stateless IP/ICMP translation (SIIT) or stateful 155 NAT64, analogous to the IPv4 NAPT. Refer to the upstream 156 [documentation](https://nicmx.github.io/Jool/en/intro-xlat.html) for 157 the supported modes of translation and how to configure them. 158 159 Enabling this option will install the Jool kernel module and the 160 command line tools for controlling it. 161 ''; 162 }; 163 164 networking.jool.nat64 = lib.mkOption { 165 type = lib.types.attrsOf nat64Options; 166 default = { }; 167 example = lib.literalExpression '' 168 { 169 default = { 170 # custom NAT64 prefix 171 global.pool6 = "2001:db8:64::/96"; 172 173 # Port forwarding 174 bib = [ 175 { # SSH 192.0.2.16 2001:db8:a::1 176 "protocol" = "TCP"; 177 "ipv4 address" = "192.0.2.16#22"; 178 "ipv6 address" = "2001:db8:a::1#22"; 179 } 180 { # DNS (TCP) 192.0.2.16 2001:db8:a::2 181 "protocol" = "TCP"; 182 "ipv4 address" = "192.0.2.16#53"; 183 "ipv6 address" = "2001:db8:a::2#53"; 184 } 185 { # DNS (UDP) 192.0.2.16 2001:db8:a::2 186 "protocol" = "UDP"; 187 "ipv4 address" = "192.0.2.16#53"; 188 "ipv6 address" = "2001:db8:a::2#53"; 189 } 190 ]; 191 192 pool4 = [ 193 # Port ranges for dynamic translation 194 { protocol = "TCP"; prefix = "192.0.2.16/32"; "port range" = "40001-65535"; } 195 { protocol = "UDP"; prefix = "192.0.2.16/32"; "port range" = "40001-65535"; } 196 { protocol = "ICMP"; prefix = "192.0.2.16/32"; "port range" = "40001-65535"; } 197 198 # Ports for static BIB entries 199 { protocol = "TCP"; prefix = "192.0.2.16/32"; "port range" = "22"; } 200 { protocol = "UDP"; prefix = "192.0.2.16/32"; "port range" = "53"; } 201 ]; 202 }; 203 } 204 ''; 205 description = '' 206 Definitions of NAT64 instances of Jool. 207 See the 208 [documentation](https://nicmx.github.io/Jool/en/config-atomic.html) for 209 the available options. Also check out the 210 [tutorial](https://nicmx.github.io/Jool/en/run-nat64.html) for an 211 introduction to NAT64 and how to troubleshoot the setup. 212 213 The attribute name defines the name of the instance, with the main one 214 being `default`: this can be accessed from the command line without 215 specifying the name with `-i`. 216 217 ::: {.note} 218 Instances created imperatively from the command line will not interfere 219 with the NixOS instances, provided the respective `pool4` addresses and 220 port ranges are not overlapping. 221 ::: 222 223 ::: {.warning} 224 Changes to an instance performed via `jool -i <name>` are applied 225 correctly but will be lost after restarting the respective 226 `jool-nat64-<name>.service`. 227 ::: 228 ''; 229 }; 230 231 networking.jool.siit = lib.mkOption { 232 type = lib.types.attrsOf siitOptions; 233 default = { }; 234 example = lib.literalExpression '' 235 { 236 default = { 237 # Maps any IPv4 address x.y.z.t to 2001:db8::x.y.z.t and v.v. 238 global.pool6 = "2001:db8::/96"; 239 240 # Explicit address mappings 241 eamt = [ 242 # 2001:db8:1:: 192.0.2.0 243 { "ipv6 prefix" = "2001:db8:1::/128"; "ipv4 prefix" = "192.0.2.0"; } 244 # 2001:db8:1::x 198.51.100.x 245 { "ipv6 prefix" = "2001:db8:2::/120"; "ipv4 prefix" = "198.51.100.0/24"; } 246 ]; 247 }; 248 } 249 ''; 250 description = '' 251 Definitions of SIIT instances of Jool. 252 See the 253 [documentation](https://nicmx.github.io/Jool/en/config-atomic.html) for 254 the available options. Also check out the 255 [tutorial](https://nicmx.github.io/Jool/en/run-vanilla.html) for an 256 introduction to SIIT and how to troubleshoot the setup. 257 258 The attribute name defines the name of the instance, with the main one 259 being `default`: this can be accessed from the command line without 260 specifying the name with `-i`. 261 262 ::: {.note} 263 Instances created imperatively from the command line will not interfere 264 with the NixOS instances, provided the respective EAMT addresses and 265 port ranges are not overlapping. 266 ::: 267 268 ::: {.warning} 269 Changes to an instance performed via `jool -i <name>` are applied 270 correctly but will be lost after restarting the respective 271 `jool-siit-<name>.service`. 272 ::: 273 ''; 274 }; 275 276 }; 277 278 config = lib.mkIf cfg.enable { 279 # Install kernel module and cli tools 280 boot.extraModulePackages = [ jool ]; 281 environment.systemPackages = [ jool-cli ]; 282 283 # Install services for each instance 284 systemd.services = lib.mkMerge ( 285 lib.mapAttrsToList makeNat64Unit cfg.nat64 ++ lib.mapAttrsToList makeSiitUnit cfg.siit 286 ); 287 288 # Check the configuration of each instance 289 system.checks = lib.optional (cfg.nat64 != { } || cfg.siit != { }) ( 290 pkgs.runCommand "jool-validated" 291 { 292 nativeBuildInputs = with pkgs.buildPackages; [ jool-cli ]; 293 preferLocalBuild = true; 294 } 295 ( 296 lib.concatStrings (lib.mapAttrsToList checkNat64 cfg.nat64 ++ lib.mapAttrsToList checkSiit cfg.siit) 297 ) 298 ); 299 }; 300 301 meta.maintainers = with lib.maintainers; [ rnhmjoj ]; 302 303}