10 cfg = config.services.netns;
11 inherit (config) networking;
12 # Escape as required by: https://www.freedesktop.org/software/systemd/man/systemd.unit.html
15 lib.concatMapStrings (s: if lib.isList s then "-" else s) (
16 builtins.split "[^a-zA-Z0-9_.\\-]+" name
20 options.services.netns = {
21 namespaces = mkOption {
23 Network namespaces to create.
25 Other services can join a network namespace named <code>netns</code> with:
28 JoinsNamespaceOf="netns-''${netns}.service";
31 So can <literal>iproute</literal> with:
32 <code>ip -n ''${netns}</code>
35 You should usually create (or update via your VPN configuration's up script)
36 a file named <literal>/etc/netns/''${netns}/resolv.conf</literal>
37 that will be bind-mounted by <code>ip -n ''${netns}</code> onto <literal>/etc/resolv.conf</literal>,
38 which you'll also want to configure in the services joining this network namespace:
40 BindReadOnlyPaths = ["/etc/netns/''${netns}/resolv.conf:/etc/resolv.conf"];
45 type = types.attrsOf (
47 options.nftables = mkOption {
48 description = "Nftables ruleset within the network namespace.";
50 default = networking.nftables.ruleset;
51 defaultText = "config.networking.nftables.ruleset";
53 options.sysctl = options.boot.kernel.sysctl // {
54 description = "sysctl within the network namespace.";
55 default = config.boot.kernel.sysctl;
56 defaultText = "config.boot.kernel.sysctl";
58 options.service = mkOption {
59 description = "Systemd configuration specific to this netns service";
68 systemd.services = mapAttrs' (
70 nameValuePair "netns-${escapeUnitName name}" (mkMerge [
72 description = "${name} network namespace";
73 before = [ "network.target" ];
76 RemainAfterExit = true;
77 # Let systemd create the netns so that PrivateNetwork=true
78 # with JoinsNamespaceOf="netns-${name}.service" works.
79 PrivateNetwork = true;
82 # For convenience, register the netns to the tracking mecanism of iproute,
83 # and make sure resolv.conf can be used in BindReadOnlyPaths=
84 # For propagating changes to thie file to the services bind mounting it,
85 # updatingmust not remove the file, but only truncate it.
86 (pkgs.writeShellScript "ip-netns-attach" ''
87 ${pkgs.iproute}/bin/ip netns attach ${escapeShellArg name} $$
88 mkdir -p /etc/netns/${escapeShellArg name}
89 touch /etc/netns/${escapeShellArg name}/resolv.conf
92 # Bringing the loopback interface is almost always a good thing.
93 "${pkgs.iproute}/bin/ip link set dev lo up"
95 # Use --ignore because some keys may no longer exist in that new namespace,
96 # like net.ipv6.conf.eth0.addr_gen_mode or net.core.rmem_max
98 ${pkgs.procps}/bin/sysctl --ignore -p ${
99 pkgs.writeScript "sysctl" (
102 n: v: optionalString (v != null) "${n}=${if v == false then "0" else toString v}\n"
110 # Load the nftables ruleset of this netns.
111 optional networking.nftables.enable (
112 pkgs.writeScript "nftables-ruleset" ''
113 #!${pkgs.nftables}/bin/nft -f
118 # Unregister the netns from the tracking mecanism of iproute.
119 ExecStop = "${pkgs.iproute}/bin/ip netns delete ${escapeShellArg name}";
125 meta.maintainers = with lib.maintainers; [ julm ];