python312Packages.aiohomeconnect: 0.10.0 -> 0.11.0 (#374011)
[NixPkgs.git] / nixos / modules / services / networking / jool.nix
blobe782bd587fc5c56da1e04aaf56011047c8d85a07
2   config,
3   pkgs,
4   lib,
5   ...
6 }:
8 let
9   cfg = config.networking.jool;
11   jool = config.boot.kernelPackages.jool;
12   jool-cli = pkgs.jool-cli;
14   hardening = {
15     # Run as unprivileged user
16     User = "jool";
17     Group = "jool";
18     DynamicUser = true;
20     # Restrict filesystem to only read the jool module
21     TemporaryFileSystem = [ "/" ];
22     BindReadOnlyPaths = [
23       builtins.storeDir
24       "/run/booted-system/kernel-modules"
25     ];
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" ];
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   };
46   configFormat = pkgs.formats.json { };
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; });
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   };
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   };
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   };
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   };
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   '';
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   '';
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.
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.
157         Enabling this option will install the Jool kernel module and the
158         command line tools for controlling it.
159       '';
160     };
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";
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             ];
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"; }
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.
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`.
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         :::
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     };
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";
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.
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`.
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         :::
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     };
274   };
276   config = lib.mkIf cfg.enable {
277     # Install kernel module and cli tools
278     boot.extraModulePackages = [ jool ];
279     environment.systemPackages = [ jool-cli ];
281     # Install services for each instance
282     systemd.services = lib.mkMerge (
283       lib.mapAttrsToList makeNat64Unit cfg.nat64 ++ lib.mapAttrsToList makeSiitUnit cfg.siit
284     );
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   };
299   meta.maintainers = with lib.maintainers; [ rnhmjoj ];