summary refs log tree commit diff
path: root/nixos/modules/services/networking
diff options
context:
space:
mode:
authorParnell Springmeyer <parnell@digitalmentat.com>2017-01-26 02:00:04 -0800
committerParnell Springmeyer <parnell@digitalmentat.com>2017-01-26 02:00:04 -0800
commita26a796d5c7fa305e007c2b5229e0521c8b3fb0f (patch)
tree6c35defae305b0da581f757b72b461a8f6052620 /nixos/modules/services/networking
parentad8fde5e5d9bc25a54ac238f485e28b37d6d185a (diff)
parent142696de884213e01cc518af813a20d2e2ece3cc (diff)
downloadnixpkgs-a26a796d5c7fa305e007c2b5229e0521c8b3fb0f.tar
nixpkgs-a26a796d5c7fa305e007c2b5229e0521c8b3fb0f.tar.gz
nixpkgs-a26a796d5c7fa305e007c2b5229e0521c8b3fb0f.tar.bz2
nixpkgs-a26a796d5c7fa305e007c2b5229e0521c8b3fb0f.tar.lz
nixpkgs-a26a796d5c7fa305e007c2b5229e0521c8b3fb0f.tar.xz
nixpkgs-a26a796d5c7fa305e007c2b5229e0521c8b3fb0f.tar.zst
nixpkgs-a26a796d5c7fa305e007c2b5229e0521c8b3fb0f.zip
Merging against master - updating smokingpig, rebase was going to be messy
Diffstat (limited to 'nixos/modules/services/networking')
-rw-r--r--nixos/modules/services/networking/ddclient.nix3
-rw-r--r--nixos/modules/services/networking/dhcpd.nix250
-rw-r--r--nixos/modules/services/networking/dnscrypt-wrapper.nix187
-rw-r--r--nixos/modules/services/networking/firewall.nix127
-rw-r--r--nixos/modules/services/networking/flannel.nix2
-rw-r--r--nixos/modules/services/networking/kresd.nix119
-rw-r--r--nixos/modules/services/networking/miredo.nix1
-rw-r--r--nixos/modules/services/networking/networkmanager.nix5
-rw-r--r--nixos/modules/services/networking/pdns-recursor.nix168
-rw-r--r--nixos/modules/services/networking/smokeping.nix9
10 files changed, 714 insertions, 157 deletions
diff --git a/nixos/modules/services/networking/ddclient.nix b/nixos/modules/services/networking/ddclient.nix
index d1900deceaf..5928203368d 100644
--- a/nixos/modules/services/networking/ddclient.nix
+++ b/nixos/modules/services/networking/ddclient.nix
@@ -132,7 +132,8 @@ in
         login=${config.services.ddclient.username}
         password=${config.services.ddclient.password}
         protocol=${config.services.ddclient.protocol}
-        server=${config.services.ddclient.server}
+        ${let server = config.services.ddclient.server; in
+          lib.optionalString (server != "") "server=${server}"}
         ssl=${if config.services.ddclient.ssl then "yes" else "no"}
         wildcard=YES
         ${config.services.ddclient.domain}
diff --git a/nixos/modules/services/networking/dhcpd.nix b/nixos/modules/services/networking/dhcpd.nix
index d2cd00e74a1..86bcaa96f34 100644
--- a/nixos/modules/services/networking/dhcpd.nix
+++ b/nixos/modules/services/networking/dhcpd.nix
@@ -4,11 +4,10 @@ with lib;
 
 let
 
-  cfg = config.services.dhcpd;
+  cfg4 = config.services.dhcpd4;
+  cfg6 = config.services.dhcpd6;
 
-  stateDir = "/var/lib/dhcp"; # Don't use /var/state/dhcp; not FHS-compliant.
-
-  configFile = if cfg.configFile != null then cfg.configFile else pkgs.writeText "dhcpd.conf"
+  writeConfig = cfg: pkgs.writeText "dhcpd.conf"
     ''
       default-lease-time 600;
       max-lease-time 7200;
@@ -29,131 +28,180 @@ let
       }
     '';
 
-in
-
-{
-
-  ###### interface
-
-  options = {
+  dhcpdService = postfix: cfg: optionalAttrs cfg.enable {
+    "dhcpd${postfix}" = {
+      description = "DHCPv${postfix} server";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+
+      preStart = ''
+        mkdir -m 755 -p ${cfg.stateDir}
+        touch ${cfg.stateDir}/dhcpd.leases
+      '';
+
+      serviceConfig =
+        let
+          configFile = if cfg.configFile != null then cfg.configFile else writeConfig cfg;
+          args = [ "@${pkgs.dhcp}/sbin/dhcpd" "dhcpd${postfix}" "-${postfix}"
+                   "-pf" "/run/dhcpd${postfix}/dhcpd.pid"
+                   "-cf" "${configFile}"
+                   "-lf" "${cfg.stateDir}/dhcpd.leases"
+                   "-user" "dhcpd" "-group" "nogroup"
+                 ] ++ cfg.extraFlags
+                   ++ cfg.interfaces;
+
+        in {
+          ExecStart = concatMapStringsSep " " escapeShellArg args;
+          Type = "forking";
+          Restart = "always";
+          RuntimeDirectory = [ "dhcpd${postfix}" ];
+          PIDFile = "/run/dhcpd${postfix}/dhcpd.pid";
+        };
+    };
+  };
 
-    services.dhcpd = {
+  machineOpts = {...}: {
+    config = {
 
-      enable = mkOption {
-        default = false;
-        description = "
-          Whether to enable the DHCP server.
-        ";
+      hostName = mkOption {
+        type = types.str;
+        example = "foo";
+        description = ''
+          Hostname which is assigned statically to the machine.
+        '';
       };
 
-      extraConfig = mkOption {
-        type = types.lines;
-        default = "";
-        example = ''
-          option subnet-mask 255.255.255.0;
-          option broadcast-address 192.168.1.255;
-          option routers 192.168.1.5;
-          option domain-name-servers 130.161.158.4, 130.161.33.17, 130.161.180.1;
-          option domain-name "example.org";
-          subnet 192.168.1.0 netmask 255.255.255.0 {
-            range 192.168.1.100 192.168.1.200;
-          }
+      ethernetAddress = mkOption {
+        type = types.str;
+        example = "00:16:76:9a:32:1d";
+        description = ''
+          MAC address of the machine.
         '';
-        description = "
-          Extra text to be appended to the DHCP server configuration
-          file.  Currently, you almost certainly need to specify
-          something here, such as the options specifying the subnet
-          mask, DNS servers, etc.
-        ";
       };
 
-      extraFlags = mkOption {
-        default = "";
-        example = "-6";
-        description = "
-          Additional command line flags to be passed to the dhcpd daemon.
-        ";
+      ipAddress = mkOption {
+        type = types.str;
+        example = "192.168.1.10";
+        description = ''
+          IP address of the machine.
+        '';
       };
 
-      configFile = mkOption {
-        default = null;
-        description = "
-          The path of the DHCP server configuration file.  If no file
-          is specified, a file is generated using the other options.
-        ";
-      };
+    };
+  };
 
-      interfaces = mkOption {
-        default = ["eth0"];
-        description = "
-          The interfaces on which the DHCP server should listen.
-        ";
-      };
+  dhcpConfig = postfix: {
 
-      machines = mkOption {
-        default = [];
-        example = [
-          { hostName = "foo";
-            ethernetAddress = "00:16:76:9a:32:1d";
-            ipAddress = "192.168.1.10";
-          }
-          { hostName = "bar";
-            ethernetAddress = "00:19:d1:1d:c4:9a";
-            ipAddress = "192.168.1.11";
-          }
-        ];
-        description = "
-          A list mapping ethernet addresses to IP addresses for the
-          DHCP server.
-        ";
-      };
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether to enable the DHCPv${postfix} server.
+      '';
+    };
 
+    stateDir = mkOption {
+      type = types.path;
+      # We use /var/lib/dhcp for DHCPv4 to save backwards compatibility.
+      default = "/var/lib/dhcp${if postfix == "4" then "" else postfix}";
+      description = ''
+        State directory for the DHCP server.
+      '';
     };
 
-  };
+    extraConfig = mkOption {
+      type = types.lines;
+      default = "";
+      example = ''
+        option subnet-mask 255.255.255.0;
+        option broadcast-address 192.168.1.255;
+        option routers 192.168.1.5;
+        option domain-name-servers 130.161.158.4, 130.161.33.17, 130.161.180.1;
+        option domain-name "example.org";
+        subnet 192.168.1.0 netmask 255.255.255.0 {
+          range 192.168.1.100 192.168.1.200;
+        }
+      '';
+      description = ''
+        Extra text to be appended to the DHCP server configuration
+        file. Currently, you almost certainly need to specify something
+        there, such as the options specifying the subnet mask, DNS servers,
+        etc.
+      '';
+    };
 
+    extraFlags = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      description = ''
+        Additional command line flags to be passed to the dhcpd daemon.
+      '';
+    };
 
-  ###### implementation
+    configFile = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      description = ''
+        The path of the DHCP server configuration file.  If no file
+        is specified, a file is generated using the other options.
+      '';
+    };
 
-  config = mkIf config.services.dhcpd.enable {
+    interfaces = mkOption {
+      type = types.listOf types.str;
+      default = ["eth0"];
+      description = ''
+        The interfaces on which the DHCP server should listen.
+      '';
+    };
 
-    users = {
-      extraUsers.dhcpd = {
-        uid = config.ids.uids.dhcpd;
-        description = "DHCP daemon user";
-      };
+    machines = mkOption {
+      type = types.listOf (types.submodule machineOpts);
+      default = [];
+      example = [
+        { hostName = "foo";
+          ethernetAddress = "00:16:76:9a:32:1d";
+          ipAddress = "192.168.1.10";
+        }
+        { hostName = "bar";
+          ethernetAddress = "00:19:d1:1d:c4:9a";
+          ipAddress = "192.168.1.11";
+        }
+      ];
+      description = ''
+        A list mapping Ethernet addresses to IPv${postfix} addresses for the
+        DHCP server.
+      '';
     };
 
-    systemd.services.dhcpd =
-      { description = "DHCP server";
+  };
+
+in
+
+{
+
+  ###### interface
 
-        wantedBy = [ "multi-user.target" ];
+  options = {
 
-        after = [ "network.target" ];
+    services.dhcpd4 = dhcpConfig "4";
+    services.dhcpd6 = dhcpConfig "6";
 
-        path = [ pkgs.dhcp ];
+  };
 
-        preStart =
-          ''
-            mkdir -m 755 -p ${stateDir}
 
-            touch ${stateDir}/dhcpd.leases
+  ###### implementation
 
-            mkdir -m 755 -p /run/dhcpd
-            chown dhcpd /run/dhcpd
-          '';
+  config = mkIf (cfg4.enable || cfg6.enable) {
 
-        serviceConfig =
-          { ExecStart = "@${pkgs.dhcp}/sbin/dhcpd dhcpd"
-              + " -pf /run/dhcpd/dhcpd.pid -cf ${configFile}"
-              + " -lf ${stateDir}/dhcpd.leases -user dhcpd -group nogroup"
-              + " ${cfg.extraFlags}"
-              + " ${toString cfg.interfaces}";
-            Restart = "always";
-            Type = "forking";
-            PIDFile = "/run/dhcpd/dhcpd.pid";
-          };
+    users = {
+      extraUsers.dhcpd = {
+        uid = config.ids.uids.dhcpd;
+        description = "DHCP daemon user";
       };
+    };
+
+    systemd.services = dhcpdService "4" cfg4 // dhcpdService "6" cfg6;
 
   };
 
diff --git a/nixos/modules/services/networking/dnscrypt-wrapper.nix b/nixos/modules/services/networking/dnscrypt-wrapper.nix
new file mode 100644
index 00000000000..85fac660d52
--- /dev/null
+++ b/nixos/modules/services/networking/dnscrypt-wrapper.nix
@@ -0,0 +1,187 @@
+{ config, lib, pkgs, ... }:
+with lib;
+
+let
+  cfg     = config.services.dnscrypt-wrapper;
+  dataDir = "/var/lib/dnscrypt-wrapper";
+
+  daemonArgs = with cfg; [
+    "--listen-address=${address}:${toString port}"
+    "--resolver-address=${upstream.address}:${toString upstream.port}"
+    "--provider-name=${providerName}"
+    "--provider-publickey-file=public.key"
+    "--provider-secretkey-file=secret.key"
+    "--provider-cert-file=${providerName}.crt"
+    "--crypt-secretkey-file=${providerName}.key"
+  ];
+
+  genKeys = ''
+    # generates time-limited keypairs
+    keyGen() {
+      dnscrypt-wrapper --gen-crypt-keypair \
+        --crypt-secretkey-file=${cfg.providerName}.key
+
+      dnscrypt-wrapper --gen-cert-file \
+        --crypt-secretkey-file=${cfg.providerName}.key \
+        --provider-cert-file=${cfg.providerName}.crt \
+        --provider-publickey-file=public.key \
+        --provider-secretkey-file=secret.key \
+        --cert-file-expire-days=${toString cfg.keys.expiration}
+    }
+
+    cd ${dataDir}
+
+    # generate provider keypair (first run only)
+    if [ ! -f public.key ] || [ ! -f secret.key ]; then
+      dnscrypt-wrapper --gen-provider-keypair
+    fi
+
+    # generate new keys for rotation
+    if [ ! -f ${cfg.providerName}.key ] || [ ! -f ${cfg.providerName}.crt ]; then
+      keyGen
+    fi
+  '';
+
+  rotateKeys = ''
+    # check if keys are not expired
+    keyValid() {
+      fingerprint=$(dnscrypt-wrapper --show-provider-publickey-fingerprint | awk '{print $(NF)}')
+      dnscrypt-proxy --test=${toString (cfg.keys.checkInterval + 1)} \
+        --resolver-address=127.0.0.1:${toString cfg.port} \
+        --provider-name=${cfg.providerName} \
+        --provider-key=$fingerprint
+    }
+
+    cd ${dataDir}
+
+    # archive old keys and restart the service
+    if ! keyValid; then
+      mkdir -p oldkeys
+      mv ${cfg.providerName}.key oldkeys/${cfg.providerName}-$(date +%F-%T).key
+      mv ${cfg.providerName}.crt oldkeys/${cfg.providerName}-$(date +%F-%T).crt
+      systemctl restart dnscrypt-wrapper
+    fi
+  '';
+
+in {
+
+
+  ###### interface
+
+  options.services.dnscrypt-wrapper = {
+    enable = mkEnableOption "DNSCrypt wrapper";
+
+    address = mkOption {
+      type = types.str;
+      default = "127.0.0.1";
+      description = ''
+        The DNSCrypt wrapper will bind to this IP address.
+      '';
+    };
+
+    port = mkOption {
+      type = types.int;
+      default = 5353;
+      description = ''
+        The DNSCrypt wrapper will listen for DNS queries on this port.
+      '';
+    };
+
+    providerName = mkOption {
+      type = types.str;
+      default = "2.dnscrypt-cert.${config.networking.hostName}";
+      example = "2.dnscrypt-cert.myresolver";
+      description = ''
+        The name that will be given to this DNSCrypt resolver.
+        Note: the resolver name must start with <literal>2.dnscrypt-cert.</literal>.
+      '';
+    };
+
+    upstream.address = mkOption {
+      type = types.str;
+      default = "127.0.0.1";
+      description = ''
+        The IP address of the upstream DNS server DNSCrypt will "wrap".
+      '';
+    };
+
+    upstream.port = mkOption {
+      type = types.int;
+      default = 53;
+      description = ''
+        The port of the upstream DNS server DNSCrypt will "wrap".
+      '';
+    };
+
+    keys.expiration = mkOption {
+      type = types.int;
+      default = 30;
+      description = ''
+        The duration (in days) of the time-limited secret key.
+        This will be automatically rotated before expiration.
+      '';
+    };
+
+    keys.checkInterval = mkOption {
+      type = types.int;
+      default = 1440;
+      description = ''
+        The time interval (in minutes) between key expiration checks.
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    users.users.dnscrypt-wrapper = {
+      description = "dnscrypt-wrapper daemon user";
+      home = "${dataDir}";
+      createHome = true;
+    };
+    users.groups.dnscrypt-wrapper = { };
+
+
+    systemd.services.dnscrypt-wrapper = {
+      description = "dnscrypt-wrapper daemon";
+      after    = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      path     = [ pkgs.dnscrypt-wrapper ];
+
+      serviceConfig = {
+        User = "dnscrypt-wrapper";
+        WorkingDirectory = dataDir;
+        Restart   = "on-failure";
+        ExecStart = "${pkgs.dnscrypt-wrapper}/bin/dnscrypt-wrapper ${toString daemonArgs}";
+      };
+
+      preStart = genKeys;
+    };
+
+
+    systemd.services.dnscrypt-wrapper-rotate = {
+      after    = [ "network.target" ];
+      requires = [ "dnscrypt-wrapper.service" ];
+      description = "Rotates DNSCrypt wrapper keys if soon to expire";
+
+      path   = with pkgs; [ dnscrypt-wrapper dnscrypt-proxy gawk ];
+      script = rotateKeys;
+    };
+
+
+    systemd.timers.dnscrypt-wrapper-rotate = {
+      description = "Periodically check DNSCrypt wrapper keys for expiration";
+      wantedBy = [ "multi-user.target" ];
+
+      timerConfig = {
+        Unit = "dnscrypt-wrapper-rotate.service";
+        OnBootSec = "1min";
+        OnUnitActiveSec = cfg.keys.checkInterval * 60;
+      };
+    };
+
+  };
+}
diff --git a/nixos/modules/services/networking/firewall.nix b/nixos/modules/services/networking/firewall.nix
index 1c0ea5034df..34b731ad35c 100644
--- a/nixos/modules/services/networking/firewall.nix
+++ b/nixos/modules/services/networking/firewall.nix
@@ -4,17 +4,29 @@
    ‘networking.firewall.extraCommands’.  For modularity, the firewall
    uses several chains:
 
-   - ‘nixos-fw-input’ is the main chain for input packet processing.
+   - ‘nixos-fw’ is the main chain for input packet processing.
+
+   - ‘nixos-fw-accept’ is called for accepted packets.  If you want
+     additional logging, or want to reject certain packets anyway, you
+     can insert rules at the start of this chain.
 
    - ‘nixos-fw-log-refuse’ and ‘nixos-fw-refuse’ are called for
      refused packets.  (The former jumps to the latter after logging
      the packet.)  If you want additional logging, or want to accept
      certain packets anyway, you can insert rules at the start of
-     these chain.
+     this chain.
 
-   - ‘nixos-fw-accept’ is called for accepted packets.  If you want
-     additional logging, or want to reject certain packets anyway, you
-     can insert rules at the start of this chain.
+   - ‘nixos-fw-rpfilter’ is used as the main chain in the raw table,
+     called from the built-in ‘PREROUTING’ chain.  If the kernel
+     supports it and `cfg.checkReversePath` is set this chain will
+     perform a reverse path filter test.
+
+   - ‘nixos-drop’ is used while reloading the firewall in order to drop
+     all traffic.  Since reloading isn't implemented in an atomic way
+     this'll prevent any traffic from leaking through while reloading
+     the firewall.  However, if the reloading fails, the ‘firewall-stop’
+     script will be called which in return will effectively disable the
+     complete firewall (in the default configuration).
 
 */
 
@@ -26,6 +38,10 @@ let
 
   cfg = config.networking.firewall;
 
+  kernelPackages = config.boot.kernelPackages;
+
+  kernelHasRPFilter = kernelPackages.kernel.features.netfilterRPFilter or false;
+
   helpers =
     ''
       # Helper command to manipulate both the IPv4 and IPv6 tables.
@@ -49,7 +65,7 @@ let
     # firewall would be atomic.  Apparently that's possible
     # with iptables-restore.
     ip46tables -D INPUT -j nixos-fw 2> /dev/null || true
-    for chain in nixos-fw nixos-fw-accept nixos-fw-log-refuse nixos-fw-refuse FW_REFUSE; do
+    for chain in nixos-fw nixos-fw-accept nixos-fw-log-refuse nixos-fw-refuse; do
       ip46tables -F "$chain" 2> /dev/null || true
       ip46tables -X "$chain" 2> /dev/null || true
     done
@@ -172,13 +188,16 @@ let
       }-j nixos-fw-accept
     ''}
 
-    # Accept all ICMPv6 messages except redirects and node
-    # information queries (type 139).  See RFC 4890, section
-    # 4.4.
     ${optionalString config.networking.enableIPv6 ''
+      # Accept all ICMPv6 messages except redirects and node
+      # information queries (type 139).  See RFC 4890, section
+      # 4.4.
       ip6tables -A nixos-fw -p icmpv6 --icmpv6-type redirect -j DROP
       ip6tables -A nixos-fw -p icmpv6 --icmpv6-type 139 -j DROP
       ip6tables -A nixos-fw -p icmpv6 -j nixos-fw-accept
+
+      # Allow this host to act as a DHCPv6 client
+      ip6tables -A nixos-fw -d fe80::/64 -p udp --dport 546 -j nixos-fw-accept
     ''}
 
     ${cfg.extraCommands}
@@ -228,11 +247,6 @@ let
     fi
   '';
 
-  kernelPackages = config.boot.kernelPackages;
-
-  kernelHasRPFilter = kernelPackages.kernel.features.netfilterRPFilter or false;
-  kernelCanDisableHelpers = kernelPackages.kernel.features.canDisableNetfilterConntrackHelpers or false;
-
 in
 
 {
@@ -290,26 +304,30 @@ in
       default = false;
       description =
         ''
-          If set, forbidden packets are rejected rather than dropped
+          If set, refused packets are rejected rather than dropped
           (ignored).  This means that an ICMP "port unreachable" error
-          message is sent back to the client.  Rejecting packets makes
+          message is sent back to the client (or a TCP RST packet in
+          case of an existing connection).  Rejecting packets makes
           port scanning somewhat easier.
         '';
     };
 
     networking.firewall.trustedInterfaces = mkOption {
       type = types.listOf types.str;
+      default = [ ];
+      example = [ "enp0s2" ];
       description =
         ''
           Traffic coming in from these interfaces will be accepted
-          unconditionally.
+          unconditionally.  Traffic from the loopback (lo) interface
+          will always be accepted.
         '';
     };
 
     networking.firewall.allowedTCPPorts = mkOption {
-      default = [];
-      example = [ 22 80 ];
       type = types.listOf types.int;
+      default = [ ];
+      example = [ 22 80 ];
       description =
         ''
           List of TCP ports on which incoming connections are
@@ -318,9 +336,9 @@ in
     };
 
     networking.firewall.allowedTCPPortRanges = mkOption {
-      default = [];
-      example = [ { from = 8999; to = 9003; } ];
       type = types.listOf (types.attrsOf types.int);
+      default = [ ];
+      example = [ { from = 8999; to = 9003; } ];
       description =
         ''
           A range of TCP ports on which incoming connections are
@@ -329,9 +347,9 @@ in
     };
 
     networking.firewall.allowedUDPPorts = mkOption {
-      default = [];
-      example = [ 53 ];
       type = types.listOf types.int;
+      default = [ ];
+      example = [ 53 ];
       description =
         ''
           List of open UDP ports.
@@ -339,9 +357,9 @@ in
     };
 
     networking.firewall.allowedUDPPortRanges = mkOption {
-      default = [];
-      example = [ { from = 60000; to = 61000; } ];
       type = types.listOf (types.attrsOf types.int);
+      default = [ ];
+      example = [ { from = 60000; to = 61000; } ];
       description =
         ''
           Range of open UDP ports.
@@ -349,8 +367,8 @@ in
     };
 
     networking.firewall.allowPing = mkOption {
-      default = true;
       type = types.bool;
+      default = true;
       description =
         ''
           Whether to respond to incoming ICMPv4 echo requests
@@ -361,36 +379,43 @@ in
     };
 
     networking.firewall.pingLimit = mkOption {
-      default = null;
       type = types.nullOr (types.separatedString " ");
+      default = null;
+      example = "--limit 1/minute --limit-burst 5";
       description =
         ''
           If pings are allowed, this allows setting rate limits
-          on them. If non-null, this option should be in the form
-          of flags like "--limit 1/minute --limit-burst 5"
+          on them.  If non-null, this option should be in the form of
+          flags like "--limit 1/minute --limit-burst 5"
         '';
     };
 
     networking.firewall.checkReversePath = mkOption {
-      default = kernelHasRPFilter;
       type = types.either types.bool (types.enum ["strict" "loose"]);
+      default = kernelHasRPFilter;
+      example = "loose";
       description =
         ''
-          Performs a reverse path filter test on a packet.
-          If a reply to the packet would not be sent via the same interface
-          that the packet arrived on, it is refused.
+          Performs a reverse path filter test on a packet.  If a reply
+          to the packet would not be sent via the same interface that
+          the packet arrived on, it is refused.
 
-          If using asymmetric routing or other complicated routing,
-          set this option to loose mode or disable it and setup your
-          own counter-measures.
+          If using asymmetric routing or other complicated routing, set
+          this option to loose mode or disable it and setup your own
+          counter-measures.
+
+          This option can be either true (or "strict"), "loose" (only
+          drop the packet if the source address is not reachable via any
+          interface) or false.  Defaults to the value of
+          kernelHasRPFilter.
 
           (needs kernel 3.3+)
         '';
     };
 
     networking.firewall.logReversePathDrops = mkOption {
-      default = false;
       type = types.bool;
+      default = false;
       description =
         ''
           Logs dropped packets failing the reverse path filter test if
@@ -399,9 +424,9 @@ in
     };
 
     networking.firewall.connectionTrackingModules = mkOption {
-      default = [ "ftp" ];
-      example = [ "ftp" "irc" "sane" "sip" "tftp" "amanda" "h323" "netbios_sn" "pptp" "snmp" ];
       type = types.listOf types.str;
+      default = [ ];
+      example = [ "ftp" "irc" "sane" "sip" "tftp" "amanda" "h323" "netbios_sn" "pptp" "snmp" ];
       description =
         ''
           List of connection-tracking helpers that are auto-loaded.
@@ -409,17 +434,19 @@ in
 
           As helpers can pose as a security risk, it is advised to
           set this to an empty list and disable the setting
-          networking.firewall.autoLoadConntrackHelpers
+          networking.firewall.autoLoadConntrackHelpers unless you
+          know what you are doing. Connection tracking is disabled
+          by default.
 
-          Loading of helpers is recommended to be done through the new
-          CT target. More info:
+          Loading of helpers is recommended to be done through the
+          CT target.  More info:
           https://home.regit.org/netfilter-en/secure-use-of-helpers/
         '';
     };
 
     networking.firewall.autoLoadConntrackHelpers = mkOption {
-      default = true;
       type = types.bool;
+      default = false;
       description =
         ''
           Whether to auto-load connection-tracking helpers.
@@ -461,7 +488,8 @@ in
         ''
           Additional shell commands executed as part of the firewall
           shutdown script.  These are executed just after the removal
-          of the nixos input rule, or if the service enters a failed state.
+          of the NixOS input rule, or if the service enters a failed
+          state.
         '';
     };
 
@@ -478,15 +506,14 @@ in
 
     environment.systemPackages = [ pkgs.iptables ] ++ cfg.extraPackages;
 
-    boot.kernelModules = map (x: "nf_conntrack_${x}") cfg.connectionTrackingModules;
-    boot.extraModprobeConfig = optionalString (!cfg.autoLoadConntrackHelpers) ''
-      options nf_conntrack nf_conntrack_helper=0
+    boot.kernelModules = (optional cfg.autoLoadConntrackHelpers "nf_conntrack")
+      ++ map (x: "nf_conntrack_${x}") cfg.connectionTrackingModules;
+    boot.extraModprobeConfig = optionalString cfg.autoLoadConntrackHelpers ''
+      options nf_conntrack nf_conntrack_helper=1
     '';
 
     assertions = [ { assertion = (cfg.checkReversePath != false) || kernelHasRPFilter;
                      message = "This kernel does not support rpfilter"; }
-                   { assertion = cfg.autoLoadConntrackHelpers || kernelCanDisableHelpers;
-                     message = "This kernel does not support disabling conntrack helpers"; }
                  ];
 
     systemd.services.firewall = {
@@ -499,7 +526,7 @@ in
       path = [ pkgs.iptables ] ++ cfg.extraPackages;
 
       # FIXME: this module may also try to load kernel modules, but
-      # containers don't have CAP_SYS_MODULE. So the host system had
+      # containers don't have CAP_SYS_MODULE.  So the host system had
       # better have all necessary modules already loaded.
       unitConfig.ConditionCapability = "CAP_NET_ADMIN";
       unitConfig.DefaultDependencies = false;
diff --git a/nixos/modules/services/networking/flannel.nix b/nixos/modules/services/networking/flannel.nix
index ca47a18bc1f..b93e28e34ef 100644
--- a/nixos/modules/services/networking/flannel.nix
+++ b/nixos/modules/services/networking/flannel.nix
@@ -149,6 +149,6 @@ in {
       serviceConfig.ExecStart = "${cfg.package}/bin/flannel";
     };
 
-    services.etcd.enable = mkDefault cfg.etcd.endpoints == ["http://127.0.0.1:2379"];
+    services.etcd.enable = mkDefault (cfg.etcd.endpoints == ["http://127.0.0.1:2379"]);
   };
 }
diff --git a/nixos/modules/services/networking/kresd.nix b/nixos/modules/services/networking/kresd.nix
new file mode 100644
index 00000000000..18e2ab9aebf
--- /dev/null
+++ b/nixos/modules/services/networking/kresd.nix
@@ -0,0 +1,119 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.kresd;
+  package = pkgs.knot-resolver;
+
+  configFile = pkgs.writeText "kresd.conf" cfg.extraConfig;
+in
+
+{
+  meta.maintainers = [ maintainers.vcunat /* upstream developer */ ];
+
+  ###### interface
+  options.services.kresd = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether to enable knot-resolver domain name server.
+        DNSSEC validation is turned on by default.
+        You can run <literal>sudo nc -U /run/kresd/control</literal>
+        and give commands interactively to kresd.
+      '';
+    };
+    extraConfig = mkOption {
+      type = types.lines;
+      default = "";
+      description = ''
+        Extra lines to be added verbatim to the generated configuration file.
+      '';
+    };
+    cacheDir = mkOption {
+      type = types.path;
+      default = "/var/cache/kresd";
+      description = ''
+        Directory for caches.  They are intended to survive reboots.
+      '';
+    };
+    interfaces = mkOption {
+      type = with types; listOf str;
+      default = [ "::1" "127.0.0.1" ];
+      description = ''
+        What addresses the server should listen on.
+      '';
+    };
+    # TODO: perhaps options for more common stuff like cache size or forwarding
+  };
+
+  ###### implementation
+  config = mkIf cfg.enable {
+    environment.etc."kresd.conf".source = configFile; # not required
+
+    users.extraUsers = singleton
+      { name = "kresd";
+        uid = config.ids.uids.kresd;
+        group = "kresd";
+        description = "Knot-resolver daemon user";
+      };
+    users.extraGroups = singleton
+      { name = "kresd";
+        gid = config.ids.gids.kresd;
+      };
+
+    systemd.sockets.kresd = rec {
+      wantedBy = [ "sockets.target" ];
+      before = wantedBy;
+      listenStreams = map
+        # Syntax depends on being IPv6 or IPv4.
+        (iface: if elem ":" (stringToCharacters iface) then "[${iface}]:53" else "${iface}:53")
+        cfg.interfaces;
+      socketConfig.ListenDatagram = listenStreams;
+    };
+
+    systemd.sockets.kresd-control = rec {
+      wantedBy = [ "sockets.target" ];
+      before = wantedBy;
+      partOf = [ "kresd.socket" ];
+      listenStreams = [ "/run/kresd/control" ];
+      socketConfig = {
+        FileDescriptorName = "control";
+        Service = "kresd.service";
+        SocketMode = "0660"; # only root user/group may connect
+      };
+    };
+
+    # Create the cacheDir; tmpfiles don't work on nixos-rebuild switch.
+    systemd.services.kresd-cachedir = {
+      serviceConfig.Type = "oneshot";
+      script = ''
+        if [ ! -d '${cfg.cacheDir}' ]; then
+          mkdir -p '${cfg.cacheDir}'
+          chown kresd:kresd '${cfg.cacheDir}'
+        fi
+      '';
+    };
+
+    systemd.services.kresd = {
+      description = "Knot-resolver daemon";
+
+      serviceConfig = {
+        User = "kresd";
+        Type = "notify";
+        WorkingDirectory = cfg.cacheDir;
+      };
+
+      script = ''
+        exec '${package}/bin/kresd' --config '${configFile}' \
+          -k '${cfg.cacheDir}/root.key'
+      '';
+
+      after = [ "kresd-cachedir.service" ];
+      requires = [ "kresd.socket" "kresd-cachedir.service" ];
+      wantedBy = [ "sockets.target" ];
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/miredo.nix b/nixos/modules/services/networking/miredo.nix
index 932d6cf2903..3d560338e2c 100644
--- a/nixos/modules/services/networking/miredo.nix
+++ b/nixos/modules/services/networking/miredo.nix
@@ -82,7 +82,6 @@ in
       serviceConfig = {
         Restart = "always";
         RestartSec = "5s";
-        ExecStartPre = "${cfg.package}/bin/miredo-checkconf -f ${miredoConf}";
         ExecStart = "${cfg.package}/bin/miredo -c ${miredoConf} -p ${pidFile} -f";
         ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
       };
diff --git a/nixos/modules/services/networking/networkmanager.nix b/nixos/modules/services/networking/networkmanager.nix
index 8f353979d3f..c11d4434c20 100644
--- a/nixos/modules/services/networking/networkmanager.nix
+++ b/nixos/modules/services/networking/networkmanager.nix
@@ -174,7 +174,7 @@ in {
 
     assertions = [{
       assertion = config.networking.wireless.enable == false;
-      message = "You can not use networking.networkmanager with services.networking.wireless";
+      message = "You can not use networking.networkmanager with networking.wireless";
     }];
 
     boot.kernelModules = [ "ppp_mppe" ]; # Needed for most (all?) PPTP VPN connections.
@@ -239,7 +239,8 @@ in {
     # Turn off NixOS' network management
     networking = {
       useDHCP = false;
-      wireless.enable = false;
+      # use mkDefault to trigger the assertion about the conflict above
+      wireless.enable = lib.mkDefault false;
     };
 
     powerManagement.resumeCommands = ''
diff --git a/nixos/modules/services/networking/pdns-recursor.nix b/nixos/modules/services/networking/pdns-recursor.nix
new file mode 100644
index 00000000000..26be72d2a61
--- /dev/null
+++ b/nixos/modules/services/networking/pdns-recursor.nix
@@ -0,0 +1,168 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  dataDir  = "/var/lib/pdns-recursor";
+  username = "pdns-recursor";
+
+  cfg   = config.services.pdns-recursor;
+  zones = mapAttrsToList (zone: uri: "${zone}.=${uri}") cfg.forwardZones;
+
+  configFile = pkgs.writeText "recursor.conf" ''
+    local-address=${cfg.dns.address}
+    local-port=${toString cfg.dns.port}
+    allow-from=${concatStringsSep "," cfg.dns.allowFrom}
+
+    webserver-address=${cfg.api.address}
+    webserver-port=${toString cfg.api.port}
+    webserver-allow-from=${concatStringsSep "," cfg.api.allowFrom}
+
+    forward-zones=${concatStringsSep "," zones}
+    export-etc-hosts=${if cfg.exportHosts then "yes" else "no"}
+    dnssec=${cfg.dnssecValidation}
+    serve-rfc1918=${if cfg.serveRFC1918 then "yes" else "no"}
+
+    ${cfg.extraConfig}
+  '';
+
+in {
+  options.services.pdns-recursor = {
+    enable = mkEnableOption "PowerDNS Recursor, a recursive DNS server";
+
+    dns.address = mkOption {
+      type = types.str;
+      default = "0.0.0.0";
+      description = ''
+        IP address Recursor DNS server will bind to.
+      '';
+    };
+
+    dns.port = mkOption {
+      type = types.int;
+      default = 53;
+      description = ''
+        Port number Recursor DNS server will bind to.
+      '';
+    };
+
+    dns.allowFrom = mkOption {
+      type = types.listOf types.str;
+      default = [ "10.0.0.0/8" "172.16.0.0/12" "192.168.0.0/16" ];
+      example = [ "0.0.0.0/0" ];
+      description = ''
+        IP address ranges of clients allowed to make DNS queries.
+      '';
+    };
+
+    api.address = mkOption {
+      type = types.str;
+      default = "0.0.0.0";
+      description = ''
+        IP address Recursor REST API server will bind to.
+      '';
+    };
+
+    api.port = mkOption {
+      type = types.int;
+      default = 8082;
+      description = ''
+        Port number Recursor REST API server will bind to.
+      '';
+    };
+
+    api.allowFrom = mkOption {
+      type = types.listOf types.str;
+      default = [ "0.0.0.0/0" ];
+      description = ''
+        IP address ranges of clients allowed to make API requests.
+      '';
+    };
+
+    exportHosts = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+       Whether to export names and IP addresses defined in /etc/hosts.
+      '';
+    };
+
+    forwardZones = mkOption {
+      type = types.attrs;
+      example = { eth = "127.0.0.1:5353"; };
+      default = {};
+      description = ''
+        DNS zones to be forwarded to other servers.
+      '';
+    };
+
+    dnssecValidation = mkOption {
+      type = types.enum ["off" "process-no-validate" "process" "log-fail" "validate"];
+      default = "validate";
+      description = ''
+        Controls the level of DNSSEC processing done by the PowerDNS Recursor.
+        See https://doc.powerdns.com/md/recursor/dnssec/ for a detailed explanation.
+      '';
+    };
+
+    serveRFC1918 = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Whether to directly resolve the RFC1918 reverse-mapping domains:
+        <literal>10.in-addr.arpa</literal>,
+        <literal>168.192.in-addr.arpa</literal>,
+        <literal>16-31.172.in-addr.arpa</literal>
+        This saves load on the AS112 servers.
+      '';
+    };
+
+    extraConfig = mkOption {
+      type = types.lines;
+      default = "";
+      description = ''
+        Extra options to be appended to the configuration file.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    users.extraUsers."${username}" = {
+      home = dataDir;
+      createHome = true;
+      uid = config.ids.uids.pdns-recursor;
+      description = "PowerDNS Recursor daemon user";
+    };
+
+    systemd.services.pdns-recursor = {
+      unitConfig.Documentation = "man:pdns_recursor(1) man:rec_control(1)";
+      description = "PowerDNS recursive server";
+      wantedBy = [ "multi-user.target" ];
+      after    = [ "network.target" ];
+
+      serviceConfig = {
+        User = username;
+        Restart    ="on-failure";
+        RestartSec = "5";
+        PrivateTmp = true;
+        PrivateDevices = true;
+        AmbientCapabilities = "cap_net_bind_service";
+        ExecStart = ''${pkgs.pdns-recursor}/bin/pdns_recursor \
+          --config-dir=${dataDir} \
+          --socket-dir=${dataDir} \
+          --disable-syslog
+        '';
+      };
+
+      preStart = ''
+        # Link configuration file into recursor home directory
+        configPath=${dataDir}/recursor.conf
+        if [ "$(realpath $configPath)" != "${configFile}" ]; then
+          rm -f $configPath
+          ln -s ${configFile} $configPath
+        fi
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/smokeping.nix b/nixos/modules/services/networking/smokeping.nix
index 6d2f5f8d41f..67aa313c860 100644
--- a/nixos/modules/services/networking/smokeping.nix
+++ b/nixos/modules/services/networking/smokeping.nix
@@ -275,7 +275,14 @@ in
     ];
     security.permissionsWrappers.setuid = [
       { program = "fping";
-        source  = "${e.enlightenment.out}/bin/fping";
+        source  = "${pkgs.fping}/bin/fping";
+        owner   = "root";
+        group   = "root";
+        setuid  = true;
+      }
+
+      { program = "fping";
+        source  = "${pkgs.fping}/bin/fping6";
         owner   = "root";
         group   = "root";
         setuid  = true;