summary refs log tree commit diff
path: root/nixos/modules
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules')
-rw-r--r--nixos/modules/config/fonts/fonts.nix2
-rw-r--r--nixos/modules/config/gnu.nix1
-rw-r--r--nixos/modules/config/swap.nix8
-rw-r--r--nixos/modules/config/update-users-groups.pl4
-rw-r--r--nixos/modules/installer/tools/nixos-install.sh2
-rw-r--r--nixos/modules/security/audit.nix16
-rw-r--r--nixos/modules/security/grsecurity.nix5
-rw-r--r--nixos/modules/security/grsecurity.xml345
-rw-r--r--nixos/modules/security/hidepid.nix19
-rw-r--r--nixos/modules/security/setuid-wrappers.nix34
-rw-r--r--nixos/modules/services/desktops/accountsservice.nix8
-rw-r--r--nixos/modules/services/editors/emacs.nix2
-rw-r--r--nixos/modules/services/mail/opensmtpd.nix2
-rw-r--r--nixos/modules/services/misc/gitit.nix2
-rw-r--r--nixos/modules/services/misc/nix-daemon.nix2
-rw-r--r--nixos/modules/services/networking/dnscrypt-proxy.nix48
-rw-r--r--nixos/modules/services/networking/dnscrypt-proxy.xml76
-rw-r--r--nixos/modules/services/networking/mjpg-streamer.nix8
-rw-r--r--nixos/modules/services/networking/unbound.nix10
-rw-r--r--nixos/modules/services/networking/zerotierone.nix16
-rw-r--r--nixos/modules/system/activation/activation-script.nix12
-rw-r--r--nixos/modules/system/boot/loader/grub/grub.nix15
-rw-r--r--nixos/modules/system/boot/loader/grub/install-grub.pl15
-rw-r--r--nixos/modules/system/boot/stage-1-init.sh26
-rw-r--r--nixos/modules/system/boot/stage-1.nix4
-rw-r--r--nixos/modules/system/boot/stage-2-init.sh36
-rw-r--r--nixos/modules/system/boot/stage-2.nix3
-rw-r--r--nixos/modules/system/boot/systemd-unit-options.nix2
-rw-r--r--nixos/modules/tasks/cpu-freq.nix2
-rw-r--r--nixos/modules/tasks/filesystems.nix69
-rw-r--r--nixos/modules/virtualisation/amazon-grow-partition.nix24
-rw-r--r--nixos/modules/virtualisation/amazon-image.nix4
-rw-r--r--nixos/modules/virtualisation/brightbox-image.nix2
-rw-r--r--nixos/modules/virtualisation/containers.nix29
-rw-r--r--nixos/modules/virtualisation/grow-partition.nix43
-rw-r--r--nixos/modules/virtualisation/virtualbox-host.nix11
-rw-r--r--nixos/modules/virtualisation/virtualbox-image.nix7
37 files changed, 725 insertions, 189 deletions
diff --git a/nixos/modules/config/fonts/fonts.nix b/nixos/modules/config/fonts/fonts.nix
index ea0a67038572..f913b8c33e56 100644
--- a/nixos/modules/config/fonts/fonts.nix
+++ b/nixos/modules/config/fonts/fonts.nix
@@ -22,7 +22,7 @@ with lib;
   config = {
 
     fonts.fonts =
-      [ pkgs.xorg.fontbhttf
+      [
         pkgs.xorg.fontbhlucidatypewriter100dpi
         pkgs.xorg.fontbhlucidatypewriter75dpi
         pkgs.dejavu_fonts
diff --git a/nixos/modules/config/gnu.nix b/nixos/modules/config/gnu.nix
index ad0e35c8a63f..f8c35b440d12 100644
--- a/nixos/modules/config/gnu.nix
+++ b/nixos/modules/config/gnu.nix
@@ -37,6 +37,7 @@ with lib;
     services.openssh.enable = false;
     services.lshd.enable = true;
     programs.ssh.startAgent = false;
+    services.xserver.startGnuPGAgent = true;
 
     # TODO: GNU dico.
     # TODO: GNU Inetutils' inetd.
diff --git a/nixos/modules/config/swap.nix b/nixos/modules/config/swap.nix
index 62b6e011713e..e57ed2565a10 100644
--- a/nixos/modules/config/swap.nix
+++ b/nixos/modules/config/swap.nix
@@ -54,6 +54,10 @@ let
           WARNING: Don't try to hibernate when you have at least one swap partition with
           this option enabled! We have no way to set the partition into which hibernation image
           is saved, so if your image ends up on an encrypted one you would lose it!
+
+          WARNING #2: Do not use /dev/disk/by-uuid/… or /dev/disk/by-label/… as your swap device
+          when using randomEncryption as the UUIDs and labels will get erased on every boot when
+          the partition is encrypted. Best to use /dev/disk/by-partuuid/…
         '';
       };
 
@@ -72,7 +76,7 @@ let
     config = rec {
       device = mkIf options.label.isDefined
         "/dev/disk/by-label/${config.label}";
-      deviceName = escapeSystemdPath config.device;
+      deviceName = lib.replaceChars ["\\"] [""] (escapeSystemdPath config.device);
       realDevice = if config.randomEncryption then "/dev/mapper/${deviceName}" else config.device;
     };
 
@@ -121,6 +125,8 @@ in
 
         createSwapDevice = sw:
           assert sw.device != "";
+          assert !(sw.randomEncryption && lib.hasPrefix "/dev/disk/by-uuid"  sw.device);
+          assert !(sw.randomEncryption && lib.hasPrefix "/dev/disk/by-label" sw.device);
           let realDevice' = escapeSystemdPath sw.realDevice;
           in nameValuePair "mkswap-${sw.deviceName}"
           { description = "Initialisation of swap device ${sw.device}";
diff --git a/nixos/modules/config/update-users-groups.pl b/nixos/modules/config/update-users-groups.pl
index 967f427374b1..cbbe216e5a17 100644
--- a/nixos/modules/config/update-users-groups.pl
+++ b/nixos/modules/config/update-users-groups.pl
@@ -52,8 +52,8 @@ foreach my $g (@{$spec->{groups}}) {
     $gidsUsed{$g->{gid}} = 1 if defined $g->{gid};
 }
 
-foreach my $u (@{$spec->{groups}}) {
-    $uidsUsed{$u->{u}} = 1 if defined $u->{uid};
+foreach my $u (@{$spec->{users}}) {
+    $uidsUsed{$u->{uid}} = 1 if defined $u->{uid};
 }
 
 # Read the current /etc/group.
diff --git a/nixos/modules/installer/tools/nixos-install.sh b/nixos/modules/installer/tools/nixos-install.sh
index 62ea98c4676c..589a51fa7094 100644
--- a/nixos/modules/installer/tools/nixos-install.sh
+++ b/nixos/modules/installer/tools/nixos-install.sh
@@ -92,14 +92,12 @@ fi
 mkdir -m 0755 -p $mountPoint/dev $mountPoint/proc $mountPoint/sys $mountPoint/etc $mountPoint/run $mountPoint/home
 mkdir -m 01777 -p $mountPoint/tmp
 mkdir -m 0755 -p $mountPoint/tmp/root
-mkdir -m 0755 -p $mountPoint/var/setuid-wrappers
 mkdir -m 0700 -p $mountPoint/root
 mount --rbind /dev $mountPoint/dev
 mount --rbind /proc $mountPoint/proc
 mount --rbind /sys $mountPoint/sys
 mount --rbind / $mountPoint/tmp/root
 mount -t tmpfs -o "mode=0755" none $mountPoint/run
-mount -t tmpfs -o "mode=0755" none $mountPoint/var/setuid-wrappers
 rm -rf $mountPoint/var/run
 ln -s /run $mountPoint/var/run
 for f in /etc/resolv.conf /etc/hosts; do rm -f $mountPoint/$f; [ -f "$f" ] && cp -Lf $f $mountPoint/etc/; done
diff --git a/nixos/modules/security/audit.nix b/nixos/modules/security/audit.nix
index f223f52ec487..ebfe594d0c71 100644
--- a/nixos/modules/security/audit.nix
+++ b/nixos/modules/security/audit.nix
@@ -4,6 +4,7 @@ with lib;
 
 let
   cfg = config.security.audit;
+  enabled = cfg.enable == "lock" || cfg.enable;
 
   failureModes = {
     silent = 0;
@@ -11,6 +12,13 @@ let
     panic  = 2;
   };
 
+  disableScript = pkgs.writeScript "audit-disable" ''
+    #!${pkgs.stdenv.shell} -eu
+    # Explicitly disable everything, as otherwise journald might start it.
+    auditctl -D
+    auditctl -e 0 -a task,never
+  '';
+
   # TODO: it seems like people like their rules to be somewhat secret, yet they will not be if
   # put in the store like this. At the same time, it doesn't feel like a huge deal and working
   # around that is a pain so I'm leaving it like this for now.
@@ -47,7 +55,7 @@ in {
     security.audit = {
       enable = mkOption {
         type        = types.enum [ false true "lock" ];
-        default     = true; # The kernel seems to enable it by default with no rules anyway
+        default     = false;
         description = ''
           Whether to enable the Linux audit system. The special `lock' value can be used to
           enable auditing and prevent disabling it until a restart. Be careful about locking
@@ -91,7 +99,7 @@ in {
     };
   };
 
-  config = mkIf (cfg.enable == "lock" || cfg.enable) {
+  config = {
     systemd.services.audit = {
       description = "Kernel Auditing";
       wantedBy = [ "basic.target" ];
@@ -103,8 +111,8 @@ in {
       serviceConfig = {
         Type = "oneshot";
         RemainAfterExit = true;
-        ExecStart = "@${startScript} audit-start";
-        ExecStop  = "@${stopScript}  audit-stop";
+        ExecStart = "@${if enabled then startScript else disableScript} audit-start";
+        ExecStop  = "@${stopScript} audit-stop";
       };
     };
   };
diff --git a/nixos/modules/security/grsecurity.nix b/nixos/modules/security/grsecurity.nix
index c6332ca9f9f6..ea1064c2d425 100644
--- a/nixos/modules/security/grsecurity.nix
+++ b/nixos/modules/security/grsecurity.nix
@@ -20,6 +20,11 @@ let
 in
 
 {
+  meta = {
+    maintainers = with maintainers; [ joachifm ];
+    doc = ./grsecurity.xml;
+  };
+
   options.security.grsecurity = {
 
     enable = mkEnableOption "grsecurity/PaX";
diff --git a/nixos/modules/security/grsecurity.xml b/nixos/modules/security/grsecurity.xml
new file mode 100644
index 000000000000..28415e89bfab
--- /dev/null
+++ b/nixos/modules/security/grsecurity.xml
@@ -0,0 +1,345 @@
+<chapter xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         version="5.0"
+         xml:id="sec-grsecurity">
+
+  <title>Grsecurity/PaX</title>
+
+  <para>
+    Grsecurity/PaX is a set of patches against the Linux kernel that make it
+    harder to exploit bugs.  The patchset includes protections such as
+    enforcement of non-executable memory, address space layout randomization,
+    and chroot jail hardening.  These and other
+    <link xlink:href="https://grsecurity.net/features.php">features</link>
+    render entire classes of exploits inert without additional efforts on the
+    part of the adversary.
+  </para>
+
+  <para>
+    The NixOS grsecurity/PaX module is designed with casual users in mind and is
+    intended to be compatible with normal desktop usage, without unnecessarily
+    compromising security.  The following sections describe the configuration
+    and administration of a grsecurity/PaX enabled NixOS system.  For
+    more comprehensive coverage, please refer to the
+    <link xlink:href="https://en.wikibooks.org/wiki/Grsecurity">grsecurity wikibook</link>
+    and the
+    <link xlink:href="https://wiki.archlinux.org/index.php/Grsecurity">Arch
+    Linux wiki page on grsecurity</link>.
+
+    <note><para>grsecurity/PaX is only available for the latest linux -stable
+    kernel; patches against older kernels are available from upstream only for
+    a fee.</para></note>
+    <note><para>We standardise on a desktop oriented configuration primarily due
+    to lack of resources.  The grsecurity/PaX configuration state space is huge
+    and each configuration requires quite a bit of testing to ensure that the
+    resulting packages work as advertised.  Defining additional package sets
+    would likely result in a large number of functionally broken packages, to
+    nobody's benefit.</para></note>.
+  </para>
+
+  <sect1 xml:id="sec-grsec-enable"><title>Enabling grsecurity/PaX</title>
+
+  <para>
+    To make use of grsecurity/PaX on NixOS, add the following to your
+    <filename>configuration.nix</filename>:
+    <programlisting>
+      security.grsecurity.enable = true;
+    </programlisting>
+    followed by
+    <programlisting>
+      # nixos-rebuild boot
+      # reboot
+    </programlisting>
+    For most users, further configuration should be unnecessary.  All users
+    are encouraged to look over <xref linkend="sec-grsec-security" /> before
+    using the system, however.  If you experience problems, please refer to
+    <xref linkend="sec-grsec-issues" />.
+  </para>
+
+  <para>
+    Once booted into the new system, you can optionally use
+    <command>paxtest</command> to exercise various PaX features:
+    <screen><![CDATA[
+    # nix-shell -p paxtest --command 'paxtest blackhat'
+    Executable anonymous mapping             : Killed
+    Executable bss                           : Killed
+    # ... remaining output truncated for brevity
+    ]]></screen>
+  </para>
+
+  </sect1>
+
+  <sect1 xml:id="sec-grsec-declarative-tuning"><title>Declarative tuning</title>
+
+  <para>
+    The default configuration mode is strictly declarative.  Some features
+    simply cannot be changed at all after boot, while others are locked once the
+    system is up and running.  Moreover, changes to the configuration enter
+    into effect only upon booting into the new system.
+  </para>
+
+  <para>
+    The NixOS module exposes a limited number of options for tuning the behavior
+    of grsecurity/PaX.  These are options thought to be of particular interest
+    to most users.  For experts, further tuning is possible via
+    <option>boot.kernelParams</option> (see
+    <xref linkend="sec-grsec-kernel-params" />) and
+    <option>boot.kernel.sysctl."kernel.grsecurity.*"</option> (the wikibook
+    contains an <link xlink:href="https://en.wikibooks.org/wiki/Grsecurity/Appendix/Sysctl_Options">
+    exhaustive listing of grsecurity sysctl tunables</link>).
+  </para>
+
+  </sect1>
+
+  <sect1 xml:id="sec-grsec-manual-tuning"><title>Manual tuning</title>
+
+  <para>
+    To permit manual tuning of grsecurity runtime parameters, set:
+    <programlisting>
+      security.grsecurity.lockTunables = false;
+    </programlisting>
+    Once booted into this system, grsecurity features that have a corresponding
+    sysctl tunable can be changed without rebooting, either by switching into
+    a new system profile or via the <command>sysctl</command> utility.
+  </para>
+
+  <para>
+    To lock all grsecurity tunables until the next boot, do:
+    <screen>
+      # systemctl start grsec-lock
+    </screen>
+  </para>
+
+  </sect1>
+
+  <sect1 xml:id="sec-grsec-security"><title>Security considerations</title>
+
+  <para>
+    The NixOS kernel is built using upstream's recommended settings for a
+    desktop deployment that generally favours security over performance.  This
+    section details deviations from upstream's recommendations that may
+    compromise operational security.
+
+    <warning><para>There may be additional problems not covered here!</para>
+    </warning>.
+  </para>
+
+  <itemizedlist>
+
+    <listitem><para>
+      The following hardening features are disabled in the NixOS kernel:
+      <itemizedlist>
+        <listitem><para>Kernel symbol hiding: rendered useless by redistributing
+        kernel objects.</para></listitem>
+
+        <listitem><para>Randomization of kernel structures: rendered useless by
+        redistributing kernel objects.</para></listitem>
+
+        <listitem><para>TCP simultaneous OPEN connection is permitted: breaking
+        strict TCP conformance is inappropriate for a general purpose kernel.
+        The trade-off is that an attacker may be able to deny outgoing
+        connections if they are able to guess the source port allocated by your
+        OS for that connection <emphasis>and</emphasis> also manage to initiate
+        a TCP simultaneous OPEN on that port before the connection is actually
+        established.</para></listitem>
+
+        <listitem><para><filename class="directory">/sys</filename> hardening:
+        breaks systemd.</para></listitem>
+
+        <listitem><para>Trusted path execution: a desirable feature, but
+        requires some more work to operate smoothly on NixOS.</para></listitem>
+      </itemizedlist>
+    </para></listitem>
+
+    <listitem><para>
+      The NixOS module conditionally weakens <command>chroot</command>
+      restrictions to accommodate NixOS lightweight containers and sandboxed Nix
+      builds.  This is problematic if the deployment also runs a privileged
+      network facing process that <emphasis>relies</emphasis> on
+      <command>chroot</command> for isolation.
+    </para></listitem>
+
+    <listitem><para>
+      The NixOS kernel is patched to allow usermode helpers from anywhere in the
+      Nix store.  A usermode helper is an executable called by the kernel in
+      certain circumstances, e.g., <command>modprobe</command>.  Vanilla
+      grsecurity only allows usermode helpers from paths typically owned by the
+      super user.  The NixOS kernel allows an attacker to inject malicious code
+      into the Nix store which could then be executed by the kernel as a
+      usermode helper.
+    </para></listitem>
+
+    <listitem><para>
+      The following features are disabled because they overlap with
+      vanilla kernel mechanisms:
+
+      <itemizedlist>
+        <listitem><para><filename class="directory">/proc</filename> hardening:
+        use <option>security.hideProcessInformation</option> instead.  This
+        trades weaker protection for greater compatibility.
+        </para></listitem>
+
+        <listitem><para><command>dmesg</command> restrictions:
+        use <option>boot.kernel.sysctl."kernel.dmesg_restrict"</option> instead
+        </para></listitem>
+      </itemizedlist>
+    </para></listitem>
+
+  </itemizedlist>
+
+  </sect1>
+
+  <sect1 xml:id="sec-grsec-custom-kernel"><title>Using a custom grsecurity/PaX kernel</title>
+
+  <para>
+    The NixOS kernel is likely to be either too permissive or too restrictive
+    for many deployment scenarios.  In addition to producing a kernel more
+    suitable for a particular deployment, a custom kernel may improve security
+    by depriving an attacker the ability to study the kernel object code, adding
+    yet more guesswork to successfully carry out certain exploits.
+  </para>
+
+  <para>
+    To use a custom kernel with upstream's recommended settings for server
+    deployments:
+    <programlisting>
+      boot.kernelPackages =
+        let
+          kernel = pkgs.linux_grsec_nixos.override {
+            extraConfig = ''
+              GRKERNSEC y
+              PAX y
+              GRKERNSEC_CONFIG_AUTO y
+              GRKERNSEC_CONFIG_SERVER y
+              GRKERNSEC_CONFIG_SECURITY y
+            '';
+          };
+          self = pkgs.linuxPackagesFor kernel self;
+        in self;
+    </programlisting>
+    The wikibook provides an exhaustive listing of
+    <link xlink:href="https://en.wikibooks.org/wiki/Grsecurity/Appendix/Grsecurity_and_PaX_Configuration_Options">kernel configuration options</link>.
+  </para>
+
+  <para>
+    The NixOS module makes several assumptions about the kernel and so may be
+    incompatible with your customised kernel.  Most of these assumptions are
+    encoded as assertions &#x2014; mismatches should ideally result in a build
+    failure.  Currently, the only way to work around incompatibilities is to
+    eschew the NixOS module and do all configuration yourself.
+  </para>
+
+  </sect1>
+
+  <sect1 xml:id="sec-grsec-pax-flags"><title>Per-executable PaX flags</title>
+
+  <para>
+    Manual tuning of per-file PaX flags for executables in the Nix store is
+    impossible on a properly configured system.  If a package in Nixpkgs fails
+    due to PaX, that is a bug in the package recipe and should be reported to
+    the maintainer (including relevant <command>dmesg</command> output).
+  </para>
+
+  <para>
+    For executables installed outside of the Nix store, PaX flags can be set
+    using the <command>paxctl</command> utility:
+    <programlisting>
+      paxctl -czem <replaceable>foo</replaceable>
+    </programlisting>
+
+    <warning>
+      <para><command>paxctl</command> overwrites files in-place.</para>
+    </warning>
+
+    Equivalently, on file systems that support extended attributes:
+    <programlisting>
+      setfattr -n user.pax.flags -v em <replaceable>foo</replaceable>
+    </programlisting>
+
+    <!-- TODO: PaX flags via RBAC policy -->
+  </para>
+
+  </sect1>
+
+  <sect1 xml:id="sec-grsec-issues"><title>Issues and work-arounds</title>
+
+  <itemizedlist>
+    <listitem><para>User namespaces require <literal>CAP_SYS_ADMIN</literal>:
+    consequently, unprivileged namespaces are unsupported. Applications that
+    rely on namespaces for sandboxing must use a privileged helper. For chromium
+    there is <option>security.chromiumSuidSandbox.enable</option>.</para></listitem>
+
+    <listitem><para>Access to EFI runtime services is disabled by default:
+    this plugs a potential code injection attack vector; use
+    <option>security.grsecurity.disableEfiRuntimeServices</option> to override
+    this behavior.</para></listitem>
+
+    <listitem><para>Virtualization: KVM is the preferred virtualization
+    solution. Xen, Virtualbox, and VMWare are
+    <emphasis>unsupported</emphasis> and most likely require a custom kernel.
+    </para></listitem>
+
+    <listitem><para>
+      Attaching <command>gdb</command> to a running process is disallowed by
+      default: unprivileged users can only ptrace processes that are children of
+      the ptracing process.  To relax this restriction, set
+      <programlisting>
+        boot.kernel.sysctl."kernel.grsecurity.harden_ptrace" = 0;
+      </programlisting>
+    </para></listitem>
+
+    <listitem><para>
+      Overflows in boot critical code (e.g., the root filesystem module) can
+      render the system unbootable.  Work around by setting
+      <programlisting>
+        boot.kernel.kernelParams = [ "pax_size_overflow_report_only" ];
+      </programlisting>
+    </para></listitem>
+
+    <listitem><para>
+      The <citerefentry><refentrytitle>modify_ldt
+      </refentrytitle><manvolnum>2</manvolnum></citerefentry> syscall is disabled
+      by default.  This restriction can interfere with programs designed to run
+      legacy 16-bit or segmented 32-bit code.  To support applications that rely
+      on this syscall, set
+      <programlisting>
+        boot.kernel.sysctl."kernel.modify_ldt" = 1;
+      </programlisting>
+    </para></listitem>
+
+  </itemizedlist>
+
+  </sect1>
+
+  <sect1 xml:id="sec-grsec-kernel-params"><title>Grsecurity/PaX kernel parameters</title>
+
+  <para>
+    The NixOS kernel supports the following kernel command line parameters:
+    <itemizedlist>
+      <listitem><para>
+        <literal>pax_nouderef</literal>: disable UDEREF (separate kernel and
+        user address spaces).
+      </para></listitem>
+
+      <listitem><para>
+        <literal>pax_weakuderef</literal>: enable a faster but
+        weaker variant of UDEREF on 64-bit processors with PCID support
+        (check <code>grep pcid /proc/cpuinfo</code>).
+      </para></listitem>
+
+      <listitem><para>
+        <literal>pax_sanitize_slab={off|fast|full}</literal>: control kernel
+        slab object sanitization
+      </para></listitem>
+
+      <listitem><para>
+        <literal>pax_size_overflow_report_only</literal>: log size overflow
+        violations but leave the violating task running
+      </para></listitem>
+    </itemizedlist>
+  </para>
+
+  </sect1>
+
+</chapter>
diff --git a/nixos/modules/security/hidepid.nix b/nixos/modules/security/hidepid.nix
index 8271578c55d6..8f2df380cfe8 100644
--- a/nixos/modules/security/hidepid.nix
+++ b/nixos/modules/security/hidepid.nix
@@ -20,23 +20,6 @@ with lib;
   config = mkIf config.security.hideProcessInformation {
     users.groups.proc.gid = config.ids.gids.proc;
 
-    systemd.services.hidepid = {
-      wantedBy = [ "local-fs.target" ];
-      after = [ "systemd-remount-fs.service" ];
-      before = [ "local-fs-pre.target" "local-fs.target" "shutdown.target" ];
-      wants = [ "local-fs-pre.target" ];
-
-      serviceConfig = {
-        Type = "oneshot";
-        RemainAfterExit = true;
-        ExecStart = ''${pkgs.utillinux}/bin/mount -o remount,hidepid=2,gid=${toString config.ids.gids.proc} /proc'';
-        ExecStop = ''${pkgs.utillinux}/bin/mount -o remount,hidepid=0,gid=0 /proc'';
-      };
-
-      unitConfig = {
-        DefaultDependencies = false;
-        Conflicts = "shutdown.target";
-      };
-    };
+    boot.specialFileSystems."/proc".options = [ "hidepid=2" "gid=${toString config.ids.gids.proc}" ];
   };
 }
diff --git a/nixos/modules/security/setuid-wrappers.nix b/nixos/modules/security/setuid-wrappers.nix
index 99dd514feea3..e1dca477d70a 100644
--- a/nixos/modules/security/setuid-wrappers.nix
+++ b/nixos/modules/security/setuid-wrappers.nix
@@ -12,7 +12,7 @@ let
     installPhase = ''
       mkdir -p $out/bin
       cp ${./setuid-wrapper.c} setuid-wrapper.c
-      gcc -Wall -O2 -DWRAPPER_DIR=\"${wrapperDir}\" \
+      gcc -Wall -O2 -DWRAPPER_DIR=\"/run/setuid-wrapper-dirs\" \
           setuid-wrapper.c -o $out/bin/setuid-wrapper
     '';
   };
@@ -102,11 +102,11 @@ in
                 source=/nix/var/nix/profiles/default/bin/${program}
             fi
 
-            cp ${setuidWrapper}/bin/setuid-wrapper ${wrapperDir}/${program}
-            echo -n "$source" > ${wrapperDir}/${program}.real
-            chmod 0000 ${wrapperDir}/${program} # to prevent races
-            chown ${owner}.${group} ${wrapperDir}/${program}
-            chmod "u${if setuid then "+" else "-"}s,g${if setgid then "+" else "-"}s,${permissions}" ${wrapperDir}/${program}
+            cp ${setuidWrapper}/bin/setuid-wrapper $wrapperDir/${program}
+            echo -n "$source" > $wrapperDir/${program}.real
+            chmod 0000 $wrapperDir/${program} # to prevent races
+            chown ${owner}.${group} $wrapperDir/${program}
+            chmod "u${if setuid then "+" else "-"}s,g${if setgid then "+" else "-"}s,${permissions}" $wrapperDir/${program}
           '';
 
       in stringAfter [ "users" ]
@@ -115,9 +115,29 @@ in
           # programs to be wrapped.
           SETUID_PATH=${config.system.path}/bin:${config.system.path}/sbin
 
-          rm -f ${wrapperDir}/* # */
+          mkdir -p /run/setuid-wrapper-dirs
+          wrapperDir=$(mktemp --directory --tmpdir=/run/setuid-wrapper-dirs setuid-wrappers.XXXXXXXXXX)
 
           ${concatMapStrings makeSetuidWrapper setuidPrograms}
+
+          if [ -L ${wrapperDir} ]; then
+            # Atomically replace the symlink
+            # See https://axialcorps.com/2013/07/03/atomically-replacing-files-and-directories/
+            old=$(readlink ${wrapperDir})
+            ln --symbolic --force --no-dereference $wrapperDir ${wrapperDir}-tmp
+            mv --no-target-directory ${wrapperDir}-tmp ${wrapperDir}
+            rm --force --recursive $old
+          elif [ -d ${wrapperDir} ]; then
+            # Compatibility with old state, just remove the folder and symlink
+            rm -f ${wrapperDir}/*
+            # if it happens to be a tmpfs
+            umount ${wrapperDir} || true
+            rm -d ${wrapperDir}
+            ln -d --symbolic $wrapperDir ${wrapperDir}
+          else
+            # For initial setup
+            ln --symbolic $wrapperDir ${wrapperDir}
+          fi
         '';
 
   };
diff --git a/nixos/modules/services/desktops/accountsservice.nix b/nixos/modules/services/desktops/accountsservice.nix
index c28c27295761..2a7450669ea0 100644
--- a/nixos/modules/services/desktops/accountsservice.nix
+++ b/nixos/modules/services/desktops/accountsservice.nix
@@ -35,6 +35,14 @@ with lib;
     services.dbus.packages = [ pkgs.accountsservice ];
 
     systemd.packages = [ pkgs.accountsservice ];
+
+    systemd.services.accounts-daemon= {
+
+      wantedBy = [ "graphical.target" ];
+
+    } // (mkIf (!config.users.mutableUsers) {
+      environment.NIXOS_USERS_PURE = "true";
+    });
   };
 
 }
diff --git a/nixos/modules/services/editors/emacs.nix b/nixos/modules/services/editors/emacs.nix
index 43b4219c51dd..6795ec52fe4d 100644
--- a/nixos/modules/services/editors/emacs.nix
+++ b/nixos/modules/services/editors/emacs.nix
@@ -83,4 +83,6 @@ in {
       EDITOR = mkOverride 900 "${editorScript}/bin/emacseditor";
     } else {};
   };
+
+  meta.doc = ./emacs.xml;
 }
diff --git a/nixos/modules/services/mail/opensmtpd.nix b/nixos/modules/services/mail/opensmtpd.nix
index e773cdedaea2..fb94560e10aa 100644
--- a/nixos/modules/services/mail/opensmtpd.nix
+++ b/nixos/modules/services/mail/opensmtpd.nix
@@ -109,12 +109,14 @@ in {
       after = [ "network.target" ];
       preStart = ''
         mkdir -p /var/spool/smtpd
+        chmod 711 /var/spool/smtpd
 
         mkdir -p /var/spool/smtpd/offline
         chown root.smtpq /var/spool/smtpd/offline
         chmod 770 /var/spool/smtpd/offline
 
         mkdir -p /var/spool/smtpd/purge
+        chown smtpq.root /var/spool/smtpd/purge
         chmod 700 /var/spool/smtpd/purge
       '';
       serviceConfig.ExecStart = "${opensmtpd}/sbin/smtpd -d -f ${conf} ${args}";
diff --git a/nixos/modules/services/misc/gitit.nix b/nixos/modules/services/misc/gitit.nix
index befd8c628f16..44880ebeda14 100644
--- a/nixos/modules/services/misc/gitit.nix
+++ b/nixos/modules/services/misc/gitit.nix
@@ -663,7 +663,7 @@ in
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
       path = with pkgs; [ curl ]
-             ++ optional cfg.pdfExport texLiveFull
+             ++ optional cfg.pdfExport texlive.combined.scheme-basic
 	     ++ optional (cfg.repositoryType == "darcs") darcs
 	     ++ optional (cfg.repositoryType == "mercurial") mercurial
 	     ++ optional (cfg.repositoryType == "git") git;
diff --git a/nixos/modules/services/misc/nix-daemon.nix b/nixos/modules/services/misc/nix-daemon.nix
index fe5132d4973e..333782d15bcb 100644
--- a/nixos/modules/services/misc/nix-daemon.nix
+++ b/nixos/modules/services/misc/nix-daemon.nix
@@ -311,7 +311,7 @@ in
       nixPath = mkOption {
         type = types.listOf types.str;
         default =
-          [ "/nix/var/nix/profiles/per-user/root/channels/nixos"
+          [ "nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos/nixpkgs"
             "nixos-config=/etc/nixos/configuration.nix"
             "/nix/var/nix/profiles/per-user/root/channels"
           ];
diff --git a/nixos/modules/services/networking/dnscrypt-proxy.nix b/nixos/modules/services/networking/dnscrypt-proxy.nix
index cf36ccf05725..2714e8d75993 100644
--- a/nixos/modules/services/networking/dnscrypt-proxy.nix
+++ b/nixos/modules/services/networking/dnscrypt-proxy.nix
@@ -28,31 +28,15 @@ let
 in
 
 {
+  meta = {
+    maintainers = with maintainers; [ joachifm ];
+    doc = ./dnscrypt-proxy.xml;
+  };
+
   options = {
     services.dnscrypt-proxy = {
-      enable = mkEnableOption "dnscrypt-proxy" // { description = ''
-        Whether to enable the DNSCrypt client proxy. The proxy relays
-        DNS queries to a DNSCrypt enabled upstream resolver. The traffic
-        between the client and the upstream resolver is encrypted and
-        authenticated, mitigating the risk of MITM attacks and third-party
-        snooping (assuming the upstream is trustworthy).
-
-        Enabling this option does not alter the system nameserver; to relay
-        local queries, prepend <literal>127.0.0.1</literal> to
-        <option>networking.nameservers</option>.
-
-        The recommended configuration is to run DNSCrypt proxy as a forwarder
-        for a caching DNS client, as in
-        <programlisting>
-        {
-          services.dnscrypt-proxy.enable = true;
-          services.dnscrypt-proxy.localPort = 43;
-          services.dnsmasq.enable = true;
-          services.dnsmasq.servers = [ "127.0.0.1#43" ];
-          services.dnsmasq.resolveLocalQueries = true; # this is the default
-        }
-        </programlisting>
-      ''; };
+      enable = mkEnableOption "DNSCrypt client proxy";
+
       localAddress = mkOption {
         default = "127.0.0.1";
         type = types.str;
@@ -62,6 +46,7 @@ in
           of other machines (typically on the local network).
         '';
       };
+
       localPort = mkOption {
         default = 53;
         type = types.int;
@@ -72,6 +57,7 @@ in
           to a different value; otherwise leave the default.
         '';
       };
+
       resolverName = mkOption {
         default = "dnscrypt.eu-nl";
         type = types.nullOr types.str;
@@ -82,6 +68,7 @@ in
           extensions, and claims to not keep logs.
         '';
       };
+
       resolverList = mkOption {
         description = ''
           The list of upstream DNSCrypt resolvers. By default, we use the most
@@ -94,6 +81,7 @@ in
         };
         defaultText = "pkgs.fetchurl { url = ...; sha256 = ...; }";
       };
+
       customResolver = mkOption {
         default = null;
         description = ''
@@ -103,26 +91,30 @@ in
         type = types.nullOr (types.submodule ({ ... }: { options = {
           address = mkOption {
             type = types.str;
-            description = "Resolver IP address";
+            description = "IP address";
             example = "208.67.220.220";
           };
+
           port = mkOption {
             type = types.int;
-            description = "Resolver port";
+            description = "Port";
             default = 443;
           };
+
           name = mkOption {
             type = types.str;
-            description = "Provider fully qualified domain name";
+            description = "Fully qualified domain name";
             example = "2.dnscrypt-cert.opendns.com";
           };
+
           key = mkOption {
             type = types.str;
-            description = "Provider public key";
+            description = "Public key";
             example = "B735:1140:206F:225D:3E2B:D822:D7FD:691E:A1C3:3CC8:D666:8D0C:BE04:BFAB:CA43:FB79";
           };
         }; }));
       };
+
       tcpOnly = mkOption {
         default = false;
         type = types.bool;
@@ -131,6 +123,7 @@ in
           TCP instead of UDP (on port 443). Use only if the UDP port is blocked.
         '';
       };
+
       ephemeralKeys = mkOption {
         default = false;
         type = types.bool;
@@ -212,7 +205,6 @@ in
         ExecStart = "${dnscrypt-proxy}/bin/dnscrypt-proxy ${toString daemonArgs}";
 
         User = "dnscrypt-proxy";
-        Group = "dnscrypt-proxy";
 
         PrivateTmp = true;
         PrivateDevices = true;
diff --git a/nixos/modules/services/networking/dnscrypt-proxy.xml b/nixos/modules/services/networking/dnscrypt-proxy.xml
new file mode 100644
index 000000000000..e212a8d3e2c3
--- /dev/null
+++ b/nixos/modules/services/networking/dnscrypt-proxy.xml
@@ -0,0 +1,76 @@
+<chapter xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         version="5.0"
+         xml:id="sec-dnscrypt-proxy">
+
+  <title>DNSCrypt client proxy</title>
+
+  <para>
+    The DNSCrypt client proxy relays DNS queries to a DNSCrypt enabled
+    upstream resolver. The traffic between the client and the upstream
+    resolver is encrypted and authenticated, mitigating the risk of MITM
+    attacks, DNS poisoning attacks, and third-party snooping (assuming the
+    upstream is trustworthy).
+  </para>
+
+  <sect1><title>Basic configuration</title>
+
+  <para>
+    To enable the client proxy, set
+    <programlisting>
+      services.dnscrypt-proxy.enable = true;
+    </programlisting>
+  </para>
+
+  <para>
+    Enabling the client proxy does not alter the system nameserver; to
+    relay local queries, prepend <literal>127.0.0.1</literal> to
+    <option>networking.nameservers</option>.
+  </para>
+
+  </sect1>
+
+  <sect1><title>As a forwarder for a caching DNS client</title>
+
+  <para>
+    By default, DNSCrypt proxy acts as a transparent proxy for the
+    system stub resolver. Because the client does not cache lookups, this
+    setup can significantly slow down e.g., web browsing. The recommended
+    configuration is to run DNSCrypt proxy as a forwarder for a caching DNS
+    client. To achieve this, change the default proxy listening port to
+    a non-standard value and point the caching client to it:
+    <programlisting>
+      services.dnscrypt-proxy.localPort = 43;
+    </programlisting>
+  </para>
+
+  <sect2><title>dnsmasq</title>
+  <para>
+    <programlisting>
+      {
+      services.dnsmasq.enable = true;
+      services.dnsmasq.servers = [ "127.0.0.1#43" ];
+      }
+    </programlisting>
+  </para>
+  </sect2>
+
+  <sect2><title>unbound</title>
+  <para>
+    <programlisting>
+      {
+      networking.nameservers = [ "127.0.0.1" ];
+      services.unbound.enable = true;
+      services.unbound.forwardAddresses = [ "127.0.0.1@43" ];
+      services.unbound.extraConfig = ''
+        do-not-query-localhost: no
+      '';
+      }
+    </programlisting>
+  </para>
+  </sect2>
+
+  </sect1>
+
+</chapter>
diff --git a/nixos/modules/services/networking/mjpg-streamer.nix b/nixos/modules/services/networking/mjpg-streamer.nix
index 9986f549aecf..1286b0c7ef6c 100644
--- a/nixos/modules/services/networking/mjpg-streamer.nix
+++ b/nixos/modules/services/networking/mjpg-streamer.nix
@@ -59,8 +59,12 @@ in {
       description = "mjpg-streamer webcam streamer";
       wantedBy = [ "multi-user.target" ];
 
-      serviceConfig.User = cfg.user;
-      serviceConfig.Group = cfg.group;
+      serviceConfig = {
+        User = cfg.user;
+        Group = cfg.group;
+        Restart = "on-failure";
+        RestartSec = 1;
+      };
 
       script = ''
         IPLUGIN="${cfg.inputPlugin}"
diff --git a/nixos/modules/services/networking/unbound.nix b/nixos/modules/services/networking/unbound.nix
index 0dd24478f409..ed0744c44ccf 100644
--- a/nixos/modules/services/networking/unbound.nix
+++ b/nixos/modules/services/networking/unbound.nix
@@ -43,14 +43,10 @@ in
   options = {
     services.unbound = {
 
-      enable = mkOption {
-        default = false;
-        type = types.bool;
-        description = "Whether to enable the Unbound domain name server.";
-      };
+      enable = mkEnableOption "Unbound domain name server";
 
       allowedAccess = mkOption {
-        default = ["127.0.0.0/24"];
+        default = [ "127.0.0.0/24" ];
         type = types.listOf types.str;
         description = "What networks are allowed to use unbound as a resolver.";
       };
@@ -97,7 +93,7 @@ in
     };
 
     systemd.services.unbound = {
-      description="Unbound recursive Domain Name Server";
+      description = "Unbound recursive Domain Name Server";
       after = [ "network.target" ];
       before = [ "nss-lookup.target" ];
       wants = [" nss-lookup.target" ];
diff --git a/nixos/modules/services/networking/zerotierone.nix b/nixos/modules/services/networking/zerotierone.nix
index e66648f683f4..86e0204ec2f7 100644
--- a/nixos/modules/services/networking/zerotierone.nix
+++ b/nixos/modules/services/networking/zerotierone.nix
@@ -7,11 +7,19 @@ let
 in
 {
   options.services.zerotierone.enable = mkEnableOption "ZeroTierOne";
-  
+  options.services.zerotierone.package = mkOption {
+    default = pkgs.zerotierone;
+    defaultText = "pkgs.zerotierone";
+    type = types.package;
+    description = ''
+      ZeroTier One package to use.
+    '';
+  };
+
   config = mkIf cfg.enable {
     systemd.services.zerotierone = {
       description = "ZeroTierOne";
-      path = [ pkgs.zerotierone ];
+      path = [ cfg.package ];
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
       preStart =
@@ -21,7 +29,7 @@ in
         chown -R root:root /var/lib/zerotier-one
         '';
       serviceConfig = {
-        ExecStart = "${pkgs.zerotierone}/bin/zerotier-one";
+        ExecStart = "${cfg.package}/bin/zerotier-one";
         Restart = "always";
         KillMode = "process";
       };
@@ -30,6 +38,6 @@ in
     # ZeroTier does not issue DHCP leases, but some strangers might...
     networking.dhcpcd.denyInterfaces = [ "zt0" ];
 
-    environment.systemPackages = [ pkgs.zerotierone ];
+    environment.systemPackages = [ cfg.package ];
   };
 }
diff --git a/nixos/modules/system/activation/activation-script.nix b/nixos/modules/system/activation/activation-script.nix
index 4489e34831da..1c587413121e 100644
--- a/nixos/modules/system/activation/activation-script.nix
+++ b/nixos/modules/system/activation/activation-script.nix
@@ -154,9 +154,15 @@ in
 
     system.activationScripts.tmpfs =
       ''
-        ${pkgs.utillinux}/bin/mount -o "remount,size=${config.boot.devSize}" none /dev
-        ${pkgs.utillinux}/bin/mount -o "remount,size=${config.boot.devShmSize}" none /dev/shm
-        ${pkgs.utillinux}/bin/mount -o "remount,size=${config.boot.runSize}" none /run
+        specialMount() {
+          local device="$1"
+          local mountPoint="$2"
+          local options="$3"
+          local fsType="$4"
+
+          ${pkgs.utillinux}/bin/mount -t "$fsType" -o "remount,$options" "$device" "$mountPoint"
+        }
+        source ${config.system.build.earlyMountScript}
       '';
 
   };
diff --git a/nixos/modules/system/boot/loader/grub/grub.nix b/nixos/modules/system/boot/loader/grub/grub.nix
index 46776f45d96b..61c34cc2f034 100644
--- a/nixos/modules/system/boot/loader/grub/grub.nix
+++ b/nixos/modules/system/boot/loader/grub/grub.nix
@@ -341,7 +341,7 @@ in
         default = false;
         type = types.bool;
         description = ''
-          Whether GRUB should be build against libzfs.
+          Whether GRUB should be built against libzfs.
           ZFS support is only available for GRUB v2.
           This option is ignored for GRUB v1.
         '';
@@ -351,7 +351,7 @@ in
         default = false;
         type = types.bool;
         description = ''
-          Whether GRUB should be build with EFI support.
+          Whether GRUB should be built with EFI support.
           EFI support is only available for GRUB v2.
           This option is ignored for GRUB v1.
         '';
@@ -425,13 +425,20 @@ in
         { path = "/boot"; inherit (cfg) devices; inherit (efi) efiSysMountPoint; }
       ];
 
-      system.build.installBootLoader = pkgs.writeScript "install-grub.sh" (''
+      system.build.installBootLoader =
+        let
+          install-grub-pl = pkgs.substituteAll {
+            src = ./install-grub.pl;
+            inherit (pkgs) utillinux;
+            btrfsprogs = pkgs.btrfs-progs;
+          };
+        in pkgs.writeScript "install-grub.sh" (''
         #!${pkgs.stdenv.shell}
         set -e
         export PERL5LIB=${makePerlPath (with pkgs.perlPackages; [ FileSlurp XMLLibXML XMLSAX ListCompare ])}
         ${optionalString cfg.enableCryptodisk "export GRUB_ENABLE_CRYPTODISK=y"}
       '' + flip concatMapStrings cfg.mirroredBoots (args: ''
-        ${pkgs.perl}/bin/perl ${./install-grub.pl} ${grubConfig args} $@
+        ${pkgs.perl}/bin/perl ${install-grub-pl} ${grubConfig args} $@
       ''));
 
       system.build.grub = grub;
diff --git a/nixos/modules/system/boot/loader/grub/install-grub.pl b/nixos/modules/system/boot/loader/grub/install-grub.pl
index 4fa157641a4a..06eece5025f8 100644
--- a/nixos/modules/system/boot/loader/grub/install-grub.pl
+++ b/nixos/modules/system/boot/loader/grub/install-grub.pl
@@ -12,8 +12,10 @@ require List::Compare;
 use POSIX;
 use Cwd;
 
+# system.build.toplevel path
 my $defaultConfig = $ARGV[1] or die;
 
+# Grub config XML generated by grubConfig function in grub.nix
 my $dom = XML::LibXML->load_xml(location => $ARGV[0]);
 
 sub get { my ($name) = @_; return $dom->findvalue("/expr/attrs/attr[\@name = '$name']/*/\@value"); }
@@ -97,6 +99,8 @@ sub PathInMount {
     }
     return 1;
 }
+
+# Figure out what filesystem is used for the directory with init/initrd/kernel files
 sub GetFs {
     my ($dir) = @_;
     my $bestFs = Fs->new(device => "", type => "", mount => "");
@@ -136,7 +140,10 @@ my $driveid = 1;
 sub GrubFs {
     my ($dir) = @_;
     my $fs = GetFs($dir);
-    my $path = "/" . substr($dir, length($fs->mount));
+    my $path = substr($dir, length($fs->mount));
+    if (substr($path, 0, 1) ne "/") {
+      $path = "/$path";
+    }
     my $search = "";
 
     if ($grubVersion > 1) {
@@ -169,7 +176,7 @@ sub GrubFs {
                 $search = $types{$fsIdentifier} . ' ';
 
                 # Based on the type pull in the identifier from the system
-                my ($status, @devInfo) = runCommand("blkid -o export @{[$fs->device]}");
+                my ($status, @devInfo) = runCommand("@utillinux@/bin/blkid -o export @{[$fs->device]}");
                 if ($status != 0) {
                     die "Failed to get blkid info for @{[$fs->mount]} on @{[$fs->device]}";
                 }
@@ -182,7 +189,7 @@ sub GrubFs {
 
             # BTRFS is a special case in that we need to fix the referrenced path based on subvolumes
             if ($fs->type eq 'btrfs') {
-                my ($status, @id_info) = runCommand("btrfs subvol show @{[$fs->mount]}");
+                my ($status, @id_info) = runCommand("@btrfsprogs@/bin/btrfs subvol show @{[$fs->mount]}");
                 if ($status != 0) {
                     die "Failed to retrieve subvolume info for @{[$fs->mount]}\n";
                 }
@@ -190,7 +197,7 @@ sub GrubFs {
                 if ($#ids > 0) {
                     die "Btrfs subvol name for @{[$fs->device]} listed multiple times in mount\n"
                 } elsif ($#ids == 0) {
-                    my ($status, @path_info) = runCommand("btrfs subvol list @{[$fs->mount]}");
+                    my ($status, @path_info) = runCommand("@btrfsprogs@/bin/btrfs subvol list @{[$fs->mount]}");
                     if ($status != 0) {
                         die "Failed to find @{[$fs->mount]} subvolume id from btrfs\n";
                     }
diff --git a/nixos/modules/system/boot/stage-1-init.sh b/nixos/modules/system/boot/stage-1-init.sh
index 65d1dcb61681..abab5f20baac 100644
--- a/nixos/modules/system/boot/stage-1-init.sh
+++ b/nixos/modules/system/boot/stage-1-init.sh
@@ -59,22 +59,24 @@ echo
 echo "<<< NixOS Stage 1 >>>"
 echo
 
-
-# Mount special file systems.
+# Make several required directories.
 mkdir -p /etc/udev
 touch /etc/fstab # to shut up mount
-touch /etc/mtab # to shut up mke2fs
+ln -s /proc/mounts /etc/mtab # to shut up mke2fs
 touch /etc/udev/hwdb.bin # to shut up udev
 touch /etc/initrd-release
-mkdir -p /proc
-mount -t proc proc /proc
-mkdir -p /sys
-mount -t sysfs sysfs /sys
-mount -t devtmpfs -o "size=@devSize@" devtmpfs /dev
-mkdir -p /run
-mount -t tmpfs -o "mode=0755,size=@runSize@" tmpfs /run
-mkdir /dev/pts
-mount -t devpts devpts /dev/pts
+
+# Mount special file systems.
+specialMount() {
+  local device="$1"
+  local mountPoint="$2"
+  local options="$3"
+  local fsType="$4"
+
+  mkdir -m 0755 -p "$mountPoint"
+  mount -n -t "$fsType" -o "$options" "$device" "$mountPoint"
+}
+source @earlyMountScript@
 
 # Log the script output to /dev/kmsg or /run/log/stage-1-init.log.
 mkdir -p /tmp
diff --git a/nixos/modules/system/boot/stage-1.nix b/nixos/modules/system/boot/stage-1.nix
index a5c05f3dbbaf..513c121347b1 100644
--- a/nixos/modules/system/boot/stage-1.nix
+++ b/nixos/modules/system/boot/stage-1.nix
@@ -190,7 +190,9 @@ let
 
     inherit udevRules extraUtils modulesClosure;
 
-    inherit (config.boot) resumeDevice devSize runSize;
+    inherit (config.boot) resumeDevice;
+
+    inherit (config.system.build) earlyMountScript;
 
     inherit (config.boot.initrd) checkJournalingFS
       preLVMCommands preDeviceCommands postDeviceCommands postMountCommands preFailCommands kernelModules;
diff --git a/nixos/modules/system/boot/stage-2-init.sh b/nixos/modules/system/boot/stage-2-init.sh
index c5a14f0766d5..704150e77d72 100644
--- a/nixos/modules/system/boot/stage-2-init.sh
+++ b/nixos/modules/system/boot/stage-2-init.sh
@@ -37,12 +37,16 @@ fi
 # Likewise, stage 1 mounts /proc, /dev and /sys, so if we don't have a
 # stage 1, we need to do that here.
 if [ ! -e /proc/1 ]; then
-    mkdir -m 0755 -p /proc
-    mount -n -t proc proc /proc
-    mkdir -m 0755 -p /dev
-    mount -t devtmpfs devtmpfs /dev
-    mkdir -m 0755 -p /sys
-    mount -t sysfs sysfs /sys
+    specialMount() {
+        local device="$1"
+        local mountPoint="$2"
+        local options="$3"
+        local fsType="$4"
+
+        mkdir -m 0755 -p "$mountPoint"
+        mount -n -t "$fsType" -o "$options" "$device" "$mountPoint"
+    }
+    source @earlyMountScript@
 fi
 
 
@@ -87,11 +91,6 @@ done
 
 
 # More special file systems, initialise required directories.
-if ! mountpoint -q /dev/shm; then
-    mkdir -m 0755 /dev/shm
-    mount -t tmpfs -o "rw,nosuid,nodev,size=@devShmSize@" tmpfs /dev/shm
-fi
-mkdir -m 0755 -p /dev/pts
 [ -e /proc/bus/usb ] && mount -t usbfs usbfs /proc/bus/usb # UML doesn't have USB by default
 mkdir -m 01777 -p /tmp
 mkdir -m 0755 -p /var /var/log /var/lib /var/db
@@ -112,14 +111,6 @@ rm -f /etc/{group,passwd,shadow}.lock
 rm -rf /nix/var/nix/gcroots/tmp /nix/var/nix/temproots
 
 
-# Create a tmpfs on /run to hold runtime state for programs such as
-# udev (if stage 1 hasn't already done so).
-if ! mountpoint -q /run; then
-    rm -rf /run
-    mkdir -m 0755 -p /run
-    mount -t tmpfs -o "mode=0755,size=@runSize@" tmpfs /run
-fi
-
 # Create a ramfs on /run/keys to hold secrets that shouldn't be
 # written to disk (generally used for NixOps, harmless elsewhere).
 if ! mountpoint -q /run/keys; then
@@ -150,13 +141,6 @@ if [ -n "@useHostResolvConf@" -a -e /etc/resolv.conf ]; then
     cat /etc/resolv.conf | resolvconf -m 1000 -a host
 fi
 
-
-# Create /var/setuid-wrappers as a tmpfs.
-rm -rf /var/setuid-wrappers
-mkdir -m 0755 -p /var/setuid-wrappers
-mount -t tmpfs -o "mode=0755" tmpfs /var/setuid-wrappers
-
-
 # Log the script output to /dev/kmsg or /run/log/stage-2-init.log.
 # Only at this point are all the necessary prerequisites ready for these commands.
 exec {logOutFd}>&1 {logErrFd}>&2
diff --git a/nixos/modules/system/boot/stage-2.nix b/nixos/modules/system/boot/stage-2.nix
index b67f42a017e6..7e4ec2a4a670 100644
--- a/nixos/modules/system/boot/stage-2.nix
+++ b/nixos/modules/system/boot/stage-2.nix
@@ -20,10 +20,9 @@ let
     src = ./stage-2-init.sh;
     shellDebug = "${pkgs.bashInteractive}/bin/bash";
     isExecutable = true;
-    inherit (config.boot) devShmSize runSize;
     inherit (config.nix) readOnlyStore;
     inherit (config.networking) useHostResolvConf;
-    ttyGid = config.ids.gids.tty;
+    inherit (config.system.build) earlyMountScript;
     path =
       [ pkgs.coreutils
         pkgs.utillinux
diff --git a/nixos/modules/system/boot/systemd-unit-options.nix b/nixos/modules/system/boot/systemd-unit-options.nix
index f2a22e4ada8a..f4892244de47 100644
--- a/nixos/modules/system/boot/systemd-unit-options.nix
+++ b/nixos/modules/system/boot/systemd-unit-options.nix
@@ -309,7 +309,7 @@ in rec {
     };
 
     startAt = mkOption {
-      type = types.str;
+      type = with types; either str (listOf str);
       default = "";
       example = "Sun 14:00:00";
       description = ''
diff --git a/nixos/modules/tasks/cpu-freq.nix b/nixos/modules/tasks/cpu-freq.nix
index 2fe7f4f8197a..5f8b5df52acf 100644
--- a/nixos/modules/tasks/cpu-freq.nix
+++ b/nixos/modules/tasks/cpu-freq.nix
@@ -19,7 +19,7 @@ in
       description = ''
         Configure the governor used to regulate the frequence of the
         available CPUs. By default, the kernel configures the
-        on-demand governor.
+        performance governor.
       '';
     };
 
diff --git a/nixos/modules/tasks/filesystems.nix b/nixos/modules/tasks/filesystems.nix
index f146448200f9..9ab1baeacb98 100644
--- a/nixos/modules/tasks/filesystems.nix
+++ b/nixos/modules/tasks/filesystems.nix
@@ -18,7 +18,9 @@ let
 
   prioOption = prio: optionalString (prio != null) " pri=${toString prio}";
 
-  fileSystemOpts = { name, config, ... }: {
+  specialFSTypes = [ "proc" "sysfs" "tmpfs" "devtmpfs" "devpts" ];
+
+  coreFileSystemOpts = { name, config, ... }: {
 
     options = {
 
@@ -35,13 +37,6 @@ let
         description = "Location of the device.";
       };
 
-      label = mkOption {
-        default = null;
-        example = "root-partition";
-        type = types.nullOr types.str;
-        description = "Label of the device (if any).";
-      };
-
       fsType = mkOption {
         default = "auto";
         example = "ext3";
@@ -53,12 +48,28 @@ let
         default = [ "defaults" ];
         example = [ "data=journal" ];
         description = "Options used to mount the file system.";
-      } // (if versionAtLeast lib.nixpkgsVersion "16.09" then {
         type = types.listOf types.str;
-      } else {
-        type = types.either types.commas (types.listOf types.str);
-        apply = x: if isList x then x else lib.strings.splitString "," (builtins.trace "warning: passing a comma-separated string for filesystem options is deprecated; use a list of strings instead. This will become a hard error in 16.09." x);
-      });
+      };
+
+    };
+
+    config = {
+      mountPoint = mkDefault name;
+      device = mkIf (elem config.fsType specialFSTypes) (mkDefault config.fsType);
+    };
+
+  };
+
+  fileSystemOpts = { config, ... }: {
+
+    options = {
+
+      label = mkOption {
+        default = null;
+        example = "root-partition";
+        type = types.nullOr types.str;
+        description = "Label of the device (if any).";
+      };
 
       autoFormat = mkOption {
         default = false;
@@ -100,8 +111,6 @@ let
     };
 
     config = {
-      mountPoint = mkDefault name;
-      device = mkIf (config.fsType == "tmpfs") (mkDefault config.fsType);
       options = mkIf config.autoResize [ "x-nixos.autoresize" ];
 
       # -F needed to allow bare block device without partitions
@@ -110,6 +119,13 @@ let
 
   };
 
+  # Makes sequence of `specialMount device mountPoint options fsType` commands.
+  # `systemMount` should be defined in the sourcing script.
+  makeSpecialMounts = mounts:
+    pkgs.writeText "mounts.sh" (concatMapStringsSep "\n" (mount: ''
+      specialMount "${mount.device}" "${mount.mountPoint}" "${concatStringsSep "," mount.options}" "${mount.fsType}"
+    '') mounts);
+
 in
 
 {
@@ -131,8 +147,7 @@ in
           "/bigdisk".label = "bigdisk";
         }
       '';
-      type = types.loaOf types.optionSet;
-      options = [ fileSystemOpts ];
+      type = types.loaOf (types.submodule [coreFileSystemOpts fileSystemOpts]);
       description = ''
         The file systems to be mounted.  It must include an entry for
         the root directory (<literal>mountPoint = "/"</literal>).  Each
@@ -164,6 +179,15 @@ in
       description = "Names of supported filesystem types.";
     };
 
+    boot.specialFileSystems = mkOption {
+      default = {};
+      type = types.loaOf (types.submodule coreFileSystemOpts);
+      internal = true;
+      description = ''
+        Special filesystems that are mounted very early during boot.
+      '';
+    };
+
   };
 
 
@@ -181,6 +205,7 @@ in
 
     # Export for use in other modules
     system.build.fileSystems = fileSystems;
+    system.build.earlyMountScript = makeSpecialMounts (toposort fsBefore (attrValues config.boot.specialFileSystems)).result;
 
     boot.supportedFilesystems = map (fs: fs.fsType) fileSystems;
 
@@ -258,6 +283,16 @@ in
 
       in listToAttrs (map formatDevice (filter (fs: fs.autoFormat) fileSystems));
 
+    # 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}" ]; };
+      "/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}" ]; };
+    };
+
   };
 
 }
diff --git a/nixos/modules/virtualisation/amazon-grow-partition.nix b/nixos/modules/virtualisation/amazon-grow-partition.nix
deleted file mode 100644
index 69b80d900bad..000000000000
--- a/nixos/modules/virtualisation/amazon-grow-partition.nix
+++ /dev/null
@@ -1,24 +0,0 @@
-# This module automatically grows the root partition on Amazon EC2 HVM
-# instances. This allows an instance to be created with a bigger root
-# filesystem than provided by the AMI.
-
-{ config, lib, pkgs, ... }:
-
-{
-  config = lib.mkIf config.ec2.hvm {
-    boot.initrd.extraUtilsCommands = ''
-      copy_bin_and_libs ${pkgs.gawk}/bin/gawk
-      copy_bin_and_libs ${pkgs.gnused}/bin/sed
-      copy_bin_and_libs ${pkgs.utillinux}/sbin/sfdisk
-      cp -v ${pkgs.cloud-utils}/bin/growpart $out/bin/growpart
-      ln -s sed $out/bin/gnused
-    '';
-
-    boot.initrd.postDeviceCommands = ''
-      if [ -e /dev/xvda ] && [ -e /dev/xvda1 ]; then
-        TMPDIR=/run sh $(type -P growpart) /dev/xvda 1
-        udevadm settle
-      fi
-    '';
-  };
-}
diff --git a/nixos/modules/virtualisation/amazon-image.nix b/nixos/modules/virtualisation/amazon-image.nix
index ebf398fa266f..f9c3f2e53adc 100644
--- a/nixos/modules/virtualisation/amazon-image.nix
+++ b/nixos/modules/virtualisation/amazon-image.nix
@@ -11,10 +11,12 @@ with lib;
 let cfg = config.ec2; in
 
 {
-  imports = [ ../profiles/headless.nix ./ec2-data.nix ./amazon-grow-partition.nix ./amazon-init.nix ];
+  imports = [ ../profiles/headless.nix ./ec2-data.nix ./grow-partition.nix ./amazon-init.nix ];
 
   config = {
 
+    virtualisation.growPartition = cfg.hvm;
+
     fileSystems."/" = {
       device = "/dev/disk/by-label/nixos";
       autoResize = true;
diff --git a/nixos/modules/virtualisation/brightbox-image.nix b/nixos/modules/virtualisation/brightbox-image.nix
index 760a7100c6e0..ab49d8871f85 100644
--- a/nixos/modules/virtualisation/brightbox-image.nix
+++ b/nixos/modules/virtualisation/brightbox-image.nix
@@ -20,7 +20,7 @@ in
 
           postVM =
             ''
-              PATH=$PATH:${stdenv.lib.makeBinPath [ pkgs.gnutar pkgs.gzip ]}
+              PATH=$PATH:${lib.makeBinPath [ pkgs.gnutar pkgs.gzip ]}
               pushd $out
               ${pkgs.qemu_kvm}/bin/qemu-img convert -c -O qcow2 $diskImageBase nixos.qcow2
               rm $diskImageBase
diff --git a/nixos/modules/virtualisation/containers.nix b/nixos/modules/virtualisation/containers.nix
index d83841452f95..413aa94339f1 100644
--- a/nixos/modules/virtualisation/containers.nix
+++ b/nixos/modules/virtualisation/containers.nix
@@ -340,6 +340,20 @@ in
                 A specification of the desired configuration of this
                 container, as a NixOS module.
               '';
+              type = lib.mkOptionType {
+                name = "Toplevel NixOS config";
+                merge = loc: defs: (import ../../lib/eval-config.nix {
+                  inherit system;
+                  modules =
+                    let extraConfig =
+                      { boot.isContainer = true;
+                        networking.hostName = mkDefault name;
+                        networking.useDHCP = false;
+                      };
+                    in [ extraConfig ] ++ (map (x: x.value) defs);
+                  prefix = [ "containers" name ];
+                }).config;
+              };
             };
 
             path = mkOption {
@@ -410,18 +424,9 @@ in
           } // networkOptions;
 
           config = mkMerge
-            [ (mkIf options.config.isDefined {
-                path = (import ../../lib/eval-config.nix {
-                  inherit system;
-                  modules =
-                    let extraConfig =
-                      { boot.isContainer = true;
-                        networking.hostName = mkDefault name;
-                        networking.useDHCP = false;
-                      };
-                    in [ extraConfig config.config ];
-                  prefix = [ "containers" name ];
-                }).config.system.build.toplevel;
+            [
+              (mkIf options.config.isDefined {
+                path = config.config.system.build.toplevel;
               })
             ];
         }));
diff --git a/nixos/modules/virtualisation/grow-partition.nix b/nixos/modules/virtualisation/grow-partition.nix
new file mode 100644
index 000000000000..abc2e766959e
--- /dev/null
+++ b/nixos/modules/virtualisation/grow-partition.nix
@@ -0,0 +1,43 @@
+# This module automatically grows the root partition on virtual machines.
+# This allows an instance to be created with a bigger root filesystem
+# than provided by the machine image.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+
+  options = {
+
+    virtualisation.growPartition = mkOption {
+      type = types.bool;
+      default = true;
+    };
+
+  };
+
+  config = mkIf config.virtualisation.growPartition {
+
+    boot.initrd.extraUtilsCommands = ''
+      copy_bin_and_libs ${pkgs.gawk}/bin/gawk
+      copy_bin_and_libs ${pkgs.gnused}/bin/sed
+      copy_bin_and_libs ${pkgs.utillinux}/sbin/sfdisk
+      copy_bin_and_libs ${pkgs.utillinux}/sbin/lsblk
+      cp -v ${pkgs.cloud-utils}/bin/growpart $out/bin/growpart
+      ln -s sed $out/bin/gnused
+    '';
+
+    boot.initrd.postDeviceCommands = ''
+      rootDevice="${config.fileSystems."/".device}"
+      if [ -e "$rootDevice" ]; then
+        rootDevice="$(readlink -f "$rootDevice")"
+        parentDevice="$(lsblk -npo PKNAME "$rootDevice")"
+        TMPDIR=/run sh $(type -P growpart) "$parentDevice" "''${rootDevice#$parentDevice}"
+        udevadm settle
+      fi
+    '';
+
+  };
+
+}
diff --git a/nixos/modules/virtualisation/virtualbox-host.nix b/nixos/modules/virtualisation/virtualbox-host.nix
index 5fb472ebfc32..ce4abecd6762 100644
--- a/nixos/modules/virtualisation/virtualbox-host.nix
+++ b/nixos/modules/virtualisation/virtualbox-host.nix
@@ -5,7 +5,7 @@ with lib;
 let
   cfg = config.virtualisation.virtualbox.host;
   virtualbox = config.boot.kernelPackages.virtualbox.override {
-    inherit (cfg) enableHardening;
+    inherit (cfg) enableHardening headless;
   };
 
 in
@@ -47,6 +47,15 @@ in
         </para></important>
       '';
     };
+
+    headless = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Use VirtualBox installation without GUI and Qt dependency. Useful to enable on servers
+        and when virtual machines are controlled only via SSH.
+      '';
+    };
   };
 
   config = mkIf cfg.enable (mkMerge [{
diff --git a/nixos/modules/virtualisation/virtualbox-image.nix b/nixos/modules/virtualisation/virtualbox-image.nix
index 3a598a1c7dc5..b6a5b3e4788d 100644
--- a/nixos/modules/virtualisation/virtualbox-image.nix
+++ b/nixos/modules/virtualisation/virtualbox-image.nix
@@ -8,6 +8,8 @@ let
 
 in {
 
+  imports = [ ./grow-partition.nix ];
+
   options = {
     virtualbox = {
       baseImageSize = mkOption {
@@ -64,7 +66,10 @@ in {
         '';
     };
 
-    fileSystems."/".device = "/dev/disk/by-label/nixos";
+    fileSystems."/" = {
+      device = "/dev/disk/by-label/nixos";
+      autoResize = true;
+    };
 
     boot.loader.grub.device = "/dev/sda";