···
# The container's init script, a small wrapper around the regular
# NixOS stage-2 init script.
9
-
containerInit = pkgs.writeScript "container-init"
9
+
containerInit = (cfg:
11
+
renderExtraVeth = (name: cfg:
13
+
echo "Bringing ${name} up"
14
+
ip link set dev ${name} up
15
+
${optionalString (cfg . "localAddress" or null != null) ''
16
+
echo "Setting ip for ${name}"
17
+
ip addr add ${cfg . "localAddress"} dev ${name}
19
+
${optionalString (cfg . "localAddress6" or null != null) ''
20
+
echo "Setting ip6 for ${name}"
21
+
ip -6 addr add ${cfg . "localAddress6"} dev ${name}
23
+
${optionalString (cfg . "hostAddress" or null != null) ''
24
+
echo "Setting route to host for ${name}"
25
+
ip route add ${cfg . "hostAddress"} dev ${name}
27
+
${optionalString (cfg . "hostAddress6" or null != null) ''
28
+
echo "Setting route6 to host for ${name}"
29
+
ip -6 route add ${cfg . "hostAddress6"} dev ${name}
34
+
pkgs.writeScript "container-init"
36
+
#! ${pkgs.stdenv.shell} -e
38
+
# Initialise the container side of the veth pair.
39
+
if [ "$PRIVATE_NETWORK" = 1 ]; then
41
+
ip link set host0 name eth0
42
+
ip link set dev eth0 up
44
+
if [ -n "$LOCAL_ADDRESS" ]; then
45
+
ip addr add $LOCAL_ADDRESS dev eth0
47
+
if [ -n "$LOCAL_ADDRESS6" ]; then
48
+
ip -6 addr add $LOCAL_ADDRESS6 dev eth0
50
+
if [ -n "$HOST_ADDRESS" ]; then
51
+
ip route add $HOST_ADDRESS dev eth0
52
+
ip route add default via $HOST_ADDRESS
54
+
if [ -n "$HOST_ADDRESS6" ]; then
55
+
ip -6 route add $HOST_ADDRESS6 dev eth0
56
+
ip -6 route add default via $HOST_ADDRESS6
59
+
${concatStringsSep "\n" (mapAttrsToList renderExtraVeth cfg . "extraVeths" or {})}
64
+
# Start the regular stage 1 script.
69
+
nspawnExtraVethArgs = (name: cfg: "--network-veth-extra=${name}");
11
-
#! ${pkgs.stdenv.shell} -e
72
+
mkdir -p -m 0755 "$root/etc" "$root/var/lib"
73
+
mkdir -p -m 0700 "$root/var/lib/private" "$root/root" /run/containers
74
+
if ! [ -e "$root/etc/os-release" ]; then
75
+
touch "$root/etc/os-release"
78
+
if ! [ -e "$root/etc/machine-id" ]; then
79
+
touch "$root/etc/machine-id"
83
+
"/nix/var/nix/profiles/per-container/$INSTANCE" \
84
+
"/nix/var/nix/gcroots/per-container/$INSTANCE"
86
+
cp --remove-destination /etc/resolv.conf "$root/etc/resolv.conf"
13
-
# Initialise the container side of the veth pair.
if [ "$PRIVATE_NETWORK" = 1 ]; then
89
+
extraFlags+=" --network-veth"
90
+
if [ -n "$HOST_BRIDGE" ]; then
91
+
extraFlags+=" --network-bridge=$HOST_BRIDGE"
16
-
ip link set host0 name eth0
17
-
ip link set dev eth0 up
95
+
${if cfg . "extraVeths" or null != null then
96
+
''extraFlags+=" ${concatStringsSep " " (mapAttrsToList nspawnExtraVethArgs cfg . "extraVeths" or {})}"''
98
+
''# No extra veth pairs to create''
19
-
if [ -n "$LOCAL_ADDRESS" ]; then
20
-
ip addr add $LOCAL_ADDRESS dev eth0
22
-
if [ -n "$LOCAL_ADDRESS6" ]; then
23
-
ip -6 addr add $LOCAL_ADDRESS6 dev eth0
25
-
if [ -n "$HOST_ADDRESS" ]; then
26
-
ip route add $HOST_ADDRESS dev eth0
27
-
ip route add default via $HOST_ADDRESS
29
-
if [ -n "$HOST_ADDRESS6" ]; then
30
-
ip -6 route add $HOST_ADDRESS6 dev eth0
31
-
ip -6 route add default via $HOST_ADDRESS6
101
+
for iface in $INTERFACES; do
102
+
extraFlags+=" --network-interface=$iface"
105
+
for iface in $MACVLANS; do
106
+
extraFlags+=" --network-macvlan=$iface"
109
+
# If the host is 64-bit and the container is 32-bit, add a
110
+
# --personality flag.
111
+
${optionalString (config.nixpkgs.system == "x86_64-linux") ''
112
+
if [ "$(< ''${SYSTEM_PATH:-/nix/var/nix/profiles/per-container/$INSTANCE/system}/system)" = i686-linux ]; then
113
+
extraFlags+=" --personality=x86"
117
+
# Run systemd-nspawn without startup notification (we'll
118
+
# wait for the container systemd to signal readiness).
120
+
exec ${config.systemd.package}/bin/systemd-nspawn \
122
+
-M "$INSTANCE" -D "$root" $extraFlags \
123
+
$EXTRA_NSPAWN_FLAGS \
124
+
--notify-ready=yes \
125
+
--bind-ro=/nix/store \
126
+
--bind-ro=/nix/var/nix/db \
127
+
--bind-ro=/nix/var/nix/daemon-socket \
128
+
--bind="/nix/var/nix/profiles/per-container/$INSTANCE:/nix/var/nix/profiles" \
129
+
--bind="/nix/var/nix/gcroots/per-container/$INSTANCE:/nix/var/nix/gcroots" \
130
+
--setenv PRIVATE_NETWORK="$PRIVATE_NETWORK" \
131
+
--setenv HOST_BRIDGE="$HOST_BRIDGE" \
132
+
--setenv HOST_ADDRESS="$HOST_ADDRESS" \
133
+
--setenv LOCAL_ADDRESS="$LOCAL_ADDRESS" \
134
+
--setenv HOST_ADDRESS6="$HOST_ADDRESS6" \
135
+
--setenv LOCAL_ADDRESS6="$LOCAL_ADDRESS6" \
136
+
--setenv PATH="$PATH" \
137
+
${containerInit cfg} "''${SYSTEM_PATH:-/nix/var/nix/profiles/system}/init"
141
+
preStartScript = (cfg:
143
+
# Clean up existing machined registration and interfaces.
144
+
machinectl terminate "$INSTANCE" 2> /dev/null || true
146
+
if [ "$PRIVATE_NETWORK" = 1 ]; then
147
+
ip link del dev "ve-$INSTANCE" 2> /dev/null || true
148
+
ip link del dev "vb-$INSTANCE" 2> /dev/null || true
35
-
# Start the regular stage 1 script.
151
+
${concatStringsSep "\n" (
152
+
mapAttrsToList (name: cfg:
153
+
''ip link del dev ${name} 2> /dev/null || true ''
154
+
) cfg . "extraVeths" or {}
158
+
postStartScript = (cfg:
160
+
ipcall = (cfg: ipcmd: variable: attribute:
161
+
if cfg . attribute or null == null then
163
+
if [ -n "${variable}" ]; then
164
+
${ipcmd} add ${variable} dev $ifaceHost
168
+
''${ipcmd} add ${cfg . attribute} dev $ifaceHost''
170
+
renderExtraVeth = (name: cfg:
171
+
if cfg . "hostBridge" or null != null then
173
+
# Add ${name} to bridge ${cfg.hostBridge}
174
+
ip link set dev ${name} master ${cfg.hostBridge} up
178
+
# Set IPs and routes for ${name}
179
+
${optionalString (cfg . "hostAddress" or null != null) ''
180
+
ip addr add ${cfg . "hostAddress"} dev ${name}
182
+
${optionalString (cfg . "hostAddress6" or null != null) ''
183
+
ip -6 addr add ${cfg . "hostAddress6"} dev ${name}
185
+
${optionalString (cfg . "localAddress" or null != null) ''
186
+
ip route add ${cfg . "localAddress"} dev ${name}
188
+
${optionalString (cfg . "localAddress6" or null != null) ''
189
+
ip -6 route add ${cfg . "localAddress6"} dev ${name}
195
+
if [ "$PRIVATE_NETWORK" = 1 ]; then
196
+
if [ -z "$HOST_BRIDGE" ]; then
197
+
ifaceHost=ve-$INSTANCE
198
+
ip link set dev $ifaceHost up
200
+
${ipcall cfg "ip addr" "$HOST_ADDRESS" "hostAddress"}
201
+
${ipcall cfg "ip -6 addr" "$HOST_ADDRESS6" "hostAddress6"}
202
+
${ipcall cfg "ip route" "$LOCAL_ADDRESS" "localAddress"}
203
+
${ipcall cfg "ip -6 route" "$LOCAL_ADDRESS6" "localAddress6"}
205
+
${concatStringsSep "\n" (mapAttrsToList renderExtraVeth cfg . "extraVeths" or {})}
208
+
# Get the leader PID so that we can signal it in
209
+
# preStop. We can't use machinectl there because D-Bus
210
+
# might be shutting down. FIXME: in systemd 219 we can
211
+
# just signal systemd-nspawn to do a clean shutdown.
212
+
machinectl show "$INSTANCE" | sed 's/Leader=\(.*\)/\1/;t;d' > "/run/containers/$INSTANCE.pid"
system = config.nixpkgs.system;
···
mkBindFlags = bs: concatMapStrings mkBindFlag (lib.attrValues bs);
254
+
hostBridge = mkOption {
255
+
type = types.nullOr types.string;
259
+
Put the host-side of the veth-pair into the named bridge.
260
+
Only one of hostAddress* or hostBridge can be given.
264
+
hostAddress = mkOption {
265
+
type = types.nullOr types.str;
267
+
example = "10.231.136.1";
269
+
The IPv4 address assigned to the host interface.
270
+
(Not used when hostBridge is set.)
274
+
hostAddress6 = mkOption {
275
+
type = types.nullOr types.string;
277
+
example = "fc00::1";
279
+
The IPv6 address assigned to the host interface.
280
+
(Not used when hostBridge is set.)
284
+
localAddress = mkOption {
285
+
type = types.nullOr types.str;
287
+
example = "10.231.136.2";
289
+
The IPv4 address assigned to the interface in the container.
290
+
If a hostBridge is used, this should be given with netmask to access
291
+
the whole network. Otherwise the default netmask is /32 and routing is
292
+
set up from localAddress to hostAddress and back.
296
+
localAddress6 = mkOption {
297
+
type = types.nullOr types.string;
299
+
example = "fc00::2";
301
+
The IPv6 address assigned to the interface in the container.
302
+
If a hostBridge is used, this should be given with netmask to access
303
+
the whole network. Otherwise the default netmask is /128 and routing is
304
+
set up from localAddress6 to hostAddress6 and back.
···
136
-
hostBridge = mkOption {
137
-
type = types.nullOr types.string;
141
-
Put the host-side of the veth-pair into the named bridge.
142
-
Only one of hostAddress* or hostBridge can be given.
146
-
hostAddress = mkOption {
147
-
type = types.nullOr types.str;
149
-
example = "10.231.136.1";
151
-
The IPv4 address assigned to the host interface.
152
-
(Not used when hostBridge is set.)
156
-
hostAddress6 = mkOption {
157
-
type = types.nullOr types.string;
159
-
example = "fc00::1";
161
-
The IPv6 address assigned to the host interface.
162
-
(Not used when hostBridge is set.)
166
-
localAddress = mkOption {
167
-
type = types.nullOr types.str;
169
-
example = "10.231.136.2";
171
-
The IPv4 address assigned to <literal>eth0</literal>
176
-
localAddress6 = mkOption {
177
-
type = types.nullOr types.string;
179
-
example = "fc00::2";
181
-
The IPv6 address assigned to <literal>eth0</literal>
type = types.listOf types.string;
example = [ "eth1" "eth2" ];
The list of interfaces to be moved into the container.
379
+
extraVeths = mkOption {
380
+
type = types.attrsOf types.optionSet;
382
+
options = networkOptions;
384
+
Extra veth-pairs to be created for the container
···
410
+
} // networkOptions;
[ (mkIf options.config.isDefined {
···
environment.INSTANCE = "%i";
environment.root = "/var/lib/containers/%i";
277
-
# Clean up existing machined registration and interfaces.
278
-
machinectl terminate "$INSTANCE" 2> /dev/null || true
468
+
preStart = preStartScript {};
280
-
if [ "$PRIVATE_NETWORK" = 1 ]; then
281
-
ip link del dev "ve-$INSTANCE" 2> /dev/null || true
282
-
ip link del dev "vb-$INSTANCE" 2> /dev/null || true
470
+
script = startScript {};
288
-
mkdir -p -m 0755 "$root/etc" "$root/var/lib"
289
-
mkdir -p -m 0700 "$root/var/lib/private" "$root/root" /run/containers
290
-
if ! [ -e "$root/etc/os-release" ]; then
291
-
touch "$root/etc/os-release"
294
-
if ! [ -e "$root/etc/machine-id" ]; then
295
-
touch "$root/etc/machine-id"
299
-
"/nix/var/nix/profiles/per-container/$INSTANCE" \
300
-
"/nix/var/nix/gcroots/per-container/$INSTANCE"
302
-
cp --remove-destination /etc/resolv.conf "$root/etc/resolv.conf"
304
-
if [ "$PRIVATE_NETWORK" = 1 ]; then
305
-
extraFlags+=" --network-veth"
306
-
if [ -n "$HOST_BRIDGE" ]; then
307
-
extraFlags+=" --network-bridge=$HOST_BRIDGE"
311
-
for iface in $INTERFACES; do
312
-
extraFlags+=" --network-interface=$iface"
315
-
for iface in $MACVLANS; do
316
-
extraFlags+=" --network-macvlan=$iface"
319
-
# If the host is 64-bit and the container is 32-bit, add a
320
-
# --personality flag.
321
-
${optionalString (config.nixpkgs.system == "x86_64-linux") ''
322
-
if [ "$(< ''${SYSTEM_PATH:-/nix/var/nix/profiles/per-container/$INSTANCE/system}/system)" = i686-linux ]; then
323
-
extraFlags+=" --personality=x86"
327
-
# Run systemd-nspawn without startup notification (we'll
328
-
# wait for the container systemd to signal readiness).
330
-
exec ${config.systemd.package}/bin/systemd-nspawn \
332
-
-M "$INSTANCE" -D "$root" $extraFlags \
333
-
$EXTRA_NSPAWN_FLAGS \
334
-
--notify-ready=yes \
335
-
--bind-ro=/nix/store \
336
-
--bind-ro=/nix/var/nix/db \
337
-
--bind-ro=/nix/var/nix/daemon-socket \
338
-
--bind="/nix/var/nix/profiles/per-container/$INSTANCE:/nix/var/nix/profiles" \
339
-
--bind="/nix/var/nix/gcroots/per-container/$INSTANCE:/nix/var/nix/gcroots" \
340
-
--setenv PRIVATE_NETWORK="$PRIVATE_NETWORK" \
341
-
--setenv HOST_BRIDGE="$HOST_BRIDGE" \
342
-
--setenv HOST_ADDRESS="$HOST_ADDRESS" \
343
-
--setenv LOCAL_ADDRESS="$LOCAL_ADDRESS" \
344
-
--setenv HOST_ADDRESS6="$HOST_ADDRESS6" \
345
-
--setenv LOCAL_ADDRESS6="$LOCAL_ADDRESS6" \
346
-
--setenv PATH="$PATH" \
347
-
${containerInit} "''${SYSTEM_PATH:-/nix/var/nix/profiles/system}/init"
352
-
if [ "$PRIVATE_NETWORK" = 1 ]; then
353
-
if [ -z "$HOST_BRIDGE" ]; then
354
-
ifaceHost=ve-$INSTANCE
355
-
ip link set dev $ifaceHost up
356
-
if [ -n "$HOST_ADDRESS" ]; then
357
-
ip addr add $HOST_ADDRESS dev $ifaceHost
359
-
if [ -n "$HOST_ADDRESS6" ]; then
360
-
ip -6 addr add $HOST_ADDRESS6 dev $ifaceHost
362
-
if [ -n "$LOCAL_ADDRESS" ]; then
363
-
ip route add $LOCAL_ADDRESS dev $ifaceHost
365
-
if [ -n "$LOCAL_ADDRESS6" ]; then
366
-
ip -6 route add $LOCAL_ADDRESS6 dev $ifaceHost
371
-
# Get the leader PID so that we can signal it in
372
-
# preStop. We can't use machinectl there because D-Bus
373
-
# might be shutting down. FIXME: in systemd 219 we can
374
-
# just signal systemd-nspawn to do a clean shutdown.
375
-
machinectl show "$INSTANCE" | sed 's/Leader=\(.*\)/\1/;t;d' > "/run/containers/$INSTANCE.pid"
472
+
postStart = postStartScript {};
···
[{ name = "container@"; value = unit; }]
++ (mapAttrsToList (name: cfg: nameValuePair "container@${name}" (
525
+
preStart = preStartScript cfg;
526
+
script = startScript cfg;
527
+
postStart = postStartScript cfg;
wantedBy = [ "multi-user.target" ];
wants = [ "network.target" ];
after = [ "network.target" ];
restartTriggers = [ cfg.path ];
···
LOCAL_ADDRESS6=${cfg.localAddress6}
465
-
INTERFACES="${toString cfg.interfaces}"
466
-
${optionalString cfg.autoStart ''
469
-
EXTRA_NSPAWN_FLAGS="${mkBindFlags cfg.bindMounts}"
566
+
INTERFACES="${toString cfg.interfaces}"
567
+
${optionalString cfg.autoStart ''
570
+
EXTRA_NSPAWN_FLAGS="${mkBindFlags cfg.bindMounts}"