about summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
Diffstat (limited to 'nixos')
-rw-r--r--nixos/modules/config/users-groups.nix2
-rw-r--r--nixos/modules/misc/ids.nix2
-rw-r--r--nixos/modules/module-list.nix9
-rw-r--r--nixos/modules/rename.nix20
-rw-r--r--nixos/modules/security/grsecurity.nix376
-rw-r--r--nixos/modules/services/continuous-integration/buildkite-agent.nix100
-rw-r--r--nixos/modules/services/databases/openldap.nix11
-rw-r--r--nixos/modules/services/mail/postfix.nix32
-rw-r--r--nixos/modules/services/misc/confd.nix2
-rw-r--r--nixos/modules/services/misc/disnix.nix108
-rw-r--r--nixos/modules/services/misc/dysnomia.nix217
-rw-r--r--nixos/modules/services/misc/etcd.nix2
-rw-r--r--nixos/modules/services/misc/parsoid.nix2
-rw-r--r--nixos/modules/services/monitoring/bosun.nix2
-rw-r--r--nixos/modules/services/monitoring/grafana.nix2
-rw-r--r--nixos/modules/services/monitoring/scollector.nix2
-rw-r--r--nixos/modules/services/networking/consul.nix8
-rw-r--r--nixos/modules/services/networking/dnscrypt-proxy.nix4
-rw-r--r--nixos/modules/services/networking/pptpd.nix124
-rw-r--r--nixos/modules/services/networking/skydns.nix2
-rw-r--r--nixos/modules/services/networking/wpa_supplicant.nix2
-rw-r--r--nixos/modules/services/security/hologram.nix2
-rw-r--r--nixos/modules/services/security/oauth2_proxy.nix523
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/default.nix8
-rw-r--r--nixos/modules/services/x11/desktop-managers/gnome3.nix4
-rw-r--r--nixos/modules/services/x11/desktop-managers/kde5.nix2
-rw-r--r--nixos/modules/services/x11/window-managers/i3.nix62
-rw-r--r--nixos/modules/services/x11/xbanish.nix30
-rw-r--r--nixos/modules/virtualisation/docker.nix2
-rw-r--r--nixos/tests/grsecurity.nix40
30 files changed, 1259 insertions, 443 deletions
diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix
index e643b2d059b5..8231907d7999 100644
--- a/nixos/modules/config/users-groups.nix
+++ b/nixos/modules/config/users-groups.nix
@@ -468,7 +468,6 @@ in {
         home = "/root";
         shell = mkDefault cfg.defaultUserShell;
         group = "root";
-        extraGroups = [ "grsecurity" ];
         initialHashedPassword = mkDefault config.security.initialRootPassword;
       };
       nobody = {
@@ -497,7 +496,6 @@ in {
       nixbld.gid = ids.gids.nixbld;
       utmp.gid = ids.gids.utmp;
       adm.gid = ids.gids.adm;
-      grsecurity.gid = ids.gids.grsecurity;
       input.gid = ids.gids.input;
     };
 
diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix
index d421167c859c..61c49f07abbb 100644
--- a/nixos/modules/misc/ids.nix
+++ b/nixos/modules/misc/ids.nix
@@ -147,7 +147,6 @@
       foundationdb = 118;
       newrelic = 119;
       starbound = 120;
-      #grsecurity = 121; # unused
       hydra = 122;
       spiped = 123;
       teamspeak = 124;
@@ -396,7 +395,6 @@
       foundationdb = 118;
       newrelic = 119;
       starbound = 120;
-      grsecurity = 121;
       hydra = 122;
       spiped = 123;
       teamspeak = 124;
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 509634cf34c1..51c43b8c7c3b 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -125,10 +125,11 @@
   ./services/computing/torque/server.nix
   ./services/computing/torque/mom.nix
   ./services/computing/slurm/slurm.nix
+  ./services/continuous-integration/buildkite-agent.nix
+  ./services/continuous-integration/hydra/default.nix
   ./services/continuous-integration/jenkins/default.nix
-  ./services/continuous-integration/jenkins/slave.nix
   ./services/continuous-integration/jenkins/job-builder.nix
-  ./services/continuous-integration/hydra/default.nix
+  ./services/continuous-integration/jenkins/slave.nix
   ./services/databases/4store-endpoint.nix
   ./services/databases/4store.nix
   ./services/databases/couchdb.nix
@@ -218,6 +219,7 @@
   ./services/misc/confd.nix
   ./services/misc/devmon.nix
   ./services/misc/dictd.nix
+  ./services/misc/dysnomia.nix
   ./services/misc/disnix.nix
   ./services/misc/docker-registry.nix
   ./services/misc/emby.nix
@@ -369,6 +371,7 @@
   ./services/networking/ostinato.nix
   ./services/networking/pdnsd.nix
   ./services/networking/polipo.nix
+  ./services/networking/pptpd.nix
   ./services/networking/prayer.nix
   ./services/networking/privoxy.nix
   ./services/networking/prosody.nix
@@ -431,6 +434,7 @@
   ./services/security/haveged.nix
   ./services/security/hologram.nix
   ./services/security/munge.nix
+  ./services/security/oauth2_proxy.nix
   ./services/security/physlock.nix
   ./services/security/torify.nix
   ./services/security/tor.nix
@@ -490,6 +494,7 @@
   ./services/x11/window-managers/windowlab.nix
   ./services/x11/window-managers/wmii.nix
   ./services/x11/window-managers/xmonad.nix
+  ./services/x11/xbanish.nix
   ./services/x11/xfs.nix
   ./services/x11/xserver.nix
   ./system/activation/activation-script.nix
diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix
index 3440261c3965..634f91a275d3 100644
--- a/nixos/modules/rename.nix
+++ b/nixos/modules/rename.nix
@@ -114,6 +114,26 @@ with lib;
     (mkRenamedOptionModule [ "services" "iodined" "extraConfig" ] [ "services" "iodine" "server" "extraConfig" ])
     (mkRemovedOptionModule [ "services" "iodined" "client" ])
 
+    # Grsecurity
+    (mkRemovedOptionModule [ "security" "grsecurity" "kernelPatch" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "mode" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "priority" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "system" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "virtualisationConfig" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "hardwareVirtualisation" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "virtualisationSoftware" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "sysctl" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "denyChrootChmod" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "denyChrootCaps" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "denyUSB" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "restrictProc" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "restrictProcWithGroup" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "unrestrictProcGid" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "disableRBAC" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "disableSimultConnect" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "verboseVersion" ])
+    (mkRemovedOptionModule [ "security" "grsecurity" "config" "kernelExtraConfig" ])
+
     # Options that are obsolete and have no replacement.
     (mkRemovedOptionModule [ "boot" "initrd" "luks" "enable" ])
     (mkRemovedOptionModule [ "programs" "bash" "enable" ])
diff --git a/nixos/modules/security/grsecurity.nix b/nixos/modules/security/grsecurity.nix
index 3f24118ea1cb..9d0249820d5d 100644
--- a/nixos/modules/security/grsecurity.nix
+++ b/nixos/modules/security/grsecurity.nix
@@ -1,312 +1,122 @@
-{ config, lib, pkgs, ... }:
+{ config, pkgs, lib, ... }:
 
 with lib;
 
 let
   cfg = config.security.grsecurity;
-
-  customGrsecPkg =
-    (import ../../../pkgs/build-support/grsecurity {
-      grsecOptions = cfg;
-      inherit pkgs lib;
-    }).grsecPackage;
+  grsecLockPath = "/proc/sys/kernel/grsecurity/grsec_lock";
+
+  # Ascertain whether ZFS is required for booting the system; grsecurity is
+  # currently incompatible with ZFS, rendering the system unbootable.
+  zfsNeededForBoot = filter
+    (fs: (fs.neededForBoot
+          || elem fs.mountPoint [ "/" "/nix" "/nix/store" "/var" "/var/log" "/var/lib" "/etc" ])
+          && fs.fsType == "zfs")
+    (attrValues config.fileSystems) != [];
 in
-{
-  options = {
-    security.grsecurity = {
-      enable = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Enable grsecurity support. This enables advanced exploit
-          hardening for the Linux kernel, and adds support for
-          administrative Role-Based Acess Control (RBAC) via
-          <literal>gradm</literal>. It also includes traditional
-          utilities for PaX.
-        '';
-      };
-
-      kernelPatch = mkOption {
-        type = types.attrs;
-        example = lib.literalExample "pkgs.kernelPatches.grsecurity_4_1";
-        description = ''
-          Grsecurity patch to use.
-        '';
-      };
-
-      config = {
-        mode = mkOption {
-          type = types.enum [ "auto" "custom" ];
-          default = "auto";
-          description = ''
-            grsecurity configuration mode. This specifies whether
-            grsecurity is auto-configured or otherwise completely
-            manually configured.
-          '';
-        };
-
-        priority = mkOption {
-          type = types.enum [ "security" "performance" ];
-          default = "security";
-          description = ''
-            grsecurity configuration priority. This specifies whether
-            the kernel configuration should emphasize speed or
-            security.
-          '';
-        };
-
-        system = mkOption {
-          type = types.enum [ "desktop" "server" ];
-          default = "desktop";
-          description = ''
-            grsecurity system configuration.
-          '';
-        };
 
-        virtualisationConfig = mkOption {
-          type = types.nullOr (types.enum [ "host" "guest" ]);
-          default = null;
-          description = ''
-            grsecurity virtualisation configuration. This specifies
-            the virtualisation role of the machine - that is, whether
-            it will be a virtual machine guest, a virtual machine
-            host, or neither.
-          '';
-        };
-
-        hardwareVirtualisation = mkOption {
-          type = types.nullOr types.bool;
-          default = null;
-          example = true;
-          description = ''
-            grsecurity hardware virtualisation configuration. Set to
-            <literal>true</literal> if your machine supports hardware
-            accelerated virtualisation.
-          '';
-        };
-
-        virtualisationSoftware = mkOption {
-          type = types.nullOr (types.enum [ "kvm" "xen" "vmware" "virtualbox" ]);
-          default = null;
-          description = ''
-            Configure grsecurity for use with this virtualisation software.
-          '';
-        };
-
-        sysctl = mkOption {
-          type = types.bool;
-          default = false;
-          description = ''
-            If true, then set <literal>GRKERN_SYSCTL y</literal>. If
-            enabled then grsecurity can be controlled using sysctl
-            (and turned off). You are advised to *never* enable this,
-            but if you do, make sure to always set the sysctl
-            <literal>kernel.grsecurity.grsec_lock</literal> to
-            non-zero as soon as all sysctl options are set. *THIS IS
-            EXTREMELY IMPORTANT*!
-          '';
-        };
-
-        denyChrootChmod = mkOption {
-          type = types.bool;
-          default = false;
-          description = ''
-            If true, then set <literal>GRKERN_CHROOT_CHMOD
-            y</literal>. If enabled, this denies processes inside a
-            chroot from setting the suid or sgid bits using
-            <literal>chmod</literal> or <literal>fchmod</literal>.
-
-            By default this protection is disabled - it makes it
-            impossible to use Nix to build software on your system,
-            which is what most users want.
+{
+  options.security.grsecurity = {
+
+    enable = mkEnableOption "Grsecurity/PaX";
+
+    lockTunables = mkOption {
+      type = types.bool;
+      example = false;
+      default = true;
+      description = ''
+        Whether to automatically lock grsecurity tunables
+        (<option>boot.kernel.sysctl."kernel.grsecurity.*"</option>).  Disable
+        this to allow configuration of grsecurity features while the system is
+        running.  The lock can be manually engaged by activating the
+        <literal>grsec-lock</literal> service unit.
+      '';
+    };
 
-            If you are using NixOps to deploy your software to a
-            remote machine, you're encouraged to enable this as you
-            won't need to compile code.
-          '';
-        };
+  };
 
-        denyChrootCaps = mkOption {
-          type = types.bool;
-          default = false;
-          description = ''
-            Whether to lower capabilities of all processes within a chroot,
-            preventing commands that require <literal>CAP_SYS_ADMIN</literal>.
+  config = mkIf cfg.enable {
 
-            This protection is disabled by default because it breaks
-            <literal>nixos-rebuild</literal>. Whenever possible, it is
-            highly recommended to enable this protection.
-          '';
-        };
+    # Allow the user to select a different package set, subject to the stated
+    # required kernel config
+    boot.kernelPackages = mkDefault pkgs.linuxPackages_grsec_nixos;
 
-        denyUSB = mkOption {
-          type = types.bool;
-          default = false;
-          description = ''
-            If true, then set <literal>GRKERNSEC_DENYUSB y</literal>.
+    system.requiredKernelConfig = with config.lib.kernelConfig;
+      [ (isEnabled "GRKERNSEC")
+        (isEnabled "PAX")
+        (isYES "GRKERNSEC_SYSCTL")
+        (isYES "GRKERNSEC_SYSCTL_DISTRO")
+      ];
 
-            This enables a sysctl with name
-            <literal>kernel.grsecurity.deny_new_usb</literal>. Setting
-            its value to <literal>1</literal> will prevent any new USB
-            devices from being recognized by the OS.  Any attempted
-            USB device insertion will be logged.
+    # Crashing on an overflow in kernel land is user unfriendly and may prevent
+    # the system from booting, which is too severe for our use case.
+    boot.kernelParams = [ "pax_size_overflow_report_only" ];
 
-            This option is intended to be used against custom USB
-            devices designed to exploit vulnerabilities in various USB
-            device drivers.
-          '';
-        };
+    # Install PaX related utillities into the system profile.  Eventually, we
+    # also want to include gradm here.
+    environment.systemPackages = with pkgs; [ paxctl pax-utils ];
 
-        restrictProc = mkOption {
-          type = types.bool;
-          default = false;
-          description = ''
-            If true, then set <literal>GRKERN_PROC_USER
-            y</literal>. This restricts non-root users to only viewing
-            their own processes and restricts network-related
-            information, kernel symbols, and module information.
-          '';
-        };
+    # Install rules for the grsec device node
+    services.udev.packages = [ pkgs.gradm ];
 
-        restrictProcWithGroup = mkOption {
-          type = types.bool;
-          default = true;
-          description = ''
-            If true, then set <literal>GRKERN_PROC_USERGROUP
-            y</literal>. This is similar to
-            <literal>restrictProc</literal> except it allows a special
-            group (specified by <literal>unrestrictProcGid</literal>)
-            to still access otherwise classified information in
-            <literal>/proc</literal>.
-          '';
-        };
+    # This service unit is responsible for locking the Grsecurity tunables.  The
+    # unit is always defined, but only activated on bootup if lockTunables is
+    # toggled.  When lockTunables is toggled, failure to activate the unit will
+    # enter emergency mode.  The intent is to make it difficult to silently
+    # enter multi-user mode without having locked the tunables.  Some effort is
+    # made to ensure that starting the unit is an idempotent operation.
+    systemd.services.grsec-lock = {
+      description = "Lock grsecurity tunables";
 
-        unrestrictProcGid = mkOption {
-          type = types.int;
-          default = config.ids.gids.grsecurity;
-          description = ''
-            If set, specifies a GID which is exempt from
-            <literal>/proc</literal> restrictions (set by
-            <literal>GRKERN_PROC_USERGROUP</literal>). By default,
-            this is set to the GID for <literal>grsecurity</literal>,
-            a predefined NixOS group, which the
-            <literal>root</literal> account is a member of. You may
-            conveniently add other users to this group if you need
-            access to <literal>/proc</literal>
-          '';
-        };
+      wantedBy = optional cfg.lockTunables "multi-user.target";
 
-        disableRBAC = mkOption {
-          type = types.bool;
-          default = false;
-          description = ''
-            If true, then set <literal>GRKERN_NO_RBAC
-            y</literal>. This disables the
-            <literal>/dev/grsec</literal> device, which in turn
-            disables the RBAC system (and <literal>gradm</literal>).
-          '';
-        };
+      wants = [ "local-fs.target" "systemd-sysctl.service" ];
+      after = [ "local-fs.target" "systemd-sysctl.service" ];
+      conflicts = [ "shutdown.target" ];
 
-        disableSimultConnect = mkOption {
-          type = types.bool;
-          default = false;
-          description = ''
-            Disable TCP simultaneous connect.  The TCP simultaneous connect
-            feature allows two clients to connect without either of them
-            entering the listening state.  This feature of the TCP specification
-            is claimed to enable an attacker to deny the target access to a given
-            server by guessing the source port the target would use to make the
-            connection.
+      restartIfChanged = false;
 
-            This option is OFF by default because TCP simultaneous connect has
-            some legitimate uses.  Enable this option if you know what this TCP
-            feature is for and know that you do not need it.
-          '';
-        };
+      script = ''
+        if ${pkgs.gnugrep}/bin/grep -Fq 0 ${grsecLockPath} ; then
+          echo -n 1 > ${grsecLockPath}
+        fi
+      '';
 
-        verboseVersion = mkOption {
-          type = types.bool;
-          default = false;
-          description = "Use verbose version in kernel localversion.";
-        };
+      unitConfig = {
+        ConditionPathIsReadWrite = grsecLockPath;
+        DefaultDependencies = false;
+      } // optionalAttrs cfg.lockTunables {
+        OnFailure = "emergency.target";
+      };
 
-        kernelExtraConfig = mkOption {
-          type = types.str;
-          default = "";
-          description = "Extra kernel configuration parameters.";
-        };
+      serviceConfig = {
+        Type = "oneshot";
+        RemainAfterExit = true;
       };
     };
-  };
 
-  config = mkIf cfg.enable {
-    assertions =
-      [
-        { assertion = (cfg.config.restrictProc -> !cfg.config.restrictProcWithGroup) ||
-                      (cfg.config.restrictProcWithGroup -> !cfg.config.restrictProc);
-          message   = "You cannot enable both restrictProc and restrictProcWithGroup";
-        }
-        { assertion = config.boot.kernelPackages.kernel.features ? grsecurity
-                   && config.boot.kernelPackages.kernel.features.grsecurity;
-          message = "grsecurity enabled, but kernel doesn't have grsec support";
-        }
-        { assertion = (cfg.config.mode == "auto" && (cfg.config.virtualisationConfig != null)) ->
-              cfg.config.hardwareVirtualisation != null;
-          message   = "when using auto grsec mode with virtualisation, you must specify if your hardware has virtualisation extensions";
-        }
-        { assertion = (cfg.config.mode == "auto" && (cfg.config.virtualisationConfig != null)) ->
-              cfg.config.virtualisationSoftware != null;
-         message   = "grsecurity configured for virtualisation but no virtualisation software specified";
-        }
-      ];
-
-    security.grsecurity.kernelPatch = lib.mkDefault pkgs.kernelPatches.grsecurity_latest;
-
-    systemd.services.grsec-lock = mkIf cfg.config.sysctl {
-      description     = "grsecurity sysctl-lock Service";
-      wants           = [ "systemd-sysctl.service" ];
-      after           = [ "systemd-sysctl.service" ];
-      wantedBy        = [ "multi-user.target" ];
-      serviceConfig.Type = "oneshot";
-      serviceConfig.RemainAfterExit = "yes";
-      unitConfig.ConditionPathIsReadWrite = "/proc/sys/kernel/grsecurity/grsec_lock";
-      script = ''
-        locked=`cat /proc/sys/kernel/grsecurity/grsec_lock`
-        if [ "$locked" == "0" ]; then
-            echo 1 > /proc/sys/kernel/grsecurity/grsec_lock
-            echo grsecurity sysctl lock - enabled
-        else
-            echo grsecurity sysctl lock already enabled - doing nothing
-        fi
-      '';
+    # Configure system tunables
+    boot.kernel.sysctl = {
+      # Removed under grsecurity
+      "kernel.kptr_restrict" = mkForce null;
+    } // optionalAttrs config.nix.useSandbox {
+      # chroot(2) restrictions that conflict with sandboxed Nix builds
+      "kernel.grsecurity.chroot_caps" = mkForce 0;
+      "kernel.grsecurity.chroot_deny_chroot" = mkForce 0;
+      "kernel.grsecurity.chroot_deny_mount" = mkForce 0;
+      "kernel.grsecurity.chroot_deny_pivot" = mkForce 0;
+    } // optionalAttrs config.boot.enableContainers {
+      # chroot(2) restrictions that conflict with NixOS lightweight containers
+      "kernel.grsecurity.chroot_deny_chmod" = mkForce 0;
+      "kernel.grsecurity.chroot_deny_mount" = mkForce 0;
+      "kernel.grsecurity.chroot_restrict_nice" = mkForce 0;
     };
 
-#   systemd.services.grsec-learn = {
-#     description     = "grsecurity learning Service";
-#     wantedBy        = [ "local-fs.target" ];
-#     serviceConfig   = {
-#       Type = "oneshot";
-#       RemainAfterExit = "yes";
-#       ExecStart = "${pkgs.gradm}/sbin/gradm -VFL /etc/grsec/learning.logs";
-#       ExecStop  = "${pkgs.gradm}/sbin/gradm -D";
-#     };
-#   };
-
-    system.activationScripts = lib.optionalAttrs (!cfg.config.disableRBAC) { grsec = ''
-      mkdir -p /etc/grsec
-      if [ ! -f /etc/grsec/learn_config ]; then
-        cp ${pkgs.gradm}/etc/grsec/learn_config /etc/grsec
-      fi
-      if [ ! -f /etc/grsec/policy ]; then
-        cp ${pkgs.gradm}/etc/grsec/policy /etc/grsec
-      fi
-      chmod -R 0600 /etc/grsec
-    ''; };
+    assertions = [
+      { assertion = !zfsNeededForBoot;
+        message = "grsecurity is currently incompatible with ZFS";
+      }
+    ];
 
-    # Enable AppArmor, gradm udev rules, and utilities
-    security.apparmor.enable   = true;
-    boot.kernelPackages        = customGrsecPkg;
-    services.udev.packages     = lib.optional (!cfg.config.disableRBAC) pkgs.gradm;
-    environment.systemPackages = [ pkgs.paxctl pkgs.pax-utils ] ++ lib.optional (!cfg.config.disableRBAC) pkgs.gradm;
   };
 }
diff --git a/nixos/modules/services/continuous-integration/buildkite-agent.nix b/nixos/modules/services/continuous-integration/buildkite-agent.nix
new file mode 100644
index 000000000000..b1449882b04f
--- /dev/null
+++ b/nixos/modules/services/continuous-integration/buildkite-agent.nix
@@ -0,0 +1,100 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.buildkite-agent;
+  configFile = pkgs.writeText "buildkite-agent.cfg"
+    ''
+      token="${cfg.token}"
+      name="${cfg.name}"
+      meta-data="${cfg.meta-data}"
+      hooks-path="${pkgs.buildkite-agent}/share/hooks"
+      build-path="/var/lib/buildkite-agent/builds"
+      bootstrap-script="${pkgs.buildkite-agent}/share/bootstrap.sh"
+    '';
+in
+
+{
+  options = {
+    services.buildkite-agent = {
+      enable = mkEnableOption "buildkite-agent";
+
+      token = mkOption {
+        type = types.str;
+        description = ''
+          The token from your Buildkite "Agents" page.
+        '';
+      };
+
+      name = mkOption {
+        type = types.str;
+        description = ''
+          The name of the agent.
+        '';
+      };
+
+      meta-data = mkOption {
+        type = types.str;
+        default = "";
+        description = ''
+          Meta data for the agent.
+        '';
+      };
+
+      openssh =
+        { privateKey = mkOption {
+            type = types.str;
+            description = ''
+              Private agent key.
+            '';
+          };
+          publicKey = mkOption {
+            type = types.str;
+            description = ''
+              Public agent key.
+            '';
+          };
+        };
+    };
+  };
+
+  config = mkIf config.services.buildkite-agent.enable {
+    users.extraUsers.buildkite-agent =
+      { name = "buildkite-agent";
+        home = "/var/lib/buildkite-agent";
+        createHome = true;
+        description = "Buildkite agent user";
+      };
+
+    environment.systemPackages = [ pkgs.buildkite-agent ];
+
+    systemd.services.buildkite-agent =
+      { description = "Buildkite Agent";
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ];
+        environment.HOME = "/var/lib/buildkite-agent";
+        preStart = ''
+            ${pkgs.coreutils}/bin/mkdir -m 0700 -p /var/lib/buildkite-agent/.ssh
+
+            if ! [ -f /var/lib/buildkite-agent/.ssh/id_rsa ]; then
+              echo "${cfg.openssh.privateKey}" > /var/lib/buildkite-agent/.ssh/id_rsa
+              ${pkgs.coreutils}/bin/chmod 600 /var/lib/buildkite-agent/.ssh/id_rsa
+            fi
+
+            if ! [ -f /var/lib/buildkite-agent/.ssh/id_rsa.pub ]; then
+              echo "${cfg.openssh.publicKey}" > /var/lib/buildkite-agent/.ssh/id_rsa.pub
+              ${pkgs.coreutils}/bin/chmod 600 /var/lib/buildkite-agent/.ssh/id_rsa.pub
+            fi
+          '';
+
+        serviceConfig =
+          { ExecStart = "${pkgs.buildkite-agent}/bin/buildkite-agent start --config ${configFile}";
+            User = "buildkite-agent";
+            RestartSec = 5;
+            Restart = "on-failure";
+            TimeoutSec = 10;
+          };
+      };
+  };
+}
diff --git a/nixos/modules/services/databases/openldap.nix b/nixos/modules/services/databases/openldap.nix
index cbdc676d47bd..9f22aa7c92b2 100644
--- a/nixos/modules/services/databases/openldap.nix
+++ b/nixos/modules/services/databases/openldap.nix
@@ -40,6 +40,13 @@ in
         description = "Group account under which slapd runs.";
       };
 
+      urlList = mkOption {
+        type = types.listOf types.string;
+        default = [ "ldap:///" ];
+        description = "URL list slapd should listen on.";
+        example = [ "ldaps:///" ];
+      };
+
       dataDir = mkOption {
         type = types.string;
         default = "/var/db/openldap";
@@ -50,7 +57,7 @@ in
         type = types.lines;
         default = "";
         description = "
-          sldapd.conf configuration
+          slapd.conf configuration
         ";
         example = literalExample ''
             '''
@@ -89,7 +96,7 @@ in
         mkdir -p ${cfg.dataDir}
         chown -R ${cfg.user}:${cfg.group} ${cfg.dataDir}
       '';
-      serviceConfig.ExecStart = "${openldap.out}/libexec/slapd -u ${cfg.user} -g ${cfg.group} -d 0 -f ${configFile}";
+      serviceConfig.ExecStart = "${openldap.out}/libexec/slapd -u ${cfg.user} -g ${cfg.group} -d 0 -h \"${concatStringsSep " " cfg.urlList}\" -f ${configFile}";
     };
 
     users.extraUsers.openldap =
diff --git a/nixos/modules/services/mail/postfix.nix b/nixos/modules/services/mail/postfix.nix
index bad9d527f9a1..cdde41446224 100644
--- a/nixos/modules/services/mail/postfix.nix
+++ b/nixos/modules/services/mail/postfix.nix
@@ -127,11 +127,11 @@ let
     #               (yes)   (yes)   (no)    (never) (100)
     # ==========================================================================
     smtp      inet  n       -       n       -       -       smtpd
-    #submission inet n       -       n       -       -       smtpd
-    #  -o smtpd_tls_security_level=encrypt
-    #  -o smtpd_sasl_auth_enable=yes
-    #  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
-    #  -o milter_macro_daemon_name=ORIGINATING
+  '' + optionalString cfg.enableSubmission ''
+    submission inet n       -       n       -       -       smtpd
+      ${concatStringsSep "\n  " (mapAttrsToList (x: y: "-o " + x + "=" + y) cfg.submissionOptions)}
+  ''
+  + ''
     pickup    unix  n       -       n       60      1       pickup
     cleanup   unix  n       -       n       -       0       cleanup
     qmgr      unix  n       -       n       300     1       qmgr
@@ -201,6 +201,28 @@ in
         default = true;
         description = "Whether to enable smtp in master.cf.";
       };
+      
+      enableSubmission = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to enable smtp submission";
+      };
+
+      submissionOptions = mkOption {
+        type = types.attrs;
+        default = { "smtpd_tls_security_level" = "encrypt";
+                    "smtpd_sasl_auth_enable" = "yes";
+                    "smtpd_client_restrictions" = "permit_sasl_authenticated,reject";
+                    "milter_macro_daemon_name" = "ORIGINATING";
+                  };
+        description = "Options for the submission config in master.cf";
+        example = { "smtpd_tls_security_level" = "encrypt";
+                    "smtpd_sasl_auth_enable" = "yes";
+                    "smtpd_sasl_type" = "dovecot";
+                    "smtpd_client_restrictions" = "permit_sasl_authenticated,reject";
+                    "milter_macro_daemon_name" = "ORIGINATING";
+                  };
+      };
 
       setSendmail = mkOption {
         type = types.bool;
diff --git a/nixos/modules/services/misc/confd.nix b/nixos/modules/services/misc/confd.nix
index c0fbf06e6c4c..72ec68dee6b3 100644
--- a/nixos/modules/services/misc/confd.nix
+++ b/nixos/modules/services/misc/confd.nix
@@ -75,7 +75,7 @@ in {
       wantedBy = [ "multi-user.target" ];
       after = [ "network.target" ];
       serviceConfig = {
-        ExecStart = "${cfg.package}/bin/confd";
+        ExecStart = "${cfg.package.bin}/bin/confd";
       };
     };
 
diff --git a/nixos/modules/services/misc/disnix.nix b/nixos/modules/services/misc/disnix.nix
index 218802e0cf00..e5a125ad3245 100644
--- a/nixos/modules/services/misc/disnix.nix
+++ b/nixos/modules/services/misc/disnix.nix
@@ -36,49 +36,32 @@ in
         default = false;
         description = "Whether to enable the DisnixWebService interface running on Apache Tomcat";
       };
-
-      publishInfrastructure = {
-        enable = mkOption {
-          default = false;
-          description = "Whether to publish capabilities/properties of this machine in as attributes in the infrastructure option";
-        };
-
-        enableAuthentication = mkOption {
-          default = false;
-          description = "Whether to publish authentication credentials through the infrastructure attribute (not recommended in combination with Avahi)";
-        };
-      };
-
-      infrastructure = mkOption {
-        default = {};
-        description = "List of name value pairs containing properties for the infrastructure model";
-      };
-
-      publishAvahi = mkOption {
-        default = false;
-        description = "Whether to publish capabilities/properties as a Disnix service through Avahi";
+      
+      package = mkOption {
+        type = types.path;
+        description = "The Disnix package";
+        default = pkgs.disnix;
       };
 
     };
 
   };
 
-
   ###### implementation
 
   config = mkIf cfg.enable {
-    environment.systemPackages = [ pkgs.disnix pkgs.dysnomia ] ++ optional cfg.useWebServiceInterface pkgs.DisnixWebService;
+    dysnomia.enable = true;
+    
+    environment.systemPackages = [ pkgs.disnix ] ++ optional cfg.useWebServiceInterface pkgs.DisnixWebService;
 
     services.dbus.enable = true;
     services.dbus.packages = [ pkgs.disnix ];
 
-    services.avahi.enable = cfg.publishAvahi;
-
     services.tomcat.enable = cfg.useWebServiceInterface;
     services.tomcat.extraGroups = [ "disnix" ];
     services.tomcat.javaOpts = "${optionalString cfg.useWebServiceInterface "-Djava.library.path=${pkgs.libmatthew_java}/lib/jni"} ";
     services.tomcat.sharedLibs = optional cfg.useWebServiceInterface "${pkgs.DisnixWebService}/share/java/DisnixConnection.jar"
-                                 ++ optional cfg.useWebServiceInterface "${pkgs.dbus_java}/share/java/dbus.jar";
+      ++ optional cfg.useWebServiceInterface "${pkgs.dbus_java}/share/java/dbus.jar";
     services.tomcat.webapps = optional cfg.useWebServiceInterface pkgs.DisnixWebService;
 
     users.extraGroups = singleton
@@ -86,38 +69,6 @@ in
         gid = config.ids.gids.disnix;
       };
 
-    services.disnix.infrastructure =
-      optionalAttrs (cfg.publishInfrastructure.enable)
-      ( { hostname = config.networking.hostName;
-          #targetHost = config.deployment.targetHost;
-          system = if config.nixpkgs.system == "" then builtins.currentSystem else config.nixpkgs.system;
-
-          supportedTypes = (import "${pkgs.stdenv.mkDerivation {
-            name = "supportedtypes";
-            buildCommand = ''
-              ( echo -n "[ "
-                cd ${dysnomia}/libexec/dysnomia
-                for i in *
-                do
-                    echo -n "\"$i\" "
-                done
-                echo -n " ]") > $out
-            '';
-          }}");
-        }
-        #// optionalAttrs (cfg.useWebServiceInterface) { targetEPR = "http://${config.deployment.targetHost}:8080/DisnixWebService/services/DisnixWebService"; }
-        // optionalAttrs (config.services.httpd.enable) { documentRoot = config.services.httpd.documentRoot; }
-        // optionalAttrs (config.services.mysql.enable) { mysqlPort = config.services.mysql.port; }
-        // optionalAttrs (config.services.tomcat.enable) { tomcatPort = 8080; }
-        // optionalAttrs (config.services.svnserve.enable) { svnBaseDir = config.services.svnserve.svnBaseDir; }
-        // optionalAttrs (config.services.ejabberd.enable) { ejabberdUser = config.services.ejabberd.user; }
-        // optionalAttrs (cfg.publishInfrastructure.enableAuthentication) (
-          optionalAttrs (config.services.mysql.enable) { mysqlUsername = "root"; mysqlPassword = readFile config.services.mysql.rootPassword; })
-        )
-    ;
-
-    services.disnix.publishInfrastructure.enable = cfg.publishAvahi;
-
     systemd.services = {
       disnix = {
         description = "Disnix server";
@@ -133,46 +84,17 @@ in
 
         restartIfChanged = false;
 
-        path = [ pkgs.nix pkgs.disnix dysnomia "/run/current-system/sw" ];
+        path = [ config.nix.package cfg.package config.dysnomia.package "/run/current-system/sw" ];
 
         environment = {
           HOME = "/root";
-        };
-
-        preStart = ''
-          mkdir -p /etc/systemd-mutable/system
-          if [ ! -f /etc/systemd-mutable/system/dysnomia.target ]
-          then
-              ( echo "[Unit]"
-                echo "Description=Services that are activated and deactivated by Dysnomia"
-                echo "After=final.target"
-              ) > /etc/systemd-mutable/system/dysnomia.target
-          fi
-        '';
-
-        script = "disnix-service";
+        }
+        // (if config.environment.variables ? DYSNOMIA_CONTAINERS_PATH then { inherit (config.environment.variables) DYSNOMIA_CONTAINERS_PATH; } else {})
+        // (if config.environment.variables ? DYSNOMIA_MODULES_PATH then { inherit (config.environment.variables) DYSNOMIA_MODULES_PATH; } else {});
+        
+        serviceConfig.ExecStart = "${cfg.package}/bin/disnix-service";
       };
-    } // optionalAttrs cfg.publishAvahi {
-      disnixAvahi = {
-        description = "Disnix Avahi publisher";
-        wants = [ "avahi-daemon.service" ];
-        wantedBy = [ "multi-user.target" ];
 
-        script = ''
-          ${pkgs.avahi}/bin/avahi-publish-service disnix-${config.networking.hostName} _disnix._tcp 22 \
-            "mem=$(grep 'MemTotal:' /proc/meminfo | sed -e 's/kB//' -e 's/MemTotal://' -e 's/ //g')" \
-            ${concatMapStrings (infrastructureAttrName:
-              let infrastructureAttrValue = getAttr infrastructureAttrName (cfg.infrastructure);
-              in
-              if isInt infrastructureAttrValue then
-              ''${infrastructureAttrName}=${toString infrastructureAttrValue} \
-              ''
-              else
-              ''${infrastructureAttrName}=\"${infrastructureAttrValue}\" \
-              ''
-              ) (attrNames (cfg.infrastructure))}
-        '';
-      };
     };
   };
 }
diff --git a/nixos/modules/services/misc/dysnomia.nix b/nixos/modules/services/misc/dysnomia.nix
new file mode 100644
index 000000000000..df44d0a54866
--- /dev/null
+++ b/nixos/modules/services/misc/dysnomia.nix
@@ -0,0 +1,217 @@
+{pkgs, lib, config, ...}:
+
+with lib;
+
+let
+  cfg = config.dysnomia;
+  
+  printProperties = properties:
+    concatMapStrings (propertyName:
+      let
+        property = properties."${propertyName}";
+      in
+      if isList property then "${propertyName}=(${lib.concatMapStrings (elem: "\"${toString elem}\" ") (properties."${propertyName}")})\n"
+      else "${propertyName}=\"${toString property}\"\n"
+    ) (builtins.attrNames properties);
+  
+  properties = pkgs.stdenv.mkDerivation {
+    name = "dysnomia-properties";
+    buildCommand = ''
+      cat > $out << "EOF"
+      ${printProperties cfg.properties}
+      EOF
+    '';
+  };
+  
+  containersDir = pkgs.stdenv.mkDerivation {
+    name = "dysnomia-containers";
+    buildCommand = ''
+      mkdir -p $out
+      cd $out
+      
+      ${concatMapStrings (containerName:
+        let
+          containerProperties = cfg.containers."${containerName}";
+        in
+        ''
+          cat > ${containerName} <<EOF
+          ${printProperties containerProperties}
+          type=${containerName}
+          EOF
+        ''
+      ) (builtins.attrNames cfg.containers)}
+    '';
+  };
+  
+  linkMutableComponents = {containerName}:
+    ''
+      mkdir ${containerName}
+      
+      ${concatMapStrings (componentName:
+        let
+          component = cfg.components."${containerName}"."${componentName}";
+        in
+        "ln -s ${component} ${containerName}/${componentName}\n"
+      ) (builtins.attrNames (cfg.components."${containerName}" or {}))}
+    '';
+  
+  componentsDir = pkgs.stdenv.mkDerivation {
+    name = "dysnomia-components";
+    buildCommand = ''
+      mkdir -p $out
+      cd $out
+      
+      ${concatMapStrings (containerName:
+        let
+          components = cfg.components."${containerName}";
+        in
+        linkMutableComponents { inherit containerName; }
+      ) (builtins.attrNames cfg.components)}
+    '';
+  };
+in
+{
+  options = {
+    dysnomia = {
+      
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to enable Dysnomia";
+      };
+      
+      enableAuthentication = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to publish privacy-sensitive authentication credentials";
+      };
+      
+      package = mkOption {
+        type = types.path;
+        description = "The Dysnomia package";
+      };
+      
+      properties = mkOption {
+        description = "An attribute set in which each attribute represents a machine property. Optionally, these values can be shell substitutions.";
+        default = {};
+      };
+      
+      containers = mkOption {
+        description = "An attribute set in which each key represents a container and each value an attribute set providing its configuration properties";
+        default = {};
+      };
+      
+      components = mkOption {
+        description = "An atttribute set in which each key represents a container and each value an attribute set in which each key represents a component and each value a derivation constructing its initial state";
+        default = {};
+      };
+      
+      extraContainerProperties = mkOption {
+        description = "An attribute set providing additional container settings in addition to the default properties";
+        default = {};
+      };
+      
+      extraContainerPaths = mkOption {
+        description = "A list of paths containing additional container configurations that are added to the search folders";
+        default = [];
+      };
+      
+      extraModulePaths = mkOption {
+        description = "A list of paths containing additional modules that are added to the search folders";
+        default = [];
+      };
+    };
+  };
+  
+  config = mkIf cfg.enable {
+  
+    environment.etc = {
+      "dysnomia/containers" = {
+        source = containersDir;
+      };
+      "dysnomia/components" = {
+        source = componentsDir;
+      };
+      "dysnomia/properties" = {
+        source = properties;
+      };
+    };
+    
+    environment.variables = {
+      DYSNOMIA_STATEDIR = "/var/state/dysnomia-nixos";
+      DYSNOMIA_CONTAINERS_PATH = "${lib.concatMapStrings (containerPath: "${containerPath}:") cfg.extraContainerPaths}/etc/dysnomia/containers";
+      DYSNOMIA_MODULES_PATH = "${lib.concatMapStrings (modulePath: "${modulePath}:") cfg.extraModulePaths}/etc/dysnomia/modules";
+    };
+    
+    environment.systemPackages = [ cfg.package ];
+    
+    dysnomia.package = pkgs.dysnomia.override (origArgs: {
+      enableApacheWebApplication = config.services.httpd.enable;
+      enableAxis2WebService = config.services.tomcat.axis2.enable;
+      enableEjabberdDump = config.services.ejabberd.enable;
+      enableMySQLDatabase = config.services.mysql.enable;
+      enablePostgreSQLDatabase = config.services.postgresql.enable;
+      enableSubversionRepository = config.services.svnserve.enable;
+      enableTomcatWebApplication = config.services.tomcat.enable;
+      enableMongoDatabase = config.services.mongodb.enable;
+    });
+    
+    dysnomia.properties = {
+      hostname = config.networking.hostName;
+      system = if config.nixpkgs.system == "" then builtins.currentSystem else config.nixpkgs.system;
+
+      supportedTypes = (import "${pkgs.stdenv.mkDerivation {
+        name = "supportedtypes";
+        buildCommand = ''
+          ( echo -n "[ "
+            cd ${cfg.package}/libexec/dysnomia
+            for i in *
+            do
+                echo -n "\"$i\" "
+            done
+            echo -n " ]") > $out
+        '';
+      }}");
+    };
+    
+    dysnomia.containers = lib.recursiveUpdate ({
+      process = {};
+      wrapper = {};
+    }
+    // lib.optionalAttrs (config.services.httpd.enable) { apache-webapplication = {
+      documentRoot = config.services.httpd.documentRoot;
+    }; }
+    // lib.optionalAttrs (config.services.tomcat.axis2.enable) { axis2-webservice = {}; }
+    // lib.optionalAttrs (config.services.ejabberd.enable) { ejabberd-dump = {
+      ejabberdUser = config.services.ejabberd.user;
+    }; }
+    // lib.optionalAttrs (config.services.mysql.enable) { mysql-database = {
+        mysqlPort = config.services.mysql.port;
+      } // lib.optionalAttrs cfg.enableAuthentication {
+        mysqlUsername = "root";
+        mysqlPassword = builtins.readFile (config.services.mysql.rootPassword);
+      };
+    }
+    // lib.optionalAttrs (config.services.postgresql.enable && cfg.enableAuthentication) { postgresql-database = {
+      postgresqlUsername = "root";
+    }; }
+    // lib.optionalAttrs (config.services.tomcat.enable) { tomcat-webapplication = {
+      tomcatPort = 8080;
+    }; }
+    // lib.optionalAttrs (config.services.mongodb.enable) { mongo-database = {}; }
+    // lib.optionalAttrs (config.services.svnserve.enable) { subversion-repository = {
+      svnBaseDir = config.services.svnserve.svnBaseDir;
+    }; }) cfg.extraContainerProperties;
+
+    system.activationScripts.dysnomia = ''
+      mkdir -p /etc/systemd-mutable/system
+      if [ ! -f /etc/systemd-mutable/system/dysnomia.target ]
+      then
+          ( echo "[Unit]"
+            echo "Description=Services that are activated and deactivated by Dysnomia"
+            echo "After=final.target"
+          ) > /etc/systemd-mutable/system/dysnomia.target
+      fi
+    '';
+  };
+}
diff --git a/nixos/modules/services/misc/etcd.nix b/nixos/modules/services/misc/etcd.nix
index bc8064e3c879..0d6ed8eb9043 100644
--- a/nixos/modules/services/misc/etcd.nix
+++ b/nixos/modules/services/misc/etcd.nix
@@ -115,7 +115,7 @@ in {
 
       serviceConfig = {
         Type = "notify";
-        ExecStart = "${pkgs.etcd}/bin/etcd";
+        ExecStart = "${pkgs.etcd.bin}/bin/etcd";
         User = "etcd";
         PermissionsStartOnly = true;
       };
diff --git a/nixos/modules/services/misc/parsoid.nix b/nixos/modules/services/misc/parsoid.nix
index ea97d6e30e83..0844190a5490 100644
--- a/nixos/modules/services/misc/parsoid.nix
+++ b/nixos/modules/services/misc/parsoid.nix
@@ -91,7 +91,7 @@ in
       wantedBy = [ "multi-user.target" ];
       after = [ "network.target" ];
       serviceConfig = {
-        ExecStart = "${pkgs.nodePackages_0_10.parsoid}/lib/node_modules/parsoid/api/server.js -c ${confFile} -n ${toString cfg.workers}";
+        ExecStart = "${pkgs.nodePackages.parsoid}/lib/node_modules/parsoid/api/server.js -c ${confFile} -n ${toString cfg.workers}";
       };
     };
 
diff --git a/nixos/modules/services/monitoring/bosun.nix b/nixos/modules/services/monitoring/bosun.nix
index 51d38e8db4de..9a1e790d3ab6 100644
--- a/nixos/modules/services/monitoring/bosun.nix
+++ b/nixos/modules/services/monitoring/bosun.nix
@@ -148,7 +148,7 @@ in {
         User = cfg.user;
         Group = cfg.group;
         ExecStart = ''
-          ${cfg.package}/bin/bosun -c ${configFile}
+          ${cfg.package.bin}/bin/bosun -c ${configFile}
         '';
       };
     };
diff --git a/nixos/modules/services/monitoring/grafana.nix b/nixos/modules/services/monitoring/grafana.nix
index defbd9289dcd..0b7f3ce0a29b 100644
--- a/nixos/modules/services/monitoring/grafana.nix
+++ b/nixos/modules/services/monitoring/grafana.nix
@@ -228,7 +228,7 @@ in {
       after = ["networking.target"];
       environment = mapAttrs' (n: v: nameValuePair "GF_${n}" (toString v)) envOptions;
       serviceConfig = {
-        ExecStart = "${cfg.package}/bin/grafana-server -homepath ${cfg.dataDir}";
+        ExecStart = "${cfg.package.bin}/bin/grafana-server -homepath ${cfg.dataDir}";
         WorkingDirectory = cfg.dataDir;
         User = "grafana";
       };
diff --git a/nixos/modules/services/monitoring/scollector.nix b/nixos/modules/services/monitoring/scollector.nix
index 1e397435e600..2684482c6184 100644
--- a/nixos/modules/services/monitoring/scollector.nix
+++ b/nixos/modules/services/monitoring/scollector.nix
@@ -119,7 +119,7 @@ in {
         PermissionsStartOnly = true;
         User = cfg.user;
         Group = cfg.group;
-        ExecStart = "${cfg.package}/bin/scollector -conf=${conf} ${lib.concatStringsSep " " cfg.extraOpts}";
+        ExecStart = "${cfg.package.bin}/bin/scollector -conf=${conf} ${lib.concatStringsSep " " cfg.extraOpts}";
       };
     };
 
diff --git a/nixos/modules/services/networking/consul.nix b/nixos/modules/services/networking/consul.nix
index 2aa101f980da..166ee7732375 100644
--- a/nixos/modules/services/networking/consul.nix
+++ b/nixos/modules/services/networking/consul.nix
@@ -178,14 +178,14 @@ in
             (filterAttrs (n: _: hasPrefix "consul.d/" n) config.environment.etc);
 
         serviceConfig = {
-          ExecStart = "@${cfg.package}/bin/consul consul agent -config-dir /etc/consul.d"
+          ExecStart = "@${cfg.package.bin}/bin/consul consul agent -config-dir /etc/consul.d"
             + concatMapStrings (n: " -config-file ${n}") configFiles;
-          ExecReload = "${cfg.package}/bin/consul reload";
+          ExecReload = "${cfg.package.bin}/bin/consul reload";
           PermissionsStartOnly = true;
           User = if cfg.dropPrivileges then "consul" else null;
           TimeoutStartSec = "0";
         } // (optionalAttrs (cfg.leaveOnStop) {
-          ExecStop = "${cfg.package}/bin/consul leave";
+          ExecStop = "${cfg.package.bin}/bin/consul leave";
         });
 
         path = with pkgs; [ iproute gnugrep gawk consul ];
@@ -236,7 +236,7 @@ in
 
         serviceConfig = {
           ExecStart = ''
-            ${cfg.alerts.package}/bin/consul-alerts start \
+            ${cfg.alerts.package.bin}/bin/consul-alerts start \
               --alert-addr=${cfg.alerts.listenAddr} \
               --consul-addr=${cfg.alerts.consulAddr} \
               ${optionalString cfg.alerts.watchChecks "--watch-checks"} \
diff --git a/nixos/modules/services/networking/dnscrypt-proxy.nix b/nixos/modules/services/networking/dnscrypt-proxy.nix
index 2a6161ee873a..227e38acc4a6 100644
--- a/nixos/modules/services/networking/dnscrypt-proxy.nix
+++ b/nixos/modules/services/networking/dnscrypt-proxy.nix
@@ -89,8 +89,8 @@ in
         '';
         example = literalExample "${pkgs.dnscrypt-proxy}/share/dnscrypt-proxy/dnscrypt-resolvers.csv";
         default = pkgs.fetchurl {
-          url = "https://raw.githubusercontent.com/jedisct1/dnscrypt-proxy/master/dnscrypt-resolvers.csv";
-          sha256 = "0lac20qhcgjxxiiz8jzcn3hkqj4ywl58hahp5n2i6vf9akfyqp7c";
+          url = https://raw.githubusercontent.com/jedisct1/dnscrypt-proxy/master/dnscrypt-resolvers.csv;
+          sha256 = "171zvdqcqqvcw3zr7wl9h1wmdmk6m3h55xr4gq2z1j7a0x0ba2in";
         };
         defaultText = "pkgs.fetchurl { url = ...; sha256 = ...; }";
       };
diff --git a/nixos/modules/services/networking/pptpd.nix b/nixos/modules/services/networking/pptpd.nix
new file mode 100644
index 000000000000..efed604d3dda
--- /dev/null
+++ b/nixos/modules/services/networking/pptpd.nix
@@ -0,0 +1,124 @@
+{ config, stdenv, pkgs, lib, ... }:
+
+with lib;
+
+{
+  options = {
+    services.pptpd = {
+      enable = mkEnableOption "Whether pptpd should be run on startup.";
+
+      serverIp = mkOption {
+        type        = types.string;
+        description = "The server-side IP address.";
+        default     = "10.124.124.1";
+      };
+
+      clientIpRange = mkOption {
+        type        = types.string;
+        description = "The range from which client IPs are drawn.";
+        default     = "10.124.142.2-11";
+      };
+
+      maxClients = mkOption {
+        type        = types.int;
+        description = "The maximum number of simultaneous connections.";
+        default     = 10;
+      };
+
+      extraPptpdOptions = mkOption {
+        type        = types.lines;
+        description = "Adds extra lines to the pptpd configuration file.";
+        default     = "";
+      };
+
+      extraPppdOptions = mkOption {
+        type        = types.lines;
+        description = "Adds extra lines to the pppd options file.";
+        default     = "";
+        example     = ''
+          ms-dns 8.8.8.8
+          ms-dns 8.8.4.4
+        '';
+      };
+    };
+  };
+
+  config = mkIf config.services.pptpd.enable {
+    systemd.services.pptpd = let
+      cfg = config.services.pptpd;
+
+      pptpd-conf = pkgs.writeText "pptpd.conf" ''
+        # Inspired from pptpd-1.4.0/samples/pptpd.conf
+        ppp ${ppp-pptpd-wrapped}/bin/pppd
+        option ${pppd-options}
+        pidfile /run/pptpd.pid
+        localip ${cfg.serverIp}
+        remoteip ${cfg.clientIpRange}
+        connections ${toString cfg.maxClients} # (Will get harmless warning if inconsistent with IP range)
+
+        # Extra
+        ${cfg.extraPptpdOptions}
+      '';
+
+      pppd-options = pkgs.writeText "ppp-options-pptpd.conf" ''
+        # From: cat pptpd-1.4.0/samples/options.pptpd | grep -v ^# | grep -v ^$
+        name pptpd
+        refuse-pap
+        refuse-chap
+        refuse-mschap
+        require-mschap-v2
+        require-mppe-128
+        proxyarp
+        lock
+        nobsdcomp
+        novj
+        novjccomp
+        nologfd
+
+        # Extra:
+        ${cfg.extraPppdOptions}
+      '';
+
+      ppp-pptpd-wrapped = pkgs.stdenv.mkDerivation {
+        name         = "ppp-pptpd-wrapped";
+        phases       = [ "installPhase" ];
+        buildInputs  = with pkgs; [ makeWrapper ];
+        installPhase = ''
+          mkdir -p $out/bin
+          makeWrapper ${pkgs.ppp}/bin/pppd $out/bin/pppd \
+            --set LD_PRELOAD    "${pkgs.libredirect}/lib/libredirect.so" \
+            --set NIX_REDIRECTS "/etc/ppp=/etc/ppp-pptpd"
+        '';
+      };
+    in {
+      description = "pptpd server";
+
+      requires = [ "network-online.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      preStart = ''
+        mkdir -p -m 700 /etc/ppp-pptpd
+
+        secrets="/etc/ppp-pptpd/chap-secrets"
+
+        [ -f "$secrets" ] || cat > "$secrets" << EOF
+        # From: pptpd-1.4.0/samples/chap-secrets
+        # Secrets for authentication using CHAP
+        # client	server	secret		IP addresses
+        #username	pptpd	password	*
+        EOF
+
+        chown root.root "$secrets"
+        chmod 600 "$secrets"
+      '';
+
+      serviceConfig = {
+        ExecStart = "${pkgs.pptpd}/bin/pptpd --conf ${pptpd-conf}";
+        KillMode  = "process";
+        Restart   = "on-success";
+        Type      = "forking";
+        PIDFile   = "/run/pptpd.pid";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/skydns.nix b/nixos/modules/services/networking/skydns.nix
index 39ebaa45a794..ba913482e3cb 100644
--- a/nixos/modules/services/networking/skydns.nix
+++ b/nixos/modules/services/networking/skydns.nix
@@ -83,7 +83,7 @@ in {
         SKYDNS_NAMESERVERS = concatStringsSep "," cfg.nameservers;
       };
       serviceConfig = {
-        ExecStart = "${cfg.package}/bin/skydns";
+        ExecStart = "${cfg.package.bin}/bin/skydns";
       };
     };
 
diff --git a/nixos/modules/services/networking/wpa_supplicant.nix b/nixos/modules/services/networking/wpa_supplicant.nix
index 53648aef1e04..8d22c10d3f78 100644
--- a/nixos/modules/services/networking/wpa_supplicant.nix
+++ b/nixos/modules/services/networking/wpa_supplicant.nix
@@ -129,7 +129,7 @@ in {
       in {
         description = "WPA Supplicant";
 
-        after = [ "network-interfaces.target" ];
+        after = [ "network-interfaces.target" ] ++ lib.concatMap deviceUnit ifaces;
         requires = lib.concatMap deviceUnit ifaces;
         wantedBy = [ "network.target" ];
 
diff --git a/nixos/modules/services/security/hologram.nix b/nixos/modules/services/security/hologram.nix
index 58f308df7a21..e267fed27955 100644
--- a/nixos/modules/services/security/hologram.nix
+++ b/nixos/modules/services/security/hologram.nix
@@ -95,7 +95,7 @@ in {
       wantedBy    = [ "multi-user.target" ];
 
       serviceConfig = {
-        ExecStart = "${pkgs.hologram}/bin/hologram-server --debug --conf ${cfgFile}";
+        ExecStart = "${pkgs.hologram.bin}/bin/hologram-server --debug --conf ${cfgFile}";
       };
     };
   };
diff --git a/nixos/modules/services/security/oauth2_proxy.nix b/nixos/modules/services/security/oauth2_proxy.nix
new file mode 100644
index 000000000000..4c20392214fc
--- /dev/null
+++ b/nixos/modules/services/security/oauth2_proxy.nix
@@ -0,0 +1,523 @@
+# NixOS module for oauth2_proxy.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.services.oauth2_proxy;
+
+  # Use like:
+  #   repeatedArgs (arg: "--arg=${arg}") args
+  repeatedArgs = concatMapStringsSep " ";
+
+  # 'toString' doesn't quite do what we want for bools.
+  fromBool = x: if x then "true" else "false";
+
+  # oauth2_proxy provides many options that are only relevant if you are using
+  # a certain provider. This set maps from provider name to a function that
+  # takes the configuration and returns a string that can be inserted into the
+  # command-line to launch oauth2_proxy.
+  providerSpecificOptions = {
+    azure = cfg: ''
+      --azure-tenant=${cfg.azure.tenant} \
+      --resource=${cfg.azure.resource} \
+    '';
+
+    github = cfg: ''
+      $(optionalString (!isNull cfg.github.org) "--github-org=${cfg.github.org}") \
+      $(optionalString (!isNull cfg.github.team) "--github-org=${cfg.github.team}") \
+    '';
+
+    google = cfg: ''
+      --google-admin-email=${cfg.google.adminEmail} \
+      --google-service-account=${cfg.google.serviceAccountJSON} \
+      $(repeatedArgs (group: "--google-group=${group}") cfg.google.groups) \
+    '';
+  };
+
+  authenticatedEmailsFile = pkgs.writeText "authenticated-emails" cfg.email.addresses;
+
+  getProviderOptions = cfg: provider:
+    if providerSpecificOptions ? provider then providerSpecificOptions.provider cfg else "";
+
+  mkCommandLine = cfg: ''
+    --provider='${cfg.provider}' \
+    ${optionalString (!isNull cfg.email.addresses) "--authenticated-emails-file='${authenticatedEmailsFile}'"} \
+    --approval-prompt='${cfg.approvalPrompt}' \
+    ${optionalString (cfg.passBasicAuth && !isNull cfg.basicAuthPassword) "--basic-auth-password='${cfg.basicAuthPassword}'"} \
+    --client-id='${cfg.clientID}' \
+    --client-secret='${cfg.clientSecret}' \
+    ${optionalString (!isNull cfg.cookie.domain) "--cookie-domain='${cfg.cookie.domain}'"} \
+    --cookie-expire='${cfg.cookie.expire}' \
+    --cookie-httponly=${fromBool cfg.cookie.httpOnly} \
+    --cookie-name='${cfg.cookie.name}' \
+    --cookie-secret='${cfg.cookie.secret}' \
+    --cookie-secure=${fromBool cfg.cookie.secure} \
+    ${optionalString (!isNull cfg.cookie.refresh) "--cookie-refresh='${cfg.cookie.refresh}'"} \
+    ${optionalString (!isNull cfg.customTemplatesDir) "--custom-templates-dir='${cfg.customTemplatesDir}'"} \
+    ${repeatedArgs (x: "--email-domain='${x}'") cfg.email.domains} \
+    --http-address='${cfg.httpAddress}' \
+    ${optionalString (!isNull cfg.htpasswd.file) "--htpasswd-file='${cfg.htpasswd.file}' --display-htpasswd-form=${fromBool cfg.htpasswd.displayForm}"} \
+    ${optionalString (!isNull cfg.loginURL) "--login-url='${cfg.loginURL}'"} \
+    --pass-access-token=${fromBool cfg.passAccessToken} \
+    --pass-basic-auth=${fromBool cfg.passBasicAuth} \
+    --pass-host-header=${fromBool cfg.passHostHeader} \
+    --proxy-prefix='${cfg.proxyPrefix}' \
+    ${optionalString (!isNull cfg.profileURL) "--profile-url='${cfg.profileURL}'"} \
+    ${optionalString (!isNull cfg.redeemURL) "--redeem-url='${cfg.redeemURL}'"} \
+    ${optionalString (!isNull cfg.redirectURL) "--redirect-url='${cfg.redirectURL}'"} \
+    --request-logging=${fromBool cfg.requestLogging} \
+    ${optionalString (!isNull cfg.scope) "--scope='${cfg.scope}'"} \
+    ${repeatedArgs (x: "--skip-auth-regex='${x}'") cfg.skipAuthRegexes} \
+    ${optionalString (!isNull cfg.signatureKey) "--signature-key='${cfg.signatureKey}'"} \
+    --upstream='${cfg.upstream}' \
+    ${optionalString (!isNull cfg.validateURL) "--validate-url='${cfg.validateURL}'"} \
+    ${optionalString cfg.tls.enable "--tls-cert='${cfg.tls.certificate}' --tls-key='${cfg.tls.key}' --https-address='${cfg.tls.httpsAddress}'"} \
+  '' + getProviderOptions cfg cfg.provider;
+in
+{
+  options.services.oauth2_proxy = {
+    enable = mkEnableOption "oauth2_proxy";
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.oauth2_proxy;
+      defaultText = "pkgs.oauth2_proxy";
+      description = ''
+        The package that provides oauth2_proxy.
+      '';
+    };
+
+    ##############################################
+    # PROVIDER configuration
+    provider = mkOption {
+      type = types.enum [
+        "google"
+        "github"
+        "azure"
+        "gitlab"
+        "linkedin"
+        "myusa"
+      ];
+      default = "google";
+      description = ''
+        OAuth provider.
+      '';
+    };
+
+    approvalPrompt = mkOption {
+      type = types.enum ["force" "auto"];
+      default = "force";
+      description = ''
+        OAuth approval_prompt.
+      '';
+    };
+
+    clientID = mkOption {
+      type = types.str;
+      description = ''
+        The OAuth Client ID.
+      '';
+      example = "123456.apps.googleusercontent.com";
+    };
+
+    clientSecret = mkOption {
+      type = types.str;
+      description = ''
+        The OAuth Client Secret.
+      '';
+    };
+
+    skipAuthRegexes = mkOption {
+     type = types.listOf types.str;
+     default = [];
+     description = ''
+       Skip authentication for requests matching any of these regular
+       expressions.
+     '';
+    };
+
+    # XXX: Not clear whether these two options are mutually exclusive or not.
+    email = {
+      domains = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          Authenticate emails with the specified domains. Use
+          <literal>*</literal> to authenticate any email.
+        '';
+      };
+
+      addresses = mkOption {
+        type = types.nullOr types.lines;
+        default = null;
+        description = ''
+          Line-separated email addresses that are allowed to authenticate.
+        '';
+      };
+    };
+
+    loginURL = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        Authentication endpoint.
+
+        You only need to set this if you are using a self-hosted provider (e.g.
+        Github Enterprise). If you're using a publicly hosted provider
+        (e.g github.com), then the default works.
+      '';
+      example = "https://provider.example.com/oauth/authorize";
+    };
+
+    redeemURL = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        Token redemption endpoint.
+
+        You only need to set this if you are using a self-hosted provider (e.g.
+        Github Enterprise). If you're using a publicly hosted provider
+        (e.g github.com), then the default works.
+      '';
+      example = "https://provider.example.com/oauth/token";
+    };
+
+    validateURL = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        Access token validation endpoint.
+
+        You only need to set this if you are using a self-hosted provider (e.g.
+        Github Enterprise). If you're using a publicly hosted provider
+        (e.g github.com), then the default works.
+      '';
+      example = "https://provider.example.com/user/emails";
+    };
+
+    redirectURL = mkOption {
+      # XXX: jml suspects this is always necessary, but the command-line
+      # doesn't require it so making it optional.
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        The OAuth2 redirect URL.
+      '';
+      example = "https://internalapp.yourcompany.com/oauth2/callback";
+    };
+
+    azure = {
+      tenant = mkOption {
+        type = types.str;
+        default = "common";
+        description = ''
+          Go to a tenant-specific or common (tenant-independent) endpoint.
+        '';
+      };
+
+      resource = mkOption {
+        type = types.str;
+        description = ''
+          The resource that is protected.
+        '';
+      };
+    };
+
+    google = {
+      adminEmail = mkOption {
+        type = types.str;
+        description = ''
+          The Google Admin to impersonate for API calls.
+
+          Only users with access to the Admin APIs can access the Admin SDK
+          Directory API, thus the service account needs to impersonate one of
+          those users to access the Admin SDK Directory API.
+
+          See <link xlink:href="https://developers.google.com/admin-sdk/directory/v1/guides/delegation#delegate_domain-wide_authority_to_your_service_account" />.
+        '';
+      };
+
+      groups = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          Restrict logins to members of these Google groups.
+        '';
+      };
+
+      serviceAccountJSON = mkOption {
+        type = types.path;
+        description = ''
+          The path to the service account JSON credentials.
+        '';
+      };
+    };
+
+    github = {
+      org = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          Restrict logins to members of this organisation.
+        '';
+      };
+
+      team = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          Restrict logins to members of this team.
+        '';
+      };
+    };
+
+
+    ####################################################
+    # UPSTREAM Configuration
+    upstream = mkOption {
+      type = types.commas;
+      description = ''
+        The http url(s) of the upstream endpoint or <literal>file://</literal>
+        paths for static files. Routing is based on the path.
+      '';
+    };
+
+    passAccessToken = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Pass OAuth access_token to upstream via X-Forwarded-Access-Token header.
+      '';
+    };
+
+    passBasicAuth = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Pass HTTP Basic Auth, X-Forwarded-User and X-Forwarded-Email information to upstream.
+      '';
+    };
+
+    basicAuthPassword = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        The password to set when passing the HTTP Basic Auth header.
+      '';
+    };
+
+    passHostHeader = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Pass the request Host Header to upstream.
+      '';
+    };
+
+    signatureKey = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        GAP-Signature request signature key.
+      '';
+      example = "sha1:secret0";
+    };
+
+    cookie = {
+      domain = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          An optional cookie domain to force cookies to.
+        '';
+        example = ".yourcompany.com";
+      };
+
+      expire = mkOption {
+        type = types.str;
+        default = "168h0m0s";
+        description = ''
+          Expire timeframe for cookie.
+        '';
+      };
+
+      httpOnly = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Set HttpOnly cookie flag.
+        '';
+      };
+
+      name = mkOption {
+        type = types.str;
+        default = "_oauth2_proxy";
+        description = ''
+          The name of the cookie that the oauth_proxy creates.
+        '';
+      };
+
+      refresh = mkOption {
+        # XXX: Unclear what the behavior is when this is not specified.
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          Refresh the cookie after this duration; 0 to disable.
+        '';
+        example = "168h0m0s";
+      };
+
+      secret = mkOption {
+        type = types.str;
+        description = ''
+          The seed string for secure cookies.
+        '';
+      };
+
+      secure = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Set secure (HTTPS) cookie flag.
+        '';
+      };
+    };
+
+    ####################################################
+    # OAUTH2 PROXY configuration
+
+    httpAddress = mkOption {
+      type = types.str;
+      default = "127.0.0.1:4180";
+      description = ''
+        HTTPS listening address.  This module does not expose the port by
+        default. If you want this URL to be accessible to other machines, please
+        add the port to <literal>networking.firewall.allowedTCPPorts</literal>.
+      '';
+    };
+
+    htpasswd = {
+      file = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        description = ''
+          Additionally authenticate against a htpasswd file. Entries must be
+          created with <literal>htpasswd -s</literal> for SHA encryption.
+        '';
+      };
+
+      displayForm = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Display username / password login form if an htpasswd file is provided.
+        '';
+      };
+    };
+
+    customTemplatesDir = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      description = ''
+        Path to custom HTML templates.
+      '';
+    };
+
+    proxyPrefix = mkOption {
+      type = types.str;
+      default = "/oauth2";
+      description = ''
+        The url root path that this proxy should be nested under.
+      '';
+    };
+
+    tls = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to serve over TLS.
+        '';
+      };
+
+      certificate = mkOption {
+        type = types.path;
+        description = ''
+          Path to certificate file.
+        '';
+      };
+
+      key = mkOption {
+        type = types.path;
+        description = ''
+          Path to private key file.
+        '';
+      };
+
+      httpsAddress = mkOption {
+        type = types.str;
+        default = ":443";
+        description = ''
+          <literal>addr:port</literal> to listen on for HTTPS clients.
+
+          Remember to add <literal>port</literal> to
+          <literal>allowedTCPPorts</literal> if you want other machines to be
+          able to connect to it.
+        '';
+      };
+    };
+
+    requestLogging = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Log requests to stdout.
+      '';
+    };
+
+    ####################################################
+    # UNKNOWN
+
+    # XXX: Is this mandatory? Is it part of another group? Is it part of the provider specification?
+    scope = mkOption {
+      # XXX: jml suspects this is always necessary, but the command-line
+      # doesn't require it so making it optional.
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        OAuth scope specification.
+      '';
+    };
+
+    profileURL = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+      	Profile access endpoint.
+      '';
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    users.extraUsers.oauth2_proxy = {
+      description = "OAuth2 Proxy";
+    };
+
+    systemd.services.oauth2_proxy = {
+      description = "OAuth2 Proxy";
+      path = [ cfg.package ];
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network-interfaces.target" ];
+
+      serviceConfig = {
+        User = "oauth2_proxy";
+        Restart = "always";
+        ExecStart = "${cfg.package.bin}/bin/oauth2_proxy ${mkCommandLine cfg}";
+      };
+    };
+
+  };
+}
diff --git a/nixos/modules/services/web-servers/apache-httpd/default.nix b/nixos/modules/services/web-servers/apache-httpd/default.nix
index c23897192b4c..9844e3c435d1 100644
--- a/nixos/modules/services/web-servers/apache-httpd/default.nix
+++ b/nixos/modules/services/web-servers/apache-httpd/default.nix
@@ -337,6 +337,7 @@ let
         allModules =
           concatMap (svc: svc.extraModulesPre) allSubservices
           ++ map (name: {inherit name; path = "${httpd}/modules/mod_${name}.so";}) apacheModules
+          ++ optional mainCfg.enableMellon { name = "auth_mellon"; path = "${pkgs.apacheHttpdPackages.mod_auth_mellon}/modules/mod_auth_mellon.so"; }
           ++ optional enablePHP { name = "php5"; path = "${php}/modules/libphp5.so"; }
           ++ concatMap (svc: svc.extraModules) allSubservices
           ++ extraForeignModules;
@@ -541,6 +542,12 @@ in
         '';
       };
 
+      enableMellon = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to enable the mod_auth_mellon module.";
+      };
+
       enablePHP = mkOption {
         type = types.bool;
         default = false;
@@ -650,6 +657,7 @@ in
 
         environment =
           optionalAttrs enablePHP { PHPRC = phpIni; }
+          // optionalAttrs mainCfg.enableMellon { LD_LIBRARY_PATH  = "${pkgs.xmlsec}/lib"; }
           // (listToAttrs (concatMap (svc: svc.globalEnvVars) allSubservices));
 
         preStart =
diff --git a/nixos/modules/services/x11/desktop-managers/gnome3.nix b/nixos/modules/services/x11/desktop-managers/gnome3.nix
index 91601f387cbe..700faad0c695 100644
--- a/nixos/modules/services/x11/desktop-managers/gnome3.nix
+++ b/nixos/modules/services/x11/desktop-managers/gnome3.nix
@@ -78,6 +78,8 @@ in {
         type = types.listOf types.path;
         description = "List of packages for which gsettings are overridden.";
       };
+
+      debug = mkEnableOption "gnome-session debug messages";
     };  
 
     environment.gnome3.packageSet = mkOption {
@@ -159,7 +161,7 @@ in {
           # Update user dirs as described in http://freedesktop.org/wiki/Software/xdg-user-dirs/
           ${pkgs.xdg-user-dirs}/bin/xdg-user-dirs-update
 
-          ${gnome3.gnome_session}/bin/gnome-session&
+          ${gnome3.gnome_session}/bin/gnome-session ${optionalString cfg.debug "--debug"} &
           waitPID=$!
         '';
       };
diff --git a/nixos/modules/services/x11/desktop-managers/kde5.nix b/nixos/modules/services/x11/desktop-managers/kde5.nix
index 538300f5793a..060dda1a70a8 100644
--- a/nixos/modules/services/x11/desktop-managers/kde5.nix
+++ b/nixos/modules/services/x11/desktop-managers/kde5.nix
@@ -170,7 +170,9 @@ in
     services.xserver.displayManager.sddm = {
       theme = "breeze";
       themes = [
+        kde5.extra-cmake-modules # for the setup-hook
         kde5.plasma-workspace
+        kde5.breeze-icons
         (kde5.oxygen-icons or kde5.oxygen-icons5)
       ];
     };
diff --git a/nixos/modules/services/x11/window-managers/i3.nix b/nixos/modules/services/x11/window-managers/i3.nix
index 2e2e10cc33b0..aea0a8986786 100644
--- a/nixos/modules/services/x11/window-managers/i3.nix
+++ b/nixos/modules/services/x11/window-managers/i3.nix
@@ -3,37 +3,43 @@
 with lib;
 
 let
-  cfg = config.services.xserver.windowManager.i3;
-in
-
-{
-  options = {
-    services.xserver.windowManager.i3 = {
-      enable = mkEnableOption "i3";
+  wmCfg = config.services.xserver.windowManager;
 
-      configFile = mkOption {
-        default = null;
-        type = types.nullOr types.path;
-        description = ''
-          Path to the i3 configuration file.
-          If left at the default value, $HOME/.i3/config will be used.
-        '';
-      };
+  i3option = name: {
+    enable = mkEnableOption name;
+    configFile = mkOption {
+      default = null;
+      type = types.nullOr types.path;
+      description = ''
+        Path to the i3 configuration file.
+        If left at the default value, $HOME/.i3/config will be used.
+      '';
     };
   };
 
-  config = mkIf cfg.enable {
-    services.xserver.windowManager = {
-      session = [{
-        name = "i3";
-        start = ''
-          ${pkgs.i3}/bin/i3 ${optionalString (cfg.configFile != null)
-            "-c \"${cfg.configFile}\""
-          } &
-          waitPID=$!
-        '';
-      }];
-    };
-    environment.systemPackages = with pkgs; [ i3 ];
+  i3config = name: pkg: cfg: {
+    services.xserver.windowManager.session = [{
+      inherit name;
+      start = ''
+        ${pkg}/bin/i3 ${optionalString (cfg.configFile != null)
+          "-c \"${cfg.configFile}\""
+        } &
+        waitPID=$!
+      '';
+    }];
+    environment.systemPackages = [ pkg ];
+  };
+
+in
+
+{
+  options.services.xserver.windowManager = {
+    i3 = i3option "i3";
+    i3-gaps = i3option "i3-gaps";
   };
+
+  config = mkMerge [
+    (mkIf wmCfg.i3.enable (i3config "i3" pkgs.i3 wmCfg.i3))
+    (mkIf wmCfg.i3-gaps.enable (i3config "i3-gaps" pkgs.i3-gaps wmCfg.i3-gaps))
+  ];
 }
diff --git a/nixos/modules/services/x11/xbanish.nix b/nixos/modules/services/x11/xbanish.nix
new file mode 100644
index 000000000000..e1e3cbc8e441
--- /dev/null
+++ b/nixos/modules/services/x11/xbanish.nix
@@ -0,0 +1,30 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let cfg = config.services.xbanish;
+
+in {
+  options.services.xbanish = {
+
+    enable = mkEnableOption "xbanish";
+
+    arguments = mkOption {
+      description = "Arguments to pass to xbanish command";
+      default = "";
+      example = "-d -i shift";
+      type = types.str;
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.user.services.xbanish = {
+      description = "xbanish hides the mouse pointer";
+      wantedBy = [ "default.target" ];
+      serviceConfig.ExecStart = ''
+        ${pkgs.xbanish}/bin/xbanish ${cfg.arguments}
+      '';
+      serviceConfig.Restart = "always";
+    };
+  };
+}
diff --git a/nixos/modules/virtualisation/docker.nix b/nixos/modules/virtualisation/docker.nix
index 97b2927cf1bd..c99fc78d49e7 100644
--- a/nixos/modules/virtualisation/docker.nix
+++ b/nixos/modules/virtualisation/docker.nix
@@ -95,7 +95,7 @@ in
           LimitNPROC = 1048576;
         } // proxy_env;
 
-        path = [ pkgs.kmod ] ++ (optional (cfg.storageDriver == "zfs") pkgs.zfs);
+        path = [ config.system.sbin.modprobe ] ++ (optional (cfg.storageDriver == "zfs") pkgs.zfs);
         environment.MODULE_DIR = "/run/current-system/kernel-modules/lib/modules";
 
         postStart = if cfg.socketActivation then "" else cfg.postStart;
diff --git a/nixos/tests/grsecurity.nix b/nixos/tests/grsecurity.nix
index 14f1aa9ff885..aadbfd8371ff 100644
--- a/nixos/tests/grsecurity.nix
+++ b/nixos/tests/grsecurity.nix
@@ -3,17 +3,39 @@
 import ./make-test.nix ({ pkgs, ...} : {
   name = "grsecurity";
   meta = with pkgs.stdenv.lib.maintainers; {
-    maintainers = [ copumpkin ];
+    maintainers = [ copumpkin joachifm ];
   };
 
   machine = { config, pkgs, ... }:
-    { boot.kernelPackages = pkgs.linuxPackages_grsec_testing_server; };
+    { security.grsecurity.enable = true;
+      boot.kernel.sysctl."kernel.grsecurity.deter_bruteforce" = 0;
+      security.apparmor.enable = true;
+    };
 
-  testScript =
-    ''
-      $machine->succeed("uname -a") =~ /grsec/;
-      # FIXME: this seems to hang the whole test. Unclear why, but let's fix it
-      # $machine->succeed("${pkgs.paxtest}/bin/paxtest blackhat");
-    '';
-})
+  testScript = ''
+    subtest "grsec-lock", sub {
+      $machine->succeed("systemctl is-active grsec-lock");
+      $machine->succeed("grep -Fq 1 /proc/sys/kernel/grsecurity/grsec_lock");
+      $machine->fail("echo -n 0 >/proc/sys/kernel/grsecurity/grsec_lock");
+    };
+
+    subtest "paxtest", sub {
+      # TODO: running paxtest blackhat hangs the vm
+      $machine->succeed("${pkgs.paxtest}/lib/paxtest/anonmap") =~ /Killed/ or die;
+      $machine->succeed("${pkgs.paxtest}/lib/paxtest/execbss") =~ /Killed/ or die;
+      $machine->succeed("${pkgs.paxtest}/lib/paxtest/execdata") =~ /Killed/ or die;
+      $machine->succeed("${pkgs.paxtest}/lib/paxtest/execheap") =~ /Killed/ or die;
+      $machine->succeed("${pkgs.paxtest}/lib/paxtest/execstack") =~ /Killed/ or die;
+      $machine->succeed("${pkgs.paxtest}/lib/paxtest/mprotanon") =~ /Killed/ or die;
+      $machine->succeed("${pkgs.paxtest}/lib/paxtest/mprotbss") =~ /Killed/ or die;
+      $machine->succeed("${pkgs.paxtest}/lib/paxtest/mprotdata") =~ /Killed/ or die;
+      $machine->succeed("${pkgs.paxtest}/lib/paxtest/mprotheap") =~ /Killed/ or die;
+      $machine->succeed("${pkgs.paxtest}/lib/paxtest/mprotstack") =~ /Killed/ or die;
+    };
 
+    subtest "tcc", sub {
+      $machine->execute("echo -e '#include <stdio.h>\nint main(void) { puts(\"hello\"); return 0; }' >main.c");
+      $machine->succeed("${pkgs.tinycc.bin}/bin/tcc -run main.c");
+    };
+  '';
+})