summary refs log tree commit diff
path: root/nixos/modules/services/networking
diff options
context:
space:
mode:
authorVladimír Čunát <vcunat@gmail.com>2016-03-08 09:57:58 +0100
committerVladimír Čunát <vcunat@gmail.com>2016-03-08 09:58:19 +0100
commit09af15654f0c8091f1b9e0bbb2e523cdee194442 (patch)
treee648edef1ce4c64c533f2593aa22b8015cf0e506 /nixos/modules/services/networking
parentf306e67e15bdbe9a8358c9f81319fc4fcbadc2eb (diff)
parent0ee75214f336474e127c2e3546c0406a0c4d5fa7 (diff)
downloadnixlib-09af15654f0c8091f1b9e0bbb2e523cdee194442.tar
nixlib-09af15654f0c8091f1b9e0bbb2e523cdee194442.tar.gz
nixlib-09af15654f0c8091f1b9e0bbb2e523cdee194442.tar.bz2
nixlib-09af15654f0c8091f1b9e0bbb2e523cdee194442.tar.lz
nixlib-09af15654f0c8091f1b9e0bbb2e523cdee194442.tar.xz
nixlib-09af15654f0c8091f1b9e0bbb2e523cdee194442.tar.zst
nixlib-09af15654f0c8091f1b9e0bbb2e523cdee194442.zip
Merge master into closure-size
The kde-5 stuff still didn't merge well.
I hand-fixed what I saw, but there may be more problems.
Diffstat (limited to 'nixos/modules/services/networking')
-rw-r--r--nixos/modules/services/networking/bird.nix4
-rw-r--r--nixos/modules/services/networking/consul.nix1
-rw-r--r--nixos/modules/services/networking/ddclient.nix1
-rw-r--r--nixos/modules/services/networking/dnscrypt-proxy.nix5
-rw-r--r--nixos/modules/services/networking/ejabberd.nix1
-rw-r--r--nixos/modules/services/networking/ifplugd.nix82
-rw-r--r--nixos/modules/services/networking/kippo.nix6
-rw-r--r--nixos/modules/services/networking/libreswan.nix126
-rw-r--r--nixos/modules/services/networking/networkmanager.nix8
-rw-r--r--nixos/modules/services/networking/nntp-proxy.nix235
-rw-r--r--nixos/modules/services/networking/nsd.nix874
-rw-r--r--nixos/modules/services/networking/pdnsd.nix93
-rw-r--r--nixos/modules/services/networking/ssh/sshd.nix4
-rw-r--r--nixos/modules/services/networking/syncthing.nix21
-rw-r--r--nixos/modules/services/networking/tlsdated.nix1
-rw-r--r--nixos/modules/services/networking/unbound.nix46
-rw-r--r--nixos/modules/services/networking/wpa_supplicant.nix10
-rw-r--r--nixos/modules/services/networking/zerotierone.nix7
18 files changed, 1012 insertions, 513 deletions
diff --git a/nixos/modules/services/networking/bird.nix b/nixos/modules/services/networking/bird.nix
index e7e1db191529..e76cdac14ca8 100644
--- a/nixos/modules/services/networking/bird.nix
+++ b/nixos/modules/services/networking/bird.nix
@@ -30,7 +30,7 @@ in
 
       user = mkOption {
         type = types.string;
-        default = "ircd";
+        default = "bird";
         description = ''
           BIRD Internet Routing Daemon user.
         '';
@@ -38,7 +38,7 @@ in
 
       group = mkOption {
         type = types.string;
-        default = "ircd";
+        default = "bird";
         description = ''
           BIRD Internet Routing Daemon group.
         '';
diff --git a/nixos/modules/services/networking/consul.nix b/nixos/modules/services/networking/consul.nix
index 58dad56014b0..2aa101f980da 100644
--- a/nixos/modules/services/networking/consul.nix
+++ b/nixos/modules/services/networking/consul.nix
@@ -33,6 +33,7 @@ in
       package = mkOption {
         type = types.package;
         default = pkgs.consul;
+        defaultText = "pkgs.consul";
         description = ''
           The package used for the Consul agent and CLI.
         '';
diff --git a/nixos/modules/services/networking/ddclient.nix b/nixos/modules/services/networking/ddclient.nix
index e60520c742bd..c5dd1e71c189 100644
--- a/nixos/modules/services/networking/ddclient.nix
+++ b/nixos/modules/services/networking/ddclient.nix
@@ -127,7 +127,6 @@ in
       wantedBy = [ "multi-user.target" ];
       after = [ "network.target" ];
 
-      environment.SSL_CERT_FILE = "/etc/ssl/certs/ca-certificates.crt";
       serviceConfig = {
         # Uncomment this if too many problems occur:
         # Type = "forking";
diff --git a/nixos/modules/services/networking/dnscrypt-proxy.nix b/nixos/modules/services/networking/dnscrypt-proxy.nix
index 9340be28205a..016b6a12cd61 100644
--- a/nixos/modules/services/networking/dnscrypt-proxy.nix
+++ b/nixos/modules/services/networking/dnscrypt-proxy.nix
@@ -52,7 +52,10 @@ in
         default = "opendns";
         type = types.nullOr types.string;
         description = ''
-          The name of the upstream DNSCrypt resolver to use.
+          The name of the upstream DNSCrypt resolver to use. See
+          <literal>${resolverListFile}</literal> for alternative resolvers
+          (e.g., if you are concerned about logging and/or server
+          location).
         '';
       };
       customResolver = mkOption {
diff --git a/nixos/modules/services/networking/ejabberd.nix b/nixos/modules/services/networking/ejabberd.nix
index 7af11f37a43c..8ffce23a4b10 100644
--- a/nixos/modules/services/networking/ejabberd.nix
+++ b/nixos/modules/services/networking/ejabberd.nix
@@ -32,6 +32,7 @@ in {
       package = mkOption {
         type = types.package;
         default = pkgs.ejabberd;
+        defaultText = "pkgs.ejabberd";
         description = "ejabberd server package to use";
       };
 
diff --git a/nixos/modules/services/networking/ifplugd.nix b/nixos/modules/services/networking/ifplugd.nix
deleted file mode 100644
index 00b94fe2284e..000000000000
--- a/nixos/modules/services/networking/ifplugd.nix
+++ /dev/null
@@ -1,82 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-
-  inherit (pkgs) ifplugd;
-
-  cfg = config.networking.interfaceMonitor;
-
-  # The ifplugd action script, which is called whenever the link
-  # status changes (i.e., a cable is plugged in or unplugged).
-  plugScript = pkgs.writeScript "ifplugd.action"
-    ''
-      #! ${pkgs.stdenv.shell}
-      iface="$1"
-      status="$2"
-      ${cfg.commands}
-    '';
-
-in
-
-{
-
-  ###### interface
-
-  options = {
-
-    networking.interfaceMonitor.enable = mkOption {
-      type = types.bool;
-      default = false;
-      description = ''
-        If <literal>true</literal>, monitor Ethernet interfaces for
-        cables being plugged in or unplugged.  When this occurs, the
-        commands specified in
-        <option>networking.interfaceMonitor.commands</option> are
-        executed.
-      '';
-    };
-
-    networking.interfaceMonitor.beep = mkOption {
-      type = types.bool;
-      default = false;
-      description = ''
-        If <literal>true</literal>, beep when an Ethernet cable is
-        plugged in or unplugged.
-      '';
-    };
-
-    networking.interfaceMonitor.commands = mkOption {
-      type = types.lines;
-      default = "";
-      description = ''
-        Shell commands to be executed when the link status of an
-        interface changes.  On invocation, the shell variable
-        <varname>iface</varname> contains the name of the interface,
-        while the variable <varname>status</varname> contains either
-        <literal>up</literal> or <literal>down</literal> to indicate
-        the new status.
-      '';
-    };
-
-  };
-
-
-  ###### implementation
-
-  config = mkIf cfg.enable {
-    systemd.services.ifplugd = {
-      description = "Network interface connectivity monitor";
-      after = [ "network-interfaces.target" ];
-      wantedBy = [ "multi-user.target" ];
-      script = ''
-        ${ifplugd}/sbin/ifplugd --no-daemon --no-startup --no-shutdown \
-          ${if config.networking.interfaceMonitor.beep then "" else "--no-beep"} \
-          --run ${plugScript}
-      '';
-    };
-
-    environment.systemPackages = [ ifplugd ];
-  };
-}
diff --git a/nixos/modules/services/networking/kippo.nix b/nixos/modules/services/networking/kippo.nix
index 7d70a3d05fa7..5f3efcd133a1 100644
--- a/nixos/modules/services/networking/kippo.nix
+++ b/nixos/modules/services/networking/kippo.nix
@@ -54,7 +54,7 @@ rec {
   };
   config = mkIf cfg.enable {
     environment.systemPackages = with pkgs.pythonPackages; [
-      python twisted pycrypto pyasn1 ];
+      python twisted_11 pycrypto pyasn1 ];
 
     environment.etc."kippo.cfg".text = ''
         # Automatically generated by NixOS.
@@ -84,7 +84,7 @@ rec {
       description = "Kippo Web Server";
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
-      environment.PYTHONPATH = "${pkgs.kippo}/src/:${pkgs.pythonPackages.pycrypto}/lib/python2.7/site-packages/:${pkgs.pythonPackages.pyasn1}/lib/python2.7/site-packages/:${pkgs.pythonPackages.python}/lib/python2.7/site-packages/:${pkgs.pythonPackages.twisted}/lib/python2.7/site-packages/:.";
+      environment.PYTHONPATH = "${pkgs.kippo}/src/:${pkgs.pythonPackages.pycrypto}/lib/python2.7/site-packages/:${pkgs.pythonPackages.pyasn1}/lib/python2.7/site-packages/:${pkgs.pythonPackages.python}/lib/python2.7/site-packages/:${pkgs.pythonPackages.twisted_11}/lib/python2.7/site-packages/:.";
       preStart = ''
         if [ ! -d ${cfg.varPath}/ ] ; then
             mkdir -p ${cfg.logPath}/tty
@@ -107,7 +107,7 @@ rec {
         fi
       '';
 
-      serviceConfig.ExecStart = "${pkgs.pythonPackages.twisted}/bin/twistd -y ${pkgs.kippo}/src/kippo.tac --syslog --rundir=${cfg.varPath}/ --pidfile=${cfg.pidPath}/kippo.pid --prefix=kippo -n";
+      serviceConfig.ExecStart = "${pkgs.pythonPackages.twisted_11}/bin/twistd -y ${pkgs.kippo}/src/kippo.tac --syslog --rundir=${cfg.varPath}/ --pidfile=${cfg.pidPath}/kippo.pid --prefix=kippo -n";
       serviceConfig.PermissionsStartOnly = true;
       serviceConfig.User = "kippo"; 
       serviceConfig.Group = "kippo"; 
diff --git a/nixos/modules/services/networking/libreswan.nix b/nixos/modules/services/networking/libreswan.nix
new file mode 100644
index 000000000000..3866b216f8ef
--- /dev/null
+++ b/nixos/modules/services/networking/libreswan.nix
@@ -0,0 +1,126 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.libreswan;
+
+  libexec = "${pkgs.libreswan}/libexec/ipsec";
+  ipsec = "${pkgs.libreswan}/sbin/ipsec";
+
+  trim = chars: str: let
+      nonchars = filter (x : !(elem x.value chars))
+                  (imap (i: v: {ind = (sub i 1); value = v;}) (stringToCharacters str));
+    in
+      if length nonchars == 0 then ""
+      else substring (head nonchars).ind (add 1 (sub (last nonchars).ind (head nonchars).ind)) str;
+  indent = str: concatStrings (concatMap (s: ["  " (trim [" " "\t"] s) "\n"]) (splitString "\n" str));
+  configText = indent (toString cfg.configSetup);
+  connectionText = concatStrings (mapAttrsToList (n: v: 
+    ''
+      conn ${n}
+      ${indent v}
+
+    '') cfg.connections);
+  configFile = pkgs.writeText "ipsec.conf"
+    ''
+      config setup
+      ${configText}
+      
+      ${connectionText}
+    '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.libreswan = {
+
+      enable = mkEnableOption "libreswan ipsec service";
+
+      configSetup = mkOption {
+        type = types.lines;
+        default = ''
+            protostack=netkey
+            nat_traversal=yes
+            virtual_private=%v4:10.0.0.0/8,%v4:192.168.0.0/16,%v4:172.16.0.0/12,%v4:25.0.0.0/8,%v4:100.64.0.0/10,%v6:fd00::/8,%v6:fe80::/10
+        '';
+        example = ''
+            secretsfile=/root/ipsec.secrets
+            protostack=netkey
+            nat_traversal=yes
+            virtual_private=%v4:10.0.0.0/8,%v4:192.168.0.0/16,%v4:172.16.0.0/12,%v4:25.0.0.0/8,%v4:100.64.0.0/10,%v6:fd00::/8,%v6:fe80::/10
+        '';
+        description = "Options to go in the 'config setup' section of the libreswan ipsec configuration";
+      };
+
+      connections = mkOption {
+        type = types.attrsOf types.lines;
+        default = {};
+        example = {
+          myconnection = ''
+            auto=add
+            left=%defaultroute
+            leftid=@user
+
+            right=my.vpn.com
+
+            ikev2=no
+            ikelifetime=8h
+          '';
+        };
+        description = "A set of connections to define for the libreswan ipsec service";
+      };
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ pkgs.libreswan pkgs.iproute ];
+
+    systemd.services.ipsec = {
+      description = "Internet Key Exchange (IKE) Protocol Daemon for IPsec";
+      path = [
+        "${pkgs.libreswan}"
+        "${pkgs.iproute}"
+        "${pkgs.procps}"
+      ];
+
+      wants = [ "network-online.target" ];
+      after = [ "network-online.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        Type = "simple";
+        Restart = "always";
+        EnvironmentFile = "${pkgs.libreswan}/etc/sysconfig/pluto";
+        ExecStartPre = [
+          "${libexec}/addconn --config ${configFile} --checkconfig"
+          "${libexec}/_stackmanager start"
+          "${ipsec} --checknss"
+          "${ipsec} --checknflog"
+        ];
+        ExecStart = "${libexec}/pluto --config ${configFile} --nofork \$PLUTO_OPTIONS";
+        ExecStop = "${libexec}/whack --shutdown";
+        ExecStopPost = [
+          "${pkgs.iproute}/bin/ip xfrm policy flush"
+          "${pkgs.iproute}/bin/ip xfrm state flush"
+          "${ipsec} --stopnflog"
+        ];
+        ExecReload = "${libexec}/whack --listen";
+      };
+
+    };
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/networkmanager.nix b/nixos/modules/services/networking/networkmanager.nix
index 01c05fb4a245..9912ad9ae3fc 100644
--- a/nixos/modules/services/networking/networkmanager.nix
+++ b/nixos/modules/services/networking/networkmanager.nix
@@ -21,6 +21,9 @@ let
 
     [logging]
     level=WARN
+
+    [connection]
+    ipv6.ip6-privacy=2
   '';
 
   /*
@@ -228,6 +231,11 @@ in {
     users.extraUsers = [{
       name = "nm-openvpn";
       uid = config.ids.uids.nm-openvpn;
+    }
+    {
+      # to enable link-local connections
+      name = "avahi-autoipd";
+      uid = config.ids.uids.avahi-autoipd;
     }];
 
     systemd.packages = cfg.packages;
diff --git a/nixos/modules/services/networking/nntp-proxy.nix b/nixos/modules/services/networking/nntp-proxy.nix
new file mode 100644
index 000000000000..cfa662c7311b
--- /dev/null
+++ b/nixos/modules/services/networking/nntp-proxy.nix
@@ -0,0 +1,235 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  inherit (pkgs) nntp-proxy;
+
+  proxyUser = "nntp-proxy";
+
+  cfg = config.services.nntp-proxy;
+
+  configBool = b: if b then "TRUE" else "FALSE";
+
+  confFile = pkgs.writeText "nntp-proxy.conf" ''
+    nntp_server:
+    {
+      # NNTP Server host and port address
+      server = "${cfg.upstreamServer}";
+      port = ${toString cfg.upstreamPort};
+      # NNTP username
+      username = "${cfg.upstreamUser}";
+      # NNTP password in clear text
+      password = "${cfg.upstreamPassword}";
+      # Maximum number of connections allowed by the NNTP
+      max_connections = ${toString cfg.upstreamMaxConnections};
+    };
+
+    proxy:
+    {
+      # Local address and port to bind to
+      bind_ip = "${cfg.listenAddress}";
+      bind_port = ${toString cfg.port};
+
+      # SSL key and cert file
+      ssl_key = "${cfg.sslKey}";
+      ssl_cert = "${cfg.sslCert}";
+
+      # prohibit users from posting
+      prohibit_posting = ${configBool cfg.prohibitPosting};
+      # Verbose levels: ERROR, WARNING, NOTICE, INFO, DEBUG
+      verbose = "${toUpper cfg.verbosity}";
+      # Password is made with: 'mkpasswd -m sha-512 <password>'
+      users = (${concatStringsSep ",\n" (mapAttrsToList (username: userConfig:
+        ''
+          {
+              username = "${username}";
+              password = "${userConfig.passwordHash}";
+              max_connections = ${toString userConfig.maxConnections};
+          }
+        '') cfg.users)});
+    };
+  '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.nntp-proxy = {
+      enable = mkEnableOption "NNTP-Proxy";
+
+      upstreamServer = mkOption {
+        type = types.str;
+        default = "";
+        example = "ssl-eu.astraweb.com";
+        description = ''
+          Upstream server address
+        '';
+      };
+
+      upstreamPort = mkOption {
+        type = types.int;
+        default = 563;
+        description = ''
+          Upstream server port
+        '';
+      };
+
+      upstreamMaxConnections = mkOption {
+        type = types.int;
+        default = 20;
+        description = ''
+          Upstream server maximum allowed concurrent connections
+        '';
+      };
+
+      upstreamUser = mkOption {
+        type = types.str;
+        default = "";
+        description = ''
+          Upstream server username
+        '';
+      };
+
+      upstreamPassword = mkOption {
+        type = types.str;
+        default = "";
+        description = ''
+          Upstream server password
+        '';
+      };
+
+      listenAddress = mkOption {
+        type = types.str;
+        default = "127.0.0.1";
+        example = "[::]";
+        description = ''
+          Proxy listen address (IPv6 literal addresses need to be enclosed in "[" and "]" characters)
+        '';
+      };
+
+      port = mkOption {
+        type = types.int;
+        default = 5555;
+        description = ''
+          Proxy listen port
+        '';
+      };
+
+      sslKey = mkOption {
+        type = types.str;
+        default = "key.pem";
+        example = "/path/to/your/key.file";
+        description = ''
+          Proxy ssl key path
+        '';
+      };
+
+      sslCert = mkOption {
+        type = types.str;
+        default = "cert.pem";
+        example = "/path/to/your/cert.file";
+        description = ''
+          Proxy ssl certificate path
+        '';
+      };
+
+      prohibitPosting = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to prohibit posting to the upstream server
+        '';
+      };
+
+      verbosity = mkOption {
+        type = types.str;
+        default = "info";
+        example = "error";
+        description = ''
+          Verbosity level (error, warning, notice, info, debug)
+        '';
+      };
+
+      users = mkOption {
+        type = types.attrsOf (types.submodule {
+          options = {
+            username = mkOption {
+              type = types.str;
+              default = null;
+              description = ''
+                Username
+              '';
+            };
+
+            passwordHash = mkOption {
+              type = types.str;
+              default = null;
+              example = "$6$GtzE7FrpE$wwuVgFYU.TZH4Rz.Snjxk9XGua89IeVwPQ/fEUD8eujr40q5Y021yhn0aNcsQ2Ifw.BLclyzvzgegopgKcneL0";
+              description = ''
+                SHA-512 password hash (can be generated by
+                <code>mkpasswd -m sha-512 &lt;password&gt;</code>)
+              '';
+            };
+
+            maxConnections = mkOption {
+              type = types.int;
+              default = 1;
+              description = ''
+                Maximum number of concurrent connections to the proxy for this user
+              '';
+            };
+          };
+        });
+        description = ''
+          NNTP-Proxy user configuration
+        '';
+
+        default = {};
+        example = literalExample ''
+          "user1" = {
+            passwordHash = "$6$1l0t5Kn2Dk$appzivc./9l/kjq57eg5UCsBKlcfyCr0zNWYNerKoPsI1d7eAwiT0SVsOVx/CTgaBNT/u4fi2vN.iGlPfv1ek0";
+            maxConnections = 5;
+          };
+          "anotheruser" = {
+            passwordHash = "$6$6lwEsWB.TmsS$W7m1riUx4QrA8pKJz8hvff0dnF1NwtZXgdjmGqA1Dx2MDPj07tI9GNcb0SWlMglE.2/hBgynDdAd/XqqtRqVQ0";
+            maxConnections = 7;
+          };
+        '';
+      };
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    users.extraUsers = singleton
+      { name = proxyUser;
+        uid = config.ids.uids.nntp-proxy;
+        description = "NNTP-Proxy daemon user";
+      };
+
+    systemd.services.nntp-proxy = {
+      description = "NNTP proxy";
+      after = [ "network.target" "nss-lookup.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = { User="${proxyUser}"; };
+      serviceConfig.ExecStart = "${nntp-proxy}/bin/nntp-proxy ${confFile}";
+      preStart = ''
+        if [ ! \( -f ${cfg.sslCert} -a -f ${cfg.sslKey} \) ]; then
+          ${pkgs.openssl}/bin/openssl req -subj '/CN=AutoGeneratedCert/O=NixOS Service/C=US' \
+          -new -newkey rsa:2048 -days 365 -nodes -x509 -keyout ${cfg.sslKey} -out ${cfg.sslCert};
+        fi
+      '';
+    };
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/nsd.nix b/nixos/modules/services/networking/nsd.nix
index e85f26811257..333a3378c4cc 100644
--- a/nixos/modules/services/networking/nsd.nix
+++ b/nixos/modules/services/networking/nsd.nix
@@ -7,92 +7,118 @@ let
 
   username = "nsd";
   stateDir = "/var/lib/nsd";
-  pidFile  = stateDir + "/var/nsd.pid";
+  pidFile = stateDir + "/var/nsd.pid";
 
+  # build nsd with the options needed for the given config
   nsdPkg = pkgs.nsd.override {
     bind8Stats = cfg.bind8Stats;
-    ipv6       = cfg.ipv6;
-    ratelimit  = cfg.ratelimit.enable;
+    ipv6 = cfg.ipv6;
+    ratelimit = cfg.ratelimit.enable;
     rootServer = cfg.rootServer;
-    zoneStats  = length (collect (x: (x.zoneStats or null) != null) cfg.zones) > 0;
+    zoneStats = length (collect (x: (x.zoneStats or null) != null) cfg.zones) > 0;
   };
 
-  zoneFiles = pkgs.stdenv.mkDerivation {
-    preferLocalBuild = true;
+
+  nsdEnv = pkgs.buildEnv {
     name = "nsd-env";
-    buildCommand = concatStringsSep "\n"
-      [ "mkdir -p $out"
-        (concatStrings (mapAttrsToList (zoneName: zoneOptions: ''
-          cat > "$out/${zoneName}" <<_EOF_
-          ${zoneOptions.data}
-          _EOF_
-        '') zoneConfigs))
-      ];
+
+    paths = [ configFile ]
+      ++ mapAttrsToList (name: zone: writeZoneData name zone.data) zoneConfigs;
+
+    postBuild = ''
+      echo "checking zone files"
+      cd $out/zones
+
+      for zoneFile in *; do
+        ${nsdPkg}/sbin/nsd-checkzone "$zoneFile" "$zoneFile" || {
+          if grep -q \\\\\\$ "$zoneFile"; then
+            echo zone "$zoneFile" contains escaped dollar signes \\\$
+            echo Escaping them is not needed any more. Please make shure \
+                 to unescape them where they prefix a variable name
+          fi
+
+          exit 1
+        }
+      done
+
+      echo "checking configuration file"
+      ${nsdPkg}/sbin/nsd-checkconf $out/nsd.conf
+    '';
+  };
+
+  writeZoneData = name: text: pkgs.writeTextFile {
+    inherit name text;
+    destination = "/zones/${name}";
   };
 
-  configFile = pkgs.writeText "nsd.conf" ''
+
+  # options are ordered alphanumerically by the nixos option name
+  configFile = pkgs.writeTextDir "nsd.conf" ''
     server:
-      username: ${username}
       chroot:   "${stateDir}"
+      username: ${username}
 
       # The directory for zonefile: files. The daemon chdirs here.
       zonesdir: "${stateDir}"
 
       # the list of dynamically added zones.
-      zonelistfile: "${stateDir}/var/zone.list"
       database:     "${stateDir}/var/nsd.db"
       pidfile:      "${pidFile}"
       xfrdfile:     "${stateDir}/var/xfrd.state"
       xfrdir:       "${stateDir}/tmp"
+      zonelistfile: "${stateDir}/var/zone.list"
 
       # interfaces
     ${forEach "  ip-address: " cfg.interfaces}
 
-      server-count:        ${toString cfg.serverCount}
+      hide-version:        ${yesOrNo  cfg.hideVersion}
+      identity:            "${cfg.identity}"
       ip-transparent:      ${yesOrNo  cfg.ipTransparent}
       do-ip4:              ${yesOrNo  cfg.ipv4}
+      ipv4-edns-size:      ${toString cfg.ipv4EDNSSize}
       do-ip6:              ${yesOrNo  cfg.ipv6}
-      port:                ${toString cfg.port}
-      verbosity:           ${toString cfg.verbosity}
-      hide-version:        ${yesOrNo  cfg.hideVersion}
-      identity:            "${cfg.identity}"
+      ipv6-edns-size:      ${toString cfg.ipv6EDNSSize}
+      log-time-ascii:      ${yesOrNo  cfg.logTimeAscii}
       ${maybeString "nsid: " cfg.nsid}
+      port:                ${toString cfg.port}
+      reuseport:           ${yesOrNo  cfg.reuseport}
+      round-robin:         ${yesOrNo  cfg.roundRobin}
+      server-count:        ${toString cfg.serverCount}
+      ${if cfg.statistics == null then "" else "statistics:          ${toString cfg.statistics}"}
       tcp-count:           ${toString cfg.tcpCount}
       tcp-query-count:     ${toString cfg.tcpQueryCount}
       tcp-timeout:         ${toString cfg.tcpTimeout}
-      ipv4-edns-size:      ${toString cfg.ipv4EDNSSize}
-      ipv6-edns-size:      ${toString cfg.ipv6EDNSSize}
-      ${if cfg.statistics == null then "" else "statistics:          ${toString cfg.statistics}"}
+      verbosity:           ${toString cfg.verbosity}
+      ${maybeString "version: " cfg.version}
       xfrd-reload-timeout: ${toString cfg.xfrdReloadTimeout}
       zonefiles-check:     ${yesOrNo  cfg.zonefilesCheck}
 
-      rrl-size:                ${toString cfg.ratelimit.size}
-      rrl-ratelimit:           ${toString cfg.ratelimit.ratelimit}
-      rrl-whitelist-ratelimit: ${toString cfg.ratelimit.whitelistRatelimit}
-      ${maybeString "rrl-slip: "               cfg.ratelimit.slip}
       ${maybeString "rrl-ipv4-prefix-length: " cfg.ratelimit.ipv4PrefixLength}
       ${maybeString "rrl-ipv6-prefix-length: " cfg.ratelimit.ipv6PrefixLength}
+      rrl-ratelimit:           ${toString cfg.ratelimit.ratelimit}
+      ${maybeString "rrl-slip: "               cfg.ratelimit.slip}
+      rrl-size:                ${toString cfg.ratelimit.size}
+      rrl-whitelist-ratelimit: ${toString cfg.ratelimit.whitelistRatelimit}
 
     ${keyConfigFile}
 
     remote-control:
       control-enable:    ${yesOrNo  cfg.remoteControl.enable}
+      control-key-file:  "${cfg.remoteControl.controlKeyFile}"
+      control-cert-file: "${cfg.remoteControl.controlCertFile}"
     ${forEach "  control-interface: " cfg.remoteControl.interfaces}
-      control-port:      ${toString cfg.port}
+      control-port:      ${toString cfg.remoteControl.port}
       server-key-file:   "${cfg.remoteControl.serverKeyFile}"
       server-cert-file:  "${cfg.remoteControl.serverCertFile}"
-      control-key-file:  "${cfg.remoteControl.controlKeyFile}"
-      control-cert-file: "${cfg.remoteControl.controlCertFile}"
 
-    # zone files reside in "${zoneFiles}" linked to "${stateDir}/zones"
     ${concatStrings (mapAttrsToList zoneConfigFile zoneConfigs)}
 
     ${cfg.extraConfig}
   '';
 
-  yesOrNo     = b: if b then "yes" else "no";
+  yesOrNo = b: if b then "yes" else "no";
   maybeString = pre: s: if s == null then "" else ''${pre} "${s}"'';
-  forEach     = pre: l: concatMapStrings (x: pre + x + "\n") l;
+  forEach = pre: l: concatMapStrings (x: pre + x + "\n") l;
 
 
   keyConfigFile = concatStrings (mapAttrsToList (keyName: keyOptions: ''
@@ -106,22 +132,23 @@ let
     secret=$(cat "${keyOptions.keyFile}")
     dest="${stateDir}/private/${keyName}"
     echo "  secret: \"$secret\"" > "$dest"
-    ${pkgs.coreutils}/bin/chown ${username}:${username} "$dest"
-    ${pkgs.coreutils}/bin/chmod 0400 "$dest"
+    chown ${username}:${username} "$dest"
+    chmod 0400 "$dest"
   '') cfg.keys);
 
 
+  # options are ordered alphanumerically by the nixos option name
   zoneConfigFile = name: zone: ''
     zone:
       name:         "${name}"
       zonefile:     "${stateDir}/zones/${name}"
-      ${maybeString "zonestats: "          zone.zoneStats}
       ${maybeString "outgoing-interface: " zone.outgoingInterface}
     ${forEach     "  rrl-whitelist: "      zone.rrlWhitelist}
+      ${maybeString "zonestats: "          zone.zoneStats}
 
+      allow-axfr-fallback: ${yesOrNo       zone.allowAXFRFallback}
     ${forEach     "  allow-notify: "       zone.allowNotify}
     ${forEach     "  request-xfr: "        zone.requestXFR}
-      allow-axfr-fallback: ${yesOrNo       zone.allowAXFRFallback}
 
     ${forEach     "  notify: "             zone.notify}
       notify-retry:                        ${toString zone.notifyRetry}
@@ -142,7 +169,7 @@ let
       );
 
   # fighting infinite recursion
-  zoneOptions  = zoneOptionsRaw // childConfig zoneOptions1 true;
+  zoneOptions = zoneOptionsRaw // childConfig zoneOptions1 true;
   zoneOptions1 = zoneOptionsRaw // childConfig zoneOptions2 false;
   zoneOptions2 = zoneOptionsRaw // childConfig zoneOptions3 false;
   zoneOptions3 = zoneOptionsRaw // childConfig zoneOptions4 false;
@@ -152,26 +179,25 @@ let
 
   childConfig = x: v: { options.children = { type = types.attrsOf x; visible = v; }; };
 
+  # options are ordered alphanumerically
   zoneOptionsRaw = types.submodule {
     options = {
-      children = mkOption {
-        default     = {};
+
+      allowAXFRFallback = mkOption {
+        type = types.bool;
+        default = true;
         description = ''
-          Children zones inherit all options of their parents. Attributes
-          defined in a child will overwrite the ones of its parent. Only
-          leaf zones will be actually served. This way it's possible to
-          define maybe zones which share most attributes without
-          duplicating everything. This mechanism replaces nsd's patterns
-          in a save and functional way.
+          If NSD as secondary server should be allowed to AXFR if the primary
+          server does not allow IXFR.
         '';
       };
 
       allowNotify = mkOption {
-        type        = types.listOf types.str;
-        default     = [ ];
-        example     = [ "192.0.2.0/24 NOKEY" "10.0.0.1-10.0.0.5 my_tsig_key_name"
-                        "10.0.3.4&255.255.0.0 BLOCKED"
-                      ];
+        type = types.listOf types.str;
+        default = [ ];
+        example = [ "192.0.2.0/24 NOKEY" "10.0.0.1-10.0.0.5 my_tsig_key_name"
+                    "10.0.3.4&255.255.0.0 BLOCKED"
+                  ];
         description = ''
           Listed primary servers are allowed to notify this secondary server.
           <screen><![CDATA[
@@ -193,28 +219,32 @@ let
         '';
       };
 
-      requestXFR = mkOption {
-        type        = types.listOf types.str;
-        default     = [];
-        example     = [];
+      children = mkOption {
+        default = {};
         description = ''
-          Format: <code>[AXFR|UDP] &lt;ip-address&gt; &lt;key-name | NOKEY&gt;</code>
+          Children zones inherit all options of their parents. Attributes
+          defined in a child will overwrite the ones of its parent. Only
+          leaf zones will be actually served. This way it's possible to
+          define maybe zones which share most attributes without
+          duplicating everything. This mechanism replaces nsd's patterns
+          in a save and functional way.
         '';
       };
 
-      allowAXFRFallback = mkOption {
-        type        = types.bool;
-        default     = true;
+      data = mkOption {
+        type = types.str;
+        default = "";
+        example = "";
         description = ''
-          If NSD as secondary server should be allowed to AXFR if the primary
-          server does not allow IXFR.
+          The actual zone data. This is the content of your zone file.
+          Use imports or pkgs.lib.readFile if you don't want this data in your config file.
         '';
       };
 
       notify = mkOption {
-        type        = types.listOf types.str;
-        default     = [];
-        example     = [ "10.0.0.1@3721 my_key" "::5 NOKEY" ];
+        type = types.listOf types.str;
+        default = [];
+        example = [ "10.0.0.1@3721 my_key" "::5 NOKEY" ];
         description = ''
           This primary server will notify all given secondary servers about
           zone changes.
@@ -231,38 +261,47 @@ let
       };
 
       notifyRetry = mkOption {
-        type        = types.int;
-        default     = 5;
+        type = types.int;
+        default = 5;
         description = ''
           Specifies the number of retries for failed notifies. Set this along with notify.
         '';
       };
 
+      outgoingInterface = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "2000::1@1234";
+        description = ''
+          This address will be used for zone-transfere requests if configured
+          as a secondary server or notifications in case of a primary server.
+          Supply either a plain IPv4 or IPv6 address with an optional port
+          number (ip@port).
+        '';
+      };
+
       provideXFR = mkOption {
-        type        = types.listOf types.str;
-        default     = [];
-        example     = [ "192.0.2.0/24 NOKEY" "192.0.2.0/24 my_tsig_key_name" ];
+        type = types.listOf types.str;
+        default = [];
+        example = [ "192.0.2.0/24 NOKEY" "192.0.2.0/24 my_tsig_key_name" ];
         description = ''
           Allow these IPs and TSIG to transfer zones, addr TSIG|NOKEY|BLOCKED
           address range 192.0.2.0/24, 1.2.3.4&amp;255.255.0.0, 3.0.2.20-3.0.2.40
         '';
       };
 
-      outgoingInterface = mkOption {
-        type        = types.nullOr types.str;
-        default     = null;
-        example     = "2000::1@1234";
+      requestXFR = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [];
         description = ''
-          This address will be used for zone-transfere requests if configured
-          as a secondary server or notifications in case of a primary server.
-          Supply either a plain IPv4 or IPv6 address with an optional port
-          number (ip@port).
+          Format: <code>[AXFR|UDP] &lt;ip-address&gt; &lt;key-name | NOKEY&gt;</code>
         '';
       };
 
       rrlWhitelist = mkOption {
-        type        = types.listOf types.str;
-        default     = [];
+        type = types.listOf types.str;
+        default = [];
         description = ''
           Whitelists the given rrl-types.
           The RRL classification types are:  nxdomain,  error, referral, any,
@@ -270,20 +309,10 @@ let
         '';
       };
 
-      data = mkOption {
-        type        = types.str;
-        default     = "";
-        example     = "";
-        description = ''
-          The actual zone data. This is the content of your zone file.
-          Use imports or pkgs.lib.readFile if you don't want this data in your config file.
-        '';
-      };
-
       zoneStats = mkOption {
-        type        = types.nullOr types.str;
-        default     = null;
-        example     = "%s";
+        type = types.nullOr types.str;
+        default = null;
+        example = "%s";
         description = ''
           When set to something distinct to null NSD is able to collect
           statistics per zone. All statistics of this zone(s) will be added
@@ -292,419 +321,470 @@ let
           and stats_noreset.
         '';
       };
+
     };
   };
 
 in
 {
-  options = {
-    services.nsd = {
+  # options are ordered alphanumerically
+  options.services.nsd = {
 
-      enable = mkEnableOption "NSD authoritative DNS server";
-      bind8Stats = mkEnableOption "BIND8 like statistics";
+    enable = mkEnableOption "NSD authoritative DNS server";
 
-      rootServer = mkOption {
-        type        = types.bool;
-        default     = false;
-        description = ''
-          Wheter if this server will be a root server (a DNS root server, you
-          usually don't want that).
-        '';
-      };
+    bind8Stats = mkEnableOption "BIND8 like statistics";
 
-      interfaces = mkOption {
-        type        = types.listOf types.str;
-        default     = [ "127.0.0.0" "::1" ];
-        description = ''
-          What addresses the server should listen to.
-        '';
-      };
+    extraConfig = mkOption {
+      type = types.str;
+      default = "";
+      description = ''
+        Extra nsd config.
+      '';
+    };
 
-      serverCount = mkOption {
-        type        = types.int;
-        default     = 1;
-        description = ''
-          Number of NSD servers to fork. Put the number of CPUs to use here.
-        '';
-      };
+    hideVersion = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Whether NSD should answer VERSION.BIND and VERSION.SERVER CHAOS class queries.
+      '';
+    };
 
-      ipTransparent = mkOption {
-        type        = types.bool;
-        default     = false;
-        description = ''
-          Allow binding to non local addresses.
-        '';
-      };
+    identity = mkOption {
+      type = types.str;
+      default = "unidentified server";
+      description = ''
+        Identify the server (CH TXT ID.SERVER entry).
+      '';
+    };
 
-      ipv4 = mkOption {
-        type        = types.bool;
-        default     = true;
-        description = ''
-          Wheter to listen on IPv4 connections.
-        '';
-      };
+    interfaces = mkOption {
+      type = types.listOf types.str;
+      default = [ "127.0.0.0" "::1" ];
+      description = ''
+        What addresses the server should listen to.
+      '';
+    };
 
-      ipv6 = mkOption {
-        type        = types.bool;
-        default     = true;
-        description = ''
-          Wheter to listen on IPv6 connections.
-        '';
-      };
+    ipTransparent = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Allow binding to non local addresses.
+      '';
+    };
 
-      port = mkOption {
-        type        = types.int;
-        default     = 53;
-        description = ''
-          Port the service should bind do.
-        '';
-      };
+    ipv4 = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Whether to listen on IPv4 connections.
+      '';
+    };
 
-      verbosity = mkOption {
-        type        = types.int;
-        default     = 0;
-        description = ''
-          Verbosity level.
-        '';
-      };
+    ipv4EDNSSize = mkOption {
+      type = types.int;
+      default = 4096;
+      description = ''
+        Preferred EDNS buffer size for IPv4.
+      '';
+    };
+
+    ipv6 = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Whether to listen on IPv6 connections.
+      '';
+    };
+
+    ipv6EDNSSize = mkOption {
+      type = types.int;
+      default = 4096;
+      description = ''
+        Preferred EDNS buffer size for IPv6.
+      '';
+    };
+
+    logTimeAscii = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Log time in ascii, if false then in unix epoch seconds.
+      '';
+    };
+
+    nsid = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        NSID identity (hex string, or "ascii_somestring").
+      '';
+    };
+
+    port = mkOption {
+      type = types.int;
+      default = 53;
+      description = ''
+        Port the service should bind do.
+      '';
+    };
+
+    reuseport = mkOption {
+      type = types.bool;
+      default = pkgs.stdenv.isLinux;
+      description = ''
+        Whether to enable SO_REUSEPORT on all used sockets. This lets multiple
+        processes bind to the same port. This speeds up operation especially
+        if the server count is greater than one and makes fast restarts less
+        prone to fail
+      '';
+    };
+
+    rootServer = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether this server will be a root server (a DNS root server, you
+        usually don't want that).
+      '';
+    };
+
+    roundRobin = mkEnableOption "round robin rotation of records";
+
+    serverCount = mkOption {
+      type = types.int;
+      default = 1;
+      description = ''
+        Number of NSD servers to fork. Put the number of CPUs to use here.
+      '';
+    };
+
+    statistics = mkOption {
+      type = types.nullOr types.int;
+      default = null;
+      description = ''
+        Statistics are produced every number of seconds. Prints to log.
+        If null no statistics are logged.
+      '';
+    };
+
+    tcpCount = mkOption {
+      type = types.int;
+      default = 100;
+      description = ''
+        Maximum number of concurrent TCP connections per server.
+      '';
+    };
+
+    tcpQueryCount = mkOption {
+      type = types.int;
+      default = 0;
+      description = ''
+        Maximum number of queries served on a single TCP connection.
+        0 means no maximum.
+      '';
+    };
+
+    tcpTimeout = mkOption {
+      type = types.int;
+      default = 120;
+      description = ''
+        TCP timeout in seconds.
+      '';
+    };
+
+    verbosity = mkOption {
+      type = types.int;
+      default = 0;
+      description = ''
+        Verbosity level.
+      '';
+    };
+
+    version = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        The version string replied for CH TXT version.server and version.bind
+        queries. Will use the compiled package version on null.
+        See hideVersion for enabling/disabling this responses.
+      '';
+    };
+
+    xfrdReloadTimeout = mkOption {
+      type = types.int;
+      default = 1;
+      description = ''
+        Number of seconds between reloads triggered by xfrd.
+      '';
+    };
+
+    zonefilesCheck = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Whether to check mtime of all zone files on start and sighup.
+      '';
+    };
+
+
+    keys = mkOption {
+      type = types.attrsOf (types.submodule {
+        options = {
+
+          algorithm = mkOption {
+            type = types.str;
+            default = "hmac-sha256";
+            description = ''
+              Authentication algorithm for this key.
+            '';
+          };
+
+          keyFile = mkOption {
+            type = types.path;
+            description = ''
+              Path to the file which contains the actual base64 encoded
+              key. The key will be copied into "${stateDir}/private" before
+              NSD starts. The copied file is only accessibly by the NSD
+              user.
+            '';
+          };
+
+        };
+      });
+      default = {};
+      example = literalExample ''
+        { "tsig.example.org" = {
+            algorithm = "hmac-md5";
+            keyFile = "/path/to/my/key";
+          };
+        }
+      '';
+      description = ''
+        Define your TSIG keys here.
+      '';
+    };
+
+
+    ratelimit = {
+
+      enable = mkEnableOption "ratelimit capabilities";
 
-      hideVersion = mkOption {
-        type        = types.bool;
-        default     = true;
+      ipv4PrefixLength = mkOption {
+        type = types.nullOr types.int;
+        default = null;
         description = ''
-          Wheter NSD should answer VERSION.BIND and VERSION.SERVER CHAOS class queries.
+          IPv4 prefix length. Addresses are grouped by netblock.
         '';
       };
 
-      identity = mkOption {
-        type        = types.str;
-        default     = "unidentified server";
+      ipv6PrefixLength = mkOption {
+        type = types.nullOr types.int;
+        default = null;
         description = ''
-          Identify the server (CH TXT ID.SERVER entry).
+          IPv6 prefix length. Addresses are grouped by netblock.
         '';
       };
 
-      nsid = mkOption {
-        type        = types.nullOr types.str;
-        default     = null;
+      ratelimit = mkOption {
+        type = types.int;
+        default = 200;
         description = ''
-          NSID identity (hex string, or "ascii_somestring").
+          Max qps allowed from any query source.
+          0 means unlimited. With an verbosity of 2 blocked and
+          unblocked subnets will be logged.
         '';
       };
 
-      tcpCount = mkOption {
-        type        = types.int;
-        default     = 100;
+      slip = mkOption {
+        type = types.nullOr types.int;
+        default = null;
         description = ''
-          Maximum number of concurrent TCP connections per server.
+          Number of packets that get discarded before replying a SLIP response.
+          0 disables SLIP responses. 1 will make every response a SLIP response.
         '';
       };
 
-      tcpQueryCount = mkOption {
-        type        = types.int;
-        default     = 0;
+      size = mkOption {
+        type = types.int;
+        default = 1000000;
         description = ''
-          Maximum number of queries served on a single TCP connection.
-          0 means no maximum.
+          Size of the hashtable. More buckets use more memory but lower
+          the chance of hash hash collisions.
         '';
       };
 
-      tcpTimeout = mkOption {
-        type        = types.int;
-        default     = 120;
+      whitelistRatelimit = mkOption {
+        type = types.int;
+        default = 2000;
         description = ''
-          TCP timeout in seconds.
+          Max qps allowed from whitelisted sources.
+          0 means unlimited. Set the rrl-whitelist option for specific
+          queries to apply this limit instead of the default to them.
         '';
       };
 
-      ipv4EDNSSize = mkOption {
-        type        = types.int;
-        default     = 4096;
+    };
+
+
+    remoteControl = {
+
+      enable = mkEnableOption "remote control via nsd-control";
+
+      controlCertFile = mkOption {
+        type = types.path;
+        default = "/etc/nsd/nsd_control.pem";
         description = ''
-          Preferred EDNS buffer size for IPv4.
+          Path to the client certificate signed with the server certificate.
+          This file is used by nsd-control and generated by nsd-control-setup.
         '';
       };
 
-      ipv6EDNSSize = mkOption {
-        type        = types.int;
-        default     = 4096;
+      controlKeyFile = mkOption {
+        type = types.path;
+        default = "/etc/nsd/nsd_control.key";
         description = ''
-          Preferred EDNS buffer size for IPv6.
+          Path to the client private key, which is used by nsd-control
+          but not by the server. This file is generated by nsd-control-setup.
         '';
       };
 
-      statistics = mkOption {
-        type        = types.nullOr types.int;
-        default     = null;
+      interfaces = mkOption {
+        type = types.listOf types.str;
+        default = [ "127.0.0.1" "::1" ];
         description = ''
-          Statistics are produced every number of seconds. Prints to log.
-          If null no statistics are logged.
+          Which interfaces NSD should bind to for remote control.
         '';
       };
 
-      xfrdReloadTimeout = mkOption {
-        type        = types.int;
-        default     = 1;
+      port = mkOption {
+        type = types.int;
+        default = 8952;
         description = ''
-          Number of seconds between reloads triggered by xfrd.
+          Port number for remote control operations (uses TLS over TCP).
         '';
       };
 
-      zonefilesCheck = mkOption {
-        type        = types.bool;
-        default     = true;
+      serverCertFile = mkOption {
+        type = types.path;
+        default = "/etc/nsd/nsd_server.pem";
         description = ''
-          Wheter to check mtime of all zone files on start and sighup.
+          Path to the server self signed certificate, which is used by the server
+          but and by nsd-control. This file is generated by nsd-control-setup.
         '';
       };
 
-
-      extraConfig = mkOption {
-        type        = types.str;
-        default     = "";
+      serverKeyFile = mkOption {
+        type = types.path;
+        default = "/etc/nsd/nsd_server.key";
         description = ''
-          Extra nsd config.
+          Path to the server private key, which is used by the server
+          but not by nsd-control. This file is generated by nsd-control-setup.
         '';
       };
 
+    };
 
-      ratelimit = {
-        enable = mkEnableOption "ratelimit capabilities";
-
-        size = mkOption {
-          type        = types.int;
-          default     = 1000000;
-          description = ''
-            Size of the hashtable. More buckets use more memory but lower
-            the chance of hash hash collisions.
-          '';
-        };
-
-        ratelimit = mkOption {
-          type        = types.int;
-          default     = 200;
-          description = ''
-            Max qps allowed from any query source.
-            0 means unlimited. With an verbosity of 2 blocked and
-            unblocked subnets will be logged.
-          '';
-        };
-
-        whitelistRatelimit = mkOption {
-          type        = types.int;
-          default     = 2000;
-          description = ''
-            Max qps allowed from whitelisted sources.
-            0 means unlimited. Set the rrl-whitelist option for specific
-            queries to apply this limit instead of the default to them.
-          '';
-        };
-
-        slip = mkOption {
-          type        = types.nullOr types.int;
-          default     = null;
-          description = ''
-            Number of packets that get discarded before replying a SLIP response.
-            0 disables SLIP responses. 1 will make every response a SLIP response.
-          '';
-        };
-
-        ipv4PrefixLength = mkOption {
-          type        = types.nullOr types.int;
-          default     = null;
-          description = ''
-            IPv4 prefix length. Addresses are grouped by netblock.
-          '';
-        };
-
-        ipv6PrefixLength = mkOption {
-          type        = types.nullOr types.int;
-          default     = null;
-          description = ''
-            IPv6 prefix length. Addresses are grouped by netblock.
-          '';
-        };
-      };
-
-
-      remoteControl = {
-        enable = mkEnableOption "remote control via nsd-control";
-
-        interfaces = mkOption {
-          type        = types.listOf types.str;
-          default     = [ "127.0.0.1" "::1" ];
-          description = ''
-            Which interfaces NSD should bind to for remote control.
-          '';
-        };
-
-        port = mkOption {
-          type        = types.int;
-          default     = 8952;
-          description = ''
-            Port number for remote control operations (uses TLS over TCP).
-          '';
-        };
-
-        serverKeyFile = mkOption {
-          type        = types.path;
-          default     = "/etc/nsd/nsd_server.key";
-          description = ''
-            Path to the server private key, which is used by the server
-            but not by nsd-control. This file is generated by nsd-control-setup.
-          '';
-        };
-
-        serverCertFile = mkOption {
-          type        = types.path;
-          default     = "/etc/nsd/nsd_server.pem";
-          description = ''
-            Path to the server self signed certificate, which is used by the server
-            but and by nsd-control. This file is generated by nsd-control-setup.
-          '';
-        };
-
-        controlKeyFile = mkOption {
-          type        = types.path;
-          default     = "/etc/nsd/nsd_control.key";
-          description = ''
-            Path to the client private key, which is used by nsd-control
-            but not by the server. This file is generated by nsd-control-setup.
-          '';
-        };
-
-        controlCertFile = mkOption {
-          type        = types.path;
-          default     = "/etc/nsd/nsd_control.pem";
-          description = ''
-            Path to the client certificate signed with the server certificate.
-            This file is used by nsd-control and generated by nsd-control-setup.
-          '';
-        };
-      };
-
-
-      keys = mkOption {
-        type = types.attrsOf (types.submodule {
-          options = {
-            algorithm = mkOption {
-              type        = types.str;
-              default     = "hmac-sha256";
-              description = ''
-                Authentication algorithm for this key.
-              '';
-            };
-
-            keyFile = mkOption {
-              type        = types.path;
-              description = ''
-                Path to the file which contains the actual base64 encoded
-                key. The key will be copied into "${stateDir}/private" before
-                NSD starts. The copied file is only accessibly by the NSD
-                user.
-              '';
-            };
-          };
-        });
-        default = {};
-        example = {
-          "tsig.example.org" = {
-            algorithm = "hmac-md5";
-            secret    = "aaaaaabbbbbbccccccdddddd";
-          };
-        };
-        description = ''
-          Define your TSIG keys here.
-        '';
-      };
 
-      zones = mkOption {
-        type        = types.attrsOf zoneOptions;
-        default     = {};
-        example     = {
-          "serverGroup1" = {
+    zones = mkOption {
+      type = types.attrsOf zoneOptions;
+      default = {};
+      example = literalExample ''
+        { "serverGroup1" = {
             provideXFR = [ "10.1.2.3 NOKEY" ];
             children = {
               "example.com." = {
-                data = ''
+                data = '''
                   $ORIGIN example.com.
                   $TTL    86400
                   @ IN SOA a.ns.example.com. admin.example.com. (
                   ...
-                '';
+                ''';
               };
               "example.org." = {
-                data = ''
+                data = '''
                   $ORIGIN example.org.
                   $TTL    86400
                   @ IN SOA a.ns.example.com. admin.example.com. (
                   ...
-                '';
+                ''';
               };
             };
           };
 
           "example.net." = {
             provideXFR = [ "10.3.2.1 NOKEY" ];
-            data = ''...'';
+            data = '''
+              ...
+            ''';
           };
-        };
-        description = ''
-          Define your zones here. Zones can cascade other zones and therefore
-          inherit settings from parent zones. Look at the definition of
-          children to learn about inheritance and child zones.
-          The given example will define 3 zones (example.(com|org|net).). Both
-          example.com. and example.org. inherit their configuration from
-          serverGroup1.
-        '';
-      };
-
+        }
+      '';
+      description = ''
+        Define your zones here. Zones can cascade other zones and therefore
+        inherit settings from parent zones. Look at the definition of
+        children to learn about inheritance and child zones.
+        The given example will define 3 zones (example.(com|org|net).). Both
+        example.com. and example.org. inherit their configuration from
+        serverGroup1.
+      '';
     };
+
   };
 
   config = mkIf cfg.enable {
 
     users.extraGroups = singleton {
       name = username;
-      gid  = config.ids.gids.nsd;
+      gid = config.ids.gids.nsd;
     };
 
     users.extraUsers = singleton {
-      name        = username;
+      name = username;
       description = "NSD service user";
-      home        = stateDir;
+      home = stateDir;
       createHome  = true;
-      uid         = config.ids.uids.nsd;
-      group       = username;
+      uid = config.ids.uids.nsd;
+      group = username;
     };
 
     systemd.services.nsd = {
       description = "NSD authoritative only domain name service";
-      wantedBy    = [ "multi-user.target" ];
-      after       = [ "network.target" ];
+
+      after = [ "keys.target" "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      wants = [ "keys.target" ];
 
       serviceConfig = {
-        PIDFile   = pidFile;
-        Restart   = "always";
-        ExecStart = "${nsdPkg}/sbin/nsd -d -c ${configFile}";
+        ExecStart = "${nsdPkg}/sbin/nsd -d -c ${nsdEnv}/nsd.conf";
+        PIDFile = pidFile;
+        Restart = "always";
+        RestartSec = "4s";
+        StartLimitBurst = 4;
+        StartLimitInterval = "5min";
       };
 
       preStart = ''
-        ${pkgs.coreutils}/bin/mkdir -m 0700 -p "${stateDir}/private"
-        ${pkgs.coreutils}/bin/mkdir -m 0700 -p "${stateDir}/tmp"
-        ${pkgs.coreutils}/bin/mkdir -m 0700 -p "${stateDir}/var"
+        rm -Rf "${stateDir}/private/"
+        rm -Rf "${stateDir}/tmp/"
 
-        ${pkgs.coreutils}/bin/touch "${stateDir}/don't touch anything in here"
+        mkdir -m 0700 -p "${stateDir}/private"
+        mkdir -m 0700 -p "${stateDir}/tmp"
+        mkdir -m 0700 -p "${stateDir}/var"
 
-        ${pkgs.coreutils}/bin/rm -f "${stateDir}/private/"*
-        ${pkgs.coreutils}/bin/rm -f "${stateDir}/tmp/"*
+        cat > "${stateDir}/don't touch anything in here" << EOF
+        Everything in this directory except NSD's state in var is
+        automatically generated and will be purged and redeployed
+        by the nsd.service pre-start script.
+        EOF
 
-        ${pkgs.coreutils}/bin/chown nsd:nsd -R "${stateDir}/private"
-        ${pkgs.coreutils}/bin/chown nsd:nsd -R "${stateDir}/tmp"
-        ${pkgs.coreutils}/bin/chown nsd:nsd -R "${stateDir}/var"
+        chown ${username}:${username} -R "${stateDir}/private"
+        chown ${username}:${username} -R "${stateDir}/tmp"
+        chown ${username}:${username} -R "${stateDir}/var"
 
-        ${pkgs.coreutils}/bin/rm -rf "${stateDir}/zones"
-        ${pkgs.coreutils}/bin/cp -r  "${zoneFiles}" "${stateDir}/zones"
+        rm -rf "${stateDir}/zones"
+        cp -rL "${nsdEnv}/zones" "${stateDir}/zones"
 
         ${copyKeys}
       '';
diff --git a/nixos/modules/services/networking/pdnsd.nix b/nixos/modules/services/networking/pdnsd.nix
new file mode 100644
index 000000000000..f4467b818958
--- /dev/null
+++ b/nixos/modules/services/networking/pdnsd.nix
@@ -0,0 +1,93 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.services.pdnsd;
+  pdnsd = pkgs.pdnsd;
+  pdnsdUser = "pdnsd";
+  pdnsdGroup = "pdnsd";
+  pdnsdConf = pkgs.writeText "pdnsd.conf"
+    ''
+      global {
+        run_as=${pdnsdUser};
+        cache_dir="${cfg.cacheDir}";
+        ${cfg.globalConfig}
+      }
+
+      server {
+        ${cfg.serverConfig}
+      }
+      ${cfg.extraConfig}
+    '';
+in
+
+{ options =
+    { services.pdnsd =
+        { enable = mkEnableOption "pdnsd";
+
+          cacheDir = mkOption {
+            type = types.str;
+            default = "/var/cache/pdnsd";
+            description = "Directory holding the pdnsd cache";
+          };
+
+          globalConfig = mkOption {
+            type = types.lines;
+            default = "";
+            description = ''
+              Global configuration that should be added to the global directory
+              of <literal>pdnsd.conf</literal>.
+            '';
+          };
+
+          serverConfig = mkOption {
+            type = types.lines;
+            default = "";
+            description = ''
+              Server configuration that should be added to the server directory
+              of <literal>pdnsd.conf</literal>.
+            '';
+          };
+
+          extraConfig = mkOption {
+            type = types.lines;
+            default = "";
+            description = ''
+              Extra configuration directives that should be added to
+              <literal>pdnsd.conf</literal>.
+            '';
+          };
+        };
+    };
+
+  config = mkIf cfg.enable {
+    users.extraUsers = singleton {
+      name = pdnsdUser;
+      uid = config.ids.uids.pdnsd;
+      group = pdnsdGroup;
+      description = "pdnsd user";
+    };
+
+    users.extraGroups = singleton {
+      name = pdnsdGroup;
+      gid = config.ids.gids.pdnsd;
+    };
+
+    systemd.services.pdnsd =
+      { wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ];
+        preStart =
+          ''
+            mkdir -p "${cfg.cacheDir}"
+            touch "${cfg.cacheDir}/pdnsd.cache"
+            chown -R ${pdnsdUser}:${pdnsdGroup} "${cfg.cacheDir}"
+          '';
+        description = "pdnsd";
+        serviceConfig =
+          {
+            ExecStart = "${pdnsd}/bin/pdnsd -c ${pdnsdConf}";
+          };
+      };
+  };
+}
diff --git a/nixos/modules/services/networking/ssh/sshd.nix b/nixos/modules/services/networking/ssh/sshd.nix
index ba3efc8c0c2a..5971a5a250d3 100644
--- a/nixos/modules/services/networking/ssh/sshd.nix
+++ b/nixos/modules/services/networking/ssh/sshd.nix
@@ -263,7 +263,7 @@ in
 
             serviceConfig =
               { ExecStart =
-                  "${cfgc.package}/sbin/sshd " + (optionalString cfg.startWhenNeeded "-i ") +
+                  "${cfgc.package}/bin/sshd " + (optionalString cfg.startWhenNeeded "-i ") +
                   "-f ${pkgs.writeText "sshd_config" cfg.extraConfig}";
                 KillMode = "process";
               } // (if cfg.startWhenNeeded then {
@@ -304,7 +304,7 @@ in
     services.openssh.authorizedKeysFiles =
       [ ".ssh/authorized_keys" ".ssh/authorized_keys2" "/etc/ssh/authorized_keys.d/%u" ];
 
-    services.openssh.extraConfig =
+    services.openssh.extraConfig = mkOrder 0
       ''
         PidFile /run/sshd.pid
 
diff --git a/nixos/modules/services/networking/syncthing.nix b/nixos/modules/services/networking/syncthing.nix
index f5d5e1d25561..67b90516b996 100644
--- a/nixos/modules/services/networking/syncthing.nix
+++ b/nixos/modules/services/networking/syncthing.nix
@@ -33,6 +33,17 @@ in
         '';
       };
 
+      all_proxy = mkOption {
+        type = types.string;
+        default = "";
+        example = "socks5://address.com:1234";
+        description = ''
+          Overwrites all_proxy environment variable for the syncthing process to
+          the given value. This is normaly used to let relay client connect
+          through SOCKS5 proxy server.
+        '';
+      };
+
       dataDir = mkOption {
         default = "/var/lib/syncthing";
         description = ''
@@ -51,7 +62,6 @@ in
       };
 
 
-
     };
 
   };
@@ -66,8 +76,13 @@ in
         description = "Syncthing service";
         after = [ "network.target" ];
         wantedBy = [ "multi-user.target" ];
-        environment.STNORESTART = "yes";  # do not self-restart
-        environment.STNOUPGRADE = "yes";
+        environment = {
+          STNORESTART = "yes";  # do not self-restart
+          STNOUPGRADE = "yes";
+        } //
+        (config.networking.proxy.envVars) //
+        (if cfg.all_proxy != "" then { all_proxy = cfg.all_proxy; } else {});
+
         serviceConfig = {
           User = "${cfg.user}";
           PermissionsStartOnly = true;
diff --git a/nixos/modules/services/networking/tlsdated.nix b/nixos/modules/services/networking/tlsdated.nix
index ff7d0178a81a..757cce287607 100644
--- a/nixos/modules/services/networking/tlsdated.nix
+++ b/nixos/modules/services/networking/tlsdated.nix
@@ -26,6 +26,7 @@ in
 
       extraOptions = mkOption {
         type = types.string;
+        default = "";
         description = ''
           Additional command line arguments to pass to tlsdated.
         '';
diff --git a/nixos/modules/services/networking/unbound.nix b/nixos/modules/services/networking/unbound.nix
index 73b10c1d5611..89762fe52488 100644
--- a/nixos/modules/services/networking/unbound.nix
+++ b/nixos/modules/services/networking/unbound.nix
@@ -16,6 +16,11 @@ let
     "forward-zone:\n  name: .\n" +
     concatMapStrings (x: "  forward-addr: ${x}\n") cfg.forwardAddresses;
 
+  rootTrustAnchorFile = "${stateDir}/root.key";
+
+  trustAnchor = optionalString cfg.enableRootTrustAnchor
+    "auto-trust-anchor-file: ${rootTrustAnchorFile}";
+
   confFile = pkgs.writeText "unbound.conf" ''
     server:
       directory: "${stateDir}"
@@ -24,6 +29,7 @@ let
       pidfile: ""
       ${interfaces}
       ${access}
+      ${trustAnchor}
     ${cfg.extraConfig}
     ${forward}
   '';
@@ -38,28 +44,39 @@ in
     services.unbound = {
 
       enable = mkOption {
-	default = false;
-	description = "Whether to enable the Unbound domain name server.";
+        default = false;
+        type = types.bool;
+        description = "Whether to enable the Unbound domain name server.";
       };
 
       allowedAccess = mkOption {
-	default = ["127.0.0.0/24"];
-	description = "What networks are allowed to use unbound as a resolver.";
+        default = ["127.0.0.0/24"];
+        type = types.listOf types.str;
+        description = "What networks are allowed to use unbound as a resolver.";
       };
 
       interfaces = mkOption {
-	default = [ "127.0.0.1" "::1" ];
-	description = "What addresses the server should listen on.";
+        default = [ "127.0.0.1" "::1" ];
+        type = types.listOf types.str;
+        description = "What addresses the server should listen on.";
       };
 
       forwardAddresses = mkOption {
-	default = [ ];
-	description = "What servers to forward queries to.";
+        default = [ ];
+        type = types.listOf types.str;
+        description = "What servers to forward queries to.";
+      };
+
+      enableRootTrustAnchor = mkOption {
+        default = true;
+        type = types.bool;
+        description = "Use and update root trust anchor for DNSSEC validation.";
       };
 
       extraConfig = mkOption {
-	default = "";
-	description = "Extra lines of unbound config.";
+        default = "";
+        type = types.str;
+        description = "Extra lines of unbound config.";
       };
 
     };
@@ -88,14 +105,15 @@ in
 
       preStart = ''
         mkdir -m 0755 -p ${stateDir}/dev/
-	cp ${confFile} ${stateDir}/unbound.conf
-	chown unbound ${stateDir}
-	touch ${stateDir}/dev/random
+        cp ${confFile} ${stateDir}/unbound.conf
+        ${pkgs.unbound}/bin/unbound-anchor -a ${rootTrustAnchorFile}
+        chown unbound ${stateDir} ${rootTrustAnchorFile}
+        touch ${stateDir}/dev/random
         ${pkgs.utillinux}/bin/mount --bind -n /dev/random ${stateDir}/dev/random
       '';
 
       serviceConfig = {
-        ExecStart = "${pkgs.unbound}/sbin/unbound -d -c ${stateDir}/unbound.conf";
+        ExecStart = "${pkgs.unbound}/bin/unbound -d -c ${stateDir}/unbound.conf";
         ExecStopPost="${pkgs.utillinux}/bin/umount ${stateDir}/dev/random";
       };
     };
diff --git a/nixos/modules/services/networking/wpa_supplicant.nix b/nixos/modules/services/networking/wpa_supplicant.nix
index 1558c5832892..a8f445a2c73c 100644
--- a/nixos/modules/services/networking/wpa_supplicant.nix
+++ b/nixos/modules/services/networking/wpa_supplicant.nix
@@ -78,10 +78,11 @@ in {
         '';
         default = {};
         example = literalExample ''
-          echelon = {
-            psk = "abcdefgh";
-          };
-          "free.wifi" = {};
+          { echelon = {
+              psk = "abcdefgh";
+            };
+            "free.wifi" = {};
+          }
         '';
       };
 
@@ -127,6 +128,7 @@ in {
       in {
         description = "WPA Supplicant";
 
+        after = [ "network-interfaces.target" ];
         wantedBy = [ "network.target" ];
 
         path = [ pkgs.wpa_supplicant ];
diff --git a/nixos/modules/services/networking/zerotierone.nix b/nixos/modules/services/networking/zerotierone.nix
index 886ea18d9809..6237f95b127b 100644
--- a/nixos/modules/services/networking/zerotierone.nix
+++ b/nixos/modules/services/networking/zerotierone.nix
@@ -21,10 +21,9 @@ in
         chown -R root:root /var/lib/zerotier-one
         '';
       serviceConfig = {
-        Type = "forking";
-        User = "root";
-        PIDFile = "/var/lib/zerotier-one/zerotier-one.pid";
-        ExecStart = "${pkgs.zerotierone}/bin/zerotier-one -d";
+        ExecStart = "${pkgs.zerotierone}/bin/zerotier-one";
+        Restart = "always";
+        KillMode = "process";
       };
     };
   environment.systemPackages = [ pkgs.zerotierone ];