about summary refs log tree commit diff
path: root/nixos/modules/tasks
diff options
context:
space:
mode:
authorParnell Springmeyer <parnell@digitalmentat.com>2017-01-25 11:08:05 -0800
committerParnell Springmeyer <parnell@digitalmentat.com>2017-01-25 11:08:05 -0800
commitbae00e8aa8f3faff90e28e19cd5074b8c26d0d0e (patch)
tree56abaf30f11ad2f24b9fb7729f74c5fff50fbd93 /nixos/modules/tasks
parent1f9494b752082ec3ac048e56d1c6364a2e23a675 (diff)
parent104c3db6594043dbb81005303f055b02145305a5 (diff)
downloadnixlib-bae00e8aa8f3faff90e28e19cd5074b8c26d0d0e.tar
nixlib-bae00e8aa8f3faff90e28e19cd5074b8c26d0d0e.tar.gz
nixlib-bae00e8aa8f3faff90e28e19cd5074b8c26d0d0e.tar.bz2
nixlib-bae00e8aa8f3faff90e28e19cd5074b8c26d0d0e.tar.lz
nixlib-bae00e8aa8f3faff90e28e19cd5074b8c26d0d0e.tar.xz
nixlib-bae00e8aa8f3faff90e28e19cd5074b8c26d0d0e.tar.zst
nixlib-bae00e8aa8f3faff90e28e19cd5074b8c26d0d0e.zip
setcap-wrapper: Merging with upstream master and resolving conflicts
Diffstat (limited to 'nixos/modules/tasks')
-rw-r--r--nixos/modules/tasks/filesystems.nix15
-rw-r--r--nixos/modules/tasks/filesystems/nfs.nix16
-rw-r--r--nixos/modules/tasks/filesystems/zfs.nix50
-rw-r--r--nixos/modules/tasks/network-interfaces-scripted.nix108
-rw-r--r--nixos/modules/tasks/network-interfaces-systemd.nix2
-rw-r--r--nixos/modules/tasks/network-interfaces.nix555
6 files changed, 408 insertions, 338 deletions
diff --git a/nixos/modules/tasks/filesystems.nix b/nixos/modules/tasks/filesystems.nix
index 9ab1baeacb98..49ba66ad50af 100644
--- a/nixos/modules/tasks/filesystems.nix
+++ b/nixos/modules/tasks/filesystems.nix
@@ -18,7 +18,7 @@ let
 
   prioOption = prio: optionalString (prio != null) " pri=${toString prio}";
 
-  specialFSTypes = [ "proc" "sysfs" "tmpfs" "devtmpfs" "devpts" ];
+  specialFSTypes = [ "proc" "sysfs" "tmpfs" "ramfs" "devtmpfs" "devpts" ];
 
   coreFileSystemOpts = { name, config, ... }: {
 
@@ -258,7 +258,7 @@ in
           let
             mountPoint' = "${escapeSystemdPath fs.mountPoint}.mount";
             device'  = escapeSystemdPath fs.device;
-            device'' = "${device}.device";
+            device'' = "${device'}.device";
           in nameValuePair "mkfs-${device'}"
           { description = "Initialisation of Filesystem ${fs.device}";
             wantedBy = [ mountPoint' ];
@@ -286,11 +286,18 @@ in
     # Sync mount options with systemd's src/core/mount-setup.c: mount_table.
     boot.specialFileSystems = {
       "/proc" = { fsType = "proc"; options = [ "nosuid" "noexec" "nodev" ]; };
-      "/sys" = { fsType = "sysfs"; options = [ "nosuid" "noexec" "nodev" ]; };
-      "/run" = { fsType = "tmpfs"; options = [ "nosuid" "nodev" "strictatime" "mode=755" "size=${config.boot.runSize}" ]; };
+      "/run" = { fsType = "tmpfs"; options = [ "nodev" "strictatime" "mode=755" "size=${config.boot.runSize}" ]; };
       "/dev" = { fsType = "devtmpfs"; options = [ "nosuid" "strictatime" "mode=755" "size=${config.boot.devSize}" ]; };
       "/dev/shm" = { fsType = "tmpfs"; options = [ "nosuid" "nodev" "strictatime" "mode=1777" "size=${config.boot.devShmSize}" ]; };
       "/dev/pts" = { fsType = "devpts"; options = [ "nosuid" "noexec" "mode=620" "gid=${toString config.ids.gids.tty}" ]; };
+
+      # To hold secrets that shouldn't be written to disk (generally used for NixOps, harmless elsewhere)
+      "/run/keys" = { fsType = "ramfs"; options = [ "nosuid" "nodev" "mode=750" "gid=${toString config.ids.gids.keys}" ]; };
+    } // optionalAttrs (!config.boot.isContainer) {
+      # systemd-nspawn populates /sys by itself, and remounting it causes all
+      # kinds of weird issues (most noticeably, waiting for host disk device
+      # nodes).
+      "/sys" = { fsType = "sysfs"; options = [ "nosuid" "noexec" "nodev" ]; };
     };
 
   };
diff --git a/nixos/modules/tasks/filesystems/nfs.nix b/nixos/modules/tasks/filesystems/nfs.nix
index e454eca3a0e5..e9a7ccc721a9 100644
--- a/nixos/modules/tasks/filesystems/nfs.nix
+++ b/nixos/modules/tasks/filesystems/nfs.nix
@@ -38,15 +38,17 @@ in
         default = null;
         example = 4000;
         description = ''
-          Use fixed port for rpc.statd, useful if NFS server is behind firewall.
+          Use a fixed port for <command>rpc.statd</command>. This is
+          useful if the NFS server is behind a firewall.
         '';
       };
       lockdPort = mkOption {
         default = null;
         example = 4001;
         description = ''
-          Use fixed port for NFS lock manager kernel module (lockd/nlockmgr),
-          useful if NFS server is behind firewall.
+          Use a fixed port for the NFS lock manager kernel module
+          (<literal>lockd/nlockmgr</literal>).  This is useful if the
+          NFS server is behind a firewall.
         '';
       };
     };
@@ -68,13 +70,16 @@ in
 
     boot.initrd.kernelModules = mkIf inInitrd [ "nfs" ];
 
+    # FIXME: should use upstream units from nfs-utils.
+
     systemd.services.statd =
       { description = "NFSv3 Network Status Monitor";
 
         path = [ pkgs.nfs-utils pkgs.sysvtools pkgs.utillinux ];
 
-        wantedBy = [ "remote-fs-pre.target" ];
+        wants = [ "remote-fs-pre.target" ];
         before = [ "remote-fs-pre.target" ];
+        wantedBy = [ "remote-fs.target" ];
         requires = [ "basic.target" "rpcbind.service" ];
         after = [ "basic.target" "rpcbind.service" ];
 
@@ -100,8 +105,9 @@ in
 
         path = [ pkgs.sysvtools pkgs.utillinux ];
 
-        wantedBy = [ "remote-fs-pre.target" ];
+        wants = [ "remote-fs-pre.target" ];
         before = [ "remote-fs-pre.target" ];
+        wantedBy = [ "remote-fs.target" ];
         requires = [ "rpcbind.service" ];
         after = [ "rpcbind.service" ];
 
diff --git a/nixos/modules/tasks/filesystems/zfs.nix b/nixos/modules/tasks/filesystems/zfs.nix
index c5f41cc338cf..045cbeb7cff8 100644
--- a/nixos/modules/tasks/filesystems/zfs.nix
+++ b/nixos/modules/tasks/filesystems/zfs.nix
@@ -22,12 +22,18 @@ let
 
   kernel = config.boot.kernelPackages;
 
-  splKernelPkg = kernel.spl;
-  zfsKernelPkg = kernel.zfs;
-  zfsUserPkg = pkgs.zfs;
+  packages = if config.boot.zfs.enableUnstable then {
+    spl = kernel.splUnstable;
+    zfs = kernel.zfsUnstable;
+    zfsUser = pkgs.zfsUnstable;
+  } else {
+    spl = kernel.spl;
+    zfs = kernel.zfs;
+    zfsUser = pkgs.zfs;
+  };
 
   autosnapPkg = pkgs.zfstools.override {
-    zfs = zfsUserPkg;
+    zfs = packages.zfsUser;
   };
 
   zfsAutoSnap = "${autosnapPkg}/bin/zfs-auto-snapshot";
@@ -54,6 +60,18 @@ in
 
   options = {
     boot.zfs = {
+      enableUnstable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Use the unstable zfs package. This might be an option, if the latest
+          kernel is not yet supported by a published release of ZFS. Enabling
+          this option will install a development version of ZFS on Linux. The
+          version will have already passed an extensive test suite, but it is
+          more likely to hit an undiscovered bug compared to running a released
+          version of ZFS on Linux.
+        '';
+      };
 
       extraPools = mkOption {
         type = types.listOf types.str;
@@ -218,16 +236,16 @@ in
 
       boot = {
         kernelModules = [ "spl" "zfs" ] ;
-        extraModulePackages = [ splKernelPkg zfsKernelPkg ];
+        extraModulePackages = with packages; [ spl zfs ];
       };
 
       boot.initrd = mkIf inInitrd {
         kernelModules = [ "spl" "zfs" ];
         extraUtilsCommands =
           ''
-            copy_bin_and_libs ${zfsUserPkg}/sbin/zfs
-            copy_bin_and_libs ${zfsUserPkg}/sbin/zdb
-            copy_bin_and_libs ${zfsUserPkg}/sbin/zpool
+            copy_bin_and_libs ${packages.zfsUser}/sbin/zfs
+            copy_bin_and_libs ${packages.zfsUser}/sbin/zdb
+            copy_bin_and_libs ${packages.zfsUser}/sbin/zpool
           '';
         extraUtilsCommandsTest = mkIf inInitrd
           ''
@@ -264,14 +282,14 @@ in
         zfsSupport = true;
       };
 
-      environment.etc."zfs/zed.d".source = "${zfsUserPkg}/etc/zfs/zed.d/*";
+      environment.etc."zfs/zed.d".source = "${packages.zfsUser}/etc/zfs/zed.d/*";
 
-      system.fsPackages = [ zfsUserPkg ];                  # XXX: needed? zfs doesn't have (need) a fsck
-      environment.systemPackages = [ zfsUserPkg ]
-        ++ optional enableAutoSnapshots autosnapPkg;       # so the user can run the command to see flags
+      system.fsPackages = [ packages.zfsUser ]; # XXX: needed? zfs doesn't have (need) a fsck
+      environment.systemPackages = [ packages.zfsUser ]
+        ++ optional enableAutoSnapshots autosnapPkg; # so the user can run the command to see flags
 
-      services.udev.packages = [ zfsUserPkg ];             # to hook zvol naming, etc.
-      systemd.packages = [ zfsUserPkg ];
+      services.udev.packages = [ packages.zfsUser ]; # to hook zvol naming, etc.
+      systemd.packages = [ packages.zfsUser ];
 
       systemd.services = let
         getPoolFilesystems = pool:
@@ -298,7 +316,7 @@ in
               RemainAfterExit = true;
             };
             script = ''
-              zpool_cmd="${zfsUserPkg}/sbin/zpool"
+              zpool_cmd="${packages.zfsUser}/sbin/zpool"
               ("$zpool_cmd" list "${pool}" >/dev/null) || "$zpool_cmd" import -d ${cfgZfs.devNodes} -N ${optionalString cfgZfs.forceImportAll "-f"} "${pool}"
             '';
           };
@@ -314,7 +332,7 @@ in
               RemainAfterExit = true;
             };
             script = ''
-              ${zfsUserPkg}/sbin/zfs set nixos:shutdown-time="$(date)" "${pool}"
+              ${packages.zfsUser}/sbin/zfs set nixos:shutdown-time="$(date)" "${pool}"
             '';
           };
 
diff --git a/nixos/modules/tasks/network-interfaces-scripted.nix b/nixos/modules/tasks/network-interfaces-scripted.nix
index c960e401f9b1..c50ea5c79643 100644
--- a/nixos/modules/tasks/network-interfaces-scripted.nix
+++ b/nixos/modules/tasks/network-interfaces-scripted.nix
@@ -46,6 +46,23 @@ in
     systemd.services =
       let
 
+        deviceDependency = dev:
+          if (config.boot.isContainer == false)
+          then
+            # Trust udev when not in the container
+            optional (dev != null) (subsystemDevice dev)
+          else
+            # When in the container, check whether the interface is built from other definitions
+            if (hasAttr dev cfg.bridges) ||
+               (hasAttr dev cfg.bonds) ||
+               (hasAttr dev cfg.macvlans) ||
+               (hasAttr dev cfg.sits) ||
+               (hasAttr dev cfg.vlans) ||
+               (hasAttr dev cfg.vswitches) ||
+               (hasAttr dev cfg.wlanInterfaces)
+            then [ "${dev}-netdev.service" ]
+            else [];
+
         networkLocalCommands = {
           after = [ "network-setup.service" ];
           bindsTo = [ "network-setup.service" ];
@@ -54,16 +71,22 @@ in
         networkSetup =
           { description = "Networking Setup";
 
-            after = [ "network-interfaces.target" "network-pre.target" ];
-            before = [ "network.target" ];
-            wantedBy = [ "network.target" ];
+            after = [ "network-pre.target" "systemd-udevd.service" "systemd-sysctl.service" ];
+            before = [ "network.target" "shutdown.target" ];
+            wants = [ "network.target" ];
+            conflicts = [ "shutdown.target" ];
+            wantedBy = [ "multi-user.target" ];
 
             unitConfig.ConditionCapability = "CAP_NET_ADMIN";
 
             path = [ pkgs.iproute ];
 
-            serviceConfig.Type = "oneshot";
-            serviceConfig.RemainAfterExit = true;
+            serviceConfig = {
+              Type = "oneshot";
+              RemainAfterExit = true;
+            };
+
+            unitConfig.DefaultDependencies = false;
 
             script =
               ''
@@ -108,10 +131,14 @@ in
           in
           nameValuePair "network-addresses-${i.name}"
           { description = "Address configuration of ${i.name}";
-            wantedBy = [ "network-interfaces.target" ];
-            before = [ "network-interfaces.target" ];
-            bindsTo = [ (subsystemDevice i.name) ];
-            after = [ (subsystemDevice i.name) "network-pre.target" ];
+            wantedBy = [ "network-setup.service" ];
+            # propagate stop and reload from network-setup
+            partOf = [ "network-setup.service" ];
+            # order before network-setup because the routes that are configured
+            # there may need ip addresses configured
+            before = [ "network-setup.service" ];
+            bindsTo = deviceDependency i.name;
+            after = [ "network-pre.target" ] ++ (deviceDependency i.name);
             serviceConfig.Type = "oneshot";
             serviceConfig.RemainAfterExit = true;
             path = [ pkgs.iproute ];
@@ -129,21 +156,11 @@ in
                   echo "checking ip ${address}..."
                   if out=$(ip addr add "${address}" dev "${i.name}" 2>&1); then
                     echo "added ip ${address}..."
-                    restart_network_setup=true
                   elif ! echo "$out" | grep "File exists" >/dev/null 2>&1; then
                     echo "failed to add ${address}"
                     exit 1
                   fi
-                '')
-              + optionalString (ips != [ ])
-                ''
-                  if [ "$restart_network_setup" = "true" ]; then
-                    # Ensure that the default gateway remains set.
-                    # (Flushing this interface may have removed it.)
-                    ${config.systemd.package}/bin/systemctl try-restart --no-block network-setup.service
-                  fi
-                  ${config.systemd.package}/bin/systemctl start ip-up.target
-                '';
+                '');
             preStop = flip concatMapStrings (ips) (ip:
                 let
                   address = "${ip.address}/${toString ip.prefixLength}";
@@ -157,10 +174,11 @@ in
 
         createTunDevice = i: nameValuePair "${i.name}-netdev"
           { description = "Virtual Network Interface ${i.name}";
-            requires = [ "dev-net-tun.device" ];
+            bindsTo = [ "dev-net-tun.device" ];
             after = [ "dev-net-tun.device" "network-pre.target" ];
-            wantedBy = [ "network.target" (subsystemDevice i.name) ];
-            before = [ "network-interfaces.target" (subsystemDevice i.name) ];
+            wantedBy = [ "network-setup.service" (subsystemDevice i.name) ];
+            partOf = [ "network-setup.service" ];
+            before = [ "network-setup.service" (subsystemDevice i.name) ];
             path = [ pkgs.iproute ];
             serviceConfig = {
               Type = "oneshot";
@@ -178,15 +196,15 @@ in
 
         createBridgeDevice = n: v: nameValuePair "${n}-netdev"
           (let
-            deps = map subsystemDevice v.interfaces;
+            deps = concatLists (map deviceDependency v.interfaces);
           in
           { description = "Bridge Interface ${n}";
-            wantedBy = [ "network.target" (subsystemDevice n) ];
+            wantedBy = [ "network-setup.service" (subsystemDevice n) ];
             bindsTo = deps ++ optional v.rstp "mstpd.service";
-            partOf = optional v.rstp "mstpd.service";
+            partOf = [ "network-setup.service" ] ++ optional v.rstp "mstpd.service";
             after = [ "network-pre.target" "mstpd.service" ] ++ deps
               ++ concatMap (i: [ "network-addresses-${i}.service" "network-link-${i}.service" ]) v.interfaces;
-            before = [ "network-interfaces.target" (subsystemDevice n) ];
+            before = [ "network-setup.service" (subsystemDevice n) ];
             serviceConfig.Type = "oneshot";
             serviceConfig.RemainAfterExit = true;
             path = [ pkgs.iproute ];
@@ -219,15 +237,15 @@ in
 
         createVswitchDevice = n: v: nameValuePair "${n}-netdev"
           (let
-            deps = map subsystemDevice v.interfaces;
+            deps = concatLists (map deviceDependency v.interfaces);
             ofRules = pkgs.writeText "vswitch-${n}-openFlowRules" v.openFlowRules;
           in
           { description = "Open vSwitch Interface ${n}";
-            wantedBy = [ "network.target" "vswitchd.service" ] ++ deps;
+            wantedBy = [ "network-setup.service" "vswitchd.service" ] ++ deps;
             bindsTo =  [ "vswitchd.service" (subsystemDevice n) ] ++ deps;
-            partOf = [ "vswitchd.service" ];
+            partOf = [ "network-setup.service" "vswitchd.service" ];
             after = [ "network-pre.target" "vswitchd.service" ] ++ deps;
-            before = [ "network-interfaces.target" ];
+            before = [ "network-setup.service" ];
             serviceConfig.Type = "oneshot";
             serviceConfig.RemainAfterExit = true;
             path = [ pkgs.iproute config.virtualisation.vswitch.package ];
@@ -252,14 +270,15 @@ in
 
         createBondDevice = n: v: nameValuePair "${n}-netdev"
           (let
-            deps = map subsystemDevice v.interfaces;
+            deps = concatLists (map deviceDependency v.interfaces);
           in
           { description = "Bond Interface ${n}";
-            wantedBy = [ "network.target" (subsystemDevice n) ];
+            wantedBy = [ "network-setup.service" (subsystemDevice n) ];
             bindsTo = deps;
+            partOf = [ "network-setup.service" ];
             after = [ "network-pre.target" ] ++ deps
               ++ concatMap (i: [ "network-addresses-${i}.service" "network-link-${i}.service" ]) v.interfaces;
-            before = [ "network-interfaces.target" (subsystemDevice n) ];
+            before = [ "network-setup.service" (subsystemDevice n) ];
             serviceConfig.Type = "oneshot";
             serviceConfig.RemainAfterExit = true;
             path = [ pkgs.iproute pkgs.gawk ];
@@ -289,13 +308,14 @@ in
 
         createMacvlanDevice = n: v: nameValuePair "${n}-netdev"
           (let
-            deps = [ (subsystemDevice v.interface) ];
+            deps = deviceDependency v.interface;
           in
           { description = "Vlan Interface ${n}";
-            wantedBy = [ "network.target" (subsystemDevice n) ];
+            wantedBy = [ "network-setup.service" (subsystemDevice n) ];
             bindsTo = deps;
+            partOf = [ "network-setup.service" ];
             after = [ "network-pre.target" ] ++ deps;
-            before = [ "network-interfaces.target" (subsystemDevice n) ];
+            before = [ "network-setup.service" (subsystemDevice n) ];
             serviceConfig.Type = "oneshot";
             serviceConfig.RemainAfterExit = true;
             path = [ pkgs.iproute ];
@@ -313,13 +333,14 @@ in
 
         createSitDevice = n: v: nameValuePair "${n}-netdev"
           (let
-            deps = optional (v.dev != null) (subsystemDevice v.dev);
+            deps = deviceDependency v.dev;
           in
           { description = "6-to-4 Tunnel Interface ${n}";
-            wantedBy = [ "network.target" (subsystemDevice n) ];
+            wantedBy = [ "network-setup.service" (subsystemDevice n) ];
             bindsTo = deps;
+            partOf = [ "network-setup.service" ];
             after = [ "network-pre.target" ] ++ deps;
-            before = [ "network-interfaces.target" (subsystemDevice n) ];
+            before = [ "network-setup.service" (subsystemDevice n) ];
             serviceConfig.Type = "oneshot";
             serviceConfig.RemainAfterExit = true;
             path = [ pkgs.iproute ];
@@ -340,13 +361,14 @@ in
 
         createVlanDevice = n: v: nameValuePair "${n}-netdev"
           (let
-            deps = [ (subsystemDevice v.interface) ];
+            deps = deviceDependency v.interface;
           in
           { description = "Vlan Interface ${n}";
-            wantedBy = [ "network.target" (subsystemDevice n) ];
+            wantedBy = [ "network-setup.service" (subsystemDevice n) ];
             bindsTo = deps;
+            partOf = [ "network-setup.service" ];
             after = [ "network-pre.target" ] ++ deps;
-            before = [ "network-interfaces.target" (subsystemDevice n) ];
+            before = [ "network-setup.service" (subsystemDevice n) ];
             serviceConfig.Type = "oneshot";
             serviceConfig.RemainAfterExit = true;
             path = [ pkgs.iproute ];
diff --git a/nixos/modules/tasks/network-interfaces-systemd.nix b/nixos/modules/tasks/network-interfaces-systemd.nix
index 301ee43fd0e5..974041d7e1a5 100644
--- a/nixos/modules/tasks/network-interfaces-systemd.nix
+++ b/nixos/modules/tasks/network-interfaces-systemd.nix
@@ -43,7 +43,7 @@ in
       message = "networking.bridges.${n}.rstp is not supported by networkd.";
     });
 
-    systemd.services.dhcpcd.enable = mkDefault false;
+    networking.dhcpcd.enable = mkDefault false;
 
     systemd.services.network-local-commands = {
       after = [ "systemd-networkd.service" ];
diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix
index 1e0b874297a2..a69435ff5937 100644
--- a/nixos/modules/tasks/network-interfaces.nix
+++ b/nixos/modules/tasks/network-interfaces.nix
@@ -97,21 +97,22 @@ let
 
   addrOpts = v:
     assert v == 4 || v == 6;
-    {
-      address = mkOption {
-        type = types.str;
-        description = ''
-          IPv${toString v} address of the interface.  Leave empty to configure the
-          interface using DHCP.
-        '';
-      };
+    { options = {
+        address = mkOption {
+          type = types.str;
+          description = ''
+            IPv${toString v} address of the interface.  Leave empty to configure the
+            interface using DHCP.
+          '';
+        };
 
-      prefixLength = mkOption {
-        type = types.addCheck types.int (n: n >= 0 && n <= (if v == 4 then 32 else 128));
-        description = ''
-          Subnet mask of the interface, specified as the number of
-          bits in the prefix (<literal>${if v == 4 then "24" else "64"}</literal>).
-        '';
+        prefixLength = mkOption {
+          type = types.addCheck types.int (n: n >= 0 && n <= (if v == 4 then 32 else 128));
+          description = ''
+            Subnet mask of the interface, specified as the number of
+            bits in the prefix (<literal>${if v == 4 then "24" else "64"}</literal>).
+          '';
+        };
       };
     };
 
@@ -141,8 +142,7 @@ let
           { address = "10.0.0.1"; prefixLength = 16; }
           { address = "192.168.1.1"; prefixLength = 24; }
         ];
-        type = types.listOf types.optionSet;
-        options = addrOpts 4;
+        type = with types; listOf (submodule (addrOpts 4));
         description = ''
           List of IPv4 addresses that will be statically assigned to the interface.
         '';
@@ -154,8 +154,7 @@ let
           { address = "fdfd:b3f0:482::1"; prefixLength = 48; }
           { address = "2001:1470:fffd:2098::e006"; prefixLength = 64; }
         ];
-        type = types.listOf types.optionSet;
-        options = addrOpts 6;
+        type = with types; listOf (submodule (addrOpts 6));
         description = ''
           List of IPv6 addresses that will be statically assigned to the interface.
         '';
@@ -231,7 +230,7 @@ let
         type = types.bool;
         description = ''
           Whether this interface is virtual and should be created by tunctl.
-          This is mainly useful for creating bridges between a host a virtual
+          This is mainly useful for creating bridges between a host and a virtual
           network such as VPN or a virtual machine.
         '';
       };
@@ -246,7 +245,7 @@ let
 
       virtualType = mkOption {
         default = null;
-        type = types.nullOr (types.addCheck types.str (v: v == "tun" || v == "tap"));
+        type = with types; nullOr (enum [ "tun" "tap" ]);
         description = ''
           The explicit type of interface to create. Accepts tun or tap strings.
           Also accepts null to implicitly detect the type of device.
@@ -311,9 +310,9 @@ in
         generate a random 32-bit ID using the following commands:
 
         <literal>cksum /etc/machine-id | while read c rest; do printf "%x" $c; done</literal>
-        
+
         (this derives it from the machine-id that systemd generates) or
-        
+
         <literal>head -c4 /dev/urandom | od -A none -t x4</literal>
       '';
     };
@@ -391,7 +390,7 @@ in
     };
 
     networking.localCommands = mkOption {
-      type = types.str;
+      type = types.lines;
       default = "";
       example = "text=anything; echo You can put $text here.";
       description = ''
@@ -415,8 +414,7 @@ in
         <option>networking.useDHCP</option> is true, then every
         interface not listed here will be configured using DHCP.
       '';
-      type = types.loaOf types.optionSet;
-      options = [ interfaceOpts ];
+      type = with types; loaOf (submodule interfaceOpts);
     };
 
     networking.vswitches = mkOption {
@@ -434,53 +432,55 @@ in
           interface.
         '';
 
-      type = types.attrsOf types.optionSet;
+      type = with types; attrsOf (submodule {
 
-      options = {
+        options = {
 
-        interfaces = mkOption {
-          example = [ "eth0" "eth1" ];
-          type = types.listOf types.str;
-          description =
-            "The physical network interfaces connected by the vSwitch.";
-        };
+          interfaces = mkOption {
+            example = [ "eth0" "eth1" ];
+            type = types.listOf types.str;
+            description =
+              "The physical network interfaces connected by the vSwitch.";
+          };
 
-        controllers = mkOption {
-          type = types.listOf types.str;
-          default = [];
-          example = [ "ptcp:6653:[::1]" ];
-          description = ''
-            Specify the controller targets. For the allowed options see <literal>man 8 ovs-vsctl</literal>.
-          '';
-        };
+          controllers = mkOption {
+            type = types.listOf types.str;
+            default = [];
+            example = [ "ptcp:6653:[::1]" ];
+            description = ''
+              Specify the controller targets. For the allowed options see <literal>man 8 ovs-vsctl</literal>.
+            '';
+          };
 
-        openFlowRules = mkOption {
-          type = types.lines;
-          default = "";
-          example = ''
-            actions=normal
-          '';
-          description = ''
-            OpenFlow rules to insert into the Open vSwitch. All <literal>openFlowRules</literal> are
-            loaded with <literal>ovs-ofctl</literal> within one atomic operation.
-          '';
-        };
+          openFlowRules = mkOption {
+            type = types.lines;
+            default = "";
+            example = ''
+              actions=normal
+            '';
+            description = ''
+              OpenFlow rules to insert into the Open vSwitch. All <literal>openFlowRules</literal> are
+              loaded with <literal>ovs-ofctl</literal> within one atomic operation.
+            '';
+          };
+
+          extraOvsctlCmds = mkOption {
+            type = types.lines;
+            default = "";
+            example = ''
+              set-fail-mode <switch_name> secure
+              set Bridge <switch_name> stp_enable=true
+            '';
+            description = ''
+              Commands to manipulate the Open vSwitch database. Every line executed with <literal>ovs-vsctl</literal>.
+              All commands are bundled together with the operations for adding the interfaces
+              into one atomic operation.
+            '';
+          };
 
-        extraOvsctlCmds = mkOption {
-          type = types.lines;
-          default = "";
-          example = ''
-            set-fail-mode <switch_name> secure
-            set Bridge <switch_name> stp_enable=true
-          '';
-          description = ''
-            Commands to manipulate the Open vSwitch database. Every line executed with <literal>ovs-vsctl</literal>.
-            All commands are bundled together with the operations for adding the interfaces
-            into one atomic operation.
-          '';
         };
 
-      };
+      });
 
     };
 
@@ -499,25 +499,27 @@ in
           bridge's network interface.
         '';
 
-      type = types.attrsOf types.optionSet;
+      type = with types; attrsOf (submodule {
 
-      options = {
+        options = {
 
-        interfaces = mkOption {
-          example = [ "eth0" "eth1" ];
-          type = types.listOf types.str;
-          description =
-            "The physical network interfaces connected by the bridge.";
-        };
+          interfaces = mkOption {
+            example = [ "eth0" "eth1" ];
+            type = types.listOf types.str;
+            description =
+              "The physical network interfaces connected by the bridge.";
+          };
+
+          rstp = mkOption {
+            example = true;
+            default = false;
+            type = types.bool;
+            description = "Whether the bridge interface should enable rstp.";
+          };
 
-        rstp = mkOption {
-          example = true;
-          default = false;
-          type = types.bool;
-          description = "Whether the bridge interface should enable rstp.";
         };
 
-      };
+      });
 
     };
 
@@ -538,65 +540,66 @@ in
         name specifying the name of the bond's network interface
       '';
 
-      type = types.attrsOf types.optionSet;
-
-      options = {
-
-        interfaces = mkOption {
-          example = [ "enp4s0f0" "enp4s0f1" "wlan0" ];
-          type = types.listOf types.str;
-          description = "The interfaces to bond together";
-        };
+      type = with types; attrsOf (submodule {
 
-        lacp_rate = mkOption {
-          default = null;
-          example = "fast";
-          type = types.nullOr types.str;
-          description = ''
-            Option specifying the rate in which we'll ask our link partner
-            to transmit LACPDU packets in 802.3ad mode.
-          '';
-        };
+        options = {
 
-        miimon = mkOption {
-          default = null;
-          example = 100;
-          type = types.nullOr types.int;
-          description = ''
-            Miimon is the number of millisecond in between each round of polling
-            by the device driver for failed links. By default polling is not
-            enabled and the driver is trusted to properly detect and handle
-            failure scenarios.
-          '';
-        };
+          interfaces = mkOption {
+            example = [ "enp4s0f0" "enp4s0f1" "wlan0" ];
+            type = types.listOf types.str;
+            description = "The interfaces to bond together";
+          };
 
-        mode = mkOption {
-          default = null;
-          example = "active-backup";
-          type = types.nullOr types.str;
-          description = ''
-            The mode which the bond will be running. The default mode for
-            the bonding driver is balance-rr, optimizing for throughput.
-            More information about valid modes can be found at
-            https://www.kernel.org/doc/Documentation/networking/bonding.txt
-          '';
-        };
+          lacp_rate = mkOption {
+            default = null;
+            example = "fast";
+            type = types.nullOr types.str;
+            description = ''
+              Option specifying the rate in which we'll ask our link partner
+              to transmit LACPDU packets in 802.3ad mode.
+            '';
+          };
+
+          miimon = mkOption {
+            default = null;
+            example = 100;
+            type = types.nullOr types.int;
+            description = ''
+              Miimon is the number of millisecond in between each round of polling
+              by the device driver for failed links. By default polling is not
+              enabled and the driver is trusted to properly detect and handle
+              failure scenarios.
+            '';
+          };
+
+          mode = mkOption {
+            default = null;
+            example = "active-backup";
+            type = types.nullOr types.str;
+            description = ''
+              The mode which the bond will be running. The default mode for
+              the bonding driver is balance-rr, optimizing for throughput.
+              More information about valid modes can be found at
+              https://www.kernel.org/doc/Documentation/networking/bonding.txt
+            '';
+          };
+
+          xmit_hash_policy = mkOption {
+            default = null;
+            example = "layer2+3";
+            type = types.nullOr types.str;
+            description = ''
+              Selects the transmit hash policy to use for slave selection in
+              balance-xor, 802.3ad, and tlb modes.
+            '';
+          };
 
-        xmit_hash_policy = mkOption {
-          default = null;
-          example = "layer2+3";
-          type = types.nullOr types.str;
-          description = ''
-            Selects the transmit hash policy to use for slave selection in
-            balance-xor, 802.3ad, and tlb modes.
-          '';
         };
 
-      };
+      });
     };
 
     networking.macvlans = mkOption {
-      type = types.attrsOf types.optionSet;
       default = { };
       example = literalExample {
         wan = {
@@ -608,26 +611,28 @@ in
         This option allows you to define macvlan interfaces which should
         be automatically created.
       '';
-      options = {
-
-        interface = mkOption {
-          example = "enp4s0";
-          type = types.str;
-          description = "The interface the macvlan will transmit packets through.";
-        };
+      type = with types; attrsOf (submodule {
+        options = {
+
+          interface = mkOption {
+            example = "enp4s0";
+            type = types.str;
+            description = "The interface the macvlan will transmit packets through.";
+          };
+
+          mode = mkOption {
+            default = null;
+            type = types.nullOr types.str;
+            example = "vepa";
+            description = "The mode of the macvlan device.";
+          };
 
-        mode = mkOption {
-          default = null;
-          type = types.nullOr types.str;
-          example = "vepa";
-          description = "The mode of the macvlan device.";
         };
 
-      };
+      });
     };
 
     networking.sits = mkOption {
-      type = types.attrsOf types.optionSet;
       default = { };
       example = literalExample {
         hurricane = {
@@ -644,46 +649,49 @@ in
       description = ''
         This option allows you to define 6-to-4 interfaces which should be automatically created.
       '';
-      options = {
-
-        remote = mkOption {
-          type = types.nullOr types.str;
-          default = null;
-          example = "10.0.0.1";
-          description = ''
-            The address of the remote endpoint to forward traffic over.
-          '';
-        };
-
-        local = mkOption {
-          type = types.nullOr types.str;
-          default = null;
-          example = "10.0.0.22";
-          description = ''
-            The address of the local endpoint which the remote
-            side should send packets to.
-          '';
-        };
-
-        ttl = mkOption {
-          type = types.nullOr types.int;
-          default = null;
-          example = 255;
-          description = ''
-            The time-to-live of the connection to the remote tunnel endpoint.
-          '';
-        };
+      type = with types; attrsOf (submodule {
+        options = {
+
+          remote = mkOption {
+            type = types.nullOr types.str;
+            default = null;
+            example = "10.0.0.1";
+            description = ''
+              The address of the remote endpoint to forward traffic over.
+            '';
+          };
+
+          local = mkOption {
+            type = types.nullOr types.str;
+            default = null;
+            example = "10.0.0.22";
+            description = ''
+              The address of the local endpoint which the remote
+              side should send packets to.
+            '';
+          };
+
+          ttl = mkOption {
+            type = types.nullOr types.int;
+            default = null;
+            example = 255;
+            description = ''
+              The time-to-live of the connection to the remote tunnel endpoint.
+            '';
+          };
+
+          dev = mkOption {
+            type = types.nullOr types.str;
+            default = null;
+            example = "enp4s0f0";
+            description = ''
+              The underlying network device on which the tunnel resides.
+            '';
+          };
 
-        dev = mkOption {
-          type = types.nullOr types.str;
-          default = null;
-          example = "enp4s0f0";
-          description = ''
-            The underlying network device on which the tunnel resides.
-          '';
         };
 
-      };
+      });
     };
 
     networking.vlans = mkOption {
@@ -706,23 +714,26 @@ in
           specifying the name of the vlan interface.
         '';
 
-      type = types.attrsOf types.optionSet;
+      type = with types; attrsOf (submodule {
 
-      options = {
+        options = {
 
-        id = mkOption {
-          example = 1;
-          type = types.int;
-          description = "The vlan identifier";
-        };
+          id = mkOption {
+            example = 1;
+            type = types.int;
+            description = "The vlan identifier";
+          };
+
+          interface = mkOption {
+            example = "enp4s0";
+            type = types.str;
+            description = "The interface the vlan will transmit packets through.";
+          };
 
-        interface = mkOption {
-          example = "enp4s0";
-          type = types.str;
-          description = "The interface the vlan will transmit packets through.";
         };
 
-      };
+      });
+
     };
 
     networking.wlanInterfaces = mkOption {
@@ -760,73 +771,69 @@ in
           would have to be created explicitly.
         '';
 
-      type = types.attrsOf types.optionSet;
+      type = with types; attrsOf (submodule {
 
-      options = {
-
-        device = mkOption {
-          type = types.string;
-          example = "wlp6s0";
-          description = "The name of the underlying hardware WLAN device as assigned by <literal>udev</literal>.";
-        };
-
-        type = mkOption {
-          type = types.string;
-          default = "managed";
-          example = "ibss";
-          description = ''
-            The type of the WLAN interface. The type has to be either <literal>managed</literal>,
-            <literal>ibss</literal>, <literal>monitor</literal>, <literal>mesh</literal> or <literal>wds</literal>.
-            Also, the type has to be supported by the underlying hardware of the device.
-          '';
-        };
+        options = {
 
-        meshID = mkOption {
-          type = types.nullOr types.string;
-          default = null;
-          description = "MeshID of interface with type <literal>mesh</literal>.";
-        };
+          device = mkOption {
+            type = types.string;
+            example = "wlp6s0";
+            description = "The name of the underlying hardware WLAN device as assigned by <literal>udev</literal>.";
+          };
 
-        flags = mkOption {
-          type = types.nullOr types.string;
-          default = null;
-          example = "control";
-          description = ''
-            Flags for interface of type <literal>monitor</literal>. The valid flags are:
-            none:     no special flags
-            fcsfail:  show frames with FCS errors
-            control:  show control frames
-            otherbss: show frames from other BSSes
-            cook:     use cooked mode
-            active:   use active mode (ACK incoming unicast packets)
-          '';
-        };
+          type = mkOption {
+            type = types.enum [ "managed" "ibss" "monitor" "mesh" "wds" ];
+            default = "managed";
+            example = "ibss";
+            description = ''
+              The type of the WLAN interface.
+              The type has to be supported by the underlying hardware of the device.
+            '';
+          };
+
+          meshID = mkOption {
+            type = types.nullOr types.string;
+            default = null;
+            description = "MeshID of interface with type <literal>mesh</literal>.";
+          };
+
+          flags = mkOption {
+            type = with types; nullOr (enum [ "none" "fcsfail" "control" "otherbss" "cook" "active" ]);
+            default = null;
+            example = "control";
+            description = ''
+              Flags for interface of type <literal>monitor</literal>.
+            '';
+          };
+
+          fourAddr = mkOption {
+            type = types.nullOr types.bool;
+            default = null;
+            description = "Whether to enable <literal>4-address mode</literal> with type <literal>managed</literal>.";
+          };
+
+          mac = mkOption {
+            type = types.nullOr types.str;
+            default = null;
+            example = "02:00:00:00:00:01";
+            description = ''
+              MAC address to use for the device. If <literal>null</literal>, then the MAC of the
+              underlying hardware WLAN device is used.
+
+              INFO: Locally administered MAC addresses are of the form:
+              <itemizedlist>
+              <listitem><para>x2:xx:xx:xx:xx:xx</para></listitem>
+              <listitem><para>x6:xx:xx:xx:xx:xx</para></listitem>
+              <listitem><para>xA:xx:xx:xx:xx:xx</para></listitem>
+              <listitem><para>xE:xx:xx:xx:xx:xx</para></listitem>
+              </itemizedlist>
+            '';
+          };
 
-        fourAddr = mkOption {
-          type = types.nullOr types.bool;
-          default = null;
-          description = "Whether to enable <literal>4-address mode</literal> with type <literal>managed</literal>.";
         };
 
-        mac = mkOption {
-          type = types.nullOr types.str;
-          default = null;
-          example = "02:00:00:00:00:01";
-          description = ''
-            MAC address to use for the device. If <literal>null</literal>, then the MAC of the
-            underlying hardware WLAN device is used.
-
-            INFO: Locally administered MAC addresses are of the form:
-            <itemizedlist>
-            <listitem><para>x2:xx:xx:xx:xx:xx</para></listitem>
-            <listitem><para>x6:xx:xx:xx:xx:xx</para></listitem>
-            <listitem><para>xA:xx:xx:xx:xx:xx</para></listitem>
-            <listitem><para>xE:xx:xx:xx:xx:xx</para></listitem>
-            </itemizedlist>
-          '';
-        };
+      });
 
-      };
     };
 
     networking.useDHCP = mkOption {
@@ -938,20 +945,23 @@ in
         domainname "${cfg.domain}"
       '';
 
-    environment.etc = mkIf (cfg.hostId != null)
-      [
-        {
-          target = "hostid";
-          source = pkgs.runCommand "gen-hostid" {} ''
-            hi="${cfg.hostId}"
-            ${if pkgs.stdenv.isBigEndian then ''
-              echo -ne "\x''${hi:0:2}\x''${hi:2:2}\x''${hi:4:2}\x''${hi:6:2}" > $out
-            '' else ''
-              echo -ne "\x''${hi:6:2}\x''${hi:4:2}\x''${hi:2:2}\x''${hi:0:2}" > $out
-            ''}
-          '';
-        }
-      ];
+    environment.etc."hostid" = mkIf (cfg.hostId != null)
+      { source = pkgs.runCommand "gen-hostid" {} ''
+          hi="${cfg.hostId}"
+          ${if pkgs.stdenv.isBigEndian then ''
+            echo -ne "\x''${hi:0:2}\x''${hi:2:2}\x''${hi:4:2}\x''${hi:6:2}" > $out
+          '' else ''
+            echo -ne "\x''${hi:6:2}\x''${hi:4:2}\x''${hi:2:2}\x''${hi:0:2}" > $out
+          ''}
+        '';
+      };
+
+    # static hostname configuration needed for hostnamectl and the
+    # org.freedesktop.hostname1 dbus service (both provided by systemd)
+    environment.etc."hostname" = mkIf (cfg.hostName != "")
+      {
+        text = cfg.hostName + "\n";
+      };
 
     environment.systemPackages =
       [ pkgs.host
@@ -967,8 +977,10 @@ in
       ]
       ++ bridgeStp;
 
+    # The network-interfaces target is kept for backwards compatibility.
+    # New modules must NOT use it.
     systemd.targets."network-interfaces" =
-      { description = "All Network Interfaces";
+      { description = "All Network Interfaces (deprecated)";
         wantedBy = [ "network.target" ];
         before = [ "network.target" ];
         after = [ "network-pre.target" ];
@@ -991,12 +1003,17 @@ in
         '';
       };
     } // (listToAttrs (flip map interfaces (i:
+      let
+        deviceDependency = if config.boot.isContainer
+          then []
+          else [ (subsystemDevice i.name) ];
+      in
       nameValuePair "network-link-${i.name}"
       { description = "Link configuration of ${i.name}";
         wantedBy = [ "network-interfaces.target" ];
         before = [ "network-interfaces.target" ];
-        bindsTo = [ (subsystemDevice i.name) ];
-        after = [ (subsystemDevice i.name) "network-pre.target" ];
+        bindsTo = deviceDependency;
+        after = [ "network-pre.target" ] ++ deviceDependency;
         path = [ pkgs.iproute ];
         serviceConfig = {
           Type = "oneshot";