summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
Diffstat (limited to 'nixos')
-rw-r--r--nixos/lib/test-driver/Machine.pm3
-rwxr-xr-xnixos/maintainers/scripts/ec2/create-amis.sh2
-rw-r--r--nixos/modules/hardware/video/capture/mwprocapture.nix61
-rw-r--r--nixos/modules/hardware/video/nvidia.nix4
-rw-r--r--nixos/modules/installer/scan/detected.nix2
-rw-r--r--nixos/modules/installer/scan/not-detected.nix2
-rw-r--r--nixos/modules/module-list.nix2
-rw-r--r--nixos/modules/programs/mtr.nix27
-rw-r--r--nixos/modules/security/acme.nix2
-rw-r--r--nixos/modules/services/audio/mpd.nix19
-rw-r--r--nixos/modules/services/databases/stanchion.nix10
-rw-r--r--nixos/modules/services/hardware/bluetooth.nix49
-rw-r--r--nixos/modules/services/logging/logcheck.nix4
-rw-r--r--nixos/modules/services/misc/taskserver/default.nix135
-rw-r--r--nixos/modules/services/misc/taskserver/doc.xml6
-rw-r--r--nixos/modules/services/misc/taskserver/helper-tool.py43
-rw-r--r--nixos/modules/services/monitoring/munin.nix1
-rw-r--r--nixos/modules/services/network-filesystems/samba.nix33
-rw-r--r--nixos/modules/services/network-filesystems/tahoe.nix4
-rw-r--r--nixos/modules/services/networking/tinc.nix4
-rw-r--r--nixos/modules/services/networking/znc.nix2
-rw-r--r--nixos/modules/services/system/dbus-session-local.conf.in5
-rw-r--r--nixos/modules/services/system/dbus-system-local.conf.in6
-rw-r--r--nixos/modules/services/system/dbus.nix30
-rw-r--r--nixos/modules/services/web-apps/mattermost.nix1
-rw-r--r--nixos/modules/services/web-servers/zope2.nix2
-rw-r--r--nixos/modules/services/x11/desktop-managers/xfce.nix8
-rw-r--r--nixos/modules/system/activation/activation-script.nix5
-rw-r--r--nixos/modules/system/boot/kernel.nix2
-rw-r--r--nixos/modules/system/boot/loader/grub/grub.nix15
-rw-r--r--nixos/modules/system/boot/loader/grub/install-grub.pl14
-rw-r--r--nixos/modules/tasks/network-interfaces-scripted.nix35
-rw-r--r--nixos/modules/tasks/network-interfaces-systemd.nix2
-rw-r--r--nixos/modules/virtualisation/ec2-amis.nix4
-rw-r--r--nixos/tests/taskserver.nix129
35 files changed, 492 insertions, 181 deletions
diff --git a/nixos/lib/test-driver/Machine.pm b/nixos/lib/test-driver/Machine.pm
index 85bc376f67fa..30664406b26d 100644
--- a/nixos/lib/test-driver/Machine.pm
+++ b/nixos/lib/test-driver/Machine.pm
@@ -607,7 +607,8 @@ sub waitForWindow {
 sub copyFileFromHost {
     my ($self, $from, $to) = @_;
     my $s = `cat $from` or die;
-    $self->mustSucceed("echo '$s' > $to"); # !!! escaping
+    $s =~ s/'/'\\''/g;
+    $self->mustSucceed("echo '$s' > $to");
 }
 
 
diff --git a/nixos/maintainers/scripts/ec2/create-amis.sh b/nixos/maintainers/scripts/ec2/create-amis.sh
index 7cceac8cbf5a..1e397b0f1761 100755
--- a/nixos/maintainers/scripts/ec2/create-amis.sh
+++ b/nixos/maintainers/scripts/ec2/create-amis.sh
@@ -19,7 +19,7 @@ rm -f ec2-amis.nix
 
 types="hvm pv"
 stores="ebs s3"
-regions="eu-west-1 eu-west-2 eu-central-1 us-east-1 us-east-2 us-west-1 us-west-2 ap-southeast-1 ap-southeast-2 ap-northeast-1 ap-northeast-2 sa-east-1 ap-south-1"
+regions="eu-west-1 eu-west-2 eu-central-1 us-east-1 us-east-2 us-west-1 us-west-2 ca-central-1 ap-southeast-1 ap-southeast-2 ap-northeast-1 ap-northeast-2 sa-east-1 ap-south-1"
 
 for type in $types; do
     link=$stateDir/$type
diff --git a/nixos/modules/hardware/video/capture/mwprocapture.nix b/nixos/modules/hardware/video/capture/mwprocapture.nix
new file mode 100644
index 000000000000..aee15dcec6e5
--- /dev/null
+++ b/nixos/modules/hardware/video/capture/mwprocapture.nix
@@ -0,0 +1,61 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.hardware.mwProCapture;
+
+  kernelPackages = config.boot.kernelPackages;
+
+in
+
+{
+
+  options.hardware.mwProCapture.enable = mkEnableOption "Magewell Pro Capture family kernel module";
+
+  config = mkIf cfg.enable {
+
+    assertions = singleton {
+      assertion = versionAtLeast kernelPackages.kernel.version "3.2";
+      message = "Magewell Pro Capture family module is not supported for kernels older than 3.2";
+    };
+
+    boot.kernelModules = [ "ProCapture" ];
+
+    environment.systemPackages = [ kernelPackages.mwprocapture ];
+
+    boot.extraModulePackages = [ kernelPackages.mwprocapture ];
+
+    boot.extraModprobeConfig = ''
+      # Set the png picture to be displayed when no input signal is detected.
+      options ProCapture nosignal_file=${kernelPackages.mwprocapture}/res/NoSignal.png
+
+      # Set the png picture to be displayed when an unsupported input signal is detected.
+      options ProCapture unsupported_file=${kernelPackages.mwprocapture}/res/Unsupported.png
+
+      # Set the png picture to be displayed when an loking input signal is detected.
+      options ProCapture locking_file=${kernelPackages.mwprocapture}/res/Locking.png
+
+      # Message signaled interrupts switch
+      #options ProCapture disable_msi=0
+
+      # Set the debug level
+      #options ProCapture debug_level=0
+
+      # Force init switch eeprom
+      #options ProCapture init_switch_eeprom=0
+
+      # Min frame interval for VIDIOC_ENUM_FRAMEINTERVALS (default: 166666(100ns))
+      #options ProCapture enum_frameinterval_min=166666
+
+      # VIDIOC_ENUM_FRAMESIZES type (1: DISCRETE; 2: STEPWISE; otherwise: CONTINUOUS )
+      #options ProCapture enum_framesizes_type=0
+
+      # Parameters for internal usage
+      #options ProCapture internal_params=""
+    '';
+
+  };
+
+}
diff --git a/nixos/modules/hardware/video/nvidia.nix b/nixos/modules/hardware/video/nvidia.nix
index cf723d53269b..161ed9457af9 100644
--- a/nixos/modules/hardware/video/nvidia.nix
+++ b/nixos/modules/hardware/video/nvidia.nix
@@ -49,6 +49,10 @@ in
         Option "RandRRotation" "on"
       '';
 
+    environment.etc."nvidia/nvidia-application-profiles-rc" = mkIf nvidia_x11.useProfiles {
+      source = "${nvidia_x11.bin}/share/nvidia/nvidia-application-profiles-rc";
+    };
+
     hardware.opengl.package = nvidiaPackage nvidia_x11 pkgs;
     hardware.opengl.package32 = nvidiaPackage nvidia_libs32 pkgs_i686;
 
diff --git a/nixos/modules/installer/scan/detected.nix b/nixos/modules/installer/scan/detected.nix
index f350cd986afa..e72c78532943 100644
--- a/nixos/modules/installer/scan/detected.nix
+++ b/nixos/modules/installer/scan/detected.nix
@@ -1,4 +1,4 @@
-# List all devices which are detected by nixos-hardware-scan.
+# List all devices which are detected by nixos-generate-config.
 # Common devices are enabled by default.
 { config, lib, pkgs, ... }:
 
diff --git a/nixos/modules/installer/scan/not-detected.nix b/nixos/modules/installer/scan/not-detected.nix
index b30c569ed2a7..e1a3052ba957 100644
--- a/nixos/modules/installer/scan/not-detected.nix
+++ b/nixos/modules/installer/scan/not-detected.nix
@@ -1,4 +1,4 @@
-# List all devices which are _not_ detected by nixos-hardware-scan.
+# List all devices which are _not_ detected by nixos-generate-config.
 # Common devices are enabled by default.
 { config, lib, pkgs, ... }:
 
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index e60f93d52d98..a304336c731e 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -41,6 +41,7 @@
   ./hardware/video/amdgpu.nix
   ./hardware/video/amdgpu-pro.nix
   ./hardware/video/ati.nix
+  ./hardware/video/capture/mwprocapture.nix
   ./hardware/video/bumblebee.nix
   ./hardware/video/displaylink.nix
   ./hardware/video/nvidia.nix
@@ -80,6 +81,7 @@
   ./programs/light.nix
   ./programs/man.nix
   ./programs/mosh.nix
+  ./programs/mtr.nix
   ./programs/nano.nix
   ./programs/oblogout.nix
   ./programs/screen.nix
diff --git a/nixos/modules/programs/mtr.nix b/nixos/modules/programs/mtr.nix
new file mode 100644
index 000000000000..927fe68be875
--- /dev/null
+++ b/nixos/modules/programs/mtr.nix
@@ -0,0 +1,27 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.mtr;
+in {
+  options = {
+    programs.mtr = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to add mtr to the global environment and configure a
+          setcap wrapper for it.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    security.wrappers.mtr = {
+      source = "${pkgs.mtr}/bin/mtr";
+      capabilities = "cap_net_raw+p";
+    };
+  };
+}
diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix
index 4e7c966a463a..78bd09441f83 100644
--- a/nixos/modules/security/acme.nix
+++ b/nixos/modules/security/acme.nix
@@ -129,7 +129,7 @@ in
 
       certs = mkOption {
         default = { };
-        type = with types; loaOf (submodule certOpts);
+        type = with types; attrsOf (submodule certOpts);
         description = ''
           Attribute set of certificates to get signed and renewed.
         '';
diff --git a/nixos/modules/services/audio/mpd.nix b/nixos/modules/services/audio/mpd.nix
index a89215d73828..56af8fe152e0 100644
--- a/nixos/modules/services/audio/mpd.nix
+++ b/nixos/modules/services/audio/mpd.nix
@@ -4,6 +4,8 @@ with lib;
 
 let
 
+  name = "mpd";
+
   uid = config.ids.uids.mpd;
   gid = config.ids.gids.mpd;
   cfg = config.services.mpd;
@@ -54,13 +56,14 @@ in {
         description = ''
           Extra directives added to to the end of MPD's configuration file,
           mpd.conf. Basic configuration like file location and uid/gid
-          is added automatically to the beginning of the file.
+          is added automatically to the beginning of the file. For available
+          options see <literal>man 5 mpd.conf</literal>'.
         '';
       };
 
       dataDir = mkOption {
         type = types.path;
-        default = "/var/lib/mpd";
+        default = "/var/lib/${name}";
         description = ''
           The directory where MPD stores its state, tag cache,
           playlists etc.
@@ -69,13 +72,13 @@ in {
 
       user = mkOption {
         type = types.str;
-        default = "mpd";
+        default = name;
         description = "User account under which MPD runs.";
       };
 
       group = mkOption {
         type = types.str;
-        default = "mpd";
+        default = name;
         description = "Group account under which MPD runs.";
       };
 
@@ -131,17 +134,17 @@ in {
       };
     };
 
-    users.extraUsers = optionalAttrs (cfg.user == "mpd") (singleton {
+    users.extraUsers = optionalAttrs (cfg.user == name) (singleton {
       inherit uid;
-      name = "mpd";
+      inherit name;
       group = cfg.group;
       extraGroups = [ "audio" ];
       description = "Music Player Daemon user";
       home = "${cfg.dataDir}";
     });
 
-    users.extraGroups = optionalAttrs (cfg.group == "mpd") (singleton {
-      name = "mpd";
+    users.extraGroups = optionalAttrs (cfg.group == name) (singleton {
+      inherit name;
       gid = gid;
     });
   };
diff --git a/nixos/modules/services/databases/stanchion.nix b/nixos/modules/services/databases/stanchion.nix
index f2dbb78b5c4b..a4597cac3cd6 100644
--- a/nixos/modules/services/databases/stanchion.nix
+++ b/nixos/modules/services/databases/stanchion.nix
@@ -76,14 +76,6 @@ in
         '';
       };
 
-      stanchionSsl = mkOption {
-        type = types.bool;
-        default = true;
-        description = ''
-          Tell stanchion to use SSL.
-        '';
-      };
-
       distributedCookie = mkOption {
         type = types.str;
         default = "riak";
@@ -148,8 +140,6 @@ in
 
       distributed_cookie = ${cfg.distributedCookie}
 
-      stanchion_ssl=${if cfg.stanchionSsl then "on" else "off"}
-
       ${cfg.extraConfig}
     '';
 
diff --git a/nixos/modules/services/hardware/bluetooth.nix b/nixos/modules/services/hardware/bluetooth.nix
index de0d48032113..71b3a93a2e0d 100644
--- a/nixos/modules/services/hardware/bluetooth.nix
+++ b/nixos/modules/services/hardware/bluetooth.nix
@@ -1,8 +1,11 @@
 { config, lib, pkgs, ... }:
 
 with lib;
+
 let
   bluez-bluetooth = pkgs.bluez;
+  cfg = config.hardware.bluetooth;
+
 in
 
 {
@@ -11,33 +14,53 @@ in
 
   options = {
 
-    hardware.bluetooth.enable = mkOption {
-      type = types.bool;
-      default = false;
-      description = "Whether to enable support for Bluetooth.";
+    hardware.bluetooth.enable = mkEnableOption "support for Bluetooth.";
+
+    hardware.bluetooth.powerOnBoot = mkOption {
+      type    = types.bool;
+      default = true;
+      description = "Whether to power up the default Bluetooth controller on boot.";
     };
 
   };
 
   ###### implementation
 
-  config = mkIf config.hardware.bluetooth.enable {
+  config = mkIf cfg.enable {
 
     environment.systemPackages = [ bluez-bluetooth pkgs.openobex pkgs.obexftp ];
 
     services.udev.packages = [ bluez-bluetooth ];
-
     services.dbus.packages = [ bluez-bluetooth ];
+    systemd.packages       = [ bluez-bluetooth ];
+
+    services.udev.extraRules = optionalString cfg.powerOnBoot ''
+      ACTION=="add", KERNEL=="hci[0-9]*", ENV{SYSTEMD_WANTS}="bluetooth-power@%k.service"
+    '';
+
+    systemd.services = {
+      bluetooth = {
+        wantedBy = [ "bluetooth.target" ];
+        aliases  = [ "dbus-org.bluez.service" ];
+      };
+
+      "bluetooth-power@" = mkIf cfg.powerOnBoot {
+        description = "Power up bluetooth controller";
+        after = [
+          "bluetooth.service"
+          "suspend.target"
+          "sys-subsystem-bluetooth-devices-%i.device"
+        ];
+        wantedBy = [ "suspend.target" ];
+
+        serviceConfig.Type      = "oneshot";
+        serviceConfig.ExecStart = "${pkgs.bluez.out}/bin/hciconfig %i up";
+      };
 
-    systemd.packages = [ bluez-bluetooth ];
-
-    systemd.services.bluetooth = {
-      wantedBy = [ "bluetooth.target" ];
-      aliases = [ "dbus-org.bluez.service" ];
     };
 
-    systemd.user.services.obex = {
-      aliases = [ "dbus-org.bluez.obex.service" ];
+    systemd.user.services = {
+      obex.aliases = [ "dbus-org.bluez.obex.service" ];
     };
 
   };
diff --git a/nixos/modules/services/logging/logcheck.nix b/nixos/modules/services/logging/logcheck.nix
index 72925b95cae4..2a8ac414720b 100644
--- a/nixos/modules/services/logging/logcheck.nix
+++ b/nixos/modules/services/logging/logcheck.nix
@@ -184,7 +184,7 @@ in
         description = ''
           This option defines extra ignore rules.
         '';
-        type = with types; loaOf (submodule ignoreOptions);
+        type = with types; attrsOf (submodule ignoreOptions);
       };
 
       ignoreCron = mkOption {
@@ -192,7 +192,7 @@ in
         description = ''
           This option defines extra ignore rules for cronjobs.
         '';
-        type = with types; loaOf (submodule ignoreCronOptions);
+        type = with types; attrsOf (submodule ignoreCronOptions);
       };
 
       extraGroups = mkOption {
diff --git a/nixos/modules/services/misc/taskserver/default.nix b/nixos/modules/services/misc/taskserver/default.nix
index d28c5dc7af85..826f463bbd75 100644
--- a/nixos/modules/services/misc/taskserver/default.nix
+++ b/nixos/modules/services/misc/taskserver/default.nix
@@ -94,44 +94,6 @@ let
     in flatten (mapAttrsToList mkSublist attrs);
   in all isNull (findPkiDefinitions [] manualPkiOptions);
 
-  configFile = pkgs.writeText "taskdrc" (''
-    # systemd related
-    daemon = false
-    log = -
-
-    # logging
-    ${mkConfLine "debug" cfg.debug}
-    ${mkConfLine "ip.log" cfg.ipLog}
-
-    # general
-    ${mkConfLine "ciphers" cfg.ciphers}
-    ${mkConfLine "confirmation" cfg.confirmation}
-    ${mkConfLine "extensions" cfg.extensions}
-    ${mkConfLine "queue.size" cfg.queueSize}
-    ${mkConfLine "request.limit" cfg.requestLimit}
-
-    # client
-    ${mkConfLine "client.allow" cfg.allowedClientIDs}
-    ${mkConfLine "client.deny" cfg.disallowedClientIDs}
-
-    # server
-    server = ${cfg.listenHost}:${toString cfg.listenPort}
-    ${mkConfLine "trust" cfg.trust}
-
-    # PKI options
-    ${if needToCreateCA then ''
-      ca.cert = ${cfg.dataDir}/keys/ca.cert
-      server.cert = ${cfg.dataDir}/keys/server.cert
-      server.key = ${cfg.dataDir}/keys/server.key
-      server.crl = ${cfg.dataDir}/keys/server.crl
-    '' else ''
-      ca.cert = ${cfg.pki.manual.ca.cert}
-      server.cert = ${cfg.pki.manual.server.cert}
-      server.key = ${cfg.pki.manual.server.key}
-      server.crl = ${cfg.pki.manual.server.crl}
-    ''}
-  '' + cfg.extraConfig);
-
   orgOptions = { name, ... }: {
     options.users = mkOption {
       type = types.uniq (types.listOf types.str);
@@ -154,9 +116,8 @@ let
 
   certtool = "${pkgs.gnutls.bin}/bin/certtool";
 
-  nixos-taskserver = pkgs.pythonPackages.buildPythonPackage {
+  nixos-taskserver = pkgs.pythonPackages.buildPythonApplication {
     name = "nixos-taskserver";
-    namePrefix = "";
 
     src = pkgs.runCommand "nixos-taskserver-src" {} ''
       mkdir -p "$out"
@@ -167,6 +128,7 @@ let
         certBits = cfg.pki.auto.bits;
         clientExpiration = cfg.pki.auto.expiration.client;
         crlExpiration = cfg.pki.auto.expiration.crl;
+        isAutoConfig = if needToCreateCA then "True" else "False";
       }}" > "$out/main.py"
       cat > "$out/setup.py" <<EOF
       from setuptools import setup
@@ -365,20 +327,57 @@ in {
       pki.manual = manualPkiOptions;
       pki.auto = autoPkiOptions;
 
-      extraConfig = mkOption {
-        type = types.lines;
-        default = "";
-        example = "client.cert = /tmp/debugging.cert";
+      config = mkOption {
+        type = types.attrs;
+        example.client.cert = "/tmp/debugging.cert";
         description = ''
-          Extra lines to append to the taskdrc configuration file.
+          Configuration options to pass to Taskserver.
+
+          The options here are the same as described in <citerefentry>
+            <refentrytitle>taskdrc</refentrytitle>
+            <manvolnum>5</manvolnum>
+          </citerefentry>, but with one difference:
+
+          The <literal>server</literal> option is
+          <literal>server.listen</literal> here, because the
+          <literal>server</literal> option would collide with other options
+          like <literal>server.cert</literal> and we would run in a type error
+          (attribute set versus string).
+
+          Nix types like integers or booleans are automatically converted to
+          the right values Taskserver would expect.
         '';
+        apply = let
+          mkKey = path: if path == ["server" "listen"] then "server"
+                        else concatStringsSep "." path;
+          recurse = path: attrs: let
+            mapper = name: val: let
+              newPath = path ++ [ name ];
+              scalar = if val == true then "true"
+                       else if val == false then "false"
+                       else toString val;
+            in if isAttrs val then recurse newPath val
+               else [ "${mkKey newPath}=${scalar}" ];
+          in concatLists (mapAttrsToList mapper attrs);
+        in recurse [];
       };
     };
   };
 
+  imports = [
+    (mkRemovedOptionModule ["services" "taskserver" "extraConfig"] ''
+      This option was removed in favor of `services.taskserver.config` with
+      different semantics (it's now a list of attributes instead of lines).
+
+      Please look up the documentation of `services.taskserver.config' to get
+      more information about the new way to pass additional configuration
+      options.
+    '')
+  ];
+
   config = mkMerge [
     (mkIf cfg.enable {
-      environment.systemPackages = [ pkgs.taskserver nixos-taskserver ];
+      environment.systemPackages = [ nixos-taskserver ];
 
       users.users = optional (cfg.user == "taskd") {
         name = "taskd";
@@ -392,6 +391,44 @@ in {
         gid = config.ids.gids.taskd;
       };
 
+      services.taskserver.config = {
+        # systemd related
+        daemon = false;
+        log = "-";
+
+        # logging
+        debug = cfg.debug;
+        ip.log = cfg.ipLog;
+
+        # general
+        ciphers = cfg.ciphers;
+        confirmation = cfg.confirmation;
+        extensions = cfg.extensions;
+        queue.size = cfg.queueSize;
+        request.limit = cfg.requestLimit;
+
+        # client
+        client.allow = cfg.allowedClientIDs;
+        client.deny = cfg.disallowedClientIDs;
+
+        # server
+        trust = cfg.trust;
+        server = {
+          listen = "${cfg.listenHost}:${toString cfg.listenPort}";
+        } // (if needToCreateCA then {
+          cert = "${cfg.dataDir}/keys/server.cert";
+          key = "${cfg.dataDir}/keys/server.key";
+          crl = "${cfg.dataDir}/keys/server.crl";
+        } else {
+          cert = "${cfg.pki.manual.server.cert}";
+          key = "${cfg.pki.manual.server.key}";
+          crl = "${cfg.pki.manual.server.crl}";
+        });
+
+        ca.cert = if needToCreateCA then "${cfg.dataDir}/keys/ca.cert"
+                  else "${cfg.pki.manual.ca.cert}";
+      };
+
       systemd.services.taskserver-init = {
         wantedBy = [ "taskserver.service" ];
         before = [ "taskserver.service" ];
@@ -404,7 +441,6 @@ in {
 
         script = ''
           ${taskd} init
-          echo "include ${configFile}" > "${cfg.dataDir}/config"
           touch "${cfg.dataDir}/.is_initialized"
         '';
 
@@ -436,7 +472,10 @@ in {
         in "${helperTool} process-json '${jsonFile}'";
 
         serviceConfig = {
-          ExecStart = "@${taskd} taskd server";
+          ExecStart = let
+            mkCfgFlag = flag: escapeShellArg "--${flag}";
+            cfgFlags = concatMapStringsSep " " mkCfgFlag cfg.config;
+          in "@${taskd} taskd server ${cfgFlags}";
           ExecReload = "${pkgs.coreutils}/bin/kill -USR1 $MAINPID";
           Restart = "on-failure";
           PermissionsStartOnly = true;
diff --git a/nixos/modules/services/misc/taskserver/doc.xml b/nixos/modules/services/misc/taskserver/doc.xml
index 48591129264a..6d4d2a9b488c 100644
--- a/nixos/modules/services/misc/taskserver/doc.xml
+++ b/nixos/modules/services/misc/taskserver/doc.xml
@@ -136,9 +136,9 @@ $ ssh server nixos-taskserver user export my-company alice | sh
 
     <para>
       If you set any options within
-      <option>service.taskserver.pki.manual.*</option>, the automatic user and
-      CA management by the <command>nixos-taskserver</command> is disabled and
-      you need to create certificates and keys by yourself.
+      <option>service.taskserver.pki.manual.*</option>,
+      <command>nixos-taskserver</command> won't issue certificates, but you can
+      still use it for adding or removing user accounts.
     </para>
   </section>
 </chapter>
diff --git a/nixos/modules/services/misc/taskserver/helper-tool.py b/nixos/modules/services/misc/taskserver/helper-tool.py
index 03e7cdf8987a..b97bc1df74f7 100644
--- a/nixos/modules/services/misc/taskserver/helper-tool.py
+++ b/nixos/modules/services/misc/taskserver/helper-tool.py
@@ -13,6 +13,7 @@ from tempfile import NamedTemporaryFile
 
 import click
 
+IS_AUTO_CONFIG = @isAutoConfig@ # NOQA
 CERTTOOL_COMMAND = "@certtool@"
 CERT_BITS = "@certBits@"
 CLIENT_EXPIRATION = "@clientExpiration@"
@@ -149,6 +150,12 @@ def create_template(contents):
 
 
 def generate_key(org, user):
+    if not IS_AUTO_CONFIG:
+        msg = "Automatic PKI handling is disabled, you need to " \
+              "manually issue a client certificate for user {}.\n"
+        sys.stderr.write(msg.format(user))
+        return
+
     basedir = os.path.join(TASKD_DATA_DIR, "keys", org, user)
     if os.path.exists(basedir):
         raise OSError("Keyfile directory for {} already exists.".format(user))
@@ -243,26 +250,32 @@ class User(object):
         self.key = key
 
     def export(self):
-        pubcert = getkey(self.__org, self.name, "public.cert")
-        privkey = getkey(self.__org, self.name, "private.key")
-        cacert = getkey("ca.cert")
-
-        keydir = "${TASKDATA:-$HOME/.task}/keys"
-
         credentials = '/'.join([self.__org, self.name, self.key])
         allow_unquoted = string.ascii_letters + string.digits + "/-_."
         if not all((c in allow_unquoted) for c in credentials):
             credentials = "'" + credentials.replace("'", r"'\''") + "'"
 
-        script = [
-            "umask 0077",
-            'mkdir -p "{}"'.format(keydir),
-            mktaskkey("certificate", os.path.join(keydir, "public.cert"),
-                      pubcert),
-            mktaskkey("key", os.path.join(keydir, "private.key"), privkey),
-            mktaskkey("ca", os.path.join(keydir, "ca.cert"), cacert),
+        script = []
+
+        if IS_AUTO_CONFIG:
+            pubcert = getkey(self.__org, self.name, "public.cert")
+            privkey = getkey(self.__org, self.name, "private.key")
+            cacert = getkey("ca.cert")
+
+            keydir = "${TASKDATA:-$HOME/.task}/keys"
+
+            script += [
+                "umask 0077",
+                'mkdir -p "{}"'.format(keydir),
+                mktaskkey("certificate", os.path.join(keydir, "public.cert"),
+                          pubcert),
+                mktaskkey("key", os.path.join(keydir, "private.key"), privkey),
+                mktaskkey("ca", os.path.join(keydir, "ca.cert"), cacert)
+            ]
+
+        script.append(
             "task config taskd.credentials -- {}".format(credentials)
-        ]
+        )
 
         return "\n".join(script) + "\n"
 
@@ -526,7 +539,7 @@ def export_user(organisation, user):
     userobj = organisation.get_user(user)
     if userobj is None:
         msg = "User {} doesn't exist in organisation {}."
-        sys.exit(msg.format(userobj.name, organisation.name))
+        sys.exit(msg.format(user, organisation.name))
 
     sys.stdout.write(userobj.export())
 
diff --git a/nixos/modules/services/monitoring/munin.nix b/nixos/modules/services/monitoring/munin.nix
index 6d2ce5383687..364f18e7543d 100644
--- a/nixos/modules/services/monitoring/munin.nix
+++ b/nixos/modules/services/monitoring/munin.nix
@@ -76,6 +76,7 @@ let
       # wrapped plugins by makeWrapper being with dots
       ignore_file ^\.
 
+      allow ^::1$
       allow ^127\.0\.0\.1$
 
       ${nodeCfg.extraConfig}
diff --git a/nixos/modules/services/network-filesystems/samba.nix b/nixos/modules/services/network-filesystems/samba.nix
index 09a11585bc92..6ae5292fc303 100644
--- a/nixos/modules/services/network-filesystems/samba.nix
+++ b/nixos/modules/services/network-filesystems/samba.nix
@@ -91,6 +91,26 @@ in
         '';
       };
 
+      enableNmbd = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to enable Samba's nmbd, which replies to NetBIOS over IP name
+          service requests. It also participates in the browsing protocols
+          which make up the Windows "Network Neighborhood" view.
+        '';
+      };
+
+      enableWinbindd = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to enable Samba's winbindd, which provides a number of services
+          to the Name Service Switch capability found in most modern C libraries,
+          to arbitrary applications via PAM and ntlm_auth and to Samba itself.
+        '';
+      };
+
       package = mkOption {
         type = types.package;
         default = pkgs.samba;
@@ -185,7 +205,12 @@ in
   ###### implementation
 
   config = mkMerge
-    [ { # Always provide a smb.conf to shut up programs like smbclient and smbspool.
+    [ { assertions =
+          [ { assertion = cfg.nsswins -> cfg.enableWinbindd;
+              message   = "If samba.nsswins is enabled, then samba.enableWinbindd must also be enabled";
+            }
+          ];
+        # Always provide a smb.conf to shut up programs like smbclient and smbspool.
         environment.etc = singleton
           { source =
               if cfg.enable then configFile
@@ -194,7 +219,7 @@ in
           };
       }
 
-      (mkIf config.services.samba.enable {
+      (mkIf cfg.enable {
 
         system.nssModules = optional cfg.nsswins samba;
 
@@ -207,9 +232,9 @@ in
           };
 
           services = {
-            "samba-nmbd" = daemonService "nmbd" "-F";
             "samba-smbd" = daemonService "smbd" "-F";
-            "samba-winbindd" = daemonService "winbindd" "-F";
+            "samba-nmbd" = mkIf cfg.enableNmbd (daemonService "nmbd" "-F");
+            "samba-winbindd" = mkIf cfg.enableWinbindd (daemonService "winbindd" "-F");
             "samba-setup" = {
               description = "Samba Setup Task";
               script = setupScript;
diff --git a/nixos/modules/services/network-filesystems/tahoe.nix b/nixos/modules/services/network-filesystems/tahoe.nix
index f63f641d00be..3d78ac096a2b 100644
--- a/nixos/modules/services/network-filesystems/tahoe.nix
+++ b/nixos/modules/services/network-filesystems/tahoe.nix
@@ -8,7 +8,7 @@ in
     options.services.tahoe = {
       introducers = mkOption {
         default = {};
-        type = with types; loaOf (submodule {
+        type = with types; attrsOf (submodule {
           options = {
             nickname = mkOption {
               type = types.str;
@@ -49,7 +49,7 @@ in
       };
       nodes = mkOption {
         default = {};
-        type = with types; loaOf (submodule {
+        type = with types; attrsOf (submodule {
           options = {
             nickname = mkOption {
               type = types.str;
diff --git a/nixos/modules/services/networking/tinc.nix b/nixos/modules/services/networking/tinc.nix
index f8e68fda7fc2..6cb40185274d 100644
--- a/nixos/modules/services/networking/tinc.nix
+++ b/nixos/modules/services/networking/tinc.nix
@@ -18,7 +18,7 @@ in
 
       networks = mkOption {
         default = { };
-        type = with types; loaOf (submodule {
+        type = with types; attrsOf (submodule {
           options = {
 
             extraConfig = mkOption {
@@ -59,7 +59,7 @@ in
 
             hosts = mkOption {
               default = { };
-              type = types.loaOf types.lines;
+              type = types.attrsOf types.lines;
               description = ''
                 The name of the host in the network as well as the configuration for that host.
                 This name should only contain alphanumerics and underscores.
diff --git a/nixos/modules/services/networking/znc.nix b/nixos/modules/services/networking/znc.nix
index 76ba78ff366f..0d41e3ea92ce 100644
--- a/nixos/modules/services/networking/znc.nix
+++ b/nixos/modules/services/networking/znc.nix
@@ -208,7 +208,7 @@ in
 
         networks = mkOption {
           default = { };
-          type = with types; loaOf (submodule networkOpts);
+          type = with types; attrsOf (submodule networkOpts);
           description = ''
             IRC networks to connect the user to.
           '';
diff --git a/nixos/modules/services/system/dbus-session-local.conf.in b/nixos/modules/services/system/dbus-session-local.conf.in
deleted file mode 100644
index 5fd6f80a3539..000000000000
--- a/nixos/modules/services/system/dbus-session-local.conf.in
+++ /dev/null
@@ -1,5 +0,0 @@
-<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
- "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
-<busconfig>
-  @extra@
-</busconfig>
diff --git a/nixos/modules/services/system/dbus-system-local.conf.in b/nixos/modules/services/system/dbus-system-local.conf.in
deleted file mode 100644
index edbb476f585a..000000000000
--- a/nixos/modules/services/system/dbus-system-local.conf.in
+++ /dev/null
@@ -1,6 +0,0 @@
-<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
- "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
-<busconfig>
-  <servicehelper>@servicehelper@</servicehelper>
-  @extra@
-</busconfig>
diff --git a/nixos/modules/services/system/dbus.nix b/nixos/modules/services/system/dbus.nix
index 33bc890a78c8..643bec188142 100644
--- a/nixos/modules/services/system/dbus.nix
+++ b/nixos/modules/services/system/dbus.nix
@@ -10,32 +10,10 @@ let
 
   homeDir = "/run/dbus";
 
-  systemExtraxml = concatStrings (flip concatMap cfg.packages (d: [
-    "<servicedir>${d}/share/dbus-1/system-services</servicedir>"
-    "<includedir>${d}/etc/dbus-1/system.d</includedir>"
-  ]));
-
-  sessionExtraxml = concatStrings (flip concatMap cfg.packages (d: [
-    "<servicedir>${d}/share/dbus-1/services</servicedir>"
-    "<includedir>${d}/etc/dbus-1/session.d</includedir>"
-  ]));
-
-  configDir = pkgs.runCommand "dbus-conf"
-    { preferLocalBuild = true;
-      allowSubstitutes = false;
-    }
-    ''
-      mkdir -p $out
-
-      sed '${./dbus-system-local.conf.in}' \
-        -e 's,@servicehelper@,${config.security.wrapperDir}/dbus-daemon-launch-helper,g' \
-        -e 's,@extra@,${systemExtraxml},' \
-        > "$out/system-local.conf"
-
-      sed '${./dbus-session-local.conf.in}' \
-        -e 's,@extra@,${sessionExtraxml},' \
-        > "$out/session-local.conf"
-    '';
+  configDir = pkgs.makeDBusConf {
+    suidHelper = "${config.security.wrapperDir}/dbus-daemon-launch-helper";
+    serviceDirectories = cfg.packages;
+  };
 
 in
 
diff --git a/nixos/modules/services/web-apps/mattermost.nix b/nixos/modules/services/web-apps/mattermost.nix
index bf3a8eed6004..8e6baf6a17e3 100644
--- a/nixos/modules/services/web-apps/mattermost.nix
+++ b/nixos/modules/services/web-apps/mattermost.nix
@@ -202,6 +202,7 @@ in
           ExecStart = "${pkgs.mattermost}/bin/mattermost-platform";
           WorkingDirectory = "${cfg.statePath}";
           PrivateTmp = true;
+          JoinsNamespaceOf = mkIf cfg.localDatabaseCreate "postgresql.service";
           Restart = "always";
           RestartSec = "10";
           LimitNOFILE = "49152";
diff --git a/nixos/modules/services/web-servers/zope2.nix b/nixos/modules/services/web-servers/zope2.nix
index 8a453e015577..496e34db4a96 100644
--- a/nixos/modules/services/web-servers/zope2.nix
+++ b/nixos/modules/services/web-servers/zope2.nix
@@ -74,7 +74,7 @@ in
 
     services.zope2.instances = mkOption {
       default = {};
-      type = with types; loaOf (submodule zope2Opts);
+      type = with types; attrsOf (submodule zope2Opts);
       example = literalExample ''
         {
           plone01 = {
diff --git a/nixos/modules/services/x11/desktop-managers/xfce.nix b/nixos/modules/services/x11/desktop-managers/xfce.nix
index 530468be5f96..37523feb4140 100644
--- a/nixos/modules/services/x11/desktop-managers/xfce.nix
+++ b/nixos/modules/services/x11/desktop-managers/xfce.nix
@@ -41,6 +41,12 @@ in
           Shell commands executed just before XFCE is started.
         '';
       };
+
+      enableXfwm = mkOption {
+        type = types.bool;
+        default = true;
+        description = "Enable the XFWM (default) window manager.";
+      };
     };
 
   };
@@ -87,7 +93,6 @@ in
         pkgs.xfce.xfce4volumed
         pkgs.xfce.xfce4-screenshooter
         pkgs.xfce.xfconf
-        pkgs.xfce.xfwm4
         # This supplies some "abstract" icons such as
         # "utilities-terminal" and "accessories-text-editor".
         pkgs.gnome3.defaultIconTheme
@@ -99,6 +104,7 @@ in
         pkgs.xfce.xfce4_appfinder
         pkgs.xfce.tumbler       # found via dbus
       ]
+      ++ optional cfg.enableXfwm pkgs.xfce.xfwm4
       ++ optional config.powerManagement.enable pkgs.xfce.xfce4_power_manager
       ++ optional config.networking.networkmanager.enable pkgs.networkmanagerapplet
       ++ optionals (!cfg.noDesktop)
diff --git a/nixos/modules/system/activation/activation-script.nix b/nixos/modules/system/activation/activation-script.nix
index dcf105eb7844..c2ac731d433d 100644
--- a/nixos/modules/system/activation/activation-script.nix
+++ b/nixos/modules/system/activation/activation-script.nix
@@ -19,6 +19,7 @@ let
       glibc # needed for getent
       shadow
       nettools # needed for hostname
+      utillinux # needed for mount and mountpoint
     ];
 
 in
@@ -168,12 +169,12 @@ in
           local options="$3"
           local fsType="$4"
 
-          if ${pkgs.utillinux}/bin/mountpoint -q "$mountPoint"; then
+          if mountpoint -q "$mountPoint"; then
             local options="remount,$options"
           else
             mkdir -m 0755 -p "$mountPoint"
           fi
-          ${pkgs.utillinux}/bin/mount -t "$fsType" -o "$options" "$device" "$mountPoint"
+          mount -t "$fsType" -o "$options" "$device" "$mountPoint"
         }
         source ${config.system.build.earlyMountScript}
       '';
diff --git a/nixos/modules/system/boot/kernel.nix b/nixos/modules/system/boot/kernel.nix
index e751ff141f70..cf70a891c0ca 100644
--- a/nixos/modules/system/boot/kernel.nix
+++ b/nixos/modules/system/boot/kernel.nix
@@ -176,7 +176,7 @@ in
 
     boot.initrd.availableKernelModules =
       [ # Note: most of these (especially the SATA/PATA modules)
-        # shouldn't be included by default since nixos-hardware-scan
+        # shouldn't be included by default since nixos-generate-config
         # detects them, but I'm keeping them for now for backwards
         # compatibility.
 
diff --git a/nixos/modules/system/boot/loader/grub/grub.nix b/nixos/modules/system/boot/loader/grub/grub.nix
index 23b970186a39..5ab2d0775518 100644
--- a/nixos/modules/system/boot/loader/grub/grub.nix
+++ b/nixos/modules/system/boot/loader/grub/grub.nix
@@ -54,7 +54,7 @@ let
       inherit (efi) canTouchEfiVariables;
       inherit (cfg)
         version extraConfig extraPerEntryConfig extraEntries forceInstall useOSProber
-        extraEntriesBeforeNixOS extraPrepareConfig configurationLimit copyKernels
+        extraEntriesBeforeNixOS extraPrepareConfig extraInitrd configurationLimit copyKernels
         default fsIdentifier efiSupport efiInstallAsRemovable gfxmodeEfi gfxmodeBios;
       path = (makeBinPath ([
         pkgs.coreutils pkgs.gnused pkgs.gnugrep pkgs.findutils pkgs.diffutils pkgs.btrfs-progs
@@ -267,6 +267,19 @@ in
         '';
       };
 
+      extraInitrd = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        example = "/boot/extra_initrafms.gz";
+        description = ''
+          The path to a second initramfs to be supplied to the kernel.
+          This ramfs will not be copied to the store, so that it can
+          contain secrets such as LUKS keyfiles or ssh keys.
+          This implies that rolling back to a previous configuration
+          won't rollback the state of this file.
+        '';
+      };
+
       useOSProber = mkOption {
         default = false;
         type = types.bool;
diff --git a/nixos/modules/system/boot/loader/grub/install-grub.pl b/nixos/modules/system/boot/loader/grub/install-grub.pl
index c9a51288747b..c7559cd634a2 100644
--- a/nixos/modules/system/boot/loader/grub/install-grub.pl
+++ b/nixos/modules/system/boot/loader/grub/install-grub.pl
@@ -49,6 +49,7 @@ my $extraPrepareConfig = get("extraPrepareConfig");
 my $extraPerEntryConfig = get("extraPerEntryConfig");
 my $extraEntries = get("extraEntries");
 my $extraEntriesBeforeNixOS = get("extraEntriesBeforeNixOS") eq "true";
+my $extraInitrd = get("extraInitrd");
 my $splashImage = get("splashImage");
 my $configurationLimit = int(get("configurationLimit"));
 my $copyKernels = get("copyKernels") eq "true";
@@ -226,6 +227,13 @@ my $grubStore;
 if ($copyKernels == 0) {
     $grubStore = GrubFs($storePath);
 }
+my $extraInitrdPath;
+if ($extraInitrd) {
+    if (! -f $extraInitrd) {
+        print STDERR "Warning: the specified extraInitrd " . $extraInitrd . " doesn't exist. Your system won't boot without it.\n";
+    }
+    $extraInitrdPath = GrubFs($extraInitrd);
+}
 
 # Generate the header.
 my $conf .= "# Automatically generated.  DO NOT EDIT THIS FILE!\n";
@@ -336,6 +344,9 @@ sub addEntry {
 
     my $kernel = copyToKernelsDir(Cwd::abs_path("$path/kernel"));
     my $initrd = copyToKernelsDir(Cwd::abs_path("$path/initrd"));
+    if ($extraInitrd) {
+        $initrd .= " " .$extraInitrdPath->path;
+    }
     my $xen = -e "$path/xen.gz" ? copyToKernelsDir(Cwd::abs_path("$path/xen.gz")) : undef;
 
     # FIXME: $confName
@@ -358,6 +369,9 @@ sub addEntry {
         if ($copyKernels == 0) {
             $conf .= $grubStore->search . "\n";
         }
+        if ($extraInitrd) {
+            $conf .= $extraInitrdPath->search . "\n";
+        }
         $conf .= "  $extraPerEntryConfig\n" if $extraPerEntryConfig;
         $conf .= "  multiboot $xen $xenParams\n" if $xen;
         $conf .= "  " . ($xen ? "module" : "linux") . " $kernel $kernelParams\n";
diff --git a/nixos/modules/tasks/network-interfaces-scripted.nix b/nixos/modules/tasks/network-interfaces-scripted.nix
index d94d9db54ca5..179300ef1667 100644
--- a/nixos/modules/tasks/network-interfaces-scripted.nix
+++ b/nixos/modules/tasks/network-interfaces-scripted.nix
@@ -60,21 +60,18 @@ let
       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 [];
+          # Use systemd service if we manage device creation, else
+          # trust udev when not in a container
+          if (hasAttr dev (filterAttrs (k: v: v.virtual) cfg.interfaces)) ||
+             (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 optional (dev != null && !config.boot.isContainer) (subsystemDevice dev);
 
         networkLocalCommands = {
           after = [ "network-setup.service" ];
@@ -211,7 +208,7 @@ let
               user "${i.virtualOwner}"
             '';
             postStop = ''
-              ip link del ${i.name}
+              ip link del ${i.name} || true
             '';
           };
 
@@ -349,7 +346,7 @@ let
               ip link set "${n}" up
             '';
             postStop = ''
-              ip link delete "${n}"
+              ip link delete "${n}" || true
             '';
           });
 
@@ -377,7 +374,7 @@ let
               ip link set "${n}" up
             '';
             postStop = ''
-              ip link delete "${n}"
+              ip link delete "${n}" || true
             '';
           });
 
@@ -401,7 +398,7 @@ let
               ip link set "${n}" up
             '';
             postStop = ''
-              ip link delete "${n}"
+              ip link delete "${n}" || true
             '';
           });
 
diff --git a/nixos/modules/tasks/network-interfaces-systemd.nix b/nixos/modules/tasks/network-interfaces-systemd.nix
index 736292400fd4..8b85ff0057f9 100644
--- a/nixos/modules/tasks/network-interfaces-systemd.nix
+++ b/nixos/modules/tasks/network-interfaces-systemd.nix
@@ -126,7 +126,7 @@ in
               ms    = trans (v: v + "ms");
               in {
                 Mode                       = simp "mode";
-                TransmitHashPolixy         = simp "xmit_hash_policy";
+                TransmitHashPolicy         = simp "xmit_hash_policy";
                 LACPTransmitRate           = simp "lacp_rate";
                 MIIMonitorSec              = ms "miimon";
                 UpDelaySec                 = ms "updelay";
diff --git a/nixos/modules/virtualisation/ec2-amis.nix b/nixos/modules/virtualisation/ec2-amis.nix
index 0753e2ce9948..d592a23c303f 100644
--- a/nixos/modules/virtualisation/ec2-amis.nix
+++ b/nixos/modules/virtualisation/ec2-amis.nix
@@ -156,6 +156,10 @@ let self = {
   "16.09".ap-southeast-2.hvm-s3 = "ami-87f4f0e4";
   "16.09".ap-southeast-2.pv-ebs = "ami-d8ede9bb";
   "16.09".ap-southeast-2.pv-s3 = "ami-a6ebefc5";
+  "16.09".ca-central-1.hvm-ebs = "ami-9f863bfb";
+  "16.09".ca-central-1.hvm-s3 = "ami-ea85388e";
+  "16.09".ca-central-1.pv-ebs = "ami-ce8a37aa";
+  "16.09".ca-central-1.pv-s3 = "ami-448a3720";
   "16.09".eu-central-1.hvm-ebs = "ami-1b884774";
   "16.09".eu-central-1.hvm-s3 = "ami-b08c43df";
   "16.09".eu-central-1.pv-ebs = "ami-888946e7";
diff --git a/nixos/tests/taskserver.nix b/nixos/tests/taskserver.nix
index d770b20a7757..cdccb11d8887 100644
--- a/nixos/tests/taskserver.nix
+++ b/nixos/tests/taskserver.nix
@@ -1,4 +1,62 @@
-import ./make-test.nix {
+import ./make-test.nix ({ pkgs, ... }: let
+  snakeOil = pkgs.runCommand "snakeoil-certs" {
+    outputs = [ "out" "cacert" "cert" "key" "crl" ];
+    buildInputs = [ pkgs.gnutls.bin ];
+    caTemplate = pkgs.writeText "snakeoil-ca.template" ''
+      cn = server
+      expiration_days = -1
+      cert_signing_key
+      ca
+    '';
+    certTemplate = pkgs.writeText "snakeoil-cert.template" ''
+      cn = server
+      expiration_days = -1
+      tls_www_server
+      encryption_key
+      signing_key
+    '';
+    crlTemplate = pkgs.writeText "snakeoil-crl.template" ''
+      expiration_days = -1
+    '';
+    userCertTemplace = pkgs.writeText "snakoil-user-cert.template" ''
+      organization = snakeoil
+      cn = server
+      expiration_days = -1
+      tls_www_client
+      encryption_key
+      signing_key
+    '';
+  } ''
+    certtool -p --bits 4096 --outfile ca.key
+    certtool -s --template "$caTemplate" --load-privkey ca.key \
+                --outfile "$cacert"
+    certtool -p --bits 4096 --outfile "$key"
+    certtool -c --template "$certTemplate" \
+                --load-ca-privkey ca.key \
+                --load-ca-certificate "$cacert" \
+                --load-privkey "$key" \
+                --outfile "$cert"
+    certtool --generate-crl --template "$crlTemplate" \
+                            --load-ca-privkey ca.key \
+                            --load-ca-certificate "$cacert" \
+                            --outfile "$crl"
+
+    mkdir "$out"
+
+    # Stripping key information before the actual PEM-encoded values is solely
+    # to make test output a bit less verbose when copying the client key to the
+    # actual client.
+    certtool -p --bits 4096 | sed -n \
+      -e '/^----* *BEGIN/,/^----* *END/p' > "$out/alice.key"
+
+    certtool -c --template "$userCertTemplace" \
+                --load-privkey "$out/alice.key" \
+                --load-ca-privkey ca.key \
+                --load-ca-certificate "$cacert" \
+                --outfile "$out/alice.cert"
+  '';
+
+in {
   name = "taskserver";
 
   nodes = rec {
@@ -12,6 +70,23 @@ import ./make-test.nix {
       };
     };
 
+    # New generation of the server with manual config
+    newServer = { lib, nodes, ... }: {
+      imports = [ server ];
+      services.taskserver.pki.manual = {
+        ca.cert = snakeOil.cacert;
+        server.cert = snakeOil.cert;
+        server.key = snakeOil.key;
+        server.crl = snakeOil.crl;
+      };
+      # This is to avoid assigning a different network address to the new
+      # generation.
+      networking = lib.mapAttrs (lib.const lib.mkForce) {
+        inherit (nodes.server.config.networking)
+          hostName interfaces primaryIPAddress extraHosts;
+      };
+    };
+
     client1 = { pkgs, ... }: {
       environment.systemPackages = [ pkgs.taskwarrior pkgs.gnutls ];
       users.users.alice.isNormalUser = true;
@@ -26,6 +101,8 @@ import ./make-test.nix {
   testScript = { nodes, ... }: let
     cfg = nodes.server.config.services.taskserver;
     portStr = toString cfg.listenPort;
+    newServerSystem = nodes.newServer.config.system.build.toplevel;
+    switchToNewServer = "${newServerSystem}/bin/switch-to-configuration test";
   in ''
     sub su ($$) {
       my ($user, $cmd) = @_;
@@ -33,8 +110,8 @@ import ./make-test.nix {
       return "su - $user -c '$esc'";
     }
 
-    sub setupClientsFor ($$) {
-      my ($org, $user) = @_;
+    sub setupClientsFor ($$;$) {
+      my ($org, $user, $extraInit) = @_;
 
       for my $client ($client1, $client2) {
         $client->nest("initialize client for user $user", sub {
@@ -58,6 +135,8 @@ import ./make-test.nix {
             }
           });
 
+          eval { &$extraInit($client, $org, $user) };
+
           $client->succeed(su $user,
             "task config taskd.server server:${portStr} >&2"
           );
@@ -104,7 +183,10 @@ import ./make-test.nix {
       return su $user, $cmd;
     }
 
-    startAll;
+    # Explicitly start the VMs so that we don't accidentally start newServer
+    $server->start;
+    $client1->start;
+    $client2->start;
 
     $server->waitForUnit("taskserver.service");
 
@@ -162,5 +244,42 @@ import ./make-test.nix {
       restartServer;
       testSync "bar";
     };
+
+    subtest "check manual configuration", sub {
+      $server->succeed('${switchToNewServer} >&2');
+      $server->waitForUnit("taskserver.service");
+      $server->waitForOpenPort(${portStr});
+
+      $server->succeed(
+        "nixos-taskserver org add manualOrg",
+        "nixos-taskserver user add manualOrg alice"
+      );
+
+      setupClientsFor "manualOrg", "alice", sub {
+        my ($client, $org, $user) = @_;
+        my $cfgpath = "/home/$user/.task";
+
+        $client->copyFileFromHost("${snakeOil.cacert}", "$cfgpath/ca.cert");
+        for my $file ('alice.key', 'alice.cert') {
+          $client->copyFileFromHost("${snakeOil}/$file", "$cfgpath/$file");
+        }
+
+        for my $file ("$user.key", "$user.cert") {
+          $client->copyFileFromHost(
+            "${snakeOil}/$file", "$cfgpath/$file"
+          );
+        }
+        $client->copyFileFromHost(
+          "${snakeOil.cacert}", "$cfgpath/ca.cert"
+        );
+        $client->succeed(
+          (su "alice", "task config taskd.ca $cfgpath/ca.cert"),
+          (su "alice", "task config taskd.key $cfgpath/$user.key"),
+          (su $user, "task config taskd.certificate $cfgpath/$user.cert")
+        );
+      };
+
+      testSync "alice";
+    };
   '';
-}
+})