]> Git — Sourcephile - julm/julm-nix.git/blob - nixos/modules/services/networking/netns.nix
+use/op(jj): enable watchman
[julm/julm-nix.git] / nixos / modules / services / networking / netns.nix
1 {
2 pkgs,
3 lib,
4 config,
5 options,
6 ...
7 }:
8 with lib;
9 let
10 cfg = config.services.netns;
11 inherit (config) networking;
12 # Escape as required by: https://www.freedesktop.org/software/systemd/man/systemd.unit.html
13 escapeUnitName =
14 name:
15 lib.concatMapStrings (s: if lib.isList s then "-" else s) (
16 builtins.split "[^a-zA-Z0-9_.\\-]+" name
17 );
18 in
19 {
20 options.services.netns = {
21 namespaces = mkOption {
22 description = mdDoc ''
23 Network namespaces to create.
24
25 Other services can join a network namespace named `netns` with:
26 ```
27 PrivateNetwork=true;
28 JoinsNamespaceOf="netns-''${netns}.service";
29 ```
30
31 So can `iproute` with: `ip -n ''${netns}`
32
33 ::: {.warning}
34 You should usually create (or update via your VPN configuration's up script)
35 a file named `/etc/netns/''${netns}/resolv.conf`
36 that will be bind-mounted by `ip -n ''${netns}` onto `/etc/resolv.conf`,
37 which you'll also want to configure in the services joining this network namespace:
38 ```
39 BindReadOnlyPaths = ["/etc/netns/''${netns}/resolv.conf:/etc/resolv.conf"];
40 ```
41 :::
42 '';
43 default = { };
44 type = types.attrsOf (
45 types.submodule {
46 options.nftables = mkOption {
47 description = mdDoc "Nftables ruleset within the network namespace.";
48 type = types.lines;
49 default = networking.nftables.ruleset;
50 defaultText = "config.networking.nftables.ruleset";
51 };
52 options.sysctl = options.boot.kernel.sysctl // {
53 description = mdDoc "sysctl within the network namespace.";
54 default = config.boot.kernel.sysctl;
55 defaultText = literalMD "config.boot.kernel.sysctl";
56 };
57 options.service = mkOption {
58 description = mdDoc "Systemd configuration specific to this netns service";
59 type = types.attrs;
60 default = { };
61 };
62 }
63 );
64 };
65 };
66 config = {
67 systemd.services = mapAttrs' (
68 name: c:
69 nameValuePair "netns-${escapeUnitName name}" (mkMerge [
70 {
71 description = "${name} network namespace";
72 before = [ "network.target" ];
73 serviceConfig = {
74 Type = "oneshot";
75 RemainAfterExit = true;
76 # Explanation: let systemd create the netns
77 # so that PrivateNetwork=true
78 # with JoinsNamespaceOf="netns-${name}.service" works.
79 PrivateNetwork = true;
80 # Explanation: using PrivateMounts=true would prevent
81 # the sharing of the mount bind /var/run/netns/$name
82 # done by `ip netns attach`,
83 # causing outside `ip netns exec $name $SHELL` to fail with:
84 # Error: Peer netns reference is invalid.
85 PrivateMounts = false;
86 ExecStart = [
87 # Explanation: for convenience, register the netns
88 # to the tracking mecanism of iproute,
89 # and make sure resolv.conf can be used in BindReadOnlyPaths=
90 # For propagating changes in that file to the services bind mounting it,
91 # updating must not remove the file, but only truncate it.
92 (pkgs.writeShellScript "ip-netns-attach" ''
93 ${pkgs.iproute}/bin/ip netns attach ${escapeShellArg name} $$
94 mkdir -p /etc/netns/${escapeShellArg name}
95 touch /etc/netns/${escapeShellArg name}/resolv.conf
96 '')
97
98 # Explanation: bringing the loopback interface is almost always a good thing.
99 "${pkgs.iproute}/bin/ip link set dev lo up"
100
101 # Explanation: use --ignore because some keys
102 # may no longer exist in that new namespace,
103 # like net.ipv6.conf.eth0.addr_gen_mode or net.core.rmem_max .
104 ''
105 ${pkgs.procps}/bin/sysctl --ignore -p ${
106 pkgs.writeScript "sysctl" (
107 concatStrings (
108 mapAttrsToList (
109 n: v: optionalString (v != null) "${n}=${if v == false then "0" else toString v}\n"
110 ) c.sysctl
111 )
112 )
113 }
114 ''
115 ]
116 ++
117 # Load the nftables ruleset of this netns.
118 optional networking.nftables.enable (
119 pkgs.writeScript "nftables-ruleset" ''
120 #!${pkgs.nftables}/bin/nft -f
121 flush ruleset
122 ${c.nftables}
123 ''
124 );
125 # Unregister the netns from the tracking mecanism of iproute.
126 ExecStop = "${pkgs.iproute}/bin/ip netns delete ${escapeShellArg name}";
127 };
128 }
129 c.service
130 ])
131 ) cfg.namespaces;
132 meta.maintainers = with lib.maintainers; [ julm ];
133 };
134 }