summary refs log tree commit diff
path: root/nixos/modules
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules')
-rw-r--r--nixos/modules/misc/ids.nix2
-rw-r--r--nixos/modules/module-list.nix4
-rw-r--r--nixos/modules/profiles/hardened.nix6
-rw-r--r--nixos/modules/rename.nix3
-rw-r--r--nixos/modules/security/pam.nix4
-rw-r--r--nixos/modules/services/audio/mpd.nix17
-rw-r--r--nixos/modules/services/continuous-integration/gitlab-runner.nix56
-rw-r--r--nixos/modules/services/continuous-integration/hail.nix61
-rw-r--r--nixos/modules/services/databases/postage.nix205
-rw-r--r--nixos/modules/services/editors/emacs.xml4
-rw-r--r--nixos/modules/services/monitoring/munin.nix4
-rw-r--r--nixos/modules/services/monitoring/prometheus/unifi-exporter.nix1
-rw-r--r--nixos/modules/services/network-filesystems/ipfs.nix39
-rw-r--r--nixos/modules/services/networking/ddclient.nix100
-rw-r--r--nixos/modules/services/security/sks.nix82
-rw-r--r--nixos/modules/services/security/tor.nix434
-rw-r--r--nixos/modules/services/ttys/agetty.nix10
-rw-r--r--nixos/modules/services/web-apps/pgpkeyserver-lite.nix75
-rw-r--r--nixos/modules/services/web-servers/phpfpm/default.nix1
-rw-r--r--nixos/modules/services/web-servers/varnish/default.nix44
-rw-r--r--nixos/modules/testing/minimal-kernel.nix2
-rw-r--r--nixos/modules/virtualisation/libvirtd.nix42
-rw-r--r--nixos/modules/virtualisation/vmware-guest.nix2
23 files changed, 967 insertions, 231 deletions
diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix
index 6ec3c1a112dd..859838182ed4 100644
--- a/nixos/modules/misc/ids.nix
+++ b/nixos/modules/misc/ids.nix
@@ -335,7 +335,7 @@
       dialout = 27;
       #polkituser = 28; # currently unused, polkitd doesn't need a group
       utmp = 29;
-      #ddclient = 30; # unused
+      ddclient = 30;
       davfs2 = 31;
       disnix = 33;
       osgi = 34;
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 1da1ea5ae115..1bb950ca7d65 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -165,6 +165,7 @@
   ./services/continuous-integration/buildbot/master.nix
   ./services/continuous-integration/buildbot/worker.nix
   ./services/continuous-integration/buildkite-agent.nix
+  ./services/continuous-integration/hail.nix
   ./services/continuous-integration/hydra/default.nix
   ./services/continuous-integration/gitlab-runner.nix
   ./services/continuous-integration/gocd-agent/default.nix
@@ -185,6 +186,7 @@
   ./services/databases/neo4j.nix
   ./services/databases/openldap.nix
   ./services/databases/opentsdb.nix
+  ./services/databases/postage.nix
   ./services/databases/postgresql.nix
   ./services/databases/redis.nix
   ./services/databases/riak.nix
@@ -555,6 +557,7 @@
   ./services/security/oauth2_proxy.nix
   ./services/security/physlock.nix
   ./services/security/shibboleth-sp.nix
+  ./services/security/sks.nix
   ./services/security/sshguard.nix
   ./services/security/tor.nix
   ./services/security/torify.nix
@@ -582,6 +585,7 @@
   ./services/web-apps/frab.nix
   ./services/web-apps/mattermost.nix
   ./services/web-apps/nixbot.nix
+  ./services/web-apps/pgpkeyserver-lite.nix
   ./services/web-apps/piwik.nix
   ./services/web-apps/pump.io.nix
   ./services/web-apps/tt-rss.nix
diff --git a/nixos/modules/profiles/hardened.nix b/nixos/modules/profiles/hardened.nix
index 0a0838431da7..0ab210cc4c39 100644
--- a/nixos/modules/profiles/hardened.nix
+++ b/nixos/modules/profiles/hardened.nix
@@ -59,4 +59,10 @@ with lib;
   # the feature at runtime.  Attempting to create a user namespace
   # with unshare will then fail with "no space left on device".
   boot.kernel.sysctl."user.max_user_namespaces" = mkDefault 0;
+
+  # Raise ASLR entropy for 64bit & 32bit, respectively.
+  #
+  # Note: mmap_rnd_compat_bits may not exist on 64bit.
+  boot.kernel.sysctl."vm.mmap_rnd_bits" = mkDefault 32;
+  boot.kernel.sysctl."vm.mmap_rnd_compat_bits" = mkDefault 16;
 }
diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix
index 08146d1f5687..2079ed544aee 100644
--- a/nixos/modules/rename.nix
+++ b/nixos/modules/rename.nix
@@ -25,6 +25,7 @@ with lib;
     (mkRenamedOptionModule [ "services" "sslh" "host" ] [ "services" "sslh" "listenAddress" ])
     (mkRenamedOptionModule [ "services" "statsd" "host" ] [ "services" "statsd" "listenAddress" ])
     (mkRenamedOptionModule [ "services" "subsonic" "host" ] [ "services" "subsonic" "listenAddress" ])
+    (mkRenamedOptionModule [ "services" "tor" "relay" "portSpec" ] [ "services" "tor" "relay" "port" ])
     (mkRenamedOptionModule [ "jobs" ] [ "systemd" "services" ])
 
     (mkRenamedOptionModule [ "services" "gitlab" "stateDir" ] [ "services" "gitlab" "statePath" ])
@@ -195,6 +196,8 @@ with lib;
     (mkRemovedOptionModule [ "services" "openvpn" "enable" ] "")
     (mkRemovedOptionModule [ "services" "printing" "cupsFilesConf" ] "")
     (mkRemovedOptionModule [ "services" "printing" "cupsdConf" ] "")
+    (mkRemovedOptionModule [ "services" "tor" "relay" "isBridge" ] "Use services.tor.relay.role instead.")
+    (mkRemovedOptionModule [ "services" "tor" "relay" "isExit" ] "Use services.tor.relay.role instead.")
     (mkRemovedOptionModule [ "services" "xserver" "startGnuPGAgent" ]
       "See the 16.09 release notes for more information.")
     (mkRemovedOptionModule [ "services" "phpfpm" "phpIni" ] "")
diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix
index 5632500df2e0..ede4ace5ed03 100644
--- a/nixos/modules/security/pam.nix
+++ b/nixos/modules/security/pam.nix
@@ -281,7 +281,7 @@ let
                 "auth optional ${pkgs.pam_mount}/lib/security/pam_mount.so"}
               ${optionalString cfg.enableKwallet
                 ("auth optional ${pkgs.plasma5.kwallet-pam}/lib/security/pam_kwallet5.so" +
-                 " kwalletd=${pkgs.libsForQt5.kwallet}/bin/kwalletd5")}
+                 " kwalletd=${pkgs.libsForQt5.kwallet.bin}/bin/kwalletd5")}
             '') + ''
           ${optionalString cfg.unixAuth
               "auth sufficient pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} likeauth try_first_pass"}
@@ -350,7 +350,7 @@ let
               "session optional ${pkgs.apparmor-pam}/lib/security/pam_apparmor.so order=user,group,default debug"}
           ${optionalString (cfg.enableKwallet)
               ("session optional ${pkgs.plasma5.kwallet-pam}/lib/security/pam_kwallet5.so" +
-               " kwalletd=${pkgs.libsForQt5.kwallet}/bin/kwalletd5")}
+               " kwalletd=${pkgs.libsForQt5.kwallet.bin}/bin/kwalletd5")}
         '');
     };
 
diff --git a/nixos/modules/services/audio/mpd.nix b/nixos/modules/services/audio/mpd.nix
index bd6c316243c8..5f379b392ea8 100644
--- a/nixos/modules/services/audio/mpd.nix
+++ b/nixos/modules/services/audio/mpd.nix
@@ -10,11 +10,9 @@ let
   gid = config.ids.gids.mpd;
   cfg = config.services.mpd;
 
-  playlistDir = "${cfg.dataDir}/playlists";
-
   mpdConf = pkgs.writeText "mpd.conf" ''
     music_directory     "${cfg.musicDirectory}"
-    playlist_directory  "${playlistDir}"
+    playlist_directory  "${cfg.playlistDirectory}"
     db_file             "${cfg.dbFile}"
     state_file          "${cfg.dataDir}/state"
     sticker_file        "${cfg.dataDir}/sticker.sql"
@@ -57,11 +55,21 @@ in {
       musicDirectory = mkOption {
         type = types.path;
         default = "${cfg.dataDir}/music";
+        defaultText = ''''${dataDir}/music'';
         description = ''
           The directory where mpd reads music from.
         '';
       };
 
+      playlistDirectory = mkOption {
+        type = types.path;
+        default = "${cfg.dataDir}/playlists";
+        defaultText = ''''${dataDir}/playlists'';
+        description = ''
+          The directory where mpd stores playlists.
+        '';
+      };
+
       extraConfig = mkOption {
         type = types.lines;
         default = "";
@@ -120,6 +128,7 @@ in {
       dbFile = mkOption {
         type = types.str;
         default = "${cfg.dataDir}/tag_cache";
+        defaultText = ''''${dataDir}/tag_cache'';
         description = ''
           The path to MPD's database.
         '';
@@ -153,7 +162,7 @@ in {
 
       preStart = ''
         mkdir -p "${cfg.dataDir}" && chown -R ${cfg.user}:${cfg.group} "${cfg.dataDir}"
-        mkdir -p "${playlistDir}" && chown -R ${cfg.user}:${cfg.group} "${playlistDir}"
+        mkdir -p "${cfg.playlistDirectory}" && chown -R ${cfg.user}:${cfg.group} "${cfg.playlistDirectory}"
       '';
       serviceConfig = {
         User = "${cfg.user}";
diff --git a/nixos/modules/services/continuous-integration/gitlab-runner.nix b/nixos/modules/services/continuous-integration/gitlab-runner.nix
index b11bc031b3ff..ce0583dad54d 100644
--- a/nixos/modules/services/continuous-integration/gitlab-runner.nix
+++ b/nixos/modules/services/continuous-integration/gitlab-runner.nix
@@ -4,15 +4,65 @@ with lib;
 
 let
   cfg = config.services.gitlab-runner;
-  configFile = pkgs.writeText "config.toml" cfg.configText;
+  configFile =
+    if (cfg.configFile == null) then
+      (pkgs.runCommand "config.toml" {
+        buildInputs = [ pkgs.remarshal ];
+      } ''
+        remarshal -if json -of toml \
+          < ${pkgs.writeText "config.json" (builtins.toJSON cfg.configOptions)} \
+          > $out
+      '')
+    else
+      cfg.configFile;
   hasDocker = config.virtualisation.docker.enable;
 in
 {
   options.services.gitlab-runner = {
     enable = mkEnableOption "Gitlab Runner";
 
-    configText = mkOption {
-      description = "Verbatim config.toml to use";
+    configFile = mkOption {
+      default = null;
+      description = ''
+        Configuration file for gitlab-runner.
+        Use this option in favor of configOptions to avoid placing CI tokens in the nix store.
+
+        <option>configFile</option> takes precedence over <option>configOptions</option>.
+
+        Warning: Not using <option>configFile</option> will potentially result in secrets
+        leaking into the WORLD-READABLE nix store.
+      '';
+      type = types.nullOr types.path;
+    };
+
+    configOptions = mkOption {
+      description = ''
+        Configuration for gitlab-runner
+        <option>configFile</option> will take precedence over this option.
+
+        Warning: all Configuration, especially CI token, will be stored in a
+        WORLD-READABLE file in the Nix Store.
+
+        If you want to protect your CI token use <option>configFile</option> instead.
+      '';
+      type = types.attrs;
+      example = {
+        concurrent = 2;
+        runners = [{
+          name = "docker-nix-1.11";
+          url = "https://CI/";
+          token = "TOKEN";
+          executor = "docker";
+          builds_dir = "";
+          docker = {
+            host = "";
+            image = "nixos/nix:1.11";
+            privileged = true;
+            disable_cache = true;
+            cache_dir = "";
+          };
+        }];
+      };
     };
 
     gracefulTermination = mkOption {
diff --git a/nixos/modules/services/continuous-integration/hail.nix b/nixos/modules/services/continuous-integration/hail.nix
new file mode 100644
index 000000000000..5d0c3f7b4ab3
--- /dev/null
+++ b/nixos/modules/services/continuous-integration/hail.nix
@@ -0,0 +1,61 @@
+{ config, lib, pkgs, ...}:
+
+with lib;
+
+let
+  cfg = config.services.hail;
+in {
+
+
+  ###### interface
+
+  options.services.hail = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Enables the Hail Auto Update Service. Hail can automatically deploy artifacts
+        built by a Hydra Continous Integration server. A common use case is to provide
+        continous deployment for single services or a full NixOS configuration.'';
+    };
+    profile = mkOption {
+      type = types.str;
+      default = "hail-profile";
+      description = "The name of the Nix profile used by Hail.";
+    };
+    hydraJobUri = mkOption {
+      type = types.str;
+      description = "The URI of the Hydra Job.";
+    };
+    netrc = mkOption {
+      type = types.nullOr types.path;
+      description = "The netrc file to use when fetching data from Hydra.";
+      default = null;
+    };
+    package = mkOption {
+      type = types.package;
+      default = pkgs.haskellPackages.hail;
+      defaultText = "pkgs.haskellPackages.hail";
+      description = "Hail package to use.";
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    systemd.services.hail = {
+      description = "Hail Auto Update Service";
+      wants = [ "network-online.target" ];
+      wantedBy = [ "multi-user.target" ];
+      path = with pkgs; [ nix ];
+      environment = {
+        HOME = "/var/lib/empty";
+      };
+      serviceConfig = {
+        ExecStart = "${cfg.package}/bin/hail --profile ${cfg.profile} --job-uri ${cfg.hydraJobUri}"
+          + lib.optionalString (cfg.netrc != null) " --netrc-file ${cfg.netrc}";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/databases/postage.nix b/nixos/modules/services/databases/postage.nix
new file mode 100644
index 000000000000..d49c9a83a46f
--- /dev/null
+++ b/nixos/modules/services/databases/postage.nix
@@ -0,0 +1,205 @@
+{ lib, pkgs, config, ... } :
+
+with lib;
+
+let
+  cfg = config.services.postage;
+
+  confFile = pkgs.writeTextFile {
+    name = "postage.conf";
+    text =  ''
+      connection_file = ${postageConnectionsFile}
+
+      allow_custom_connections = ${builtins.toJSON cfg.allowCustomConnections}
+
+      postage_port = ${toString cfg.port}
+
+      super_only = ${builtins.toJSON cfg.superOnly}
+
+      ${optionalString (!isNull cfg.loginGroup) "login_group = ${cfg.loginGroup}"}
+
+      login_timeout = ${toString cfg.loginTimeout}
+
+      web_root = ${cfg.package}/etc/postage/web_root
+
+      data_root = ${cfg.dataRoot}
+
+      ${optionalString (!isNull cfg.tls) ''
+      tls_cert = ${cfg.tls.cert}
+      tls_key = ${cfg.tls.key}
+      ''}
+
+      log_level = ${cfg.logLevel}
+    '';
+  };
+
+  postageConnectionsFile = pkgs.writeTextFile {
+    name = "postage-connections.conf";
+    text = concatStringsSep "\n"
+      (mapAttrsToList (name : conn : "${name}: ${conn}") cfg.connections);
+  };
+
+  postage = "postage";
+in {
+
+  options.services.postage = {
+    enable = mkEnableOption "PostgreSQL Administration for the web";
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.postage;
+      defaultText = "pkgs.postage";
+      description = ''
+        The postage package to use.
+      '';
+    };
+
+    connections = mkOption {
+      type = types.attrsOf types.str;
+      default = {};
+      example = {
+        "nuc-server"  = "hostaddr=192.168.0.100 port=5432 dbname=postgres";
+        "mini-server" = "hostaddr=127.0.0.1 port=5432 dbname=postgres sslmode=require";
+      };
+      description = ''
+        Postage requires at least one PostgreSQL server be defined.
+        </para><para>
+        Detailed information about PostgreSQL connection strings is available at:
+        <link xlink:href="http://www.postgresql.org/docs/current/static/libpq-connect.html"/>
+        </para><para>
+        Note that you should not specify your user name or password. That
+        information will be entered on the login screen. If you specify a
+        username or password, it will be removed by Postage before attempting to
+        connect to a database.
+      '';
+    };
+
+    allowCustomConnections = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        This tells Postage whether or not to allow anyone to use a custom
+        connection from the login screen.
+      '';
+    };
+
+    port = mkOption {
+      type = types.int;
+      default = 8080;
+      description = ''
+        This tells Postage what port to listen on for browser requests.
+      '';
+    };
+
+    localOnly = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        This tells Postage whether or not to set the listening socket to local
+        addresses only.
+      '';
+    };
+
+    superOnly = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        This tells Postage whether or not to only allow super users to
+        login. The recommended value is true and will restrict users who are not
+        super users from logging in to any PostgreSQL instance through
+        Postage. Note that a connection will be made to PostgreSQL in order to
+        test if the user is a superuser.
+      '';
+    };
+
+    loginGroup = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        This tells Postage to only allow users in a certain PostgreSQL group to
+        login to Postage. Note that a connection will be made to PostgreSQL in
+        order to test if the user is a member of the login group.
+      '';
+    };
+
+    loginTimeout = mkOption {
+      type = types.int;
+      default = 3600;
+      description = ''
+        Number of seconds of inactivity before user is automatically logged
+        out.
+      '';
+    };
+
+    dataRoot = mkOption {
+      type = types.str;
+      default = "/var/lib/postage";
+      description = ''
+        This tells Postage where to put the SQL file history. All tabs are saved
+        to this location so that if you get disconnected from Postage you
+        don't lose your work.
+      '';
+    };
+
+    tls = mkOption {
+      type = types.nullOr (types.submodule {
+        options = {
+          cert = mkOption {
+            type = types.str;
+            description = "TLS certificate";
+          };
+          key = mkOption {
+            type = types.str;
+            description = "TLS key";
+          };
+        };
+      });
+      default = null;
+      description = ''
+        These options tell Postage where the TLS Certificate and Key files
+        reside. If you use these options then you'll only be able to access
+        Postage through a secure TLS connection. These options are only
+        necessary if you wish to connect directly to Postage using a secure TLS
+        connection. As an alternative, you can set up Postage in a reverse proxy
+        configuration. This allows your web server to terminate the secure
+        connection and pass on the request to Postage. You can find help to set
+        up this configuration in:
+        <link xlink:href="https://github.com/workflowproducts/postage/blob/master/INSTALL_NGINX.md"/>
+      '';
+    };
+
+    logLevel = mkOption {
+      type = types.enum ["error" "warn" "notice" "info"];
+      default = "error";
+      description = ''
+        Verbosity of logs
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.postage = {
+      description = "postage - PostgreSQL Administration for the web";
+      wants    = [ "postgresql.service" ];
+      after    = [ "postgresql.service" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        User         = postage;
+        Group        = postage;
+        ExecStart    = "${pkgs.postage}/sbin/postage -c ${confFile}" +
+                       optionalString cfg.localOnly " --local-only=true";
+      };
+    };
+    users = {
+      users."${postage}" = {
+        name  = postage;
+        group = postage;
+        home  = cfg.dataRoot;
+        createHome = true;
+      };
+      groups."${postage}" = {
+        name = postage;
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/editors/emacs.xml b/nixos/modules/services/editors/emacs.xml
index 9d6395ebd74c..dd66bac442c6 100644
--- a/nixos/modules/services/editors/emacs.xml
+++ b/nixos/modules/services/editors/emacs.xml
@@ -24,7 +24,7 @@
   <para>
     Emacs runs within a graphical desktop environment using the X
     Window System, but works equally well on a text terminal. Under
-    <productname>OS X</productname>, a "Mac port" edition is
+    <productname>macOS</productname>, a "Mac port" edition is
     available, which uses Apple's native GUI frameworks.
   </para>
 
@@ -84,7 +84,7 @@
             <listitem>
               <para>
                 Emacs 25 with the "Mac port" patches, providing a more
-                native look and feel under OS X.
+                native look and feel under macOS.
               </para>
             </listitem>
           </varlistentry>
diff --git a/nixos/modules/services/monitoring/munin.nix b/nixos/modules/services/monitoring/munin.nix
index b26bcba64059..723b04dc0fe9 100644
--- a/nixos/modules/services/monitoring/munin.nix
+++ b/nixos/modules/services/monitoring/munin.nix
@@ -26,7 +26,9 @@ let
 
       for file in $out/*; do
         case "$file" in
-            plugin.sh) continue;;
+            */plugin.sh|*/plugins.history)
+              chmod +x "$file"
+              continue;;
         esac
 
         # read magic makers from the file
diff --git a/nixos/modules/services/monitoring/prometheus/unifi-exporter.nix b/nixos/modules/services/monitoring/prometheus/unifi-exporter.nix
index e3059e485098..0a56d6ae95a5 100644
--- a/nixos/modules/services/monitoring/prometheus/unifi-exporter.nix
+++ b/nixos/modules/services/monitoring/prometheus/unifi-exporter.nix
@@ -83,6 +83,7 @@ in {
       description = "Prometheus exporter for UniFi Controller metrics";
       unitConfig.Documentation = "https://github.com/mdlayher/unifi_exporter";
       wantedBy = [ "multi-user.target" ];
+      after = optional config.services.unifi.enable "unifi.service";
       serviceConfig = {
         User = "nobody";
         Restart = "always";
diff --git a/nixos/modules/services/network-filesystems/ipfs.nix b/nixos/modules/services/network-filesystems/ipfs.nix
index 10c1d751ac5d..60822b5b547f 100644
--- a/nixos/modules/services/network-filesystems/ipfs.nix
+++ b/nixos/modules/services/network-filesystems/ipfs.nix
@@ -49,6 +49,12 @@ in
         description = "The data dir for IPFS";
       };
 
+      defaultMode = mkOption {
+        description = "systemd service that is enabled by default";
+        type = types.enum [ "online" "offline" "norouting" ];
+        default = "online";
+      };
+
       autoMigrate = mkOption {
         type = types.bool;
         default = false;
@@ -147,10 +153,11 @@ in
     systemd.services.ipfs = {
       description = "IPFS Daemon";
 
-      wantedBy = [ "multi-user.target" ];
+      wantedBy = mkIf (cfg.defaultMode == "online") [ "multi-user.target" ];
+
       after = [ "network.target" "local-fs.target" "ipfs-init.service" ];
 
-      conflicts = [ "ipfs-offline.service" ];
+      conflicts = [ "ipfs-offline.service" "ipfs-norouting.service"];
       wants = [ "ipfs-init.service" ];
 
       environment.IPFS_PATH = cfg.dataDir;
@@ -169,9 +176,11 @@ in
     systemd.services.ipfs-offline = {
       description = "IPFS Daemon (offline mode)";
 
+      wantedBy = mkIf (cfg.defaultMode == "offline") [ "multi-user.target" ];
+
       after = [ "local-fs.target" "ipfs-init.service" ];
 
-      conflicts = [ "ipfs.service" ];
+      conflicts = [ "ipfs.service" "ipfs-norouting.service"];
       wants = [ "ipfs-init.service" ];
 
       environment.IPFS_PATH = cfg.dataDir;
@@ -186,5 +195,29 @@ in
         RestartSec = 1;
       };
     };
+
+    systemd.services.ipfs-norouting = {
+      description = "IPFS Daemon (no routing mode)";
+
+      wantedBy = mkIf (cfg.defaultMode == "norouting") [ "multi-user.target" ];
+
+      after = [ "local-fs.target" "ipfs-init.service" ];
+
+      conflicts = [ "ipfs.service" "ipfs-offline.service"];
+      wants = [ "ipfs-init.service" ];
+
+      environment.IPFS_PATH = cfg.dataDir;
+
+      path  = [ pkgs.ipfs ];
+
+      serviceConfig = {
+        ExecStart = "${ipfs}/bin/ipfs daemon ${ipfsFlags} --routing=none";
+        User = cfg.user;
+        Group = cfg.group;
+        Restart = "on-failure";
+        RestartSec = 1;
+      };
+    };
+
   };
 }
diff --git a/nixos/modules/services/networking/ddclient.nix b/nixos/modules/services/networking/ddclient.nix
index 28c96a9baefc..9e56545f746c 100644
--- a/nixos/modules/services/networking/ddclient.nix
+++ b/nixos/modules/services/networking/ddclient.nix
@@ -1,17 +1,33 @@
 { config, pkgs, lib, ... }:
 
 let
-
-  inherit (lib) mkOption mkIf singleton;
-  inherit (pkgs) ddclient;
-
-  stateDir = "/var/spool/ddclient";
-  ddclientUser = "ddclient";
-  ddclientFlags = "-foreground -file ${config.services.ddclient.configFile}";
-  ddclientPIDFile = "${stateDir}/ddclient.pid";
+  cfg = config.services.ddclient;
+  boolToStr = bool: if bool then "yes" else "no";
+
+  configText = ''
+    # This file can be used as a template for configFile or is automatically generated by Nix options.
+    daemon=${toString cfg.interval}
+    cache=${cfg.homeDir}/ddclient.cache
+    pid=/run/ddclient/ddclient.pid
+    foreground=NO
+    use=${cfg.use}
+    login=${cfg.username}
+    password=${cfg.password}
+    protocol=${cfg.protocol}
+    ${let server = cfg.server; in
+      lib.optionalString (server != "") "server=${server}"}
+    ssl=${boolToStr cfg.ssl}
+    wildcard=YES
+    quiet=${boolToStr cfg.quiet}
+    verbose=${boolToStr cfg.verbose}
+    ${cfg.domain}
+    ${cfg.extraConfig}
+  '';
 
 in
 
+with lib;
+
 {
 
   ###### interface
@@ -28,6 +44,12 @@ in
         '';
       };
 
+      homeDir = mkOption {
+        default = "/var/lib/ddclient";
+        type = str;
+        description = "Home directory for the daemon user.";
+      };
+
       domain = mkOption {
         default = "";
         type = str;
@@ -52,6 +74,12 @@ in
         '';
       };
 
+      interval = mkOption {
+        default = 600;
+        type = int;
+        description = "The interval at which to run the check and update.";
+      };
+
       configFile = mkOption {
         default = "/etc/ddclient.conf";
         type = path;
@@ -126,37 +154,24 @@ in
 
   config = mkIf config.services.ddclient.enable {
 
-    environment.systemPackages = [ ddclient ];
+    users = {
+      extraGroups.ddclient.gid = config.ids.gids.ddclient;
 
-    users.extraUsers = singleton {
-      name = ddclientUser;
-      uid = config.ids.uids.ddclient;
-      description = "ddclient daemon user";
-      home = stateDir;
+      extraUsers.ddclient = {
+        uid = config.ids.uids.ddclient;
+        description = "ddclient daemon user";
+        group = "ddclient";
+        home = cfg.homeDir;
+        createHome = true;
+      };
     };
 
     environment.etc."ddclient.conf" = {
-      enable = config.services.ddclient.configFile == "/etc/ddclient.conf";
+      enable = cfg.configFile == "/etc/ddclient.conf";
       uid = config.ids.uids.ddclient;
+      gid = config.ids.gids.ddclient;
       mode = "0600";
-      text = ''
-        # This file can be used as a template for configFile or is automatically generated by Nix options.
-        daemon=600
-        cache=${stateDir}/ddclient.cache
-        pid=${ddclientPIDFile}
-        use=${config.services.ddclient.use}
-        login=${config.services.ddclient.username}
-        password=${config.services.ddclient.password}
-        protocol=${config.services.ddclient.protocol}
-        ${let server = config.services.ddclient.server; in
-          lib.optionalString (server != "") "server=${server}"}
-        ssl=${if config.services.ddclient.ssl then "yes" else "no"}
-        wildcard=YES
-        quiet=${if config.services.ddclient.quiet then "yes" else "no"}
-        verbose=${if config.services.ddclient.verbose then "yes" else "no"}
-        ${config.services.ddclient.domain}
-        ${config.services.ddclient.extraConfig}
-      '';
+      text = configText;
     };
 
     systemd.services.ddclient = {
@@ -166,17 +181,14 @@ in
       restartTriggers = [ config.environment.etc."ddclient.conf".source ];
 
       serviceConfig = {
-        # Uncomment this if too many problems occur:
-        # Type = "forking";
-        User = ddclientUser;
-        Group = "nogroup"; #TODO get this to work
-        PermissionsStartOnly = "true";
-        PIDFile = ddclientPIDFile;
-        ExecStartPre = ''
-          ${pkgs.stdenv.shell} -c "${pkgs.coreutils}/bin/mkdir -m 0755 -p ${stateDir} && ${pkgs.coreutils}/bin/chown ${ddclientUser} ${stateDir}"
-        '';
-        ExecStart = "${ddclient}/bin/ddclient ${ddclientFlags}";
-        #ExecStartPost = "${pkgs.coreutils}/bin/rm -r ${stateDir}"; # Should we have this?
+        RuntimeDirectory = "ddclient";
+        # we cannot run in forking mode as it swallows all the program output
+        Type = "simple";
+        User = "ddclient";
+        Group = "ddclient";
+        ExecStart = "${lib.getBin pkgs.ddclient}/bin/ddclient -foreground -file ${cfg.configFile}";
+        ProtectSystem = "full";
+        PrivateTmp = true;
       };
     };
   };
diff --git a/nixos/modules/services/security/sks.nix b/nixos/modules/services/security/sks.nix
new file mode 100644
index 000000000000..62308428f326
--- /dev/null
+++ b/nixos/modules/services/security/sks.nix
@@ -0,0 +1,82 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.sks;
+
+  sksPkg = cfg.package;
+
+in
+
+{
+
+  options = {
+
+    services.sks = {
+
+      enable = mkEnableOption "sks";
+
+      package = mkOption {
+        default = pkgs.sks;
+        defaultText = "pkgs.sks";
+        type = types.package;
+        description = "
+          Which sks derivation to use.
+        ";
+      };
+
+      hkpAddress = mkOption {
+        default = [ "127.0.0.1" "::1" ];
+        type = types.listOf types.str;
+        description = "
+          Wich ip addresses the sks-keyserver is listening on.
+        ";
+      };
+
+      hkpPort = mkOption {
+        default = 11371;
+        type = types.int;
+        description = "
+          Which port the sks-keyserver is listening on.
+        ";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ sksPkg ];
+    
+    users.users.sks = {
+      createHome = true;
+      home = "/var/db/sks";
+      isSystemUser = true;
+      shell = "${pkgs.coreutils}/bin/true";
+    };
+
+    systemd.services = let
+      hkpAddress = "'" + (builtins.concatStringsSep " " cfg.hkpAddress) + "'" ;
+      hkpPort = builtins.toString cfg.hkpPort;
+      home = config.users.users.sks.home;
+      user = config.users.users.sks.name;
+    in {
+      sks-keyserver = {
+        wantedBy = [ "multi-user.target" ];
+        preStart = ''
+          mkdir -p ${home}/dump
+          ${pkgs.sks}/bin/sks build ${home}/dump/*.gpg -n 10 -cache 100 || true #*/
+          ${pkgs.sks}/bin/sks cleandb || true
+          ${pkgs.sks}/bin/sks pbuild -cache 20 -ptree_cache 70 || true
+        '';
+        serviceConfig = {
+          WorkingDirectory = home;
+          User = user;
+          Restart = "always";
+          ExecStart = "${pkgs.sks}/bin/sks db -hkp_address ${hkpAddress} -hkp_port ${hkpPort}";
+        };
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/security/tor.nix b/nixos/modules/services/security/tor.nix
index 3f1450ebfbd7..04b065f6ae4b 100644
--- a/nixos/modules/services/security/tor.nix
+++ b/nixos/modules/services/security/tor.nix
@@ -7,7 +7,7 @@ let
   torDirectory = "/var/lib/tor";
 
   opt    = name: value: optionalString (value != null) "${name} ${value}";
-  optint = name: value: optionalString (value != 0)    "${name} ${toString value}";
+  optint = name: value: optionalString (value != null && value != 0)    "${name} ${toString value}";
 
   torRc = ''
     User tor
@@ -17,7 +17,7 @@ let
       GeoIPv6File ${pkgs.tor.geoip}/share/tor/geoip6
     ''}
 
-    ${optint "ControlPort" cfg.controlPort}
+    ${optint "ControlPort" (toString cfg.controlPort)}
   ''
   # Client connection config
   + optionalString cfg.client.enable  ''
@@ -27,7 +27,8 @@ let
   ''
   # Relay config
   + optionalString cfg.relay.enable ''
-    ORPort ${cfg.relay.portSpec}
+    ORPort ${toString cfg.relay.port}
+    ${opt "Address" cfg.relay.address}
     ${opt "Nickname" cfg.relay.nickname}
     ${opt "ContactInfo" cfg.relay.contactInfo}
 
@@ -36,31 +37,32 @@ let
     ${opt "AccountingMax" cfg.relay.accountingMax}
     ${opt "AccountingStart" cfg.relay.accountingStart}
 
-    ${if cfg.relay.isExit then
+    ${if (cfg.relay.role == "exit") then
         opt "ExitPolicy" cfg.relay.exitPolicy
       else
         "ExitPolicy reject *:*"}
 
-    ${optionalString cfg.relay.isBridge ''
+    ${optionalString (elem cfg.relay.role ["bridge" "private-bridge"]) ''
       BridgeRelay 1
       ServerTransportPlugin obfs2,obfs3 exec ${pkgs.pythonPackages.obfsproxy}/bin/obfsproxy managed
+      ExtORPort auto
+      ${optionalString (cfg.relay.role == "private-bridge") ''
+        ExtraInfoStatistics 0
+        PublishServerDescriptor 0
+      ''}
     ''}
   ''
-  + hiddenServices
+  # Hidden services
+  + concatStrings (flip mapAttrsToList cfg.hiddenServices (n: v: ''
+    HiddenServiceDir ${torDirectory}/onion/${v.name}
+    ${flip concatMapStrings v.map (p: ''
+      HiddenServicePort ${toString p.port} ${p.destination}
+    '')}
+  ''))
   + cfg.extraConfig;
 
-  hiddenServices = concatStrings (mapAttrsToList (hiddenServiceDir: hs:
-    let
-      hsports = concatStringsSep "\n" (map mkHiddenServicePort hs.hiddenServicePorts);
-    in
-      "HiddenServiceDir ${hiddenServiceDir}\n${hsports}\n${hs.extraConfig}\n"
-    ) cfg.hiddenServices);
-
-    mkHiddenServicePort = hsport: let
-      trgt = optionalString (hsport.target != null) (" " + hsport.target);
-    in "HiddenServicePort ${toString hsport.virtualPort}${trgt}";
-
   torRcFile = pkgs.writeText "torrc" torRc;
+
 in
 {
   options = {
@@ -96,8 +98,8 @@ in
       };
 
       controlPort = mkOption {
-        type = types.int;
-        default = 0;
+        type = types.nullOr (types.either types.int types.str);
+        default = null;
         example = 9051;
         description = ''
           If set, Tor will accept connections on the specified port
@@ -133,9 +135,10 @@ in
           example = "192.168.0.1:9101";
           description = ''
             Bind to this address to listen for connections from
-            Socks-speaking applications. Same as socksListenAddress
-            but uses weaker circuit isolation to provide performance
-            suitable for a web browser.
+            Socks-speaking applications. Same as
+            <option>socksListenAddress</option> but uses weaker
+            circuit isolation to provide performance suitable for a
+            web browser.
            '';
          };
 
@@ -145,9 +148,9 @@ in
           example = "accept 192.168.0.0/16, reject *";
           description = ''
             Entry policies to allow/deny SOCKS requests based on IP
-            address.  First entry that matches wins. If no SocksPolicy
+            address. First entry that matches wins. If no SocksPolicy
             is set, we accept all (and only) requests from
-            SocksListenAddress.
+            <option>socksListenAddress</option>.
           '';
         };
 
@@ -176,45 +179,147 @@ in
           description = ''
             Whether to enable relaying TOR traffic for others.
 
-            See https://www.torproject.org/docs/tor-doc-relay for details.
-          '';
-        };
-
-        isBridge = mkOption {
-          type = types.bool;
-          default = false;
-          description = ''
-            Bridge relays (or "bridges") are Tor relays that aren't
-            listed in the main directory. Since there is no complete
-            public list of them, even if an ISP is filtering
-            connections to all the known Tor relays, they probably
-            won't be able to block all the bridges.
-
-            A bridge relay can't be an exit relay.
-
-            You need to set relay.enable to true for this option to
-            take effect.
+            See <link xlink:href="https://www.torproject.org/docs/tor-doc-relay" />
+            for details.
 
-            The bridge is set up with an obfuscated transport proxy.
-
-            See https://www.torproject.org/bridges.html.en for more info.
+            Setting this to true requires setting
+            <option>services.tor.relay.role</option>
+            and
+            <option>services.tor.relay.port</option>
+            options.
           '';
         };
 
-        isExit = mkOption {
-          type = types.bool;
-          default = false;
+        role = mkOption {
+          type = types.enum [ "exit" "relay" "bridge" "private-bridge" ];
           description = ''
-            An exit relay allows Tor users to access regular Internet
-            services.
-
-            Unlike running a non-exit relay, running an exit relay may
-            expose you to abuse complaints. See
-            https://www.torproject.org/faq.html.en#ExitPolicies for
-            more info.
-
-            You can specify which services Tor users may access via
-            your exit relay using exitPolicy option.
+            Your role in Tor network. There're several options:
+
+            <variablelist>
+            <varlistentry>
+              <term><literal>exit</literal></term>
+              <listitem>
+                <para>
+                  An exit relay. This allows Tor users to access regular
+                  Internet services through your public IP.
+                </para>
+
+                <important><para>
+                  Running an exit relay may expose you to abuse
+                  complaints. See
+                  <link xlink:href="https://www.torproject.org/faq.html.en#ExitPolicies" />
+                  for more info.
+                </para></important>
+
+                <para>
+                  You can specify which services Tor users may access via
+                  your exit relay using <option>exitPolicy</option> option.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><literal>relay</literal></term>
+              <listitem>
+                <para>
+                  Regular relay. This allows Tor users to relay onion
+                  traffic to other Tor nodes, but not to public
+                  Internet.
+                </para>
+
+                <important><para>
+                  Note that some misconfigured and/or disrespectful
+                  towards privacy sites will block you even if your
+                  relay is not an exit relay. That is, just being listed
+                  in a public relay directory can have unwanted
+                  consequences.
+
+                  Which means you might not want to use
+                  this role if you browse public Internet from the same
+                  network as your relay, unless you want to write
+                  e-mails to those sites (you should!).
+                </para></important>
+
+                <para>
+                  See
+                  <link xlink:href="https://www.torproject.org/docs/tor-doc-relay.html.en" />
+                  for more info.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><literal>bridge</literal></term>
+              <listitem>
+                <para>
+                  Regular bridge. Works like a regular relay, but
+                  doesn't list you in the public relay directory and
+                  hides your Tor node behind obfsproxy.
+                </para>
+
+                <para>
+                  Using this option will make Tor advertise your bridge
+                  to users through various mechanisms like
+                  <link xlink:href="https://bridges.torproject.org/" />, though.
+                </para>
+
+                <important>
+                  <para>
+                    WARNING: THE FOLLOWING PARAGRAPH IS NOT LEGAL ADVISE.
+                    Consult with your lawer when in doubt.
+                  </para>
+
+                  <para>
+                    This role should be safe to use in most situations
+                    (unless the act of forwarding traffic for others is
+                    a punishable offence under your local laws, which
+                    would be pretty insane as it would make ISP
+                    illegal).
+                  </para>
+                </important>
+
+                <para>
+                  See <link xlink:href="https://www.torproject.org/docs/bridges.html.en" />
+                  for more info.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><literal>private-bridge</literal></term>
+              <listitem>
+                <para>
+                  Private bridge. Works like regular bridge, but does
+                  not advertise your node in any way.
+                </para>
+
+                <para>
+                  Using this role means that you won't contribute to Tor
+                  network in any way unless you advertise your node
+                  yourself in some way.
+                </para>
+
+                <para>
+                  Use this if you want to run a private bridge, for
+                  example because you'll give out your bridge address
+                  manually to your friends.
+                </para>
+
+                <para>
+                  Switching to this role after measurable time in
+                  "bridge" role is pretty useless as some Tor users
+                  would have learned about your node already. In the
+                  latter case you can still change
+                  <option>port</option> option.
+                </para>
+
+                <para>
+                  See <link xlink:href="https://www.torproject.org/docs/bridges.html.en" />
+                  for more info.
+                </para>
+              </listitem>
+            </varlistentry>
+            </variablelist>
           '';
         };
 
@@ -268,8 +373,8 @@ in
         };
 
         bandwidthRate = mkOption {
-          type = types.int;
-          default = 0;
+          type = types.nullOr types.int;
+          default = null;
           example = 100;
           description = ''
             Specify this to limit the bandwidth usage of relayed (server)
@@ -278,7 +383,7 @@ in
         };
 
         bandwidthBurst = mkOption {
-          type = types.int;
+          type = types.nullOr types.int;
           default = cfg.relay.bandwidthRate;
           example = 200;
           description = ''
@@ -288,9 +393,19 @@ in
           '';
         };
 
-        portSpec = mkOption {
-          type    = types.str;
-          example = "143";
+        address = mkOption {
+          type    = types.nullOr types.str;
+          default = null;
+          example = "noname.example.com";
+          description = ''
+            The IP address or full DNS name for advertised address of your relay.
+            Leave unset and Tor will guess.
+          '';
+        };
+
+        port = mkOption {
+          type    = types.either types.int types.str;
+          example = 143;
           description = ''
             What port to advertise for Tor connections. This corresponds to the
             <literal>ORPort</literal> section in the Tor manual; see
@@ -313,13 +428,15 @@ in
             considered first to last, and the first match wins. If you
             want to _replace_ the default exit policy, end this with
             either a reject *:* or an accept *:*. Otherwise, you're
-            _augmenting_ (prepending to) the default exit
-            policy. Leave commented to just use the default, which is
+            _augmenting_ (prepending to) the default exit policy.
+            Leave commented to just use the default, which is
             available in the man page or at
-            https://www.torproject.org/documentation.html
+            <link xlink:href="https://www.torproject.org/documentation.html" />.
 
-            Look at https://www.torproject.org/faq-abuse.html#TypicalAbuses
-            for issues you might encounter if you use the default exit policy.
+            Look at
+            <link xlink:href="https://www.torproject.org/faq-abuse.html#TypicalAbuses" />
+            for issues you might encounter if you use the default
+            exit policy.
 
             If certain IPs and ports are blocked externally, e.g. by
             your firewall, you should update your exit policy to
@@ -330,79 +447,122 @@ in
       };
 
       hiddenServices = mkOption {
-        type = types.attrsOf (types.submodule ({
-          options = {
-            hiddenServicePorts = mkOption {
-              type = types.listOf (types.submodule {
-                options = {
-                  virtualPort = mkOption {
-                    type = types.int;
-                    example = 80;
-                    description = "Virtual port.";
-                  };
-                  target = mkOption {
-                    type = types.nullOr types.str;
-                    default = null;
-                    example = "127.0.0.1:8080";
-                    description = ''
-                      Target virtual Port shall be mapped to.
-
-                      You may override the target port, address, or both by
-                      specifying a target of addr, port, addr:port, or
-                      unix:path. (You can specify an IPv6 target as
-                      [addr]:port. Unix paths may be quoted, and may use
-                      standard C escapes.)
-                    '';
-                  };
-                };
-              });
-              example = [ { virtualPort = 80; target = "127.0.0.1:8080"; } { virtualPort = 6667; } ];
-              description = ''
-                If target is <literal>null</literal> the virtual port is mapped
-                to the same port on 127.0.0.1 over TCP. You may use
-                <literal>target</literal> to overwrite this behaviour (see
-                description of target).
-
-                This corresponds to the <literal>HiddenServicePort VIRTPORT
-                [TARGET]</literal> option by looking at the tor manual
-                <citerefentry><refentrytitle>tor</refentrytitle>
-                <manvolnum>1</manvolnum></citerefentry> for more information.
-              '';
-            };
-            extraConfig = mkOption {
-              type = types.str;
-              default = "";
-              description = ''
-                Extra configuration. Contents will be added in the current
-                hidden service context.
-              '';
-            };
-          };
-        }));
+        description = ''
+          A set of static hidden services that terminate their Tor
+          circuits at this node.
+
+          Every element in this set declares a virtual onion host.
+
+          You can specify your onion address by putting corresponding
+          private key to an appropriate place in ${torDirectory}.
+
+          For services without private keys in ${torDirectory} Tor
+          daemon will generate random key pairs (which implies random
+          onion addresses) on restart. The latter could take a while,
+          please be patient.
+
+          <note><para>
+            Hidden services can be useful even if you don't intend to
+            actually <emphasis>hide</emphasis> them, since they can
+            also be seen as a kind of NAT traversal mechanism.
+
+            E.g. the example will make your sshd, whatever runs on
+            "8080" and your mail server available from anywhere where
+            the Tor network is available (which, with the help from
+            bridges, is pretty much everywhere), even if both client
+            and server machines are behind NAT you have no control
+            over.
+          </para></note>
+        '';
         default = {};
-        example = {
-          "/var/lib/tor/webserver" = {
-            hiddenServicePorts = [ { virtualPort = 80; } ];
+        example = literalExample ''
+          { "my-hidden-service-example".map = [
+              { port = 22; }                # map ssh port to this machine's ssh
+              { port = 80; toPort = 8080; } # map http port to whatever runs on 8080
+              { port = "sip"; toHost = "mail.example.com"; toPort = "imap"; } # because we can
+            ];
+          }
+        '';
+        type = types.loaOf (types.submodule ({name, config, ...}: {
+          options = {
+
+             name = mkOption {
+               type = types.str;
+               description = ''
+                 Name of this tor hidden service.
+
+                 This is purely descriptive.
+
+                 After restarting Tor daemon you should be able to
+                 find your .onion address in
+                 <literal>${torDirectory}/onion/$name/hostname</literal>.
+               '';
+             };
+
+             map = mkOption {
+               default = [];
+               description = "Port mapping for this hidden service.";
+               type = types.listOf (types.submodule ({config, ...}: {
+                 options = {
+
+                   port = mkOption {
+                     type = types.either types.int types.str;
+                     example = 80;
+                     description = ''
+                       Hidden service port to "bind to".
+                     '';
+                   };
+
+                   destination = mkOption {
+                     internal = true;
+                     type = types.str;
+                     description = "Forward these connections where?";
+                   };
+
+                   toHost = mkOption {
+                     type = types.str;
+                     default = "127.0.0.1";
+                     description = "Mapping destination host.";
+                   };
+
+                   toPort = mkOption {
+                     type = types.either types.int types.str;
+                     example = 8080;
+                     description = "Mapping destination port.";
+                   };
+
+                 };
+
+                 config = {
+                   toPort = mkDefault config.port;
+                   destination = mkDefault "${config.toHost}:${toString config.toPort}";
+                 };
+               }));
+             };
+
           };
-        };
-        description = ''
-          Configure hidden services.
 
-          Please consult the tor manual
-          <citerefentry><refentrytitle>tor</refentrytitle>
-          <manvolnum>1</manvolnum></citerefentry> for a more detailed
-          explanation. (search for 'HIDDEN').
-        '';
+          config = {
+            name = mkDefault name;
+          };
+        }));
       };
     };
   };
 
   config = mkIf cfg.enable {
-    assertions = singleton
-      { message = "Can't be both an exit and a bridge relay at the same time";
-        assertion =
-          cfg.relay.enable -> !(cfg.relay.isBridge && cfg.relay.isExit);
-      };
+    # Not sure if `cfg.relay.role == "private-bridge"` helps as tor
+    # sends a lot of stats
+    warnings = optional (cfg.relay.enable && cfg.hiddenServices != {})
+      ''
+        Running Tor hidden services on a public relay makes the
+        presence of hidden services visible through simple statistical
+        analysis of publicly available data.
+
+        You can safely ignore this warning if you don't intend to
+        actually hide your hidden services. In either case, you can
+        always create a container/VM with a separate Tor daemon instance.
+      '';
 
     users.extraGroups.tor.gid = config.ids.gids.tor;
     users.extraUsers.tor =
@@ -422,9 +582,13 @@ in
         restartTriggers = [ torRcFile ];
 
         # Translated from the upstream contrib/dist/tor.service.in
+        preStart = ''
+          install -o tor -g tor -d ${torDirectory}/onion
+          ${pkgs.tor}/bin/tor -f ${torRcFile} --verify-config
+        '';
+
         serviceConfig =
           { Type         = "simple";
-            ExecStartPre = "${pkgs.tor}/bin/tor -f ${torRcFile} --verify-config";
             ExecStart    = "${pkgs.tor}/bin/tor -f ${torRcFile} --RunAsDaemon 0";
             ExecReload   = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
             KillSignal   = "SIGINT";
diff --git a/nixos/modules/services/ttys/agetty.nix b/nixos/modules/services/ttys/agetty.nix
index f8dd75ea2c46..3429397d2cc2 100644
--- a/nixos/modules/services/ttys/agetty.nix
+++ b/nixos/modules/services/ttys/agetty.nix
@@ -85,12 +85,18 @@ in
       };
 
     systemd.services."container-getty@" =
-      { serviceConfig.ExecStart = gettyCmd "--noclear --keep-baud pts/%I 115200,38400,9600 $TERM";
+      { serviceConfig.ExecStart = [
+          "" # override upstream default with an empty ExecStart
+          (gettyCmd "--noclear --keep-baud pts/%I 115200,38400,9600 $TERM")
+        ];
         restartIfChanged = false;
       };
 
     systemd.services."console-getty" =
-      { serviceConfig.ExecStart = gettyCmd "--noclear --keep-baud console 115200,38400,9600 $TERM";
+      { serviceConfig.ExecStart = [
+          "" # override upstream default with an empty ExecStart
+          (gettyCmd "--noclear --keep-baud console 115200,38400,9600 $TERM")
+        ];
         serviceConfig.Restart = "always";
         restartIfChanged = false;
         enable = mkDefault config.boot.isContainer;
diff --git a/nixos/modules/services/web-apps/pgpkeyserver-lite.nix b/nixos/modules/services/web-apps/pgpkeyserver-lite.nix
new file mode 100644
index 000000000000..93f69bd12651
--- /dev/null
+++ b/nixos/modules/services/web-apps/pgpkeyserver-lite.nix
@@ -0,0 +1,75 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.pgpkeyserver-lite;
+  sksCfg = config.services.sks;
+
+  webPkg = cfg.package;
+
+in
+
+{
+
+  options = {
+
+    services.pgpkeyserver-lite = {
+
+      enable = mkEnableOption "pgpkeyserver-lite on a nginx vHost proxying to a gpg keyserver";
+
+      package = mkOption {
+        default = pkgs.pgpkeyserver-lite;
+        defaultText = "pkgs.pgpkeyserver-lite";
+        type = types.package;
+        description = "
+          Which webgui derivation to use.
+        ";
+      };
+
+      hostname = mkOption {
+        type = types.str;
+        description = "
+          Which hostname to set the vHost to that is proxying to sks.
+        ";
+      };     
+
+      hkpAddress = mkOption {
+        default = builtins.head sksCfg.hkpAddress;
+        type = types.str;
+        description = "
+          Wich ip address the sks-keyserver is listening on.
+        ";
+      };
+
+      hkpPort = mkOption {
+        default = sksCfg.hkpPort;
+        type = types.int;
+        description = "
+          Which port the sks-keyserver is listening on.
+        ";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    services.nginx.enable = true;
+
+    services.nginx.virtualHosts = let
+      hkpPort = builtins.toString cfg.hkpPort;
+    in {
+      "${cfg.hostname}" = {
+        root = webPkg;
+        locations = {
+          "/pks".extraConfig = ''
+            proxy_pass         http://${cfg.hkpAddress}:${hkpPort};
+            proxy_pass_header  Server;
+            add_header         Via "1.1 ${cfg.hostname}";
+          '';
+        };
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/web-servers/phpfpm/default.nix b/nixos/modules/services/web-servers/phpfpm/default.nix
index 26f546022035..f9febbfbacdf 100644
--- a/nixos/modules/services/web-servers/phpfpm/default.nix
+++ b/nixos/modules/services/web-servers/phpfpm/default.nix
@@ -150,7 +150,6 @@ in {
           PrivateDevices = true;
           ProtectSystem = "full";
           ProtectHome = true;
-          NoNewPrivileges = true;
           RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6";
           Type = "notify";
           ExecStart = "${cfg.phpPackage}/bin/php-fpm -y ${cfgFile} -c ${phpIni}";
diff --git a/nixos/modules/services/web-servers/varnish/default.nix b/nixos/modules/services/web-servers/varnish/default.nix
index 5433db3b91c8..c3bc065d4651 100644
--- a/nixos/modules/services/web-servers/varnish/default.nix
+++ b/nixos/modules/services/web-servers/varnish/default.nix
@@ -7,14 +7,10 @@ with lib;
 {
   options = {
     services.varnish = {
-      enable = mkOption {
-        default = false;
-        description = "
-          Enable the Varnish Server.
-        ";
-      };
+      enable = mkEnableOption "Varnish Server";
 
       http_address = mkOption {
+        type = types.str;
         default = "*:6081";
         description = "
           HTTP listen address and port.
@@ -22,17 +18,37 @@ with lib;
       };
 
       config = mkOption {
+        type = types.lines;
         description = "
           Verbatim default.vcl configuration.
         ";
       };
 
       stateDir = mkOption {
+        type = types.path;
         default = "/var/spool/varnish/${config.networking.hostName}";
         description = "
           Directory holding all state for Varnish to run.
         ";
       };
+
+      extraModules = mkOption {
+        type = types.listOf types.package;
+        default = [];
+        example = literalExample "[ pkgs.varnish-geoip ]";
+        description = "
+          Varnish modules (except 'std').
+        ";
+      };
+
+      extraCommandLine = mkOption {
+        type = types.str;
+        default = "";
+        example = "-s malloc,256M";
+        description = "
+          Command line switches for varnishd (run 'varnishd -?' to get list of options)
+        ";
+      };
     };
 
   };
@@ -42,6 +58,7 @@ with lib;
     systemd.services.varnish = {
       description = "Varnish";
       wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
       preStart = ''
         mkdir -p ${cfg.stateDir}
         chown -R varnish:varnish ${cfg.stateDir}
@@ -49,8 +66,19 @@ with lib;
       postStop = ''
         rm -rf ${cfg.stateDir}
       '';
-      serviceConfig.ExecStart = "${pkgs.varnish}/sbin/varnishd -a ${cfg.http_address} -f ${pkgs.writeText "default.vcl" cfg.config} -n ${cfg.stateDir} -u varnish";
-      serviceConfig.Type = "forking";
+      serviceConfig = {
+        Type = "simple";
+        PermissionsStartOnly = true;
+        ExecStart = "${pkgs.varnish}/sbin/varnishd -a ${cfg.http_address} -f ${pkgs.writeText "default.vcl" cfg.config} -n ${cfg.stateDir} -F ${cfg.extraCommandLine}"
+          + optionalString (cfg.extraModules != []) " -p vmod_path='${makeSearchPathOutput "lib" "lib/varnish/vmods" ([pkgs.varnish] ++ cfg.extraModules)}' -r vmod_path";
+        Restart = "always";
+        RestartSec = "5s";
+        User = "varnish";
+        Group = "varnish";
+        AmbientCapabilities = "cap_net_bind_service";
+        NoNewPrivileges = true;
+        LimitNOFILE = 131072;
+      };
     };
 
     environment.systemPackages = [ pkgs.varnish ];
diff --git a/nixos/modules/testing/minimal-kernel.nix b/nixos/modules/testing/minimal-kernel.nix
index a463cb803ade..7c2b9c05cf9a 100644
--- a/nixos/modules/testing/minimal-kernel.nix
+++ b/nixos/modules/testing/minimal-kernel.nix
@@ -6,7 +6,7 @@ let
   );
 
   origKernel = pkgs.buildLinux {
-    inherit (pkgs.linux) src version;
+    inherit (pkgs.linux) src version stdenv;
     inherit configfile;
     allowImportFromDerivation = true;
     kernelPatches = [ pkgs.kernelPatches.cifs_timeout_2_6_38 ];
diff --git a/nixos/modules/virtualisation/libvirtd.nix b/nixos/modules/virtualisation/libvirtd.nix
index b24ea0f33c9e..ade0ba531cfa 100644
--- a/nixos/modules/virtualisation/libvirtd.nix
+++ b/nixos/modules/virtualisation/libvirtd.nix
@@ -15,7 +15,7 @@ let
   '';
   qemuConfigFile = pkgs.writeText "qemu.conf" ''
     ${optionalString cfg.qemuOvmf ''
-      nvram = ["${pkgs.OVMF.fd}/FV/OVMF_CODE.fd:${pkgs.OVMF.fd}/FV/OVMF_VARS.fd"]
+      nvram = ["/run/libvirt/nix-ovmf/OVMF_CODE.fd:/run/libvirt/nix-ovmf/OVMF_VARS.fd"]
     ''}
     ${cfg.qemuVerbatimConfig}
   '';
@@ -102,9 +102,7 @@ in {
 
   config = mkIf cfg.enable {
 
-    environment.systemPackages = with pkgs;
-      [ libvirt netcat-openbsd ]
-       ++ optional cfg.enableKVM qemu_kvm;
+    environment.systemPackages = with pkgs; [ libvirt netcat-openbsd ];
 
     boot.kernelModules = [ "tun" ];
 
@@ -129,7 +127,6 @@ in {
           dnsmasq
           ebtables
         ]
-        ++ optional cfg.enableKVM qemu_kvm
         ++ optional vswitch.enable vswitch.package;
 
       preStart = ''
@@ -155,34 +152,31 @@ in {
         # Copy generated qemu config to libvirt directory
         cp -f ${qemuConfigFile} /var/lib/libvirt/qemu.conf
 
-        # libvirtd puts the full path of the emulator binary in the machine
-        # config file. But this path can unfortunately be garbage collected
-        # while still being used by the virtual machine. So update the
-        # emulator path on each startup to something valid (re-scan $PATH).
-        for file in /var/lib/libvirt/qemu/*.xml /var/lib/libvirt/lxc/*.xml; do
-            test -f "$file" || continue
-            # get (old) emulator path from config file
-            emulator=$("${pkgs.xmlstarlet}/bin/xmlstarlet" select --template --value-of "/domain/devices/emulator" "$file")
-            # get a (definitely) working emulator path by re-scanning $PATH
-            new_emulator=$(PATH=${pkgs.libvirt}/libexec:$PATH command -v $(basename "$emulator"))
-            # write back
-            "${pkgs.xmlstarlet}/bin/xmlstarlet" edit --inplace --update "/domain/devices/emulator" -v "$new_emulator" "$file"
-
-            # Also refresh the OVMF path. Files with no matches are ignored.
-            "${pkgs.xmlstarlet}/bin/xmlstarlet" edit --inplace --update "/domain/os/loader" -v "${pkgs.OVMF.fd}/FV/OVMF_CODE.fd" "$file"
-        done
-      ''; # */
+        # stable (not GC'able as in /nix/store) paths for using in <emulator> section of xml configs
+        mkdir -p /run/libvirt/nix-emulators
+        ln -s --force ${pkgs.libvirt}/libexec/libvirt_lxc /run/libvirt/nix-emulators/
+        ${optionalString pkgs.stdenv.isAarch64 "ln -s --force ${pkgs.qemu}/bin/qemu-system-aarch64 /run/libvirt/nix-emulators/"}
+        ${optionalString cfg.enableKVM         "ln -s --force ${pkgs.qemu_kvm}/bin/qemu-kvm        /run/libvirt/nix-emulators/"}
+
+        ${optionalString cfg.qemuOvmf ''
+            mkdir -p /run/libvirt/nix-ovmf
+            ln -s --force ${pkgs.OVMF.fd}/FV/OVMF_CODE.fd /run/libvirt/nix-ovmf/
+            ln -s --force ${pkgs.OVMF.fd}/FV/OVMF_VARS.fd /run/libvirt/nix-ovmf/
+        ''}
+      '';
 
       serviceConfig = {
         Type = "notify";
         KillMode = "process"; # when stopping, leave the VMs alone
-        Restart = "on-failure";
+        Restart = "no";
       };
+      restartIfChanged = false;
     };
 
     systemd.services.libvirt-guests = {
       wantedBy = [ "multi-user.target" ];
       path = with pkgs; [ coreutils libvirt gawk ];
+      restartIfChanged = false;
     };
 
     systemd.sockets.virtlogd = {
@@ -194,6 +188,7 @@ in {
     systemd.services.virtlogd = {
       description = "Virtual machine log manager";
       serviceConfig.ExecStart = "@${pkgs.libvirt}/sbin/virtlogd virtlogd";
+      restartIfChanged = false;
     };
 
     systemd.sockets.virtlockd = {
@@ -205,6 +200,7 @@ in {
     systemd.services.virtlockd = {
       description = "Virtual machine lock manager";
       serviceConfig.ExecStart = "@${pkgs.libvirt}/sbin/virtlockd virtlockd";
+      restartIfChanged = false;
     };
   };
 }
diff --git a/nixos/modules/virtualisation/vmware-guest.nix b/nixos/modules/virtualisation/vmware-guest.nix
index ce1224a8f131..68930a0e3254 100644
--- a/nixos/modules/virtualisation/vmware-guest.nix
+++ b/nixos/modules/virtualisation/vmware-guest.nix
@@ -33,7 +33,7 @@ in
         serviceConfig.ExecStart = "${open-vm-tools}/bin/vmtoolsd";
       };
 
-    environment.etc."vmware-tools".source = "${pkgs.open-vm-tools}/etc/vmware-tools/*";
+    environment.etc."vmware-tools".source = "${open-vm-tools}/etc/vmware-tools/*";
 
     services.xserver = mkIf (!cfg.headless) {
       videoDrivers = mkOverride 50 [ "vmware" ];