about summary refs log tree commit diff
path: root/nixos/modules
diff options
context:
space:
mode:
authorFranz Pletz <fpletz@fnordicwalking.de>2016-04-24 20:33:46 +0200
committerFranz Pletz <fpletz@fnordicwalking.de>2016-04-24 20:33:46 +0200
commit8cca66f7740119bd6f581b9daff20997499810da (patch)
treec11851c69836b63719a46678a5cf6151888642c4 /nixos/modules
parentcf4a69e2ec73c5abf5812e20a558bf45b9646e56 (diff)
parent3c819f28f57ee1485f15faf917f2fc6a861a6883 (diff)
downloadnixlib-8cca66f7740119bd6f581b9daff20997499810da.tar
nixlib-8cca66f7740119bd6f581b9daff20997499810da.tar.gz
nixlib-8cca66f7740119bd6f581b9daff20997499810da.tar.bz2
nixlib-8cca66f7740119bd6f581b9daff20997499810da.tar.lz
nixlib-8cca66f7740119bd6f581b9daff20997499810da.tar.xz
nixlib-8cca66f7740119bd6f581b9daff20997499810da.tar.zst
nixlib-8cca66f7740119bd6f581b9daff20997499810da.zip
Merge pull request #14018 from kampfschlaefer/feature/hostbridge_and_ipv6_for_containers
containers: hostbridge and IPv6
Diffstat (limited to 'nixos/modules')
-rw-r--r--nixos/modules/virtualisation/containers.nix345
1 files changed, 197 insertions, 148 deletions
diff --git a/nixos/modules/virtualisation/containers.nix b/nixos/modules/virtualisation/containers.nix
index 121ecbc9bf2c..fca21a8610be 100644
--- a/nixos/modules/virtualisation/containers.nix
+++ b/nixos/modules/virtualisation/containers.nix
@@ -28,14 +28,23 @@ let
 
       # Initialise the container side of the veth pair.
       if [ "$PRIVATE_NETWORK" = 1 ]; then
+
         ip link set host0 name eth0
         ip link set dev eth0 up
+
+        if [ -n "$LOCAL_ADDRESS" ]; then
+          ip addr add $LOCAL_ADDRESS dev eth0
+        fi
+        if [ -n "$LOCAL_ADDRESS6" ]; then
+          ip -6 addr add $LOCAL_ADDRESS6 dev eth0
+        fi
         if [ -n "$HOST_ADDRESS" ]; then
           ip route add $HOST_ADDRESS dev eth0
           ip route add default via $HOST_ADDRESS
         fi
-        if [ -n "$LOCAL_ADDRESS" ]; then
-          ip addr add $LOCAL_ADDRESS dev eth0
+        if [ -n "$HOST_ADDRESS6" ]; then
+          ip -6 route add $HOST_ADDRESS6 dev eth0
+          ip -6 route add default via $HOST_ADDRESS6
         fi
       fi
 
@@ -48,7 +57,7 @@ let
   system = config.nixpkgs.system;
 
   bindMountOpts = { name, config, ... }: {
-  
+
     options = {
       mountPoint = mkOption {
         example = "/mnt/usb";
@@ -68,13 +77,13 @@ let
         description = "Determine whether the mounted path will be accessed in read-only mode.";
       };
     };
-    
+
     config = {
       mountPoint = mkDefault name;
     };
-    
+
   };
-  
+
   mkBindFlag = d:
                let flagPrefix = if d.isReadOnly then " --bind-ro=" else " --bind=";
                    mountstr = if d.hostPath != null then "${d.hostPath}:${d.mountPoint}" else "${d.mountPoint}";
@@ -142,12 +151,33 @@ in
               '';
             };
 
+            hostBridge = mkOption {
+              type = types.nullOr types.string;
+              default = null;
+              example = "br0";
+              description = ''
+                Put the host-side of the veth-pair into the named bridge.
+                Only one of hostAddress* or hostBridge can be given.
+              '';
+            };
+
             hostAddress = mkOption {
               type = types.nullOr types.str;
               default = null;
               example = "10.231.136.1";
               description = ''
                 The IPv4 address assigned to the host interface.
+                (Not used when hostBridge is set.)
+              '';
+            };
+
+            hostAddress6 = mkOption {
+              type = types.nullOr types.string;
+              default = null;
+              example = "fc00::1";
+              description = ''
+                The IPv6 address assigned to the host interface.
+                (Not used when hostBridge is set.)
               '';
             };
 
@@ -161,6 +191,16 @@ in
               '';
             };
 
+            localAddress6 = mkOption {
+              type = types.nullOr types.string;
+              default = null;
+              example = "fc00::2";
+              description = ''
+                The IPv6 address assigned to <literal>eth0</literal>
+                in the container.
+              '';
+            };
+
             interfaces = mkOption {
               type = types.listOf types.string;
               default = [];
@@ -185,7 +225,7 @@ in
               example = { "/home" = { hostPath = "/home/alice";
                                       isReadOnly = false; };
                         };
-                        
+
               description =
                 ''
                   An extra list of directories that is bound to the container.
@@ -238,154 +278,180 @@ in
   };
 
 
-  config = mkIf (config.boot.enableContainers) {
+  config = mkIf (config.boot.enableContainers) (let
 
-    systemd.services."container@" =
-      { description = "Container '%i'";
+    unit = {
+      description = "Container '%i'";
 
-        unitConfig.RequiresMountsFor = [ "/var/lib/containers/%i" ];
+      unitConfig.RequiresMountsFor = [ "/var/lib/containers/%i" ];
 
-        path = [ pkgs.iproute ];
+      path = [ pkgs.iproute ];
 
-        environment.INSTANCE = "%i";
-        environment.root = "/var/lib/containers/%i";
+      environment.INSTANCE = "%i";
+      environment.root = "/var/lib/containers/%i";
 
-        preStart =
-          ''
-            # Clean up existing machined registration and interfaces.
-            machinectl terminate "$INSTANCE" 2> /dev/null || true
-
-            if [ "$PRIVATE_NETWORK" = 1 ]; then
-              ip link del dev "ve-$INSTANCE" 2> /dev/null || true
-            fi
+      preStart =
+        ''
+          # Clean up existing machined registration and interfaces.
+          machinectl terminate "$INSTANCE" 2> /dev/null || true
 
+          if [ "$PRIVATE_NETWORK" = 1 ]; then
+            ip link del dev "ve-$INSTANCE" 2> /dev/null || true
+            ip link del dev "vb-$INSTANCE" 2> /dev/null || true
+          fi
+       '';
 
-            if [ "$PRIVATE_NETWORK" = 1 ]; then
-              ip link del dev "ve-$INSTANCE" 2> /dev/null || true
-            fi
-         '';
-
-        script =
-          ''
-            mkdir -p -m 0755 "$root/etc" "$root/var/lib"
-            mkdir -p -m 0700 "$root/var/lib/private" "$root/root" /run/containers
-            if ! [ -e "$root/etc/os-release" ]; then
-              touch "$root/etc/os-release"
+      script =
+        ''
+          mkdir -p -m 0755 "$root/etc" "$root/var/lib"
+          mkdir -p -m 0700 "$root/var/lib/private" "$root/root" /run/containers
+          if ! [ -e "$root/etc/os-release" ]; then
+            touch "$root/etc/os-release"
+          fi
+
+          mkdir -p -m 0755 \
+            "/nix/var/nix/profiles/per-container/$INSTANCE" \
+            "/nix/var/nix/gcroots/per-container/$INSTANCE"
+
+          cp --remove-destination /etc/resolv.conf "$root/etc/resolv.conf"
+
+          if [ "$PRIVATE_NETWORK" = 1 ]; then
+            extraFlags+=" --network-veth"
+            if [ -n "$HOST_BRIDGE" ]; then
+              extraFlags+=" --network-bridge=$HOST_BRIDGE"
             fi
+          fi
 
-            mkdir -p -m 0755 \
-              "/nix/var/nix/profiles/per-container/$INSTANCE" \
-              "/nix/var/nix/gcroots/per-container/$INSTANCE"
+          for iface in $INTERFACES; do
+            extraFlags+=" --network-interface=$iface"
+          done
 
-            cp --remove-destination /etc/resolv.conf "$root/etc/resolv.conf"
+          for iface in $MACVLANS; do
+            extraFlags+=" --network-macvlan=$iface"
+          done
 
-            if [ "$PRIVATE_NETWORK" = 1 ]; then
-              extraFlags+=" --network-veth"
+          # If the host is 64-bit and the container is 32-bit, add a
+          # --personality flag.
+          ${optionalString (config.nixpkgs.system == "x86_64-linux") ''
+            if [ "$(< ''${SYSTEM_PATH:-/nix/var/nix/profiles/per-container/$INSTANCE/system}/system)" = i686-linux ]; then
+              extraFlags+=" --personality=x86"
             fi
+          ''}
+
+
+
+          # Run systemd-nspawn without startup notification (we'll
+          # wait for the container systemd to signal readiness).
+          EXIT_ON_REBOOT=1 NOTIFY_SOCKET= \
+          exec ${config.systemd.package}/bin/systemd-nspawn \
+            --keep-unit \
+            -M "$INSTANCE" -D "$root" $extraFlags \
+            $EXTRA_NSPAWN_FLAGS \
+            --bind-ro=/nix/store \
+            --bind-ro=/nix/var/nix/db \
+            --bind-ro=/nix/var/nix/daemon-socket \
+            --bind=/run/systemd/notify:/var/lib/private/host-notify \
+            --bind="/nix/var/nix/profiles/per-container/$INSTANCE:/nix/var/nix/profiles" \
+            --bind="/nix/var/nix/gcroots/per-container/$INSTANCE:/nix/var/nix/gcroots" \
+            --setenv PRIVATE_NETWORK="$PRIVATE_NETWORK" \
+            --setenv HOST_BRIDGE="$HOST_BRIDGE" \
+            --setenv HOST_ADDRESS="$HOST_ADDRESS" \
+            --setenv LOCAL_ADDRESS="$LOCAL_ADDRESS" \
+            --setenv HOST_ADDRESS6="$HOST_ADDRESS6" \
+            --setenv LOCAL_ADDRESS6="$LOCAL_ADDRESS6" \
+            --setenv PATH="$PATH" \
+            ${containerInit} "''${SYSTEM_PATH:-/nix/var/nix/profiles/system}/init"
+        '';
 
-            for iface in $INTERFACES; do
-              extraFlags+=" --network-interface=$iface"
-            done
-
-            for iface in $MACVLANS; do
-              extraFlags+=" --network-macvlan=$iface"
-            done
-
-            # If the host is 64-bit and the container is 32-bit, add a
-            # --personality flag.
-            ${optionalString (config.nixpkgs.system == "x86_64-linux") ''
-              if [ "$(< ''${SYSTEM_PATH:-/nix/var/nix/profiles/per-container/$INSTANCE/system}/system)" = i686-linux ]; then
-                extraFlags+=" --personality=x86"
-              fi
-            ''}
-
-
-
-            # Run systemd-nspawn without startup notification (we'll
-            # wait for the container systemd to signal readiness).
-            EXIT_ON_REBOOT=1 NOTIFY_SOCKET= \
-            exec ${config.systemd.package}/bin/systemd-nspawn \
-              --keep-unit \
-              -M "$INSTANCE" -D "$root" $extraFlags \
-              $EXTRA_NSPAWN_FLAGS \
-              --bind-ro=/nix/store \
-              --bind-ro=/nix/var/nix/db \
-              --bind-ro=/nix/var/nix/daemon-socket \
-              --bind=/run/systemd/notify:/var/lib/private/host-notify \
-              --bind="/nix/var/nix/profiles/per-container/$INSTANCE:/nix/var/nix/profiles" \
-              --bind="/nix/var/nix/gcroots/per-container/$INSTANCE:/nix/var/nix/gcroots" \
-              --setenv PRIVATE_NETWORK="$PRIVATE_NETWORK" \
-              --setenv HOST_ADDRESS="$HOST_ADDRESS" \
-              --setenv LOCAL_ADDRESS="$LOCAL_ADDRESS" \
-              --setenv PATH="$PATH" \
-              ${containerInit} "''${SYSTEM_PATH:-/nix/var/nix/profiles/system}/init"
-          '';
-
-        postStart =
-          ''
-            if [ "$PRIVATE_NETWORK" = 1 ]; then
+      postStart =
+        ''
+          if [ "$PRIVATE_NETWORK" = 1 ]; then
+            if [ -z "$HOST_BRIDGE" ]; then
               ifaceHost=ve-$INSTANCE
               ip link set dev $ifaceHost up
               if [ -n "$HOST_ADDRESS" ]; then
                 ip addr add $HOST_ADDRESS dev $ifaceHost
               fi
+              if [ -n "$HOST_ADDRESS6" ]; then
+                ip -6 addr add $HOST_ADDRESS6 dev $ifaceHost
+              fi
               if [ -n "$LOCAL_ADDRESS" ]; then
                 ip route add $LOCAL_ADDRESS dev $ifaceHost
               fi
+              if [ -n "$LOCAL_ADDRESS6" ]; then
+                ip -6 route add $LOCAL_ADDRESS6 dev $ifaceHost
+              fi
             fi
+          fi
 
-            # Get the leader PID so that we can signal it in
-            # preStop. We can't use machinectl there because D-Bus
-            # might be shutting down. FIXME: in systemd 219 we can
-            # just signal systemd-nspawn to do a clean shutdown.
-            machinectl show "$INSTANCE" | sed 's/Leader=\(.*\)/\1/;t;d' > "/run/containers/$INSTANCE.pid"
-          '';
+          # Get the leader PID so that we can signal it in
+          # preStop. We can't use machinectl there because D-Bus
+          # might be shutting down. FIXME: in systemd 219 we can
+          # just signal systemd-nspawn to do a clean shutdown.
+          machinectl show "$INSTANCE" | sed 's/Leader=\(.*\)/\1/;t;d' > "/run/containers/$INSTANCE.pid"
+        '';
 
-        preStop =
-          ''
-            pid="$(cat /run/containers/$INSTANCE.pid)"
-            if [ -n "$pid" ]; then
-              kill -RTMIN+4 "$pid"
-            fi
-            rm -f "/run/containers/$INSTANCE.pid"
-          '';
+      preStop =
+        ''
+          pid="$(cat /run/containers/$INSTANCE.pid)"
+          if [ -n "$pid" ]; then
+            kill -RTMIN+4 "$pid"
+          fi
+          rm -f "/run/containers/$INSTANCE.pid"
+        '';
 
-        restartIfChanged = false;
-        #reloadIfChanged = true; # FIXME
+      restartIfChanged = false;
 
-        serviceConfig = {
-          ExecReload = pkgs.writeScript "reload-container"
-            ''
-              #! ${pkgs.stdenv.shell} -e
-              ${nixos-container}/bin/nixos-container run "$INSTANCE" -- \
-                bash --login -c "''${SYSTEM_PATH:-/nix/var/nix/profiles/system}/bin/switch-to-configuration test"
-            '';
+      serviceConfig = {
+        ExecReload = pkgs.writeScript "reload-container"
+          ''
+            #! ${pkgs.stdenv.shell} -e
+            ${nixos-container}/bin/nixos-container run "$INSTANCE" -- \
+              bash --login -c "''${SYSTEM_PATH:-/nix/var/nix/profiles/system}/bin/switch-to-configuration test"
+          '';
 
-          SyslogIdentifier = "container %i";
+        SyslogIdentifier = "container %i";
 
-          EnvironmentFile = "-/etc/containers/%i.conf";
+        EnvironmentFile = "-/etc/containers/%i.conf";
 
-          Type = "notify";
+        Type = "notify";
 
-          NotifyAccess = "all";
+        NotifyAccess = "all";
 
-          # Note that on reboot, systemd-nspawn returns 133, so this
-          # unit will be restarted. On poweroff, it returns 0, so the
-          # unit won't be restarted.
-          RestartForceExitStatus = "133";
-          SuccessExitStatus = "133";
+        # Note that on reboot, systemd-nspawn returns 133, so this
+        # unit will be restarted. On poweroff, it returns 0, so the
+        # unit won't be restarted.
+        RestartForceExitStatus = "133";
+        SuccessExitStatus = "133";
 
-          Restart = "on-failure";
+        Restart = "on-failure";
 
-          # Hack: we don't want to kill systemd-nspawn, since we call
-          # "machinectl poweroff" in preStop to shut down the
-          # container cleanly. But systemd requires sending a signal
-          # (at least if we want remaining processes to be killed
-          # after the timeout). So send an ignored signal.
-          KillMode = "mixed";
-          KillSignal = "WINCH";
-        };
+        # Hack: we don't want to kill systemd-nspawn, since we call
+        # "machinectl poweroff" in preStop to shut down the
+        # container cleanly. But systemd requires sending a signal
+        # (at least if we want remaining processes to be killed
+        # after the timeout). So send an ignored signal.
+        KillMode = "mixed";
+        KillSignal = "WINCH";
       };
+    };
+  in {
+    systemd.services = listToAttrs (filter (x: x.value != null) (
+      # The generic container template used by imperative containers
+      [{ name = "container@"; value = unit; }]
+      # declarative containers
+      ++ (mapAttrsToList (name: cfg: nameValuePair "container@${name}" (
+        if cfg.autoStart then
+          unit // {
+            wantedBy = [ "multi-user.target" ];
+            wants = [ "network.target" ];
+            after = [ "network.target" ];
+            restartTriggers = [ cfg.path ];
+            reloadIfChanged = true;
+          }
+        else null
+      )) config.containers)
+    ));
 
     # Generate a configuration file in /etc/containers for each
     # container so that container@.target can get the container
@@ -396,12 +462,21 @@ in
             SYSTEM_PATH=${cfg.path}
             ${optionalString cfg.privateNetwork ''
               PRIVATE_NETWORK=1
+              ${optionalString (cfg.hostBridge != null) ''
+                HOST_BRIDGE=${cfg.hostBridge}
+              ''}
               ${optionalString (cfg.hostAddress != null) ''
                 HOST_ADDRESS=${cfg.hostAddress}
               ''}
+              ${optionalString (cfg.hostAddress6 != null) ''
+                HOST_ADDRESS6=${cfg.hostAddress6}
+              ''}
               ${optionalString (cfg.localAddress != null) ''
                 LOCAL_ADDRESS=${cfg.localAddress}
               ''}
+              ${optionalString (cfg.localAddress6 != null) ''
+                LOCAL_ADDRESS6=${cfg.localAddress6}
+              ''}
             ''}
              INTERFACES="${toString cfg.interfaces}"
            ${optionalString cfg.autoStart ''
@@ -420,31 +495,5 @@ in
     networking.dhcpcd.denyInterfaces = [ "ve-*" ];
 
     environment.systemPackages = [ nixos-container ];
-
-    # Start containers at boot time.
-    systemd.services.all-containers =
-      { description = "All Containers";
-
-        wantedBy = [ "multi-user.target" ];
-
-        unitConfig.ConditionDirectoryNotEmpty = "/etc/containers";
-
-        serviceConfig.Type = "oneshot";
-
-        script =
-          ''
-            res=0
-            shopt -s nullglob
-            for i in /etc/containers/*.conf; do
-              AUTO_START=
-              source "$i"
-              if [ "$AUTO_START" = 1 ]; then
-                systemctl start "container@$(basename "$i" .conf).service" || res=1
-              fi
-            done
-            exit $res
-          ''; # */
-      };
-
-  };
+  });
 }