summaryrefslogtreecommitdiff
path: root/modules/netns.nix
diff options
context:
space:
mode:
Diffstat (limited to 'modules/netns.nix')
-rw-r--r--modules/netns.nix155
1 files changed, 155 insertions, 0 deletions
diff --git a/modules/netns.nix b/modules/netns.nix
new file mode 100644
index 00000000..18e066e5
--- /dev/null
+++ b/modules/netns.nix
@@ -0,0 +1,155 @@
1{ pkgs, config, lib, ... }:
2
3with lib;
4
5let
6 cfg = config.networking.namespaces;
7
8 containerOpts = { name, ... }: {
9 options = {
10 config = mkOption {
11 description = ''
12 A specification of the desired configuration of this
13 container, as a NixOS module.
14 '';
15 type = let
16 confPkgs = if config.pkgs == null then pkgs else config.pkgs;
17 in mkOptionType {
18 name = "Toplevel NixOS config";
19 merge = loc: defs: (import (pkgs.path + "/nixos/lib/eval-config.nix") {
20 inherit (config.nixpkgs.localSystem) system;
21 inherit pkgs;
22 baseModules = import (pkgs.path + "/nixos/modules/module-list.nix");
23 inherit (pkgs) lib;
24 modules =
25 let
26 extraConfig = {
27 _file = "module at ${__curPos.file}:${toString __curPos.line}";
28 config = {
29 boot.isContainer = true;
30 networking.hostName = mkDefault name;
31 system.stateVersion = config.system.nixos.release; # No state
32 };
33 };
34 in [ extraConfig ] ++ (map (x: x.value) defs);
35 prefix = [ "containers" "upstream" ];
36 }).config;
37 };
38 };
39
40 netns = mkOption {
41 example = "upstream";
42 type = types.str;
43 description = "Name of network namespace to put the container in.";
44 };
45 };
46
47 config = {
48 netns = mkDefault name;
49 };
50 };
51
52 mkContainerService = containerName: containerCfg: nameValuePair "netns-container@${containerName}" {
53 after = ["network.target" "systemd-udevd.service" "systemd-sysctl.service" "netns@${containerCfg.netns}.service"];
54 bindsTo = ["netns@${containerCfg.netns}.service"];
55 before = ["shutdown.target"];
56 wants = ["network.target"];
57 conflicts = ["shutdown.target"];
58
59 path = with pkgs; [ iproute config.systemd.package ];
60
61 serviceConfig = {
62 SyslogIdentifier = "netns container ${containerName}";
63 Type = "notify";
64
65 RestartForceExitStatus = "133";
66 SuccessExitStatus = "133";
67
68 Restart = "no";
69
70 DevicePolicy = "closed";
71
72 RuntimeDirectory = ["netns-containers/${containerName}"];
73 };
74 unitConfig = {
75 ConditionCapability = ["CAP_SYS_TTY_CONFIG" "CAP_NET_ADMIN" "CAP_NET_RAW" "CAP_SYS_ADMIN"];
76 };
77
78 script = let
79 containerInit = pkgs.writeScript "container-init" ''
80 #!${pkgs.runtimeShell} -e
81 exec "$1"
82 '';
83 in ''
84 mkdir -p -m 0755 "''${RUNTIME_DIRECTORY}/etc" "''${RUNTIME_DIRECTORY}/var/lib"
85 mkdir -p -m 0700 "''${RUNTIME_DIRECTORY}/var/lib/private" "''${RUNTIME_DIRECTORY}/root" /run/containers
86 if ! [ -e "''${RUNTIME_DIRECTORY}/etc/os-release" ]; then
87 touch "''${RUNTIME_DIRECTORY}/etc/os-release"
88 fi
89 if ! [ -e "''${RUNTIME_DIRECTORY}/etc/machine-id" ]; then
90 touch "''${RUNTIME_DIRECTORY}/etc/machine-id"
91 fi
92 mkdir -p -m 0755 \
93 "/nix/var/nix/profiles/per-container/${containerName}" \
94 "/nix/var/nix/gcroots/per-container/${containerName}"
95 credsBind=""
96 if [ -n "''${CREDENTIALS_DIRECTORY}" ]; then
97 credsBind="--bind-ro=''${CREDENTIALS_DIRECTORY}:/run/host/credentials"
98 fi
99 # Run systemd-nspawn without startup notification (we'll
100 # wait for the container systemd to signal readiness).
101 exec ${config.systemd.package}/bin/systemd-nspawn \
102 --keep-unit \
103 -M "${containerName}" -D "''${RUNTIME_DIRECTORY}" \
104 --notify-ready=yes \
105 --bind-ro=/nix/store \
106 --bind-ro=/nix/var/nix/db \
107 --bind-ro=/nix/var/nix/daemon-socket \
108 $credsBind \
109 --bind="/nix/var/nix/profiles/per-container/${containerName}:/nix/var/nix/profiles" \
110 --bind="/nix/var/nix/gcroots/per-container/${containerName}:/nix/var/nix/gcroots" \
111 --setenv PATH="$PATH" \
112 --capability=CAP_SYS_TTY_CONFIG,CAP_NET_ADMIN,CAP_NET_RAW,CAP_SYS_ADMIN \
113 --ephemeral \
114 --network-namespace-path=/run/netns/${containerCfg.netns} \
115 ${containerInit} "${containerCfg.config.system.build.toplevel}/init"
116 '';
117 };
118in {
119 options = {
120 networking.namespaces = {
121 enable = mkEnableOption "netns@ service template";
122
123 containers = mkOption {
124 default = {};
125 type = types.attrsOf (types.submodule containerOpts);
126 };
127 };
128 };
129
130 config = {
131 assertions = [
132 { assertion = cfg.containers != {} -> cfg.enable; message = "netns containers require netns@ service template"; }
133 ];
134
135 systemd.services = {
136 "netns@" = mkIf cfg.enable {
137 description = "%I network namspace";
138 before = [ "network-pre.target" ];
139 wants = [ "network-pre.target" ];
140 path = with pkgs; [ iproute utillinux ];
141 serviceConfig = {
142 Type = "oneshot";
143 RemainAfterExit = true;
144 PrivateNetwork = true;
145 ExecStart = "${pkgs.writers.writeDash "netns-up" ''
146 ip netns add "$1"
147 umount /var/run/netns/"$1"
148 mount --bind /proc/self/ns/net /var/run/netns/"$1"
149 ''} %I";
150 ExecStop = "${pkgs.iproute}/bin/ip netns del %I";
151 };
152 };
153 } // mapAttrs' mkContainerService cfg.containers;
154 };
155}