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.nix4
-rw-r--r--nixos/modules/module-list.nix9
-rw-r--r--nixos/modules/rename.nix20
-rw-r--r--nixos/modules/security/acme.nix2
-rw-r--r--nixos/modules/security/grsecurity.nix376
-rw-r--r--nixos/modules/services/audio/squeezelite.nix67
-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/opendkim.nix2
-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/misc/taskserver/default.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/ejabberd.nix2
-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.nix2
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/default.nix8
-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/tasks/filesystems.nix20
-rw-r--r--nixos/modules/virtualisation/lxd.nix2
-rw-r--r--nixos/tests/grsecurity.nix40
32 files changed, 786 insertions, 450 deletions
diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix
index 95b3e3ca1857..277a4264137b 100644
--- a/nixos/modules/config/users-groups.nix
+++ b/nixos/modules/config/users-groups.nix
@@ -476,7 +476,6 @@ in {
         home = "/root";
         shell = mkDefault cfg.defaultUserShell;
         group = "root";
-        extraGroups = [ "grsecurity" ];
         initialHashedPassword = mkDefault config.security.initialRootPassword;
       };
       nobody = {
@@ -508,7 +507,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..84e735584c82 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;
@@ -269,6 +268,7 @@
       nzbget = 245;
       mosquitto = 246;
       toxvpn = 247;
+      squeezelite = 248;
 
       # When adding a uid, make sure it doesn't match an existing gid. And don't use uids above 399!
 
@@ -396,7 +396,6 @@
       foundationdb = 118;
       newrelic = 119;
       starbound = 120;
-      grsecurity = 121;
       hydra = 122;
       spiped = 123;
       teamspeak = 124;
@@ -508,6 +507,7 @@
       nzbget = 245;
       mosquitto = 246;
       #toxvpn = 247; # unused
+      #squeezelite = 248; #unused
 
       # When adding a gid, make sure it doesn't match an existing
       # uid. Users and groups with the same name should have equal
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 7d93801dbe1f..7b04ebaba279 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -110,6 +110,7 @@
   ./services/audio/liquidsoap.nix
   ./services/audio/mpd.nix
   ./services/audio/mopidy.nix
+  ./services/audio/squeezelite.nix
   ./services/backup/almir.nix
   ./services/backup/bacula.nix
   ./services/backup/crashplan.nix
@@ -125,10 +126,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 +220,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 +372,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
@@ -491,6 +495,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/acme.nix b/nixos/modules/security/acme.nix
index ef6da788e619..f646602221a4 100644
--- a/nixos/modules/security/acme.nix
+++ b/nixos/modules/security/acme.nix
@@ -187,7 +187,7 @@ in
                   script = ''
                     cd '${cpath}'
                     set +e
-                    simp_le ${concatMapStringsSep " " (arg: escapeShellArg (toString arg)) cmdline}
+                    simp_le ${escapeShellArgs cmdline}
                     EXITCODE=$?
                     set -e
                     echo "$EXITCODE" > /tmp/lastExitCode
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/audio/squeezelite.nix b/nixos/modules/services/audio/squeezelite.nix
new file mode 100644
index 000000000000..f1a60be992d8
--- /dev/null
+++ b/nixos/modules/services/audio/squeezelite.nix
@@ -0,0 +1,67 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  uid = config.ids.uids.squeezelite;
+  cfg = config.services.squeezelite;
+
+in {
+
+  ###### interface
+
+  options = {
+
+    services.squeezelite= {
+
+      enable = mkEnableOption "Squeezelite, a software Squeezebox emulator";
+
+      dataDir = mkOption {
+        default = "/var/lib/squeezelite";
+        type = types.str;
+        description = ''
+          The directory where Squeezelite stores its name file.
+        '';
+      };
+
+      extraArguments = mkOption {
+        default = "";
+        type = types.str;
+        description = ''
+          Additional command line arguments to pass to Squeezelite.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    systemd.services.squeezelite= {
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" "sound.target" ];
+      description = "Software Squeezebox emulator";
+      preStart = "mkdir -p ${cfg.dataDir} && chown -R squeezelite ${cfg.dataDir}";
+      serviceConfig = {
+        ExecStart = "${pkgs.squeezelite}/bin/squeezelite -N ${cfg.dataDir}/player-name ${cfg.extraArguments}";
+        User = "squeezelite";
+        PermissionsStartOnly = true;
+      };
+    };
+
+    users.extraUsers.squeezelite= {
+      inherit uid;
+      group = "nogroup";
+      extraGroups = [ "audio" ];
+      description = "Squeezelite user";
+      home = "${cfg.dataDir}";
+    };
+
+  };
+
+}
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/opendkim.nix b/nixos/modules/services/mail/opendkim.nix
index af996758f41f..f065208ddfc1 100644
--- a/nixos/modules/services/mail/opendkim.nix
+++ b/nixos/modules/services/mail/opendkim.nix
@@ -101,7 +101,7 @@ in {
       wantedBy = [ "multi-user.target" ];
 
       serviceConfig = {
-        ExecStart = "${pkgs.opendkim}/bin/opendkim ${concatMapStringsSep " " escapeShellArg args}";
+        ExecStart = "${pkgs.opendkim}/bin/opendkim ${escapeShellArgs args}";
         User = cfg.user;
         Group = cfg.group;
         RuntimeDirectory = optional (cfg.socket == defaultSock) "opendkim";
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/misc/taskserver/default.nix b/nixos/modules/services/misc/taskserver/default.nix
index b7d14e90a2b7..c846ffd04551 100644
--- a/nixos/modules/services/misc/taskserver/default.nix
+++ b/nixos/modules/services/misc/taskserver/default.nix
@@ -152,8 +152,6 @@ let
     };
   };
 
-  mkShellStr = val: "'${replaceStrings ["'"] ["'\\''"] val}'";
-
   certtool = "${pkgs.gnutls.bin}/bin/certtool";
 
   nixos-taskserver = pkgs.buildPythonPackage {
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/ejabberd.nix b/nixos/modules/services/networking/ejabberd.nix
index 9868f303ab2b..8ecc16257db8 100644
--- a/nixos/modules/services/networking/ejabberd.nix
+++ b/nixos/modules/services/networking/ejabberd.nix
@@ -13,7 +13,7 @@ let
 
   ectl = ''${cfg.package}/bin/ejabberdctl ${if cfg.configFile == null then "" else "--config ${cfg.configFile}"} --ctl-config "${ctlcfg}" --spool "${cfg.spoolDir}" --logs "${cfg.logsDir}"'';
 
-  dumps = lib.concatMapStringsSep " " lib.escapeShellArg cfg.loadDumps;
+  dumps = lib.escapeShellArgs cfg.loadDumps;
 
 in {
 
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
index b149373076a6..4c20392214fc 100644
--- a/nixos/modules/services/security/oauth2_proxy.nix
+++ b/nixos/modules/services/security/oauth2_proxy.nix
@@ -515,7 +515,7 @@ in
       serviceConfig = {
         User = "oauth2_proxy";
         Restart = "always";
-        ExecStart = "${cfg.package}/bin/oauth2_proxy ${mkCommandLine cfg}";
+        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/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/tasks/filesystems.nix b/nixos/modules/tasks/filesystems.nix
index dd351306cb63..cf8232c36154 100644
--- a/nixos/modules/tasks/filesystems.nix
+++ b/nixos/modules/tasks/filesystems.nix
@@ -111,15 +111,17 @@ in
 
     fileSystems = mkOption {
       default = {};
-      example = {
-        "/".device = "/dev/hda1";
-        "/data" = {
-          device = "/dev/hda2";
-          fsType = "ext3";
-          options = [ "data=journal" ];
-        };
-        "/bigdisk".label = "bigdisk";
-      };
+      example = literalExample ''
+        {
+          "/".device = "/dev/hda1";
+          "/data" = {
+            device = "/dev/hda2";
+            fsType = "ext3";
+            options = [ "data=journal" ];
+          };
+          "/bigdisk".label = "bigdisk";
+        }
+      '';
       type = types.loaOf types.optionSet;
       options = [ fileSystemOpts ];
       description = ''
diff --git a/nixos/modules/virtualisation/lxd.nix b/nixos/modules/virtualisation/lxd.nix
index 845f14352f3d..9d76b890872a 100644
--- a/nixos/modules/virtualisation/lxd.nix
+++ b/nixos/modules/virtualisation/lxd.nix
@@ -47,7 +47,7 @@ in
         # TODO(wkennington): Add lvm2 and thin-provisioning-tools
         path = with pkgs; [ acl rsync gnutar xz btrfs-progs ];
 
-        serviceConfig.ExecStart = "@${pkgs.lxd}/bin/lxd lxd --syslog --group lxd";
+        serviceConfig.ExecStart = "@${pkgs.lxd.bin}/bin/lxd lxd --syslog --group lxd";
         serviceConfig.Type = "simple";
         serviceConfig.KillMode = "process"; # when stopping, leave the containers alone
       };
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");
+    };
+  '';
+})