1 From 3afee8c8b5309d15528a2d132e601b5422182ef5 Mon Sep 17 00:00:00 2001
2 From: Julien Moutinho <julm+nixpkgs@sourcephile.fr>
3 Date: Sun, 17 Jan 2021 15:28:13 +0100
4 Subject: [PATCH 1/2] nixos/netns: init module to manage network namespaces
7 nixos/modules/module-list.nix | 1 +
8 nixos/modules/services/networking/netns.nix | 112 ++++++++++++++++++++
9 2 files changed, 113 insertions(+)
10 create mode 100644 nixos/modules/services/networking/netns.nix
12 diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
13 index 783f20546af4..3df0004f5f82 100644
14 --- a/nixos/modules/module-list.nix
15 +++ b/nixos/modules/module-list.nix
17 ./services/networking/netclient.nix
18 ./services/networking/networkd-dispatcher.nix
19 ./services/networking/networkmanager.nix
20 + ./services/networking/netns.nix
21 ./services/networking/nextdns.nix
22 ./services/networking/nftables.nix
23 ./services/networking/nghttpx/default.nix
24 diff --git a/nixos/modules/services/networking/netns.nix b/nixos/modules/services/networking/netns.nix
26 index 000000000000..8e37c2226848
28 +++ b/nixos/modules/services/networking/netns.nix
30 +{ pkgs, lib, config, options, ... }:
33 + cfg = config.services.netns;
34 + inherit (config) networking;
35 + # Escape as required by: https://www.freedesktop.org/software/systemd/man/systemd.unit.html
36 + escapeUnitName = name:
37 + lib.concatMapStrings (s: if lib.isList s then "-" else s)
38 + (builtins.split "[^a-zA-Z0-9_.\\-]+" name);
41 +options.services.netns = {
42 + namespaces = mkOption {
44 + Network namespaces to create.
46 + Other services can join a network namespace named `netns` with:
48 + PrivateNetwork=true;
49 + JoinsNamespaceOf="netns-''${netns}.service";
52 + So can `iproute` with: `ip -n ''${netns}`
55 + You should usually create (or update via your VPN configuration's up script)
56 + a file named `/etc/netns/''${netns}/resolv.conf`
57 + that will be bind-mounted by `ip -n ''${netns}` onto `/etc/resolv.conf`,
58 + which you'll also want to configure in the services joining this network namespace:
60 + BindReadOnlyPaths = ["/etc/netns/''${netns}/resolv.conf:/etc/resolv.conf"];
65 + type = types.attrsOf (types.submodule {
66 + options.nftables = mkOption {
67 + description = "Nftables ruleset within the network namespace.";
69 + default = networking.nftables.ruleset;
70 + defaultText = "config.networking.nftables.ruleset";
72 + options.sysctl = options.boot.kernel.sysctl // {
73 + description = "sysctl within the network namespace.";
74 + default = config.boot.kernel.sysctl;
75 + defaultText = literalMD "config.boot.kernel.sysctl";
77 + options.service = mkOption {
78 + description = "Systemd configuration specific to this netns service";
86 + systemd.services = mapAttrs' (name: c:
87 + nameValuePair "netns-${escapeUnitName name}" (mkMerge [
88 + { description = "${name} network namespace";
89 + before = [ "network.target" ];
92 + RemainAfterExit = true;
93 + # Let systemd create the netns so that PrivateNetwork=true
94 + # with JoinsNamespaceOf="netns-${name}.service" works.
95 + PrivateNetwork = true;
96 + # PrivateNetwork=true implies PrivateMounts=true by default,
97 + # which would prevent the persisting and sharing of /var/run/netns/$name
98 + # causing `ip netns exec $name $SHELL` outside of this service to fail with:
99 + # Error: Peer netns reference is invalid.
100 + # As `stat -f -c %T /var/run/netns/$name` would not be "nsfs" in those mntns.
101 + # See https://serverfault.com/questions/961504/cannot-create-nested-network-namespace
102 + PrivateMounts = false;
104 + # Register the netns with a binding mount to /var/run/netns/$name to keep it alive,
105 + # and make sure resolv.conf can be used in BindReadOnlyPaths=
106 + # For propagating changes in that file to the services bind mounting it,
107 + # updating must not remove the file, but only truncate it.
108 + (pkgs.writeShellScript "ip-netns-attach" ''
109 + ${pkgs.iproute}/bin/ip netns attach ${escapeShellArg name} $$
110 + mkdir -p /etc/netns/${escapeShellArg name}
111 + touch /etc/netns/${escapeShellArg name}/resolv.conf
114 + # Bringing the loopback interface is almost always a good thing.
115 + "${pkgs.iproute}/bin/ip link set dev lo up"
117 + # Use --ignore because some keys may no longer exist in that new namespace,
118 + # like net.ipv6.conf.eth0.addr_gen_mode or net.core.rmem_max
119 + ''${pkgs.procps}/bin/sysctl --ignore -p ${pkgs.writeScript "sysctl"
120 + (concatStrings (mapAttrsToList (n: v:
121 + optionalString (v != null)
122 + "${n}=${if v == false then "0" else toString v}\n"
126 + # Load the nftables ruleset of this netns.
127 + optional networking.nftables.enable (pkgs.writeScript "nftables-ruleset" ''
128 + #!${pkgs.nftables}/bin/nft -f
132 + # Unregister the netns from the tracking mecanism of iproute.
133 + ExecStop = "${pkgs.iproute}/bin/ip netns delete ${escapeShellArg name}";
139 + meta.maintainers = with lib.maintainers; [ julm ];