about summary refs log tree commit diff
path: root/nixos/modules/services
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules/services')
-rw-r--r--nixos/modules/services/editors/emacs.xml6
-rw-r--r--nixos/modules/services/games/ghost-one.nix3
-rw-r--r--nixos/modules/services/logging/logcheck.nix4
-rw-r--r--nixos/modules/services/mail/opensmtpd.nix2
-rw-r--r--nixos/modules/services/misc/dictd.nix10
-rw-r--r--nixos/modules/services/misc/leaps.nix62
-rw-r--r--nixos/modules/services/misc/matrix-synapse.nix30
-rw-r--r--nixos/modules/services/misc/parsoid.nix3
-rw-r--r--nixos/modules/services/misc/taskserver/default.nix4
-rw-r--r--nixos/modules/services/networking/bitlbee.nix7
-rw-r--r--nixos/modules/services/networking/cjdns.nix7
-rw-r--r--nixos/modules/services/networking/dnscrypt-proxy.nix122
-rw-r--r--nixos/modules/services/networking/i2pd.nix230
-rw-r--r--nixos/modules/services/networking/quassel.nix11
-rw-r--r--nixos/modules/services/networking/smokeping.nix4
-rw-r--r--nixos/modules/services/networking/tinc.nix2
-rw-r--r--nixos/modules/services/networking/vsftpd.nix11
-rw-r--r--nixos/modules/services/networking/znc.nix3
-rw-r--r--nixos/modules/services/web-servers/fcgiwrap.nix2
19 files changed, 435 insertions, 88 deletions
diff --git a/nixos/modules/services/editors/emacs.xml b/nixos/modules/services/editors/emacs.xml
index bcaa8b8df3d8..e03f6046de8e 100644
--- a/nixos/modules/services/editors/emacs.xml
+++ b/nixos/modules/services/editors/emacs.xml
@@ -356,14 +356,14 @@ https://nixos.org/nixpkgs/manual/#sec-modify-via-packageOverrides
         <programlisting><![CDATA[
 { pkgs ? import <nixpkgs> {} }:
 let
-  myEmacs = pkgs.lib.overrideDerivation (pkgs.emacs.override {
+  myEmacs = (pkgs.emacs.override {
     # Use gtk3 instead of the default gtk2
     withGTK3 = true;
     withGTK2 = false;
-  }) (attrs: {
+  }).overrideAttrs (attrs: {
     # I don't want emacs.desktop file because I only use
     # emacsclient.
-    postInstall = attrs.postInstall + ''
+    postInstall = (attrs.postInstall or "") + ''
       rm $out/share/applications/emacs.desktop
     '';
   });
diff --git a/nixos/modules/services/games/ghost-one.nix b/nixos/modules/services/games/ghost-one.nix
index 5762148df2bb..71ff6bb2f3f0 100644
--- a/nixos/modules/services/games/ghost-one.nix
+++ b/nixos/modules/services/games/ghost-one.nix
@@ -21,8 +21,7 @@ in
 
       language = mkOption {
         default = "English";
-        type = types.addCheck types.str
-          (lang: elem lang [ "English" "Spanish" "Russian" "Serbian" "Turkish" ]);
+        type = types.enum [ "English" "Spanish" "Russian" "Serbian" "Turkish" ];
         description = "The language of bot messages: English, Spanish, Russian, Serbian or Turkish.";
       };
 
diff --git a/nixos/modules/services/logging/logcheck.nix b/nixos/modules/services/logging/logcheck.nix
index a8a214b21550..27ed5374f561 100644
--- a/nixos/modules/services/logging/logcheck.nix
+++ b/nixos/modules/services/logging/logcheck.nix
@@ -55,9 +55,9 @@ let
 
   levelOption = mkOption {
     default = "server";
-    type = types.str;
+    type = types.enum [ "workstation" "server" "paranoid" ];
     description = ''
-      Set the logcheck level. Either "workstation", "server", or "paranoid".
+      Set the logcheck level.
     '';
   };
 
diff --git a/nixos/modules/services/mail/opensmtpd.nix b/nixos/modules/services/mail/opensmtpd.nix
index 5856b4697553..53acdba42457 100644
--- a/nixos/modules/services/mail/opensmtpd.nix
+++ b/nixos/modules/services/mail/opensmtpd.nix
@@ -115,7 +115,7 @@ in {
         chown smtpq.root /var/spool/smtpd/purge
         chmod 700 /var/spool/smtpd/purge
       '';
-      serviceConfig.ExecStart = "${opensmtpd}/sbin/smtpd -d -f ${conf} ${args}";
+      serviceConfig.ExecStart = "${pkgs.opensmtpd}/sbin/smtpd -d -f ${conf} ${args}";
       environment.OPENSMTPD_PROC_PATH = "${procEnv}/libexec/opensmtpd";
     };
 
diff --git a/nixos/modules/services/misc/dictd.nix b/nixos/modules/services/misc/dictd.nix
index ef744439c3d6..24dca15dd913 100644
--- a/nixos/modules/services/misc/dictd.nix
+++ b/nixos/modules/services/misc/dictd.nix
@@ -2,6 +2,10 @@
 
 with lib;
 
+let
+  cfg = config.services.dictd;
+in
+
 {
 
   ###### interface
@@ -20,7 +24,7 @@ with lib;
 
       DBs = mkOption {
         type = types.listOf types.package;
-        default = [];
+        default = with pkgs.dictdDBs; [ wiktionary wordnet ];
         example = [ pkgs.dictdDBs.nld2eng ];
         description = ''List of databases to make available.'';
       };
@@ -34,8 +38,8 @@ with lib;
 
   config = let dictdb = pkgs.dictDBCollector { dictlist = map (x: {
                name = x.name;
-               filename = x; } ) config.services.dictd.DBs; };
-  in mkIf config.services.dictd.enable {
+               filename = x; } ) cfg.DBs; };
+  in mkIf cfg.enable {
 
     # get the command line client on system path to make some use of the service
     environment.systemPackages = [ pkgs.dict ];
diff --git a/nixos/modules/services/misc/leaps.nix b/nixos/modules/services/misc/leaps.nix
new file mode 100644
index 000000000000..b92cf27f58dc
--- /dev/null
+++ b/nixos/modules/services/misc/leaps.nix
@@ -0,0 +1,62 @@
+{ config, pkgs, lib, ... } @ args:
+
+with lib;
+
+let
+  cfg = config.services.leaps;
+  stateDir = "/var/lib/leaps/";
+in
+{
+  options = {
+    services.leaps = {
+      enable = mkEnableOption "leaps";
+      port = mkOption {
+        type = types.int;
+        default = 8080;
+        description = "A port where leaps listens for incoming http requests";
+      };
+      address = mkOption {
+        default = "";
+        type = types.str;
+        example = "127.0.0.1";
+        description = "Hostname or IP-address to listen to. By default it will listen on all interfaces.";
+      };
+      path = mkOption {
+        default = "/";
+        type = types.path;
+        description = "Subdirectory used for reverse proxy setups";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users = {
+      users.leaps = {
+        uid             = config.ids.uids.leaps;
+        description     = "Leaps server user";
+        group           = "leaps";
+        home            = stateDir;
+        createHome      = true;
+      };
+
+      groups.leaps = {
+        gid = config.ids.gids.leaps;
+      };
+    };
+
+    systemd.services.leaps = {
+      description   = "leaps service";
+      wantedBy      = [ "multi-user.target" ];
+      after         = [ "network.target" ];
+
+      serviceConfig = {
+        User = "leaps";
+        Group = "leaps";
+        Restart = "on-failure";
+        WorkingDirectory = stateDir;
+        PrivateTmp = true;
+        ExecStart = "${pkgs.leaps.bin}/bin/leaps -path ${toString cfg.path} -address ${cfg.address}:${toString cfg.port}";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/misc/matrix-synapse.nix b/nixos/modules/services/misc/matrix-synapse.nix
index 4145f8fa957a..277fc9a39022 100644
--- a/nixos/modules/services/misc/matrix-synapse.nix
+++ b/nixos/modules/services/misc/matrix-synapse.nix
@@ -9,11 +9,15 @@ let
   mkListener = l: ''{port: ${toString l.port}, bind_address: "${l.bind_address}", type: ${l.type}, tls: ${fromBool l.tls}, x_forwarded: ${fromBool l.x_forwarded}, resources: [${concatStringsSep "," (map mkResource l.resources)}]}'';
   fromBool = x: if x then "true" else "false";
   configFile = pkgs.writeText "homeserver.yaml" ''
+${optionalString (cfg.tls_certificate_path != null) ''
 tls_certificate_path: "${cfg.tls_certificate_path}"
+''}
 ${optionalString (cfg.tls_private_key_path != null) ''
 tls_private_key_path: "${cfg.tls_private_key_path}"
 ''}
+${optionalString (cfg.tls_dh_params_path != null) ''
 tls_dh_params_path: "${cfg.tls_dh_params_path}"
+''}
 no_tls: ${fromBool cfg.no_tls}
 ${optionalString (cfg.bind_port != null) ''
 bind_port: ${toString cfg.bind_port}
@@ -146,8 +150,9 @@ in {
         '';
       };
       tls_certificate_path = mkOption {
-        type = types.str;
-        default = "/var/lib/matrix-synapse/homeserver.tls.crt";
+        type = types.nullOr types.str;
+        default = null;
+        example = "/var/lib/matrix-synapse/homeserver.tls.crt";
         description = ''
           PEM encoded X509 certificate for TLS.
           You can replace the self-signed certificate that synapse
@@ -158,16 +163,17 @@ in {
       };
       tls_private_key_path = mkOption {
         type = types.nullOr types.str;
-        default = "/var/lib/matrix-synapse/homeserver.tls.key";
-        example = null;
+        default = null;
+        example = "/var/lib/matrix-synapse/homeserver.tls.key";
         description = ''
           PEM encoded private key for TLS. Specify null if synapse is not
           speaking TLS directly.
         '';
       };
       tls_dh_params_path = mkOption {
-        type = types.str;
-        default = "/var/lib/matrix-synapse/homeserver.tls.dh";
+        type = types.nullOr types.str;
+        default = null;
+        example = "/var/lib/matrix-synapse/homeserver.tls.dh";
         description = ''
           PEM dh parameters for ephemeral keys
         '';
@@ -557,12 +563,10 @@ in {
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
       preStart = ''
-        if ! test -e /var/lib/matrix-synapse; then
-          mkdir -p /var/lib/matrix-synapse
-          chmod 700 /var/lib/matrix-synapse
-          chown -R matrix-synapse:matrix-synapse /var/lib/matrix-synapse
-          ${cfg.package}/bin/homeserver --config-path ${configFile} --keys-directory /var/lib/matrix-synapse/ --generate-keys
-        fi
+        ${cfg.package}/bin/homeserver \
+          --config-path ${configFile} \
+          --keys-directory /var/lib/matrix-synapse \
+          --generate-keys
       '';
       serviceConfig = {
         Type = "simple";
@@ -570,7 +574,7 @@ in {
         Group = "matrix-synapse";
         WorkingDirectory = "/var/lib/matrix-synapse";
         PermissionsStartOnly = true;
-        ExecStart = "${cfg.package}/bin/homeserver --config-path ${configFile}";
+        ExecStart = "${cfg.package}/bin/homeserver --config-path ${configFile} --keys-directory /var/lib/matrix-synapse";
       };
     };
   };
diff --git a/nixos/modules/services/misc/parsoid.nix b/nixos/modules/services/misc/parsoid.nix
index 0844190a5490..ab1b54068772 100644
--- a/nixos/modules/services/misc/parsoid.nix
+++ b/nixos/modules/services/misc/parsoid.nix
@@ -91,7 +91,8 @@ in
       wantedBy = [ "multi-user.target" ];
       after = [ "network.target" ];
       serviceConfig = {
-        ExecStart = "${pkgs.nodePackages.parsoid}/lib/node_modules/parsoid/api/server.js -c ${confFile} -n ${toString cfg.workers}";
+        User = "nobody";
+        ExecStart = "${pkgs.nodePackages.parsoid}/lib/node_modules/parsoid/bin/server.js -c ${confFile} -n ${toString cfg.workers}";
       };
     };
 
diff --git a/nixos/modules/services/misc/taskserver/default.nix b/nixos/modules/services/misc/taskserver/default.nix
index 233c47684b7d..ca82a733f6fc 100644
--- a/nixos/modules/services/misc/taskserver/default.nix
+++ b/nixos/modules/services/misc/taskserver/default.nix
@@ -292,7 +292,7 @@ in {
       };
 
       allowedClientIDs = mkOption {
-        type = with types; loeOf (either (enum ["all" "none"]) str);
+        type = with types; either str (listOf str);
         default = [];
         example = [ "[Tt]ask [2-9]+" ];
         description = ''
@@ -306,7 +306,7 @@ in {
       };
 
       disallowedClientIDs = mkOption {
-        type = with types; loeOf (either (enum ["all" "none"]) str);
+        type = with types; either str (listOf str);
         default = [];
         example = [ "[Tt]ask [2-9]+" ];
         description = ''
diff --git a/nixos/modules/services/networking/bitlbee.nix b/nixos/modules/services/networking/bitlbee.nix
index 5e6847097a94..e72ea20cccee 100644
--- a/nixos/modules/services/networking/bitlbee.nix
+++ b/nixos/modules/services/networking/bitlbee.nix
@@ -7,11 +7,6 @@ let
   cfg = config.services.bitlbee;
   bitlbeeUid = config.ids.uids.bitlbee;
 
-  authModeCheck = v:
-    v == "Open" ||
-    v == "Closed" ||
-    v == "Registered";
-
   bitlbeeConfig = pkgs.writeText "bitlbee.conf"
     ''
     [settings]
@@ -67,7 +62,7 @@ in
 
       authMode = mkOption {
         default = "Open";
-        type = types.addCheck types.str authModeCheck;
+        type = types.enum [ "Open" "Closed" "Registered" ];
         description = ''
           The following authentication modes are available:
             Open -- Accept connections from anyone, use NickServ for user authentication.
diff --git a/nixos/modules/services/networking/cjdns.nix b/nixos/modules/services/networking/cjdns.nix
index 5e15e40ea0c1..7e981183353d 100644
--- a/nixos/modules/services/networking/cjdns.nix
+++ b/nixos/modules/services/networking/cjdns.nix
@@ -36,7 +36,7 @@ let
       echo \'\'
       ${concatStringsSep "\n" (mapAttrsToList (k: v:
           optionalString (v.hostname != "")
-            "echo $(${pkgs.cjdns}/bin/publictoip6 ${x.key}) ${x.host}")
+            "echo $(${pkgs.cjdns}/bin/publictoip6 ${v.publicKey}) ${v.hostname}")
           (cfg.ETHInterface.connectTo // cfg.UDPInterface.connectTo))}
       echo \'\'
     '');
@@ -245,7 +245,10 @@ in
       serviceConfig = {
         Type = "forking";
         Restart = "on-failure";
-
+        CapabilityBoundingSet = "CAP_NET_ADMIN CAP_NET_RAW";
+        AmbientCapabilities = "CAP_NET_ADMIN CAP_NET_RAW";
+        ProtectSystem = "full";
+        MemoryDenyWriteExecute = true;
         ProtectHome = true;
         PrivateTmp = true;
       };
diff --git a/nixos/modules/services/networking/dnscrypt-proxy.nix b/nixos/modules/services/networking/dnscrypt-proxy.nix
index 5a24db8ccba0..82bf178f4cb7 100644
--- a/nixos/modules/services/networking/dnscrypt-proxy.nix
+++ b/nixos/modules/services/networking/dnscrypt-proxy.nix
@@ -5,15 +5,25 @@ let
   apparmorEnabled = config.security.apparmor.enable;
   dnscrypt-proxy = pkgs.dnscrypt-proxy;
   cfg = config.services.dnscrypt-proxy;
+  stateDirectory = "/var/lib/dnscrypt-proxy";
 
   localAddress = "${cfg.localAddress}:${toString cfg.localPort}";
 
-  daemonArgs =
-    [ "--local-address=${localAddress}"
-      (optionalString cfg.tcpOnly "--tcp-only")
-      (optionalString cfg.ephemeralKeys "-E")
-    ]
-    ++ resolverArgs;
+  # The minisign public key used to sign the upstream resolver list.
+  # This is somewhat more flexible than preloading the key as an
+  # embedded string.
+  upstreamResolverListPubKey = pkgs.fetchurl {
+    url = https://raw.githubusercontent.com/jedisct1/dnscrypt-proxy/master/minisign.pub;
+    sha256 = "18lnp8qr6ghfc2sd46nn1rhcpr324fqlvgsp4zaigw396cd7vnnh";
+  };
+
+  # Internal flag indicating whether the upstream resolver list is used
+  useUpstreamResolverList = cfg.resolverList == null && cfg.customResolver == null;
+
+  resolverList =
+    if (cfg.resolverList != null)
+      then cfg.resolverList
+      else "${stateDirectory}/dnscrypt-resolvers.csv";
 
   resolverArgs = if (cfg.customResolver != null)
     then
@@ -22,9 +32,16 @@ let
         "--provider-key=${cfg.customResolver.key}"
       ]
     else
-      [ "--resolvers-list=${cfg.resolverList}"
-        "--resolver-name=${toString cfg.resolverName}"
+      [ "--resolvers-list=${resolverList}"
+        "--resolver-name=${cfg.resolverName}"
       ];
+
+  # The final command line arguments passed to the daemon
+  daemonArgs =
+    [ "--local-address=${localAddress}" ]
+    ++ optional cfg.tcpOnly "--tcp-only"
+    ++ optional cfg.ephemeralKeys "-E"
+    ++ resolverArgs;
 in
 
 {
@@ -66,24 +83,20 @@ in
         default = "dnscrypt.eu-nl";
         type = types.nullOr types.str;
         description = ''
-          The name of the upstream DNSCrypt resolver to use, taken from the
-          list named in the <literal>resolverList</literal> option.
-          The default resolver is located in Holland, supports DNS security
-          extensions, and claims to not keep logs.
+          The name of the upstream DNSCrypt resolver to use, taken from
+          <filename>${resolverList}</filename>.  The default resolver is
+          located in Holland, supports DNS security extensions, and
+          <emphasis>claims</emphasis> to not keep logs.
         '';
       };
 
       resolverList = mkOption {
+        default = null;
+        type = types.nullOr types.path;
         description = ''
-          The list of upstream DNSCrypt resolvers. By default, we use the most
-          recent list published by upstream.
+          List of DNSCrypt resolvers.  The default is to use the list of
+          public resolvers provided by upstream.
         '';
-        example = literalExample "${pkgs.dnscrypt-proxy}/share/dnscrypt-proxy/dnscrypt-resolvers.csv";
-        default = pkgs.fetchurl {
-          url = https://raw.githubusercontent.com/jedisct1/dnscrypt-proxy/master/dnscrypt-resolvers.csv;
-          sha256 = "1i9wzw4zl052h5nyp28bwl8d66cgj0awvjhw5wgwz0warkjl1g8g";
-        };
-        defaultText = "pkgs.fetchurl { url = ...; sha256 = ...; }";
       };
 
       customResolver = mkOption {
@@ -150,7 +163,7 @@ in
       }
     ];
 
-    security.apparmor.profiles = mkIf apparmorEnabled (singleton (pkgs.writeText "apparmor-dnscrypt-proxy" ''
+    security.apparmor.profiles = optional apparmorEnabled (pkgs.writeText "apparmor-dnscrypt-proxy" ''
       ${dnscrypt-proxy}/bin/dnscrypt-proxy {
         /dev/null rw,
         /dev/urandom r,
@@ -177,9 +190,9 @@ in
         ${getLib pkgs.lz4}/lib/liblz4.so.* mr,
         ${getLib pkgs.attr}/lib/libattr.so.* mr,
 
-        ${cfg.resolverList} r,
+        ${resolverList} r,
       }
-    ''));
+    '');
 
     users.users.dnscrypt-proxy = {
       description = "dnscrypt-proxy daemon user";
@@ -188,11 +201,61 @@ in
     };
     users.groups.dnscrypt-proxy = {};
 
+    systemd.services.init-dnscrypt-proxy-statedir = optionalAttrs useUpstreamResolverList {
+      description = "Initialize dnscrypt-proxy state directory";
+      script = ''
+        mkdir -pv ${stateDirectory}
+        chown -c dnscrypt-proxy:dnscrypt-proxy ${stateDirectory}
+        cp --preserve=timestamps -uv \
+          ${pkgs.dnscrypt-proxy}/share/dnscrypt-proxy/dnscrypt-resolvers.csv \
+          ${stateDirectory}
+      '';
+      serviceConfig = {
+        Type = "oneshot";
+        RemainAfterExit = true;
+      };
+    };
+
+    systemd.services.update-dnscrypt-resolvers = optionalAttrs useUpstreamResolverList {
+      description = "Update list of DNSCrypt resolvers";
+
+      requires = [ "init-dnscrypt-proxy-statedir.service" ];
+      after = [ "init-dnscrypt-proxy-statedir.service" ];
+
+      path = with pkgs; [ curl minisign ];
+      script = ''
+        cd ${stateDirectory}
+        curl -fSsL -o dnscrypt-resolvers.csv.tmp \
+          https://download.dnscrypt.org/dnscrypt-proxy/dnscrypt-resolvers.csv
+        curl -fSsL -o dnscrypt-resolvers.csv.minisig.tmp \
+          https://download.dnscrypt.org/dnscrypt-proxy/dnscrypt-resolvers.csv.minisig
+        mv dnscrypt-resolvers.csv.minisig{.tmp,}
+        minisign -q -V -p ${upstreamResolverListPubKey} \
+          -m dnscrypt-resolvers.csv.tmp -x dnscrypt-resolvers.csv.minisig
+        mv dnscrypt-resolvers.csv{.tmp,}
+      '';
+
+      serviceConfig = {
+        PrivateTmp = true;
+        PrivateDevices = true;
+        ProtectHome = true;
+        ProtectSystem = true;
+      };
+    };
+
+    systemd.timers.update-dnscrypt-resolvers = optionalAttrs useUpstreamResolverList {
+      timerConfig = {
+        OnBootSec = "5min";
+        OnUnitActiveSec = "6h";
+      };
+      wantedBy = [ "timers.target" ];
+    };
+
     systemd.sockets.dnscrypt-proxy = {
       description = "dnscrypt-proxy listening socket";
       socketConfig = {
-        ListenStream = "${localAddress}";
-        ListenDatagram = "${localAddress}";
+        ListenStream = localAddress;
+        ListenDatagram = localAddress;
       };
       wantedBy = [ "sockets.target" ];
     };
@@ -200,8 +263,13 @@ in
     systemd.services.dnscrypt-proxy = {
       description = "dnscrypt-proxy daemon";
 
-      after = [ "network.target" ] ++ optional apparmorEnabled "apparmor.service";
-      requires = [ "dnscrypt-proxy.socket "] ++ optional apparmorEnabled "apparmor.service";
+      after = [ "network.target" ]
+        ++ optional apparmorEnabled "apparmor.service"
+        ++ optional useUpstreamResolverList "init-dnscrypt-proxy-statedir.service";
+
+      requires = [ "dnscrypt-proxy.socket "]
+        ++ optional apparmorEnabled "apparmor.service"
+        ++ optional useUpstreamResolverList "init-dnscrypt-proxy-statedir.service";
 
       serviceConfig = {
         Type = "simple";
diff --git a/nixos/modules/services/networking/i2pd.nix b/nixos/modules/services/networking/i2pd.nix
index 926857a0ff4e..578376764eba 100644
--- a/nixos/modules/services/networking/i2pd.nix
+++ b/nixos/modules/services/networking/i2pd.nix
@@ -10,7 +10,7 @@ let
 
   extip = "EXTIP=\$(${pkgs.curl.bin}/bin/curl -sf \"http://jsonip.com\" | ${pkgs.gawk}/bin/awk -F'\"' '{print $4}')";
 
-  toYesNo = b: if b then "yes" else "no";
+  toYesNo = b: if b then "true" else "false";
 
   mkEndpointOpt = name: addr: port: {
     enable = mkEnableOption name;
@@ -31,6 +31,17 @@ let
     };
   };
 
+  mkKeyedEndpointOpt = name: addr: port: keyFile:
+  (mkEndpointOpt name addr port) // {
+    keys = mkOption {
+      type = types.str;
+      default = "";
+      description = ''
+        File to persist ${lib.toUpper name} keys.
+      '';
+    };
+  };
+
   commonTunOpts = let
     i2cpOpts = {
       length = mkOption {
@@ -63,19 +74,49 @@ let
     };
   } // mkEndpointOpt name "127.0.0.1" 0;
 
-  i2pdConf = pkgs.writeText "i2pd.conf" ''
-      ipv6 = ${toYesNo cfg.enableIPv6}
-      notransit = ${toYesNo cfg.notransit}
-      floodfill = ${toYesNo cfg.floodfill}
-      ${if isNull cfg.port then "" else "port = ${toString cfg.port}"}
-      ${flip concatMapStrings
-        (collect (proto: proto ? port && proto ? address && proto ? name) cfg.proto)
-        (proto: let portStr = toString proto.port; in ''
-      [${proto.name}]
-      address = ${proto.address}
-      port = ${toString proto.port}
-      enabled = ${toYesNo proto.enable}
-      '')
+  i2pdConf = pkgs.writeText "i2pd.conf"
+  ''
+  ipv4 = ${toYesNo cfg.enableIPv4}
+  ipv6 = ${toYesNo cfg.enableIPv6}
+  notransit = ${toYesNo cfg.notransit}
+  floodfill = ${toYesNo cfg.floodfill}
+  netid = ${toString cfg.netid}
+  ${if isNull cfg.bandwidth then "" else "bandwidth = ${toString cfg.bandwidth}" }
+  ${if isNull cfg.port then "" else "port = ${toString cfg.port}"}
+
+  [limits]
+  transittunnels = ${toString cfg.limits.transittunnels}
+
+  [upnp]
+  enabled = ${toYesNo cfg.upnp.enable}
+  name = ${cfg.upnp.name}
+
+  [precomputation]
+  elgamal = ${toYesNo cfg.precomputation.elgamal}
+
+  [reseed]
+  verify = ${toYesNo cfg.reseed.verify}
+  file = ${cfg.reseed.file}
+  urls = ${builtins.concatStringsSep "," cfg.reseed.urls}
+
+  [addressbook]
+  defaulturl = ${cfg.addressbook.defaulturl}
+  subscriptions = ${builtins.concatStringsSep "," cfg.addressbook.subscriptions}
+  ${flip concatMapStrings
+      (collect (proto: proto ? port && proto ? address && proto ? name) cfg.proto)
+      (proto: let portStr = toString proto.port; in
+        ''
+          [${proto.name}]
+          enabled = ${toYesNo proto.enable}
+          address = ${proto.address}
+          port = ${toString proto.port}
+          ${if proto ? keys then "keys = ${proto.keys}" else ""}
+          ${if proto ? auth then "auth = ${toYesNo proto.auth}" else ""}
+          ${if proto ? user then "user = ${proto.user}" else ""}
+          ${if proto ? pass then "pass = ${proto.pass}" else ""}
+          ${if proto ? outproxy then "outproxy = ${proto.outproxy}" else ""}
+          ${if proto ? outproxyPort then "outproxyport = ${toString proto.outproxyPort}" else ""}
+        '')
       }
   '';
 
@@ -114,7 +155,7 @@ let
   i2pdSh = pkgs.writeScriptBin "i2pd" ''
     #!/bin/sh
     ${if isNull cfg.extIp then extip else ""}
-    ${pkgs.i2pd}/bin/i2pd --log=1 \
+    ${pkgs.i2pd}/bin/i2pd \
       --host=${if isNull cfg.extIp then "$EXTIP" else cfg.extIp} \
       --conf=${i2pdConf} \
       --tunconf=${i2pdTunnelConf}
@@ -135,6 +176,8 @@ in
         default = false;
         description = ''
           Enables I2Pd as a running service upon activation.
+          Please read http://i2pd.readthedocs.io/en/latest/ for further
+          configuration help.
         '';
       };
 
@@ -162,6 +205,22 @@ in
         '';
       };
 
+      netid = mkOption {
+        type = types.int;
+        default = 2;
+        description = ''
+          I2P overlay netid.
+        '';
+      };
+
+      bandwidth = mkOption {
+        type = with types; nullOr int;
+        default = null;
+        description = ''
+           Set a router bandwidth limit integer in kbps or letters: L (32), O (256), P (2048), X (>9000)
+        '';
+      };
+
       port = mkOption {
         type = with types; nullOr int;
         default = null;
@@ -170,6 +229,14 @@ in
         '';
       };
 
+      enableIPv4 = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Enables IPv4 connectivity. Enabled by default.
+        '';
+      };
+
       enableIPv6 = mkOption {
         type = types.bool;
         default = false;
@@ -178,16 +245,141 @@ in
         '';
       };
 
-      proto.http = mkEndpointOpt "http" "127.0.0.1" 7070;
+      upnp = {
+        enable = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Enables UPnP.
+          '';
+        };
+
+        name = mkOption {
+          type = types.str;
+          default = "I2Pd";
+          description = ''
+            Name i2pd appears in UPnP forwardings list.
+          '';
+        };
+      };
+
+      precomputation.elgamal = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Use ElGamal precomputated tables.
+        '';
+      };
+
+      reseed = {
+        verify = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Request SU3 signature verification
+          '';
+        };
+
+        file = mkOption {
+          type = types.str;
+          default = "";
+          description = ''
+            Full path to SU3 file to reseed from
+          '';
+        };
+
+        urls = mkOption {
+          type = with types; listOf str;
+          default = [
+            "https://reseed.i2p-project.de/"
+            "https://i2p.mooo.com/netDb/"
+            "https://netdb.i2p2.no/"
+            "https://us.reseed.i2p2.no:444/"
+            "https://uk.reseed.i2p2.no:444/"
+            "https://i2p.manas.ca:8443/"
+          ];
+          description = ''
+            Reseed URLs
+          '';
+        };
+      };
+
+      addressbook = {
+       defaulturl = mkOption {
+          type = types.str;
+          default = "http://joajgazyztfssty4w2on5oaqksz6tqoxbduy553y34mf4byv6gpq.b32.i2p/export/alive-hosts.txt";
+          description = ''
+            AddressBook subscription URL for initial setup
+          '';
+        };
+       subscriptions = mkOption {
+          type = with types; listOf str;
+          default = [
+            "http://inr.i2p/export/alive-hosts.txt"
+            "http://i2p-projekt.i2p/hosts.txt"
+            "http://stats.i2p/cgi-bin/newhosts.txt"
+          ];
+          description = ''
+            AddressBook subscription URLs
+          '';
+        };
+      };
+
+      limits.transittunnels = mkOption {
+        type = types.int;
+        default = 2500;
+        description = ''
+          Maximum number of active transit sessions
+        '';
+      };
+
+      proto.http = (mkEndpointOpt "http" "127.0.0.1" 7070) // {
+        auth = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Enable authentication for webconsole.
+          '';
+        };
+        user = mkOption {
+          type = types.str;
+          default = "i2pd";
+          description = ''
+            Username for webconsole access
+          '';
+        };
+        pass = mkOption {
+          type = types.str;
+          default = "i2pd";
+          description = ''
+            Password for webconsole access.
+          '';
+        };
+      };
+
+      proto.httpProxy = mkKeyedEndpointOpt "httpproxy" "127.0.0.1" 4446 "";
+      proto.socksProxy = (mkKeyedEndpointOpt "socksproxy" "127.0.0.1" 4447 "")
+      // {
+        outproxy = mkOption {
+          type = types.str;
+          default = "127.0.0.1";
+          description = "Upstream outproxy bind address.";
+        };
+        outproxyPort = mkOption {
+          type = types.int;
+          default = 4444;
+          description = "Upstream outproxy bind port.";
+        };
+      };
+
       proto.sam = mkEndpointOpt "sam" "127.0.0.1" 7656;
       proto.bob = mkEndpointOpt "bob" "127.0.0.1" 2827;
+      proto.i2cp = mkEndpointOpt "i2cp" "127.0.0.1" 7654;
       proto.i2pControl = mkEndpointOpt "i2pcontrol" "127.0.0.1" 7650;
-      proto.httpProxy = mkEndpointOpt "httpproxy" "127.0.0.1" 4446;
-      proto.socksProxy = mkEndpointOpt "socksproxy" "127.0.0.1" 4447;
 
       outTunnels = mkOption {
         default = {};
-        type = with types; loaOf (submodule ( 
+        type = with types; loaOf (submodule (
           { name, config, ... }: {
             options = commonTunOpts name;
             config = {
diff --git a/nixos/modules/services/networking/quassel.nix b/nixos/modules/services/networking/quassel.nix
index 99269c49e8f1..3f0906fdb80d 100644
--- a/nixos/modules/services/networking/quassel.nix
+++ b/nixos/modules/services/networking/quassel.nix
@@ -3,8 +3,8 @@
 with lib;
 
 let
-  quassel = pkgs.kde4.quasselDaemon;
   cfg = config.services.quassel;
+  quassel = cfg.package;
   user = if cfg.user != null then cfg.user else "quassel";
 in
 
@@ -23,6 +23,15 @@ in
         '';
       };
 
+      package = mkOption {
+        type = types.package;
+        default = pkgs.kde4.quasselDaemon;
+        description = ''
+          The package of the quassel daemon.
+        '';
+        example = pkgs.quasselDaemon;
+      };
+
       interfaces = mkOption {
         default = [ "127.0.0.1" ];
         description = ''
diff --git a/nixos/modules/services/networking/smokeping.nix b/nixos/modules/services/networking/smokeping.nix
index 0c1f8d8cdb91..005655f111a1 100644
--- a/nixos/modules/services/networking/smokeping.nix
+++ b/nixos/modules/services/networking/smokeping.nix
@@ -221,7 +221,7 @@ in
         type = types.string;
         default = ''
           + FPing
-          binary = ${pkgs.fping}/bin/fping
+          binary = ${config.security.wrapperDir}/fping
         '';
         description = "Probe configuration";
       };
@@ -284,10 +284,10 @@ in
         mkdir -m 0755 -p ${smokepingHome}/cache ${smokepingHome}/data
         rm -f ${smokepingHome}/cropper
         ln -s ${cfg.package}/htdocs/cropper ${smokepingHome}/cropper
-        chown -R ${cfg.user} ${smokepingHome}
         cp ${cgiHome} ${smokepingHome}/smokeping.fcgi
         ${cfg.package}/bin/smokeping --check --config=${configPath}
         ${cfg.package}/bin/smokeping --static --config=${configPath}
+        chown -R ${cfg.user} ${smokepingHome}
       '';
       script = ''${cfg.package}/bin/smokeping --config=${configPath} --nodaemon'';
     };
diff --git a/nixos/modules/services/networking/tinc.nix b/nixos/modules/services/networking/tinc.nix
index b26d30597b1f..f8e68fda7fc2 100644
--- a/nixos/modules/services/networking/tinc.nix
+++ b/nixos/modules/services/networking/tinc.nix
@@ -68,7 +68,7 @@ in
 
             interfaceType = mkOption {
               default = "tun";
-              type = types.addCheck types.str (n: n == "tun" || n == "tap");
+              type = types.enum [ "tun" "tap" ];
               description = ''
                 The type of virtual interface used for the network connection
               '';
diff --git a/nixos/modules/services/networking/vsftpd.nix b/nixos/modules/services/networking/vsftpd.nix
index 7ec484941ede..deff645d9bfd 100644
--- a/nixos/modules/services/networking/vsftpd.nix
+++ b/nixos/modules/services/networking/vsftpd.nix
@@ -100,6 +100,10 @@ let
         seccomp_sandbox=NO
       ''}
       anon_umask=${cfg.anonymousUmask}
+      ${optionalString cfg.anonymousUser ''
+        anon_root=${cfg.anonymousUserHome}
+      ''}
+      ${cfg.extraConfig}
     '';
 
 in
@@ -163,6 +167,13 @@ in
         description = "Anonymous write umask.";
       };
 
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        example = "ftpd_banner=Hello";
+        description = "Extra configuration to add at the bottom of the generated configuration file.";
+      };
+
     } // (listToAttrs (catAttrs "nixosOption" optionDescription));
 
   };
diff --git a/nixos/modules/services/networking/znc.nix b/nixos/modules/services/networking/znc.nix
index 676e82aa8937..76ba78ff366f 100644
--- a/nixos/modules/services/networking/znc.nix
+++ b/nixos/modules/services/networking/znc.nix
@@ -208,11 +208,10 @@ in
 
         networks = mkOption {
           default = { };
-          type = types.loaOf types.optionSet;
+          type = with types; loaOf (submodule networkOpts);
           description = ''
             IRC networks to connect the user to.
           '';
-          options = [ networkOpts ];
           example = {
             "freenode" = {
               server = "chat.freenode.net";
diff --git a/nixos/modules/services/web-servers/fcgiwrap.nix b/nixos/modules/services/web-servers/fcgiwrap.nix
index 2c5e433003c8..a64a187255a4 100644
--- a/nixos/modules/services/web-servers/fcgiwrap.nix
+++ b/nixos/modules/services/web-servers/fcgiwrap.nix
@@ -21,7 +21,7 @@ in {
       };
 
       socketType = mkOption {
-        type = types.addCheck types.str (t: t == "unix" || t == "tcp" || t == "tcp6");
+        type = types.enum [ "unix" "tcp" "tcp6" ];
         default = "unix";
         description = "Socket type: 'unix', 'tcp' or 'tcp6'.";
       };