about summary refs log tree commit diff
path: root/nixpkgs/nixos/modules/services/networking
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/nixos/modules/services/networking')
-rw-r--r--nixpkgs/nixos/modules/services/networking/3proxy.nix426
-rw-r--r--nixpkgs/nixos/modules/services/networking/amuled.nix77
-rw-r--r--nixpkgs/nixos/modules/services/networking/aria2.nix131
-rw-r--r--nixpkgs/nixos/modules/services/networking/asterisk.nix264
-rw-r--r--nixpkgs/nixos/modules/services/networking/atftpd.nix65
-rw-r--r--nixpkgs/nixos/modules/services/networking/autossh.nix113
-rw-r--r--nixpkgs/nixos/modules/services/networking/avahi-daemon.nix285
-rw-r--r--nixpkgs/nixos/modules/services/networking/babeld.nix95
-rw-r--r--nixpkgs/nixos/modules/services/networking/bind.nix205
-rw-r--r--nixpkgs/nixos/modules/services/networking/bird.nix78
-rw-r--r--nixpkgs/nixos/modules/services/networking/bitcoind.nix193
-rw-r--r--nixpkgs/nixos/modules/services/networking/bitlbee.nix193
-rw-r--r--nixpkgs/nixos/modules/services/networking/charybdis.nix107
-rw-r--r--nixpkgs/nixos/modules/services/networking/cjdns.nix294
-rw-r--r--nixpkgs/nixos/modules/services/networking/cntlm.nix121
-rw-r--r--nixpkgs/nixos/modules/services/networking/connman.nix161
-rw-r--r--nixpkgs/nixos/modules/services/networking/consul.nix254
-rw-r--r--nixpkgs/nixos/modules/services/networking/coredns.nix50
-rw-r--r--nixpkgs/nixos/modules/services/networking/corerad.nix85
-rw-r--r--nixpkgs/nixos/modules/services/networking/coturn.nix334
-rw-r--r--nixpkgs/nixos/modules/services/networking/dante.nix62
-rw-r--r--nixpkgs/nixos/modules/services/networking/ddclient.nix209
-rw-r--r--nixpkgs/nixos/modules/services/networking/dhcpcd.nix225
-rw-r--r--nixpkgs/nixos/modules/services/networking/dhcpd.nix214
-rw-r--r--nixpkgs/nixos/modules/services/networking/dnscache.nix108
-rw-r--r--nixpkgs/nixos/modules/services/networking/dnscrypt-proxy2.nix62
-rw-r--r--nixpkgs/nixos/modules/services/networking/dnscrypt-wrapper.nix281
-rw-r--r--nixpkgs/nixos/modules/services/networking/dnsdist.nix60
-rw-r--r--nixpkgs/nixos/modules/services/networking/dnsmasq.nix128
-rw-r--r--nixpkgs/nixos/modules/services/networking/ejabberd.nix157
-rw-r--r--nixpkgs/nixos/modules/services/networking/epmd.nix56
-rw-r--r--nixpkgs/nixos/modules/services/networking/ergo.nix141
-rw-r--r--nixpkgs/nixos/modules/services/networking/eternal-terminal.nix95
-rw-r--r--nixpkgs/nixos/modules/services/networking/fakeroute.nix65
-rw-r--r--nixpkgs/nixos/modules/services/networking/ferm.nix63
-rw-r--r--nixpkgs/nixos/modules/services/networking/firefox/sync-server.nix183
-rw-r--r--nixpkgs/nixos/modules/services/networking/fireqos.nix52
-rw-r--r--nixpkgs/nixos/modules/services/networking/firewall.nix585
-rw-r--r--nixpkgs/nixos/modules/services/networking/flannel.nix191
-rw-r--r--nixpkgs/nixos/modules/services/networking/flashpolicyd.nix85
-rw-r--r--nixpkgs/nixos/modules/services/networking/freenet.nix64
-rw-r--r--nixpkgs/nixos/modules/services/networking/freeradius.nix84
-rw-r--r--nixpkgs/nixos/modules/services/networking/gale.nix181
-rw-r--r--nixpkgs/nixos/modules/services/networking/gateone.nix59
-rw-r--r--nixpkgs/nixos/modules/services/networking/gdomap.nix29
-rw-r--r--nixpkgs/nixos/modules/services/networking/git-daemon.nix130
-rw-r--r--nixpkgs/nixos/modules/services/networking/gnunet.nix166
-rw-r--r--nixpkgs/nixos/modules/services/networking/go-neb.nix53
-rw-r--r--nixpkgs/nixos/modules/services/networking/go-shadowsocks2.nix30
-rw-r--r--nixpkgs/nixos/modules/services/networking/gogoclient.nix85
-rw-r--r--nixpkgs/nixos/modules/services/networking/gvpe.nix124
-rw-r--r--nixpkgs/nixos/modules/services/networking/hans.nix145
-rw-r--r--nixpkgs/nixos/modules/services/networking/haproxy.nix112
-rw-r--r--nixpkgs/nixos/modules/services/networking/helpers.nix11
-rw-r--r--nixpkgs/nixos/modules/services/networking/heyefi.nix82
-rw-r--r--nixpkgs/nixos/modules/services/networking/hostapd.nix218
-rw-r--r--nixpkgs/nixos/modules/services/networking/htpdate.nix80
-rw-r--r--nixpkgs/nixos/modules/services/networking/hylafax/default.nix31
-rw-r--r--nixpkgs/nixos/modules/services/networking/hylafax/faxq-default.nix12
-rwxr-xr-xnixpkgs/nixos/modules/services/networking/hylafax/faxq-wait.sh29
-rw-r--r--nixpkgs/nixos/modules/services/networking/hylafax/hfaxd-default.nix10
-rw-r--r--nixpkgs/nixos/modules/services/networking/hylafax/modem-default.nix22
-rw-r--r--nixpkgs/nixos/modules/services/networking/hylafax/options.nix375
-rwxr-xr-xnixpkgs/nixos/modules/services/networking/hylafax/spool.sh111
-rw-r--r--nixpkgs/nixos/modules/services/networking/hylafax/systemd.nix249
-rw-r--r--nixpkgs/nixos/modules/services/networking/i2p.nix34
-rw-r--r--nixpkgs/nixos/modules/services/networking/i2pd.nix684
-rw-r--r--nixpkgs/nixos/modules/services/networking/iodine.nix197
-rw-r--r--nixpkgs/nixos/modules/services/networking/iperf3.nix97
-rw-r--r--nixpkgs/nixos/modules/services/networking/ircd-hybrid/builder.sh31
-rw-r--r--nixpkgs/nixos/modules/services/networking/ircd-hybrid/control.in26
-rw-r--r--nixpkgs/nixos/modules/services/networking/ircd-hybrid/default.nix125
-rw-r--r--nixpkgs/nixos/modules/services/networking/ircd-hybrid/ircd.conf1051
-rw-r--r--nixpkgs/nixos/modules/services/networking/iwd.nix29
-rw-r--r--nixpkgs/nixos/modules/services/networking/keepalived/default.nix303
-rw-r--r--nixpkgs/nixos/modules/services/networking/keepalived/virtual-ip-options.nix50
-rw-r--r--nixpkgs/nixos/modules/services/networking/keepalived/vrrp-instance-options.nix135
-rw-r--r--nixpkgs/nixos/modules/services/networking/keepalived/vrrp-script-options.nix64
-rw-r--r--nixpkgs/nixos/modules/services/networking/keybase.nix47
-rw-r--r--nixpkgs/nixos/modules/services/networking/kippo.nix117
-rw-r--r--nixpkgs/nixos/modules/services/networking/knot.nix117
-rw-r--r--nixpkgs/nixos/modules/services/networking/kresd.nix145
-rw-r--r--nixpkgs/nixos/modules/services/networking/lambdabot.nix82
-rw-r--r--nixpkgs/nixos/modules/services/networking/libreswan.nix129
-rw-r--r--nixpkgs/nixos/modules/services/networking/lldpd.nix39
-rw-r--r--nixpkgs/nixos/modules/services/networking/logmein-hamachi.nix50
-rw-r--r--nixpkgs/nixos/modules/services/networking/magic-wormhole-mailbox-server.nix28
-rw-r--r--nixpkgs/nixos/modules/services/networking/mailpile.nix72
-rw-r--r--nixpkgs/nixos/modules/services/networking/matterbridge.nix120
-rw-r--r--nixpkgs/nixos/modules/services/networking/minidlna.nix193
-rw-r--r--nixpkgs/nixos/modules/services/networking/miniupnpd.nix79
-rw-r--r--nixpkgs/nixos/modules/services/networking/miredo.nix92
-rw-r--r--nixpkgs/nixos/modules/services/networking/mjpg-streamer.nix80
-rw-r--r--nixpkgs/nixos/modules/services/networking/monero.nix238
-rw-r--r--nixpkgs/nixos/modules/services/networking/morty.nix97
-rw-r--r--nixpkgs/nixos/modules/services/networking/mosquitto.nix231
-rw-r--r--nixpkgs/nixos/modules/services/networking/mstpd.nix33
-rw-r--r--nixpkgs/nixos/modules/services/networking/mtprotoproxy.nix110
-rw-r--r--nixpkgs/nixos/modules/services/networking/mullvad-vpn.nix43
-rw-r--r--nixpkgs/nixos/modules/services/networking/murmur.nix270
-rw-r--r--nixpkgs/nixos/modules/services/networking/mxisd.nix127
-rw-r--r--nixpkgs/nixos/modules/services/networking/namecoind.nix199
-rw-r--r--nixpkgs/nixos/modules/services/networking/nat.nix290
-rw-r--r--nixpkgs/nixos/modules/services/networking/ndppd.nix189
-rw-r--r--nixpkgs/nixos/modules/services/networking/networkmanager.nix491
-rw-r--r--nixpkgs/nixos/modules/services/networking/nftables.nix136
-rw-r--r--nixpkgs/nixos/modules/services/networking/nghttpx/backend-params-submodule.nix131
-rw-r--r--nixpkgs/nixos/modules/services/networking/nghttpx/backend-submodule.nix50
-rw-r--r--nixpkgs/nixos/modules/services/networking/nghttpx/default.nix118
-rw-r--r--nixpkgs/nixos/modules/services/networking/nghttpx/frontend-params-submodule.nix64
-rw-r--r--nixpkgs/nixos/modules/services/networking/nghttpx/frontend-submodule.nix36
-rw-r--r--nixpkgs/nixos/modules/services/networking/nghttpx/nghttpx-options.nix142
-rw-r--r--nixpkgs/nixos/modules/services/networking/nghttpx/server-options.nix18
-rw-r--r--nixpkgs/nixos/modules/services/networking/nghttpx/tls-submodule.nix21
-rw-r--r--nixpkgs/nixos/modules/services/networking/ngircd.nix59
-rw-r--r--nixpkgs/nixos/modules/services/networking/nix-serve.nix81
-rw-r--r--nixpkgs/nixos/modules/services/networking/nix-store-gcs-proxy.nix75
-rw-r--r--nixpkgs/nixos/modules/services/networking/nixops-dns.nix79
-rw-r--r--nixpkgs/nixos/modules/services/networking/nntp-proxy.nix234
-rw-r--r--nixpkgs/nixos/modules/services/networking/nsd.nix980
-rw-r--r--nixpkgs/nixos/modules/services/networking/ntopng.nix116
-rw-r--r--nixpkgs/nixos/modules/services/networking/ntp/chrony.nix125
-rw-r--r--nixpkgs/nixos/modules/services/networking/ntp/ntpd.nix148
-rw-r--r--nixpkgs/nixos/modules/services/networking/ntp/openntpd.nix82
-rw-r--r--nixpkgs/nixos/modules/services/networking/nullidentdmod.nix34
-rw-r--r--nixpkgs/nixos/modules/services/networking/nylon.nix166
-rw-r--r--nixpkgs/nixos/modules/services/networking/ocserv.nix99
-rw-r--r--nixpkgs/nixos/modules/services/networking/ofono.nix44
-rw-r--r--nixpkgs/nixos/modules/services/networking/oidentd.nix44
-rw-r--r--nixpkgs/nixos/modules/services/networking/openfire.nix56
-rw-r--r--nixpkgs/nixos/modules/services/networking/openvpn.nix219
-rw-r--r--nixpkgs/nixos/modules/services/networking/ostinato.nix104
-rw-r--r--nixpkgs/nixos/modules/services/networking/owamp.nix45
-rw-r--r--nixpkgs/nixos/modules/services/networking/pdns-recursor.nix224
-rw-r--r--nixpkgs/nixos/modules/services/networking/pdnsd.nix91
-rw-r--r--nixpkgs/nixos/modules/services/networking/pixiecore.nix134
-rw-r--r--nixpkgs/nixos/modules/services/networking/polipo.nix112
-rw-r--r--nixpkgs/nixos/modules/services/networking/powerdns.nix49
-rw-r--r--nixpkgs/nixos/modules/services/networking/pppd.nix136
-rw-r--r--nixpkgs/nixos/modules/services/networking/pptpd.nix124
-rw-r--r--nixpkgs/nixos/modules/services/networking/prayer.nix89
-rw-r--r--nixpkgs/nixos/modules/services/networking/privoxy.nix114
-rw-r--r--nixpkgs/nixos/modules/services/networking/prosody.nix882
-rw-r--r--nixpkgs/nixos/modules/services/networking/prosody.xml88
-rw-r--r--nixpkgs/nixos/modules/services/networking/quagga.nix185
-rw-r--r--nixpkgs/nixos/modules/services/networking/quassel.nix132
-rw-r--r--nixpkgs/nixos/modules/services/networking/quicktun.nix118
-rw-r--r--nixpkgs/nixos/modules/services/networking/quorum.nix229
-rw-r--r--nixpkgs/nixos/modules/services/networking/racoon.nix45
-rw-r--r--nixpkgs/nixos/modules/services/networking/radicale.nix89
-rw-r--r--nixpkgs/nixos/modules/services/networking/radvd.nix73
-rw-r--r--nixpkgs/nixos/modules/services/networking/rdnssd.nix80
-rw-r--r--nixpkgs/nixos/modules/services/networking/redsocks.nix272
-rw-r--r--nixpkgs/nixos/modules/services/networking/resilio.nix263
-rw-r--r--nixpkgs/nixos/modules/services/networking/rpcbind.nix46
-rw-r--r--nixpkgs/nixos/modules/services/networking/rxe.nix52
-rw-r--r--nixpkgs/nixos/modules/services/networking/sabnzbd.nix67
-rw-r--r--nixpkgs/nixos/modules/services/networking/searx.nix80
-rw-r--r--nixpkgs/nixos/modules/services/networking/seeks.nix75
-rw-r--r--nixpkgs/nixos/modules/services/networking/shadowsocks.nix112
-rw-r--r--nixpkgs/nixos/modules/services/networking/shairport-sync.nix83
-rw-r--r--nixpkgs/nixos/modules/services/networking/shorewall.nix70
-rw-r--r--nixpkgs/nixos/modules/services/networking/shorewall6.nix70
-rw-r--r--nixpkgs/nixos/modules/services/networking/shout.nix113
-rw-r--r--nixpkgs/nixos/modules/services/networking/skydns.nix92
-rw-r--r--nixpkgs/nixos/modules/services/networking/smartdns.nix61
-rw-r--r--nixpkgs/nixos/modules/services/networking/smokeping.nix320
-rw-r--r--nixpkgs/nixos/modules/services/networking/sniproxy.nix99
-rw-r--r--nixpkgs/nixos/modules/services/networking/softether.nix163
-rw-r--r--nixpkgs/nixos/modules/services/networking/spacecookie.nix83
-rw-r--r--nixpkgs/nixos/modules/services/networking/spiped.nix220
-rw-r--r--nixpkgs/nixos/modules/services/networking/squid.nix168
-rw-r--r--nixpkgs/nixos/modules/services/networking/ssh/lshd.nix183
-rw-r--r--nixpkgs/nixos/modules/services/networking/ssh/sshd.nix557
-rw-r--r--nixpkgs/nixos/modules/services/networking/sslh.nix160
-rw-r--r--nixpkgs/nixos/modules/services/networking/strongswan-swanctl/module.nix84
-rw-r--r--nixpkgs/nixos/modules/services/networking/strongswan-swanctl/param-constructors.nix162
-rw-r--r--nixpkgs/nixos/modules/services/networking/strongswan-swanctl/param-lib.nix82
-rw-r--r--nixpkgs/nixos/modules/services/networking/strongswan-swanctl/swanctl-params.nix1300
-rw-r--r--nixpkgs/nixos/modules/services/networking/strongswan.nix170
-rw-r--r--nixpkgs/nixos/modules/services/networking/stubby.nix217
-rw-r--r--nixpkgs/nixos/modules/services/networking/stunnel.nix234
-rw-r--r--nixpkgs/nixos/modules/services/networking/supplicant.nix249
-rw-r--r--nixpkgs/nixos/modules/services/networking/supybot.nix161
-rw-r--r--nixpkgs/nixos/modules/services/networking/syncplay.nix80
-rw-r--r--nixpkgs/nixos/modules/services/networking/syncthing-relay.nix121
-rw-r--r--nixpkgs/nixos/modules/services/networking/syncthing.nix528
-rw-r--r--nixpkgs/nixos/modules/services/networking/tailscale.nix49
-rw-r--r--nixpkgs/nixos/modules/services/networking/tcpcrypt.nix80
-rw-r--r--nixpkgs/nixos/modules/services/networking/teamspeak3.nix142
-rw-r--r--nixpkgs/nixos/modules/services/networking/tedicross.nix100
-rw-r--r--nixpkgs/nixos/modules/services/networking/tftpd.nix46
-rw-r--r--nixpkgs/nixos/modules/services/networking/thelounge.nix75
-rw-r--r--nixpkgs/nixos/modules/services/networking/tinc.nix212
-rw-r--r--nixpkgs/nixos/modules/services/networking/tinydns.nix55
-rw-r--r--nixpkgs/nixos/modules/services/networking/tox-bootstrapd.nix79
-rw-r--r--nixpkgs/nixos/modules/services/networking/tox-node.nix95
-rw-r--r--nixpkgs/nixos/modules/services/networking/toxvpn.nix68
-rw-r--r--nixpkgs/nixos/modules/services/networking/trickster.nix112
-rw-r--r--nixpkgs/nixos/modules/services/networking/tvheadend.nix61
-rw-r--r--nixpkgs/nixos/modules/services/networking/unbound.nix148
-rw-r--r--nixpkgs/nixos/modules/services/networking/unifi.nix180
-rw-r--r--nixpkgs/nixos/modules/services/networking/v2ray.nix87
-rw-r--r--nixpkgs/nixos/modules/services/networking/vsftpd.nix326
-rw-r--r--nixpkgs/nixos/modules/services/networking/wakeonlan.nix56
-rw-r--r--nixpkgs/nixos/modules/services/networking/websockify.nix54
-rw-r--r--nixpkgs/nixos/modules/services/networking/wg-quick.nix312
-rw-r--r--nixpkgs/nixos/modules/services/networking/wicd.nix40
-rw-r--r--nixpkgs/nixos/modules/services/networking/wireguard.nix452
-rw-r--r--nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix266
-rw-r--r--nixpkgs/nixos/modules/services/networking/xandikos.nix148
-rw-r--r--nixpkgs/nixos/modules/services/networking/xinetd.nix147
-rw-r--r--nixpkgs/nixos/modules/services/networking/xl2tpd.nix143
-rw-r--r--nixpkgs/nixos/modules/services/networking/xrdp.nix171
-rw-r--r--nixpkgs/nixos/modules/services/networking/yggdrasil.nix199
-rw-r--r--nixpkgs/nixos/modules/services/networking/zerobin.nix102
-rw-r--r--nixpkgs/nixos/modules/services/networking/zeronet.nix96
-rw-r--r--nixpkgs/nixos/modules/services/networking/zerotierone.nix82
-rw-r--r--nixpkgs/nixos/modules/services/networking/znc/default.nix308
-rw-r--r--nixpkgs/nixos/modules/services/networking/znc/options.nix270
220 files changed, 33637 insertions, 0 deletions
diff --git a/nixpkgs/nixos/modules/services/networking/3proxy.nix b/nixpkgs/nixos/modules/services/networking/3proxy.nix
new file mode 100644
index 000000000000..ae8a4958ca96
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/3proxy.nix
@@ -0,0 +1,426 @@
+{ config, lib, pkgs, ... }:
+with lib;
+let
+  pkg = pkgs._3proxy;
+  cfg = config.services._3proxy;
+  optionalList = list: if list == [ ] then "*" else concatMapStringsSep "," toString list;
+in {
+  options.services._3proxy = {
+    enable = mkEnableOption "3proxy";
+    confFile = mkOption {
+      type = types.path;
+      example = "/var/lib/3proxy/3proxy.conf";
+      description = ''
+        Ignore all other 3proxy options and load configuration from this file.
+      '';
+    };
+    usersFile = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      example = "/var/lib/3proxy/3proxy.passwd";
+      description = ''
+        Load users and passwords from this file.
+
+        Example users file with plain-text passwords:
+
+        <literal>
+          test1:CL:password1
+          test2:CL:password2
+        </literal>
+
+        Example users file with md5-crypted passwords:
+
+        <literal>
+          test1:CR:$1$tFkisVd2$1GA8JXkRmTXdLDytM/i3a1
+          test2:CR:$1$rkpibm5J$Aq1.9VtYAn0JrqZ8M.1ME.
+        </literal>
+
+        You can generate md5-crypted passwords via https://unix4lyfe.org/crypt/
+        Note that htpasswd tool generates incompatible md5-crypted passwords.
+        Consult <link xlink:href="https://github.com/z3APA3A/3proxy/wiki/How-To-(incomplete)#USERS">documentation</link> for more information.
+      '';
+    };
+    services = mkOption {
+      type = types.listOf (types.submodule {
+        options = {
+          type = mkOption {
+            type = types.enum [
+              "proxy"
+              "socks"
+              "pop3p"
+              "ftppr"
+              "admin"
+              "dnspr"
+              "tcppm"
+              "udppm"
+            ];
+            example = "proxy";
+            description = ''
+              Service type. The following values are valid:
+
+              <itemizedlist>
+                <listitem><para>
+                  <literal>"proxy"</literal>: HTTP/HTTPS proxy (default port 3128).
+                </para></listitem>
+                <listitem><para>
+                  <literal>"socks"</literal>: SOCKS 4/4.5/5 proxy (default port 1080).
+                </para></listitem>
+                <listitem><para>
+                  <literal>"pop3p"</literal>: POP3 proxy (default port 110).
+                </para></listitem>
+                <listitem><para>
+                  <literal>"ftppr"</literal>: FTP proxy (default port 21).
+                </para></listitem>
+                <listitem><para>
+                  <literal>"admin"</literal>: Web interface (default port 80).
+                </para></listitem>
+                <listitem><para>
+                  <literal>"dnspr"</literal>: Caching DNS proxy (default port 53).
+                </para></listitem>
+                <listitem><para>
+                  <literal>"tcppm"</literal>: TCP portmapper.
+                </para></listitem>
+                <listitem><para>
+                  <literal>"udppm"</literal>: UDP portmapper.
+                </para></listitem>
+              </itemizedlist>
+            '';
+          };
+          bindAddress = mkOption {
+            type = types.str;
+            default = "[::]";
+            example = "127.0.0.1";
+            description = ''
+              Address used for service.
+            '';
+          };
+          bindPort = mkOption {
+            type = types.nullOr types.int;
+            default = null;
+            example = 3128;
+            description = ''
+              Override default port used for service.
+            '';
+          };
+          maxConnections = mkOption {
+            type = types.int;
+            default = 100;
+            example = 1000;
+            description = ''
+              Maximum number of simulationeous connections to this service.
+            '';
+          };
+          auth = mkOption {
+            type = types.listOf (types.enum [ "none" "iponly" "strong" ]);
+            example = [ "iponly" "strong" ];
+            description = ''
+              Authentication type. The following values are valid:
+
+              <itemizedlist>
+                <listitem><para>
+                  <literal>"none"</literal>: disables both authentication and authorization. You can not use ACLs.
+                </para></listitem>
+                <listitem><para>
+                  <literal>"iponly"</literal>: specifies no authentication. ACLs authorization is used.
+                </para></listitem>
+                <listitem><para>
+                  <literal>"strong"</literal>: authentication by username/password. If user is not registered his access is denied regardless of ACLs.
+                </para></listitem>
+              </itemizedlist>
+
+              Double authentication is possible, e.g.
+
+              <literal>
+                {
+                  auth = [ "iponly" "strong" ];
+                  acl = [
+                    {
+                      rule = "allow";
+                      targets = [ "192.168.0.0/16" ];
+                    }
+                    {
+                      rule = "allow"
+                      users = [ "user1" "user2" ];
+                    }
+                  ];
+                }
+              </literal>
+              In this example strong username authentication is not required to access 192.168.0.0/16.
+            '';
+          };
+          acl = mkOption {
+            type = types.listOf (types.submodule {
+              options = {
+                rule = mkOption {
+                  type = types.enum [ "allow" "deny" ];
+                  example = "allow";
+                  description = ''
+                    ACL rule. The following values are valid:
+
+                    <itemizedlist>
+                      <listitem><para>
+                        <literal>"allow"</literal>: connections allowed.
+                      </para></listitem>
+                      <listitem><para>
+                        <literal>"deny"</literal>: connections not allowed.
+                      </para></listitem>
+                    </itemizedlist>
+                  '';
+                };
+                users = mkOption {
+                  type = types.listOf types.str;
+                  default = [ ];
+                  example = [ "user1" "user2" "user3" ];
+                  description = ''
+                    List of users, use empty list for any.
+                  '';
+                };
+                sources = mkOption {
+                  type = types.listOf types.str;
+                  default = [ ];
+                  example = [ "127.0.0.1" "192.168.1.0/24" ];
+                  description = ''
+                    List of source IP range, use empty list for any.
+                  '';
+                };
+                targets = mkOption {
+                  type = types.listOf types.str;
+                  default = [ ];
+                  example = [ "127.0.0.1" "192.168.1.0/24" ];
+                  description = ''
+                    List of target IP ranges, use empty list for any.
+                    May also contain host names instead of addresses.
+                    It's possible to use wildmask in the begginning and in the the end of hostname, e.g. *badsite.com or *badcontent*.
+                    Hostname is only checked if hostname presents in request.
+                  '';
+                };
+                targetPorts = mkOption {
+                  type = types.listOf types.int;
+                  default = [ ];
+                  example = [ 80 443 ];
+                  description = ''
+                    List of target ports, use empty list for any.
+                  '';
+                };
+              };
+            });
+            default = [ ];
+            example = literalExample ''
+              [
+                {
+                  rule = "allow";
+                  users = [ "user1" ];
+                }
+                {
+                  rule = "allow";
+                  sources = [ "192.168.1.0/24" ];
+                }
+                {
+                  rule = "deny";
+                }
+              ]
+            '';
+            description = ''
+              Use this option to limit user access to resources.
+            '';
+          };
+          extraArguments = mkOption {
+            type = types.nullOr types.str;
+            default = null;
+            example = "-46";
+            description = ''
+              Extra arguments for service.
+              Consult "Options" section in <link xlink:href="https://github.com/z3APA3A/3proxy/wiki/3proxy.cfg">documentation</link> for available arguments.
+            '';
+          };
+          extraConfig = mkOption {
+            type = types.nullOr types.lines;
+            default = null;
+            description = ''
+              Extra configuration for service. Use this to configure things like bandwidth limiter or ACL-based redirection.
+              Consult <link xlink:href="https://github.com/z3APA3A/3proxy/wiki/3proxy.cfg">documentation</link> for available options.
+            '';
+          };
+        };
+      });
+      default = [ ];
+      example = literalExample ''
+        [
+          {
+            type = "proxy";
+            bindAddress = "192.168.1.24";
+            bindPort = 3128;
+            auth = [ "none" ];
+          }
+          {
+            type = "proxy";
+            bindAddress = "10.10.1.20";
+            bindPort = 3128;
+            auth = [ "iponly" ];
+          }
+          {
+            type = "socks";
+            bindAddress = "172.17.0.1";
+            bindPort = 1080;
+            auth = [ "strong" ];
+          }
+        ]
+      '';
+      description = ''
+        Use this option to define 3proxy services.
+      '';
+    };
+    denyPrivate = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Whether to deny access to private IP ranges including loopback.
+      '';
+    };
+    privateRanges = mkOption {
+      type = types.listOf types.str;
+      default = [
+        "0.0.0.0/8"
+        "127.0.0.0/8"
+        "10.0.0.0/8"
+        "100.64.0.0/10"
+        "172.16.0.0/12"
+        "192.168.0.0/16"
+        "::"
+        "::1"
+        "fc00::/7"
+      ];
+      example = [
+        "0.0.0.0/8"
+        "127.0.0.0/8"
+        "10.0.0.0/8"
+        "100.64.0.0/10"
+        "172.16.0.0/12"
+        "192.168.0.0/16"
+        "::"
+        "::1"
+        "fc00::/7"
+      ];
+      description = ''
+        What IP ranges to deny access when denyPrivate is set tu true.
+      '';
+    };
+    resolution = mkOption {
+      type = types.submodule {
+        options = {
+          nserver = mkOption {
+            type = types.listOf types.str;
+            default = [ ];
+            example = [ "127.0.0.53" "192.168.1.3:5353/tcp" ];
+            description = ''
+              List of nameservers to use.
+
+              Up to 5 nservers may be specified. If no nserver is configured,
+              default system name resolution functions are used.
+            '';
+          };
+          nscache = mkOption {
+            type = types.int;
+            default = 65535;
+            example = 65535;
+            description = "Set name cache size for IPv4.";
+          };
+          nscache6 = mkOption {
+            type = types.int;
+            default = 65535;
+            example = 65535;
+            description = "Set name cache size for IPv6.";
+          };
+          nsrecord = mkOption {
+            type = types.attrsOf types.str;
+            default = { };
+            example = literalExample ''
+              {
+                "files.local" = "192.168.1.12";
+                "site.local" = "192.168.1.43";
+              }
+            '';
+            description = "Adds static nsrecords.";
+          };
+        };
+      };
+      default = { };
+      description = ''
+        Use this option to configure name resolution and DNS caching.
+      '';
+    };
+    extraConfig = mkOption {
+      type = types.nullOr types.lines;
+      default = null;
+      description = ''
+        Extra configuration, appended to the 3proxy configuration file.
+        Consult <link xlink:href="https://github.com/z3APA3A/3proxy/wiki/3proxy.cfg">documentation</link> for available options.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services._3proxy.confFile = mkDefault (pkgs.writeText "3proxy.conf" ''
+      # log to stdout
+      log
+
+      ${concatMapStringsSep "\n" (x: "nserver " + x) cfg.resolution.nserver}
+
+      nscache ${toString cfg.resolution.nscache}
+      nscache6 ${toString cfg.resolution.nscache6}
+
+      ${concatMapStringsSep "\n" (x: "nsrecord " + x)
+      (mapAttrsToList (name: value: "${name} ${value}")
+        cfg.resolution.nsrecord)}
+
+      ${optionalString (cfg.usersFile != null)
+        ''users $"${cfg.usersFile}"''
+      }
+
+      ${concatMapStringsSep "\n" (service: ''
+        auth ${concatStringsSep " " service.auth}
+
+        ${optionalString (cfg.denyPrivate)
+        "deny * * ${optionalList cfg.privateRanges}"}
+
+        ${concatMapStringsSep "\n" (acl:
+          "${acl.rule} ${
+            concatMapStringsSep " " optionalList [
+              acl.users
+              acl.sources
+              acl.targets
+              acl.targetPorts
+            ]
+          }") service.acl}
+
+        maxconn ${toString service.maxConnections}
+
+        ${optionalString (service.extraConfig != null) service.extraConfig}
+
+        ${service.type} -i${toString service.bindAddress} ${
+          optionalString (service.bindPort != null)
+          "-p${toString service.bindPort}"
+        } ${
+          optionalString (service.extraArguments != null) service.extraArguments
+        }
+
+        flush
+      '') cfg.services}
+      ${optionalString (cfg.extraConfig != null) cfg.extraConfig}
+    '');
+    systemd.services."3proxy" = {
+      description = "Tiny free proxy server";
+      documentation = [ "https://github.com/z3APA3A/3proxy/wiki" ];
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        DynamicUser = true;
+        StateDirectory = "3proxy";
+        ExecStart = "${pkg}/bin/3proxy ${cfg.confFile}";
+        Restart = "on-failure";
+      };
+    };
+  };
+
+  meta.maintainers = with maintainers; [ misuzu ];
+}
diff --git a/nixpkgs/nixos/modules/services/networking/amuled.nix b/nixpkgs/nixos/modules/services/networking/amuled.nix
new file mode 100644
index 000000000000..1128ee2c3e61
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/amuled.nix
@@ -0,0 +1,77 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.amule;
+  user = if cfg.user != null then cfg.user else "amule";
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.amule = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to run the AMule daemon. You need to manually run "amuled --ec-config" to configure the service for the first time.
+        '';
+      };
+
+      dataDir = mkOption {
+        default = ''/home/${user}/'';
+        description = ''
+          The directory holding configuration, incoming and temporary files.
+        '';
+      };
+
+      user = mkOption {
+        default = null;
+        description = ''
+          The user the AMule daemon should run as.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    users.users = mkIf (cfg.user == null) [
+      { name = "amule";
+        description = "AMule daemon";
+        group = "amule";
+        uid = config.ids.uids.amule;
+      } ];
+
+    users.groups = mkIf (cfg.user == null) [
+      { name = "amule";
+        gid = config.ids.gids.amule;
+      } ];
+
+    systemd.services.amuled = {
+      description = "AMule daemon";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+
+      preStart = ''
+        mkdir -p ${cfg.dataDir}
+        chown ${user} ${cfg.dataDir}
+      '';
+
+      script = ''
+        ${pkgs.su}/bin/su -s ${pkgs.runtimeShell} ${user} \
+            -c 'HOME="${cfg.dataDir}" ${pkgs.amuleDaemon}/bin/amuled'
+      '';
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/aria2.nix b/nixpkgs/nixos/modules/services/networking/aria2.nix
new file mode 100644
index 000000000000..156fef144791
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/aria2.nix
@@ -0,0 +1,131 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.aria2;
+
+  homeDir = "/var/lib/aria2";
+
+  settingsDir = "${homeDir}";
+  sessionFile = "${homeDir}/aria2.session";
+  downloadDir = "${homeDir}/Downloads";
+
+  rangesToStringList = map (x: builtins.toString x.from +"-"+ builtins.toString x.to);
+
+  settingsFile = pkgs.writeText "aria2.conf"
+  ''
+    dir=${cfg.downloadDir}
+    listen-port=${concatStringsSep "," (rangesToStringList cfg.listenPortRange)}
+    rpc-listen-port=${toString cfg.rpcListenPort}
+    rpc-secret=${cfg.rpcSecret}
+  '';
+
+in
+{
+  options = {
+    services.aria2 = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether or not to enable the headless Aria2 daemon service.
+
+          Aria2 daemon can be controlled via the RPC interface using
+          one of many WebUI (http://localhost:6800/ by default).
+
+          Targets are downloaded to ${downloadDir} by default and are
+          accessible to users in the "aria2" group.
+        '';
+      };
+      openPorts = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Open listen and RPC ports found in listenPortRange and rpcListenPort
+          options in the firewall.
+        '';
+      };
+      downloadDir = mkOption {
+        type = types.path;
+        default = downloadDir;
+        description = ''
+          Directory to store downloaded files.
+        '';
+      };
+      listenPortRange = mkOption {
+        type = types.listOf types.attrs;
+        default = [ { from = 6881; to = 6999; } ];
+        description = ''
+          Set UDP listening port range used by DHT(IPv4, IPv6) and UDP tracker.
+        '';
+      };
+      rpcListenPort = mkOption {
+        type = types.int;
+        default = 6800;
+        description = "Specify a port number for JSON-RPC/XML-RPC server to listen to. Possible Values: 1024-65535";
+      };
+      rpcSecret = mkOption {
+        type = types.str;
+        default = "aria2rpc";
+        description = ''
+          Set RPC secret authorization token.
+          Read https://aria2.github.io/manual/en/html/aria2c.html#rpc-auth to know how this option value is used.
+        '';
+      };
+      extraArguments = mkOption {
+        type = types.separatedString " ";
+        example = "--rpc-listen-all --remote-time=true";
+        default = "";
+        description = ''
+          Additional arguments to be passed to Aria2.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    # Need to open ports for proper functioning
+    networking.firewall = mkIf cfg.openPorts {
+      allowedUDPPortRanges = config.services.aria2.listenPortRange;
+      allowedTCPPorts = [ config.services.aria2.rpcListenPort ];
+    };
+
+    users.users.aria2 = {
+      group = "aria2";
+      uid = config.ids.uids.aria2;
+      description = "aria2 user";
+      home = homeDir;
+      createHome = false;
+    };
+
+    users.groups.aria2.gid = config.ids.gids.aria2;
+
+    systemd.tmpfiles.rules = [
+      "d '${homeDir}' 0770 aria2 aria2 - -"
+      "d '${config.services.aria2.downloadDir}' 0770 aria2 aria2 - -"
+    ];
+
+    systemd.services.aria2 = {
+      description = "aria2 Service";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      preStart = ''
+        if [[ ! -e "${sessionFile}" ]]
+        then
+          touch "${sessionFile}"
+        fi
+        cp -f "${settingsFile}" "${settingsDir}/aria2.conf"
+      '';
+
+      serviceConfig = {
+        Restart = "on-abort";
+        ExecStart = "${pkgs.aria2}/bin/aria2c --enable-rpc --conf-path=${settingsDir}/aria2.conf ${config.services.aria2.extraArguments} --save-session=${sessionFile}";
+        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+        User = "aria2";
+        Group = "aria2";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/asterisk.nix b/nixpkgs/nixos/modules/services/networking/asterisk.nix
new file mode 100644
index 000000000000..03a2544b9a7e
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/asterisk.nix
@@ -0,0 +1,264 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.asterisk;
+
+  asteriskUser = "asterisk";
+  asteriskGroup = "asterisk";
+
+  varlibdir = "/var/lib/asterisk";
+  spooldir = "/var/spool/asterisk";
+  logdir = "/var/log/asterisk";
+
+  # Add filecontents from files of useTheseDefaultConfFiles to confFiles, do not override
+  defaultConfFiles = subtractLists (attrNames cfg.confFiles) cfg.useTheseDefaultConfFiles;
+  allConfFiles =
+    cfg.confFiles //
+    builtins.listToAttrs (map (x: { name = x;
+                                    value = builtins.readFile (cfg.package + "/etc/asterisk/" + x); })
+                              defaultConfFiles);
+
+  asteriskEtc = pkgs.stdenv.mkDerivation
+  ((mapAttrs' (name: value: nameValuePair
+        # Fudge the names to make bash happy
+        ((replaceChars ["."] ["_"] name) + "_")
+        (value)
+      ) allConfFiles) //
+  {
+    confFilesString = concatStringsSep " " (
+      attrNames allConfFiles
+    );
+
+    name = "asterisk-etc";
+
+    # Default asterisk.conf file
+    # (Notice that astetcdir will be set to the path of this derivation)
+    asteriskConf = ''
+      [directories]
+      astetcdir => /etc/asterisk
+      astmoddir => ${cfg.package}/lib/asterisk/modules
+      astvarlibdir => /var/lib/asterisk
+      astdbdir => /var/lib/asterisk
+      astkeydir => /var/lib/asterisk
+      astdatadir => /var/lib/asterisk
+      astagidir => /var/lib/asterisk/agi-bin
+      astspooldir => /var/spool/asterisk
+      astrundir => /run/asterisk
+      astlogdir => /var/log/asterisk
+      astsbindir => ${cfg.package}/sbin
+    '';
+    extraConf = cfg.extraConfig;
+
+    # Loading all modules by default is considered sensible by the authors of
+    # "Asterisk: The Definitive Guide". Secure sites will likely want to
+    # specify their own "modules.conf" in the confFiles option.
+    modulesConf = ''
+      [modules]
+      autoload=yes
+    '';
+
+    # Use syslog for logging so logs can be viewed with journalctl
+    loggerConf = ''
+      [general]
+
+      [logfiles]
+      syslog.local0 => notice,warning,error
+    '';
+
+    buildCommand = ''
+      mkdir -p "$out"
+
+      # Create asterisk.conf, pointing astetcdir to the path of this derivation
+      echo "$asteriskConf" | sed "s|@out@|$out|g" > "$out"/asterisk.conf
+      echo "$extraConf" >> "$out"/asterisk.conf
+
+      echo "$modulesConf" > "$out"/modules.conf
+
+      echo "$loggerConf" > "$out"/logger.conf
+
+      # Config files specified in confFiles option override all other files
+      for i in $confFilesString; do
+        conf=$(echo "$i"_ | sed 's/\./_/g')
+        echo "''${!conf}" > "$out"/"$i"
+      done
+    '';
+  });
+in
+
+{
+  options = {
+    services.asterisk = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable the Asterisk PBX server.
+        '';
+      };
+
+      extraConfig = mkOption {
+        default = "";
+        type = types.lines;
+        example = ''
+          [options]
+          verbose=3
+          debug=3
+        '';
+        description = ''
+          Extra configuration options appended to the default
+          <literal>asterisk.conf</literal> file.
+        '';
+      };
+
+      confFiles = mkOption {
+        default = {};
+        type = types.attrsOf types.str;
+        example = literalExample
+          ''
+            {
+              "extensions.conf" = '''
+                [tests]
+                ; Dial 100 for "hello, world"
+                exten => 100,1,Answer()
+                same  =>     n,Wait(1)
+                same  =>     n,Playback(hello-world)
+                same  =>     n,Hangup()
+
+                [softphones]
+                include => tests
+
+                [unauthorized]
+              ''';
+              "sip.conf" = '''
+                [general]
+                allowguest=no              ; Require authentication
+                context=unauthorized       ; Send unauthorized users to /dev/null
+                srvlookup=no               ; Don't do DNS lookup
+                udpbindaddr=0.0.0.0        ; Listen on all interfaces
+                nat=force_rport,comedia    ; Assume device is behind NAT
+
+                [softphone](!)
+                type=friend                ; Match on username first, IP second
+                context=softphones         ; Send to softphones context in
+                                           ; extensions.conf file
+                host=dynamic               ; Device will register with asterisk
+                disallow=all               ; Manually specify codecs to allow
+                allow=g722
+                allow=ulaw
+                allow=alaw
+
+                [myphone](softphone)
+                secret=GhoshevFew          ; Change this password!
+              ''';
+              "logger.conf" = '''
+                [general]
+
+                [logfiles]
+                ; Add debug output to log
+                syslog.local0 => notice,warning,error,debug
+              ''';
+            }
+        '';
+        description = ''
+          Sets the content of config files (typically ending with
+          <literal>.conf</literal>) in the Asterisk configuration directory.
+
+          Note that if you want to change <literal>asterisk.conf</literal>, it
+          is preferable to use the <option>services.asterisk.extraConfig</option>
+          option over this option. If <literal>"asterisk.conf"</literal> is
+          specified with the <option>confFiles</option> option (not recommended),
+          you must be prepared to set your own <literal>astetcdir</literal>
+          path.
+
+          See
+          <link xlink:href="http://www.asterisk.org/community/documentation"/>
+          for more examples of what is possible here.
+        '';
+      };
+
+      useTheseDefaultConfFiles = mkOption {
+        default = [ "ari.conf" "acl.conf" "agents.conf" "amd.conf" "calendar.conf" "cdr.conf" "cdr_syslog.conf" "cdr_custom.conf" "cel.conf" "cel_custom.conf" "cli_aliases.conf" "confbridge.conf" "dundi.conf" "features.conf" "hep.conf" "iax.conf" "pjsip.conf" "pjsip_wizard.conf" "phone.conf" "phoneprov.conf" "queues.conf" "res_config_sqlite3.conf" "res_parking.conf" "statsd.conf" "udptl.conf" "unistim.conf" ];
+        type = types.listOf types.str;
+        example = [ "sip.conf" "dundi.conf" ];
+        description = ''Sets these config files to the default content. The default value for
+          this option contains all necesscary files to avoid errors at startup.
+          This does not override settings via <option>services.asterisk.confFiles</option>.
+        '';
+      };
+
+      extraArguments = mkOption {
+        default = [];
+        type = types.listOf types.str;
+        example =
+          [ "-vvvddd" "-e" "1024" ];
+        description = ''
+          Additional command line arguments to pass to Asterisk.
+        '';
+      };
+      package = mkOption {
+        type = types.package;
+        default = pkgs.asterisk;
+        defaultText = "pkgs.asterisk";
+        description = "The Asterisk package to use.";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ cfg.package ];
+
+    environment.etc.asterisk.source = asteriskEtc;
+
+    users.users.asterisk =
+      { name = asteriskUser;
+        group = asteriskGroup;
+        uid = config.ids.uids.asterisk;
+        description = "Asterisk daemon user";
+        home = varlibdir;
+      };
+
+    users.groups.asterisk =
+      { name = asteriskGroup;
+        gid = config.ids.gids.asterisk;
+      };
+
+    systemd.services.asterisk = {
+      description = ''
+        Asterisk PBX server
+      '';
+
+      wantedBy = [ "multi-user.target" ];
+
+      # Do not restart, to avoid disruption of running calls. Restart unit by yourself!
+      restartIfChanged = false;
+
+      preStart = ''
+        # Copy skeleton directory tree to /var
+        for d in '${varlibdir}' '${spooldir}' '${logdir}'; do
+          # TODO: Make exceptions for /var directories that likely should be updated
+          if [ ! -e "$d" ]; then
+            mkdir -p "$d"
+            cp --recursive ${cfg.package}/"$d"/* "$d"/
+            chown --recursive ${asteriskUser}:${asteriskGroup} "$d"
+            find "$d" -type d | xargs chmod 0755
+          fi
+        done
+      '';
+
+      serviceConfig = {
+        ExecStart =
+          let
+            # FIXME: This doesn't account for arguments with spaces
+            argString = concatStringsSep " " cfg.extraArguments;
+          in
+          "${cfg.package}/bin/asterisk -U ${asteriskUser} -C /etc/asterisk/asterisk.conf ${argString} -F";
+        ExecReload = ''${cfg.package}/bin/asterisk -x "core reload"
+          '';
+        Type = "forking";
+        PIDFile = "/run/asterisk/asterisk.pid";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/atftpd.nix b/nixpkgs/nixos/modules/services/networking/atftpd.nix
new file mode 100644
index 000000000000..e7fd48c99a85
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/atftpd.nix
@@ -0,0 +1,65 @@
+# NixOS module for atftpd TFTP server
+
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.atftpd;
+
+in
+
+{
+
+  options = {
+
+    services.atftpd = {
+
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Whether to enable the atftpd TFTP server. By default, the server
+          binds to address 0.0.0.0.
+        '';
+      };
+
+      extraOptions = mkOption {
+        default = [];
+        type = types.listOf types.str;
+        example = literalExample ''
+          [ "--bind-address 192.168.9.1"
+            "--verbose=7"
+          ]
+        '';
+        description = ''
+          Extra command line arguments to pass to atftp.
+        '';
+      };
+
+      root = mkOption {
+        default = "/srv/tftp";
+        type = types.path;
+        description = ''
+          Document root directory for the atftpd.
+        '';
+      };
+
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    systemd.services.atftpd = {
+      description = "TFTP Server";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      # runs as nobody
+      serviceConfig.ExecStart = "${pkgs.atftp}/sbin/atftpd --daemon --no-fork ${lib.concatStringsSep " " cfg.extraOptions} ${cfg.root}";
+    };
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/autossh.nix b/nixpkgs/nixos/modules/services/networking/autossh.nix
new file mode 100644
index 000000000000..a8d9a027e9fa
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/autossh.nix
@@ -0,0 +1,113 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.autossh;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.autossh = {
+
+      sessions = mkOption {
+        type = types.listOf (types.submodule {
+          options = {
+            name = mkOption {
+              type = types.str;
+              example = "socks-peer";
+              description = "Name of the local AutoSSH session";
+            };
+            user = mkOption {
+              type = types.str;
+              example = "bill";
+              description = "Name of the user the AutoSSH session should run as";
+            };
+            monitoringPort = mkOption {
+              type = types.int;
+              default = 0;
+              example = 20000;
+              description = ''
+                Port to be used by AutoSSH for peer monitoring. Note, that
+                AutoSSH also uses mport+1. Value of 0 disables the keep-alive
+                style monitoring
+              '';
+            };
+            extraArguments = mkOption {
+              type = types.separatedString " ";
+              example = "-N -D4343 bill@socks.example.net";
+              description = ''
+                Arguments to be passed to AutoSSH and retransmitted to SSH
+                process. Some meaningful options include -N (don't run remote
+                command), -D (open SOCKS proxy on local port), -R (forward
+                remote port), -L (forward local port), -v (Enable debug). Check
+                ssh manual for the complete list.
+              '';
+            };
+          };
+        });
+
+        default = [];
+        description = ''
+          List of AutoSSH sessions to start as systemd services. Each service is
+          named 'autossh-{session.name}'.
+        '';
+
+        example = [
+          {
+            name="socks-peer";
+            user="bill";
+            monitoringPort = 20000;
+            extraArguments="-N -D4343 billremote@socks.host.net";
+          }
+        ];
+
+      };
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf (cfg.sessions != []) {
+
+    systemd.services =
+
+      lib.fold ( s : acc : acc //
+        {
+          "autossh-${s.name}" =
+            let
+              mport = if s ? monitoringPort then s.monitoringPort else 0;
+            in
+            {
+              description = "AutoSSH session (" + s.name + ")";
+
+              after = [ "network.target" ];
+              wantedBy = [ "multi-user.target" ];
+
+              # To be able to start the service with no network connection
+              environment.AUTOSSH_GATETIME="0";
+
+              # How often AutoSSH checks the network, in seconds
+              environment.AUTOSSH_POLL="30";
+
+              serviceConfig = {
+                  User = "${s.user}";
+                  # AutoSSH may exit with 0 code if the SSH session was
+                  # gracefully terminated by either local or remote side.
+                  Restart = "on-success";
+                  ExecStart = "${pkgs.autossh}/bin/autossh -M ${toString mport} ${s.extraArguments}";
+              };
+            };
+        }) {} cfg.sessions;
+
+    environment.systemPackages = [ pkgs.autossh ];
+
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/avahi-daemon.nix b/nixpkgs/nixos/modules/services/networking/avahi-daemon.nix
new file mode 100644
index 000000000000..c876b252e8cd
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/avahi-daemon.nix
@@ -0,0 +1,285 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.avahi;
+
+  yesNo = yes : if yes then "yes" else "no";
+
+  avahiDaemonConf = with cfg; pkgs.writeText "avahi-daemon.conf" ''
+    [server]
+    ${# Users can set `networking.hostName' to the empty string, when getting
+      # a host name from DHCP.  In that case, let Avahi take whatever the
+      # current host name is; setting `host-name' to the empty string in
+      # `avahi-daemon.conf' would be invalid.
+      optionalString (hostName != "") "host-name=${hostName}"}
+    browse-domains=${concatStringsSep ", " browseDomains}
+    use-ipv4=${yesNo ipv4}
+    use-ipv6=${yesNo ipv6}
+    ${optionalString (interfaces!=null) "allow-interfaces=${concatStringsSep "," interfaces}"}
+    ${optionalString (domainName!=null) "domain-name=${domainName}"}
+    allow-point-to-point=${yesNo allowPointToPoint}
+    ${optionalString (cacheEntriesMax!=null) "cache-entries-max=${toString cacheEntriesMax}"}
+
+    [wide-area]
+    enable-wide-area=${yesNo wideArea}
+
+    [publish]
+    disable-publishing=${yesNo (!publish.enable)}
+    disable-user-service-publishing=${yesNo (!publish.userServices)}
+    publish-addresses=${yesNo (publish.userServices || publish.addresses)}
+    publish-hinfo=${yesNo publish.hinfo}
+    publish-workstation=${yesNo publish.workstation}
+    publish-domain=${yesNo publish.domain}
+
+    [reflector]
+    enable-reflector=${yesNo reflector}
+    ${extraConfig}
+  '';
+in
+{
+  options.services.avahi = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether to run the Avahi daemon, which allows Avahi clients
+        to use Avahi's service discovery facilities and also allows
+        the local machine to advertise its presence and services
+        (through the mDNS responder implemented by `avahi-daemon').
+      '';
+    };
+
+    hostName = mkOption {
+      type = types.str;
+      default = config.networking.hostName;
+      defaultText = literalExample "config.networking.hostName";
+      description = ''
+        Host name advertised on the LAN. If not set, avahi will use the value
+        of <option>config.networking.hostName</option>.
+      '';
+    };
+
+    domainName = mkOption {
+      type = types.str;
+      default = "local";
+      description = ''
+        Domain name for all advertisements.
+      '';
+    };
+
+    browseDomains = mkOption {
+      type = types.listOf types.str;
+      default = [ ];
+      example = [ "0pointer.de" "zeroconf.org" ];
+      description = ''
+        List of non-local DNS domains to be browsed.
+      '';
+    };
+
+    ipv4 = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Whether to use IPv4.";
+    };
+
+    ipv6 = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Whether to use IPv6.";
+    };
+
+    interfaces = mkOption {
+      type = types.nullOr (types.listOf types.str);
+      default = null;
+      description = ''
+        List of network interfaces that should be used by the <command>avahi-daemon</command>.
+        Other interfaces will be ignored. If <literal>null</literal>, all local interfaces
+        except loopback and point-to-point will be used.
+      '';
+    };
+
+    openFirewall = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Whether to open the firewall for UDP port 5353.
+      '';
+    };
+
+    allowPointToPoint = mkOption {
+      type = types.bool;
+      default = false;
+      description= ''
+        Whether to use POINTTOPOINT interfaces. Might make mDNS unreliable due to usually large
+        latencies with such links and opens a potential security hole by allowing mDNS access from Internet
+        connections.
+      '';
+    };
+
+    wideArea = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Whether to enable wide-area service discovery.";
+    };
+
+    reflector = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Reflect incoming mDNS requests to all allowed network interfaces.";
+    };
+
+    extraServiceFiles = mkOption {
+      type = with types; attrsOf (either str path);
+      default = {};
+      example = literalExample ''
+        {
+          ssh = "''${pkgs.avahi}/etc/avahi/services/ssh.service";
+          smb = '''
+            <?xml version="1.0" standalone='no'?><!--*-nxml-*-->
+            <!DOCTYPE service-group SYSTEM "avahi-service.dtd">
+            <service-group>
+              <name replace-wildcards="yes">%h</name>
+              <service>
+                <type>_smb._tcp</type>
+                <port>445</port>
+              </service>
+            </service-group>
+          ''';
+        }
+      '';
+      description = ''
+        Specify custom service definitions which are placed in the avahi service directory.
+        See the <citerefentry><refentrytitle>avahi.service</refentrytitle>
+        <manvolnum>5</manvolnum></citerefentry> manpage for detailed information.
+      '';
+    };
+
+    publish = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to allow publishing in general.";
+      };
+
+      userServices = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to publish user services. Will set <literal>addresses=true</literal>.";
+      };
+
+      addresses = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to register mDNS address records for all local IP addresses.";
+      };
+
+      hinfo = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to register a mDNS HINFO record which contains information about the
+          local operating system and CPU.
+        '';
+      };
+
+      workstation = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to register a service of type "_workstation._tcp" on the local LAN.
+        '';
+      };
+
+      domain = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to announce the locally used domain name for browsing by other hosts.";
+      };
+    };
+
+    nssmdns = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether to enable the mDNS NSS (Name Service Switch) plug-in.
+        Enabling it allows applications to resolve names in the `.local'
+        domain by transparently querying the Avahi daemon.
+      '';
+    };
+
+    cacheEntriesMax = mkOption {
+      type = types.nullOr types.int;
+      default = null;
+      description = ''
+        Number of resource records to be cached per interface. Use 0 to
+        disable caching. Avahi daemon defaults to 4096 if not set.
+      '';
+    };
+
+    extraConfig = mkOption {
+      type = types.lines;
+      default = "";
+      description = ''
+        Extra config to append to avahi-daemon.conf.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.users.avahi = {
+      description = "avahi-daemon privilege separation user";
+      home = "/var/empty";
+      group = "avahi";
+      isSystemUser = true;
+    };
+
+    users.groups.avahi = {};
+
+    system.nssModules = optional cfg.nssmdns pkgs.nssmdns;
+    system.nssDatabases.hosts = optionals cfg.nssmdns (mkMerge [
+      [ "mdns_minimal [NOTFOUND=return]" ]
+      (mkOrder 1501 [ "mdns" ]) # 1501 to ensure it's after dns
+    ]);
+
+    environment.systemPackages = [ pkgs.avahi ];
+
+    environment.etc = (mapAttrs' (n: v: nameValuePair
+      "avahi/services/${n}.service"
+      { ${if types.path.check v then "source" else "text"} = v; }
+    ) cfg.extraServiceFiles);
+
+    systemd.sockets.avahi-daemon = {
+      description = "Avahi mDNS/DNS-SD Stack Activation Socket";
+      listenStreams = [ "/run/avahi-daemon/socket" ];
+      wantedBy = [ "sockets.target" ];
+    };
+
+    systemd.tmpfiles.rules = [ "d /run/avahi-daemon - avahi avahi -" ];
+
+    systemd.services.avahi-daemon = {
+      description = "Avahi mDNS/DNS-SD Stack";
+      wantedBy = [ "multi-user.target" ];
+      requires = [ "avahi-daemon.socket" ];
+
+      # Make NSS modules visible so that `avahi_nss_support ()' can
+      # return a sensible value.
+      environment.LD_LIBRARY_PATH = config.system.nssModules.path;
+
+      path = [ pkgs.coreutils pkgs.avahi ];
+
+      serviceConfig = {
+        NotifyAccess = "main";
+        BusName = "org.freedesktop.Avahi";
+        Type = "dbus";
+        ExecStart = "${pkgs.avahi}/sbin/avahi-daemon --syslog -f ${avahiDaemonConf}";
+      };
+    };
+
+    services.dbus.enable = true;
+    services.dbus.packages = [ pkgs.avahi ];
+
+    networking.firewall.allowedUDPPorts = mkIf cfg.openFirewall [ 5353 ];
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/babeld.nix b/nixpkgs/nixos/modules/services/networking/babeld.nix
new file mode 100644
index 000000000000..e62c74d0069d
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/babeld.nix
@@ -0,0 +1,95 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.babeld;
+
+  conditionalBoolToString = value: if (isBool value) then (boolToString value) else (toString value);
+
+  paramsString = params:
+    concatMapStringsSep " " (name: "${name} ${conditionalBoolToString (getAttr name params)}")
+                   (attrNames params);
+
+  interfaceConfig = name:
+    let
+      interface = getAttr name cfg.interfaces;
+    in
+    "interface ${name} ${paramsString interface}\n";
+
+  configFile = with cfg; pkgs.writeText "babeld.conf" (
+    (optionalString (cfg.interfaceDefaults != null) ''
+      default ${paramsString cfg.interfaceDefaults}
+    '')
+    + (concatMapStrings interfaceConfig (attrNames cfg.interfaces))
+    + extraConfig);
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.babeld = {
+
+      enable = mkEnableOption "the babeld network routing daemon";
+
+      interfaceDefaults = mkOption {
+        default = null;
+        description = ''
+          A set describing default parameters for babeld interfaces.
+          See <citerefentry><refentrytitle>babeld</refentrytitle><manvolnum>8</manvolnum></citerefentry> for options.
+        '';
+        type = types.nullOr (types.attrsOf types.unspecified);
+        example =
+          {
+            type = "tunnel";
+            split-horizon = true;
+          };
+      };
+
+      interfaces = mkOption {
+        default = {};
+        description = ''
+          A set describing babeld interfaces.
+          See <citerefentry><refentrytitle>babeld</refentrytitle><manvolnum>8</manvolnum></citerefentry> for options.
+        '';
+        type = types.attrsOf (types.attrsOf types.unspecified);
+        example =
+          { enp0s2 =
+            { type = "wired";
+              hello-interval = 5;
+              split-horizon = "auto";
+            };
+          };
+      };
+
+      extraConfig = mkOption {
+        default = "";
+        description = ''
+          Options that will be copied to babeld.conf.
+          See <citerefentry><refentrytitle>babeld</refentrytitle><manvolnum>8</manvolnum></citerefentry> for details.
+        '';
+      };
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.babeld.enable {
+
+    systemd.services.babeld = {
+      description = "Babel routing daemon";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig.ExecStart = "${pkgs.babeld}/bin/babeld -c ${configFile}";
+    };
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/bind.nix b/nixpkgs/nixos/modules/services/networking/bind.nix
new file mode 100644
index 000000000000..faad88635759
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/bind.nix
@@ -0,0 +1,205 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.bind;
+
+  bindUser = "named";
+
+  confFile = pkgs.writeText "named.conf"
+    ''
+      include "/etc/bind/rndc.key";
+      controls {
+        inet 127.0.0.1 allow {localhost;} keys {"rndc-key";};
+      };
+
+      acl cachenetworks { ${concatMapStrings (entry: " ${entry}; ") cfg.cacheNetworks} };
+      acl badnetworks { ${concatMapStrings (entry: " ${entry}; ") cfg.blockedNetworks} };
+
+      options {
+        listen-on { ${concatMapStrings (entry: " ${entry}; ") cfg.listenOn} };
+        listen-on-v6 { ${concatMapStrings (entry: " ${entry}; ") cfg.listenOnIpv6} };
+        allow-query { cachenetworks; };
+        blackhole { badnetworks; };
+        forward first;
+        forwarders { ${concatMapStrings (entry: " ${entry}; ") cfg.forwarders} };
+        directory "/run/named";
+        pid-file "/run/named/named.pid";
+        ${cfg.extraOptions}
+      };
+
+      ${cfg.extraConfig}
+
+      ${ concatMapStrings
+          ({ name, file, master ? true, slaves ? [], masters ? [], extraConfig ? "" }:
+            ''
+              zone "${name}" {
+                type ${if master then "master" else "slave"};
+                file "${file}";
+                ${ if master then
+                   ''
+                     allow-transfer {
+                       ${concatMapStrings (ip: "${ip};\n") slaves}
+                     };
+                   ''
+                   else
+                   ''
+                     masters {
+                       ${concatMapStrings (ip: "${ip};\n") masters}
+                     };
+                   ''
+                }
+                allow-query { any; };
+                ${extraConfig}
+              };
+            '')
+          cfg.zones }
+    '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.bind = {
+
+      enable = mkEnableOption "BIND domain name server";
+
+      cacheNetworks = mkOption {
+        default = ["127.0.0.0/24"];
+        description = "
+          What networks are allowed to use us as a resolver.  Note
+          that this is for recursive queries -- all networks are
+          allowed to query zones configured with the `zones` option.
+          It is recommended that you limit cacheNetworks to avoid your
+          server being used for DNS amplification attacks.
+        ";
+      };
+
+      blockedNetworks = mkOption {
+        default = [];
+        description = "
+          What networks are just blocked.
+        ";
+      };
+
+      ipv4Only = mkOption {
+        default = false;
+        description = "
+          Only use ipv4, even if the host supports ipv6.
+        ";
+      };
+
+      forwarders = mkOption {
+        default = config.networking.nameservers;
+        description = "
+          List of servers we should forward requests to.
+        ";
+      };
+
+      listenOn = mkOption {
+        default = ["any"];
+        type = types.listOf types.str;
+        description = "
+          Interfaces to listen on.
+        ";
+      };
+
+      listenOnIpv6 = mkOption {
+        default = ["any"];
+        type = types.listOf types.str;
+        description = "
+          Ipv6 interfaces to listen on.
+        ";
+      };
+
+      zones = mkOption {
+        default = [];
+        description = "
+          List of zones we claim authority over.
+            master=false means slave server; slaves means addresses
+           who may request zone transfer.
+        ";
+        example = [{
+          name = "example.com";
+          master = false;
+          file = "/var/dns/example.com";
+          masters = ["192.168.0.1"];
+          slaves = [];
+          extraConfig = "";
+        }];
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = "
+          Extra lines to be added verbatim to the generated named configuration file.
+        ";
+      };
+
+      extraOptions = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Extra lines to be added verbatim to the options section of the
+          generated named configuration file.
+        '';
+      };
+
+      configFile = mkOption {
+        type = types.path;
+        default = confFile;
+        defaultText = "confFile";
+        description = "
+          Overridable config file to use for named. By default, that
+          generated by nixos.
+        ";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    networking.resolvconf.useLocalResolver = mkDefault true;
+
+    users.users.${bindUser} =
+      { uid = config.ids.uids.bind;
+        description = "BIND daemon user";
+      };
+
+    systemd.services.bind = {
+      description = "BIND Domain Name Server";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      preStart = ''
+        mkdir -m 0755 -p /etc/bind
+        if ! [ -f "/etc/bind/rndc.key" ]; then
+          ${pkgs.bind.out}/sbin/rndc-confgen -c /etc/bind/rndc.key -u ${bindUser} -a -A hmac-sha256 2>/dev/null
+        fi
+
+        ${pkgs.coreutils}/bin/mkdir -p /run/named
+        chown ${bindUser} /run/named
+      '';
+
+      serviceConfig = {
+        ExecStart  = "${pkgs.bind.out}/sbin/named -u ${bindUser} ${optionalString cfg.ipv4Only "-4"} -c ${cfg.configFile} -f";
+        ExecReload = "${pkgs.bind.out}/sbin/rndc -k '/etc/bind/rndc.key' reload";
+        ExecStop   = "${pkgs.bind.out}/sbin/rndc -k '/etc/bind/rndc.key' stop";
+      };
+
+      unitConfig.Documentation = "man:named(8)";
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/bird.nix b/nixpkgs/nixos/modules/services/networking/bird.nix
new file mode 100644
index 000000000000..4ae35875c0f0
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/bird.nix
@@ -0,0 +1,78 @@
+{ config, lib, pkgs, ... }:
+
+let
+  inherit (lib) mkEnableOption mkIf mkOption types;
+
+  generic = variant:
+    let
+      cfg = config.services.${variant};
+      pkg = pkgs.${variant};
+      birdBin = if variant == "bird6" then "bird6" else "bird";
+      birdc = if variant == "bird6" then "birdc6" else "birdc";
+      descr =
+        { bird = "1.9.x with IPv4 suport";
+          bird6 = "1.9.x with IPv6 suport";
+          bird2 = "2.x";
+        }.${variant};
+    in {
+      ###### interface
+      options = {
+        services.${variant} = {
+          enable = mkEnableOption "BIRD Internet Routing Daemon (${descr})";
+          config = mkOption {
+            type = types.lines;
+            description = ''
+              BIRD Internet Routing Daemon configuration file.
+              <link xlink:href='http://bird.network.cz/'/>
+            '';
+          };
+        };
+      };
+
+      ###### implementation
+      config = mkIf cfg.enable {
+        environment.systemPackages = [ pkg ];
+
+        environment.etc."bird/${variant}.conf".source = pkgs.writeTextFile {
+          name = "${variant}.conf";
+          text = cfg.config;
+          checkPhase = ''
+            ${pkg}/bin/${birdBin} -d -p -c $out
+          '';
+        };
+
+        systemd.services.${variant} = {
+          description = "BIRD Internet Routing Daemon (${descr})";
+          wantedBy = [ "multi-user.target" ];
+          reloadIfChanged = true;
+          restartTriggers = [ config.environment.etc."bird/${variant}.conf".source ];
+          serviceConfig = {
+            Type = "forking";
+            Restart = "on-failure";
+            ExecStart = "${pkg}/bin/${birdBin} -c /etc/bird/${variant}.conf -u ${variant} -g ${variant}";
+            ExecReload = "${pkg}/bin/${birdc} configure";
+            ExecStop = "${pkg}/bin/${birdc} down";
+            CapabilityBoundingSet = [ "CAP_CHOWN" "CAP_FOWNER" "CAP_DAC_OVERRIDE" "CAP_SETUID" "CAP_SETGID"
+                                      # see bird/sysdep/linux/syspriv.h
+                                      "CAP_NET_BIND_SERVICE" "CAP_NET_BROADCAST" "CAP_NET_ADMIN" "CAP_NET_RAW" ];
+            ProtectSystem = "full";
+            ProtectHome = "yes";
+            SystemCallFilter="~@cpu-emulation @debug @keyring @module @mount @obsolete @raw-io";
+            MemoryDenyWriteExecute = "yes";
+          };
+        };
+        users = {
+          users.${variant} = {
+            description = "BIRD Internet Routing Daemon user";
+            group = variant;
+          };
+          groups.${variant} = {};
+        };
+      };
+    };
+
+in
+
+{
+  imports = map generic [ "bird" "bird6" "bird2" ];
+}
diff --git a/nixpkgs/nixos/modules/services/networking/bitcoind.nix b/nixpkgs/nixos/modules/services/networking/bitcoind.nix
new file mode 100644
index 000000000000..4e00a8865474
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/bitcoind.nix
@@ -0,0 +1,193 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.services.bitcoind;
+  pidFile = "${cfg.dataDir}/bitcoind.pid";
+  configFile = pkgs.writeText "bitcoin.conf" ''
+    ${optionalString cfg.testnet "testnet=1"}
+    ${optionalString (cfg.dbCache != null) "dbcache=${toString cfg.dbCache}"}
+    ${optionalString (cfg.prune != null) "prune=${toString cfg.prune}"}
+
+    # Connection options
+    ${optionalString (cfg.port != null) "port=${toString cfg.port}"}
+
+    # RPC server options
+    ${optionalString (cfg.rpc.port != null) "rpcport=${toString cfg.rpc.port}"}
+    ${concatMapStringsSep  "\n"
+      (rpcUser: "rpcauth=${rpcUser.name}:${rpcUser.passwordHMAC}")
+      (attrValues cfg.rpc.users)
+    }
+
+    # Extra config options (from bitcoind nixos service)
+    ${cfg.extraConfig}
+  '';
+  cmdlineOptions = escapeShellArgs [
+    "-conf=${cfg.configFile}"
+    "-datadir=${cfg.dataDir}"
+    "-pid=${pidFile}"
+  ];
+
+  rpcUserOpts = { name, ... }: {
+    options = {
+      name = mkOption {
+        type = types.str;
+        example = "alice";
+        description = ''
+          Username for JSON-RPC connections.
+        '';
+      };
+      passwordHMAC = mkOption {
+        type = with types; uniq (strMatching "[0-9a-f]+\\$[0-9a-f]{64}");
+        example = "f7efda5c189b999524f151318c0c86$d5b51b3beffbc02b724e5d095828e0bc8b2456e9ac8757ae3211a5d9b16a22ae";
+        description = ''
+          Password HMAC-SHA-256 for JSON-RPC connections. Must be a string of the
+          format &lt;SALT-HEX&gt;$&lt;HMAC-HEX&gt;.
+        '';
+      };
+    };
+    config = {
+      name = mkDefault name;
+    };
+  };
+in {
+  options = {
+
+    services.bitcoind = {
+      enable = mkEnableOption "Bitcoin daemon";
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.bitcoind;
+        defaultText = "pkgs.bitcoind";
+        description = "The package providing bitcoin binaries.";
+      };
+      configFile = mkOption {
+        type = types.path;
+        default = configFile;
+        example = "/etc/bitcoind.conf";
+        description = "The configuration file path to supply bitcoind.";
+      };
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        example = ''
+          par=16
+          rpcthreads=16
+          logips=1
+        '';
+        description = "Additional configurations to be appended to <filename>bitcoin.conf</filename>.";
+      };
+      dataDir = mkOption {
+        type = types.path;
+        default = "/var/lib/bitcoind";
+        description = "The data directory for bitcoind.";
+      };
+
+      user = mkOption {
+        type = types.str;
+        default = "bitcoin";
+        description = "The user as which to run bitcoind.";
+      };
+      group = mkOption {
+        type = types.str;
+        default = cfg.user;
+        description = "The group as which to run bitcoind.";
+      };
+
+      rpc = {
+        port = mkOption {
+          type = types.nullOr types.port;
+          default = null;
+          description = "Override the default port on which to listen for JSON-RPC connections.";
+        };
+        users = mkOption {
+          default = {};
+          example = literalExample ''
+            {
+              alice.passwordHMAC = "f7efda5c189b999524f151318c0c86$d5b51b3beffbc02b724e5d095828e0bc8b2456e9ac8757ae3211a5d9b16a22ae";
+              bob.passwordHMAC = "b2dd077cb54591a2f3139e69a897ac$4e71f08d48b4347cf8eff3815c0e25ae2e9a4340474079f55705f40574f4ec99";
+            }
+          '';
+          type = with types; loaOf (submodule rpcUserOpts);
+          description = ''
+            RPC user information for JSON-RPC connnections.
+          '';
+        };
+      };
+
+      testnet = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to use the test chain.";
+      };
+      port = mkOption {
+        type = types.nullOr types.port;
+        default = null;
+        description = "Override the default port on which to listen for connections.";
+      };
+      dbCache = mkOption {
+        type = types.nullOr (types.ints.between 4 16384);
+        default = null;
+        example = 4000;
+        description = "Override the default database cache size in megabytes.";
+      };
+      prune = mkOption {
+        type = types.nullOr (types.coercedTo
+          (types.enum [ "disable" "manual" ])
+          (x: if x == "disable" then 0 else 1)
+          types.ints.unsigned
+        );
+        default = null;
+        example = 10000;
+        description = ''
+          Reduce storage requirements by enabling pruning (deleting) of old
+          blocks. This allows the pruneblockchain RPC to be called to delete
+          specific blocks, and enables automatic pruning of old blocks if a
+          target size in MiB is provided. This mode is incompatible with -txindex
+          and -rescan. Warning: Reverting this setting requires re-downloading
+          the entire blockchain. ("disable" = disable pruning blocks, "manual"
+          = allow manual pruning via RPC, >=550 = automatically prune block files
+          to stay under the specified target size in MiB)
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ cfg.package ];
+    systemd.tmpfiles.rules = [
+      "d '${cfg.dataDir}' 0770 '${cfg.user}' '${cfg.group}' - -"
+      "L '${cfg.dataDir}/bitcoin.conf' - - - - '${cfg.configFile}'"
+    ];
+    systemd.services.bitcoind = {
+      description = "Bitcoin daemon";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        User = cfg.user;
+        Group = cfg.group;
+        ExecStart = "${cfg.package}/bin/bitcoind ${cmdlineOptions}";
+        Restart = "on-failure";
+
+        # Hardening measures
+        PrivateTmp = "true";
+        ProtectSystem = "full";
+        NoNewPrivileges = "true";
+        PrivateDevices = "true";
+        MemoryDenyWriteExecute = "true";
+      };
+    };
+    users.users.${cfg.user} = {
+      name = cfg.user;
+      group = cfg.group;
+      description = "Bitcoin daemon user";
+      home = cfg.dataDir;
+      isSystemUser = true;
+    };
+    users.groups.${cfg.group} = {
+      name = cfg.group;
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/bitlbee.nix b/nixpkgs/nixos/modules/services/networking/bitlbee.nix
new file mode 100644
index 000000000000..9ebf382fce42
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/bitlbee.nix
@@ -0,0 +1,193 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.bitlbee;
+  bitlbeeUid = config.ids.uids.bitlbee;
+
+  bitlbeePkg = pkgs.bitlbee.override {
+    enableLibPurple = cfg.libpurple_plugins != [];
+    enablePam = cfg.authBackend == "pam";
+  };
+
+  bitlbeeConfig = pkgs.writeText "bitlbee.conf"
+    ''
+    [settings]
+    RunMode = Daemon
+    User = bitlbee
+    ConfigDir = ${cfg.configDir}
+    DaemonInterface = ${cfg.interface}
+    DaemonPort = ${toString cfg.portNumber}
+    AuthMode = ${cfg.authMode}
+    AuthBackend = ${cfg.authBackend}
+    Plugindir = ${pkgs.bitlbee-plugins cfg.plugins}/lib/bitlbee
+    ${lib.optionalString (cfg.hostName != "") "HostName = ${cfg.hostName}"}
+    ${lib.optionalString (cfg.protocols != "") "Protocols = ${cfg.protocols}"}
+    ${cfg.extraSettings}
+
+    [defaults]
+    ${cfg.extraDefaults}
+    '';
+
+  purple_plugin_path =
+    lib.concatMapStringsSep ":"
+      (plugin: "${plugin}/lib/pidgin/:${plugin}/lib/purple-2/")
+      cfg.libpurple_plugins
+    ;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.bitlbee = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to run the BitlBee IRC to other chat network gateway.
+          Running it allows you to access the MSN, Jabber, Yahoo! and ICQ chat
+          networks via an IRC client.
+        '';
+      };
+
+      interface = mkOption {
+        default = "127.0.0.1";
+        description = ''
+          The interface the BitlBee deamon will be listening to.  If `127.0.0.1',
+          only clients on the local host can connect to it; if `0.0.0.0', clients
+          can access it from any network interface.
+        '';
+      };
+
+      portNumber = mkOption {
+        default = 6667;
+        description = ''
+          Number of the port BitlBee will be listening to.
+        '';
+      };
+
+      authBackend = mkOption {
+        default = "storage";
+        type = types.enum [ "storage" "pam" ];
+        description = ''
+          How users are authenticated
+            storage -- save passwords internally
+            pam -- Linux PAM authentication
+        '';
+      };
+
+      authMode = mkOption {
+        default = "Open";
+        type = types.enum [ "Open" "Closed" "Registered" ];
+        description = ''
+          The following authentication modes are available:
+            Open -- Accept connections from anyone, use NickServ for user authentication.
+            Closed -- Require authorization (using the PASS command during login) before allowing the user to connect at all.
+            Registered -- Only allow registered users to use this server; this disables the register- and the account command until the user identifies himself.
+        '';
+      };
+
+      hostName = mkOption {
+        default = "";
+        type = types.str;
+        description = ''
+          Normally, BitlBee gets a hostname using getsockname(). If you have a nicer
+          alias for your BitlBee daemon, you can set it here and BitlBee will identify
+          itself with that name instead.
+        '';
+      };
+
+      plugins = mkOption {
+        type = types.listOf types.package;
+        default = [];
+        example = literalExample "[ pkgs.bitlbee-facebook ]";
+        description = ''
+          The list of bitlbee plugins to install.
+        '';
+      };
+
+      libpurple_plugins = mkOption {
+        type = types.listOf types.package;
+        default = [];
+        example = literalExample "[ pkgs.purple-matrix ]";
+        description = ''
+          The list of libpurple plugins to install.
+        '';
+      };
+
+      configDir = mkOption {
+        default = "/var/lib/bitlbee";
+        type = types.path;
+        description = ''
+          Specify an alternative directory to store all the per-user configuration
+          files.
+        '';
+      };
+
+      protocols = mkOption {
+        default = "";
+        type = types.str;
+        description = ''
+          This option allows to remove the support of protocol, even if compiled
+          in. If nothing is given, there are no restrictions.
+        '';
+      };
+
+      extraSettings = mkOption {
+        default = "";
+        description = ''
+          Will be inserted in the Settings section of the config file.
+        '';
+      };
+
+      extraDefaults = mkOption {
+        default = "";
+        description = ''
+          Will be inserted in the Default section of the config file.
+        '';
+      };
+
+    };
+
+  };
+
+  ###### implementation
+
+  config =  mkMerge [
+    (mkIf config.services.bitlbee.enable {
+      users.users.bitlbee = {
+        uid = bitlbeeUid;
+        description = "BitlBee user";
+        home = "/var/lib/bitlbee";
+        createHome = true;
+      };
+
+      users.groups.bitlbee = {
+        gid = config.ids.gids.bitlbee;
+      };
+
+      systemd.services.bitlbee = {
+        environment.PURPLE_PLUGIN_PATH = purple_plugin_path;
+        description = "BitlBee IRC to other chat networks gateway";
+        after = [ "network.target" ];
+        wantedBy = [ "multi-user.target" ];
+        serviceConfig.User = "bitlbee";
+        serviceConfig.ExecStart = "${bitlbeePkg}/sbin/bitlbee -F -n -c ${bitlbeeConfig}";
+      };
+
+      environment.systemPackages = [ bitlbeePkg ];
+
+    })
+    (mkIf (config.services.bitlbee.authBackend == "pam") {
+      security.pam.services.bitlbee = {};
+    })
+  ];
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/charybdis.nix b/nixpkgs/nixos/modules/services/networking/charybdis.nix
new file mode 100644
index 000000000000..43829d36e417
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/charybdis.nix
@@ -0,0 +1,107 @@
+{ config, lib, pkgs, ... }:
+
+let
+  inherit (lib) mkEnableOption mkIf mkOption singleton types;
+  inherit (pkgs) coreutils charybdis;
+  cfg = config.services.charybdis;
+
+  configFile = pkgs.writeText "charybdis.conf" ''
+    ${cfg.config}
+  '';
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.charybdis = {
+
+      enable = mkEnableOption "Charybdis IRC daemon";
+
+      config = mkOption {
+        type = types.str;
+        description = ''
+          Charybdis IRC daemon configuration file.
+        '';
+      };
+
+      statedir = mkOption {
+        type = types.path;
+        default = "/var/lib/charybdis";
+        description = ''
+          Location of the state directory of charybdis.
+        '';
+      };
+
+      user = mkOption {
+        type = types.str;
+        default = "ircd";
+        description = ''
+          Charybdis IRC daemon user.
+        '';
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "ircd";
+        description = ''
+          Charybdis IRC daemon group.
+        '';
+      };
+
+      motd = mkOption {
+        type = types.nullOr types.lines;
+        default = null;
+        description = ''
+          Charybdis MOTD text.
+
+          Charybdis will read its MOTD from /etc/charybdis/ircd.motd .
+          If set, the value of this option will be written to this path.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable (lib.mkMerge [
+    {
+      users.users.${cfg.user} = {
+        description = "Charybdis IRC daemon user";
+        uid = config.ids.uids.ircd;
+        group = cfg.group;
+      };
+
+      users.groups.${cfg.group} = {
+        gid = config.ids.gids.ircd;
+      };
+
+      systemd.tmpfiles.rules = [
+        "d ${cfg.statedir} - ${cfg.user} ${cfg.group} - -"
+      ];
+
+      systemd.services.charybdis = {
+        description = "Charybdis IRC daemon";
+        wantedBy = [ "multi-user.target" ];
+        environment = {
+          BANDB_DBPATH = "${cfg.statedir}/ban.db";
+        };
+        serviceConfig = {
+          ExecStart   = "${charybdis}/bin/charybdis -foreground -logfile /dev/stdout -configfile ${configFile}";
+          Group = cfg.group;
+          User = cfg.user;
+        };
+      };
+
+    }
+
+    (mkIf (cfg.motd != null) {
+      environment.etc."charybdis/ircd.motd".text = cfg.motd;
+    })
+  ]);
+}
diff --git a/nixpkgs/nixos/modules/services/networking/cjdns.nix b/nixpkgs/nixos/modules/services/networking/cjdns.nix
new file mode 100644
index 000000000000..5f8ac96b2292
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/cjdns.nix
@@ -0,0 +1,294 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  pkg = pkgs.cjdns;
+
+  cfg = config.services.cjdns;
+
+  connectToSubmodule =
+  { ... }:
+  { options =
+    { password = mkOption {
+      type = types.str;
+      description = "Authorized password to the opposite end of the tunnel.";
+      };
+      publicKey = mkOption {
+        type = types.str;
+        description = "Public key at the opposite end of the tunnel.";
+      };
+      hostname = mkOption {
+        default = "";
+        example = "foobar.hype";
+        type = types.str;
+        description = "Optional hostname to add to /etc/hosts; prevents reverse lookup failures.";
+      };
+    };
+  };
+
+  # Additional /etc/hosts entries for peers with an associated hostname
+  cjdnsExtraHosts = pkgs.runCommandNoCC "cjdns-hosts" {} ''
+    exec >$out
+    ${concatStringsSep "\n" (mapAttrsToList (k: v:
+        optionalString (v.hostname != "")
+          "echo $(${pkgs.cjdns}/bin/publictoip6 ${v.publicKey}) ${v.hostname}")
+        (cfg.ETHInterface.connectTo // cfg.UDPInterface.connectTo))}
+  '';
+
+  parseModules = x:
+    x // { connectTo = mapAttrs (name: value: { inherit (value) password publicKey; }) x.connectTo; };
+
+  cjdrouteConf = builtins.toJSON ( recursiveUpdate {
+    admin = {
+      bind = cfg.admin.bind;
+      password = "@CJDNS_ADMIN_PASSWORD@";
+    };
+    authorizedPasswords = map (p: { password = p; }) cfg.authorizedPasswords;
+    interfaces = {
+      ETHInterface = if (cfg.ETHInterface.bind != "") then [ (parseModules cfg.ETHInterface) ] else [ ];
+      UDPInterface = if (cfg.UDPInterface.bind != "") then [ (parseModules cfg.UDPInterface) ] else [ ];
+    };
+
+    privateKey = "@CJDNS_PRIVATE_KEY@";
+
+    resetAfterInactivitySeconds = 100;
+
+    router = {
+      interface = { type = "TUNInterface"; };
+      ipTunnel = {
+        allowedConnections = [];
+        outgoingConnections = [];
+      };
+    };
+
+    security = [ { exemptAngel = 1; setuser = "nobody"; } ];
+
+  } cfg.extraConfig);
+
+in
+
+{
+  options = {
+
+    services.cjdns = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable the cjdns network encryption
+          and routing engine. A file at /etc/cjdns.keys will
+          be created if it does not exist to contain a random
+          secret key that your IPv6 address will be derived from.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.attrs;
+        default = {};
+        example = { router.interface.tunDevice = "tun10"; };
+        description = ''
+          Extra configuration, given as attrs, that will be merged recursively
+          with the rest of the JSON generated by this module, at the root node.
+        '';
+      };
+
+      confFile = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        example = "/etc/cjdroute.conf";
+        description = ''
+          Ignore all other cjdns options and load configuration from this file.
+        '';
+      };
+
+      authorizedPasswords = mkOption {
+        type = types.listOf types.str;
+        default = [ ];
+        example = [
+          "snyrfgkqsc98qh1y4s5hbu0j57xw5s0"
+          "z9md3t4p45mfrjzdjurxn4wuj0d8swv"
+          "49275fut6tmzu354pq70sr5b95qq0vj"
+        ];
+        description = ''
+          Any remote cjdns nodes that offer these passwords on
+          connection will be allowed to route through this node.
+        '';
+      };
+
+      admin = {
+        bind = mkOption {
+          type = types.str;
+          default = "127.0.0.1:11234";
+          description = ''
+            Bind the administration port to this address and port.
+          '';
+        };
+      };
+
+      UDPInterface = {
+        bind = mkOption {
+          type = types.str;
+          default = "";
+          example = "192.168.1.32:43211";
+          description = ''
+            Address and port to bind UDP tunnels to.
+          '';
+         };
+        connectTo = mkOption {
+          type = types.attrsOf ( types.submodule ( connectToSubmodule ) );
+          default = { };
+          example = literalExample ''
+            {
+              "192.168.1.1:27313" = {
+                hostname = "homer.hype";
+                password = "5kG15EfpdcKNX3f2GSQ0H1HC7yIfxoCoImnO5FHM";
+                publicKey = "371zpkgs8ss387tmr81q04mp0hg1skb51hw34vk1cq644mjqhup0.k";
+              };
+            }
+          '';
+          description = ''
+            Credentials for making UDP tunnels.
+          '';
+        };
+      };
+
+      ETHInterface = {
+        bind = mkOption {
+          type = types.str;
+          default = "";
+          example = "eth0";
+          description =
+            ''
+              Bind to this device for native ethernet operation.
+              <literal>all</literal> is a pseudo-name which will try to connect to all devices.
+            '';
+        };
+
+        beacon = mkOption {
+          type = types.int;
+          default = 2;
+          description = ''
+            Auto-connect to other cjdns nodes on the same network.
+            Options:
+              0: Disabled.
+              1: Accept beacons, this will cause cjdns to accept incoming
+                 beacon messages and try connecting to the sender.
+              2: Accept and send beacons, this will cause cjdns to broadcast
+                 messages on the local network which contain a randomly
+                 generated per-session password, other nodes which have this
+                 set to 1 or 2 will hear the beacon messages and connect
+                 automatically.
+          '';
+        };
+
+        connectTo = mkOption {
+          type = types.attrsOf ( types.submodule ( connectToSubmodule ) );
+          default = { };
+          example = literalExample ''
+            {
+              "01:02:03:04:05:06" = {
+                hostname = "homer.hype";
+                password = "5kG15EfpdcKNX3f2GSQ0H1HC7yIfxoCoImnO5FHM";
+                publicKey = "371zpkgs8ss387tmr81q04mp0hg1skb51hw34vk1cq644mjqhup0.k";
+              };
+            }
+          '';
+          description = ''
+            Credentials for connecting look similar to UDP credientials
+            except they begin with the mac address.
+          '';
+        };
+      };
+
+      addExtraHosts = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to add cjdns peers with an associated hostname to
+          <filename>/etc/hosts</filename>.  Beware that enabling this
+          incurs heavy eval-time costs.
+        '';
+      };
+
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    boot.kernelModules = [ "tun" ];
+
+    # networking.firewall.allowedUDPPorts = ...
+
+    systemd.services.cjdns = {
+      description = "cjdns: routing engine designed for security, scalability, speed and ease of use";
+      wantedBy = [ "multi-user.target" "sleep.target"];
+      after = [ "network-online.target" ];
+      bindsTo = [ "network-online.target" ];
+
+      preStart = if cfg.confFile != null then "" else ''
+        [ -e /etc/cjdns.keys ] && source /etc/cjdns.keys
+
+        if [ -z "$CJDNS_PRIVATE_KEY" ]; then
+            shopt -s lastpipe
+            ${pkg}/bin/makekeys | { read private ipv6 public; }
+
+            umask 0077
+            echo "CJDNS_PRIVATE_KEY=$private" >> /etc/cjdns.keys
+            echo -e "CJDNS_IPV6=$ipv6\nCJDNS_PUBLIC_KEY=$public" > /etc/cjdns.public
+
+            chmod 600 /etc/cjdns.keys
+            chmod 444 /etc/cjdns.public
+        fi
+
+        if [ -z "$CJDNS_ADMIN_PASSWORD" ]; then
+            echo "CJDNS_ADMIN_PASSWORD=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 96)" \
+                >> /etc/cjdns.keys
+        fi
+      '';
+
+      script = (
+        if cfg.confFile != null then "${pkg}/bin/cjdroute < ${cfg.confFile}" else
+          ''
+            source /etc/cjdns.keys
+            (cat <<'EOF'
+            ${cjdrouteConf}
+            EOF
+            ) | sed \
+                -e "s/@CJDNS_ADMIN_PASSWORD@/$CJDNS_ADMIN_PASSWORD/g" \
+                -e "s/@CJDNS_PRIVATE_KEY@/$CJDNS_PRIVATE_KEY/g" \
+                | ${pkg}/bin/cjdroute
+         ''
+      );
+
+      serviceConfig = {
+        Type = "forking";
+        Restart = "always";
+        StartLimitInterval = 0;
+        RestartSec = 1;
+        CapabilityBoundingSet = "CAP_NET_ADMIN CAP_NET_RAW CAP_SETUID";
+        ProtectSystem = true;
+        # Doesn't work on i686, causing service to fail
+        MemoryDenyWriteExecute = !pkgs.stdenv.isi686;
+        ProtectHome = true;
+        PrivateTmp = true;
+      };
+    };
+
+    networking.hostFiles = mkIf cfg.addExtraHosts [ cjdnsExtraHosts ];
+
+    assertions = [
+      { assertion = ( cfg.ETHInterface.bind != "" || cfg.UDPInterface.bind != "" || cfg.confFile != null );
+        message = "Neither cjdns.ETHInterface.bind nor cjdns.UDPInterface.bind defined.";
+      }
+      { assertion = config.networking.enableIPv6;
+        message = "networking.enableIPv6 must be enabled for CJDNS to work";
+      }
+    ];
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/cntlm.nix b/nixpkgs/nixos/modules/services/networking/cntlm.nix
new file mode 100644
index 000000000000..5b5068e43d7c
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/cntlm.nix
@@ -0,0 +1,121 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.cntlm;
+
+  configFile = if cfg.configText != "" then
+    pkgs.writeText "cntlm.conf" ''
+      ${cfg.configText}
+    ''
+    else
+    pkgs.writeText "lighttpd.conf" ''
+      # Cntlm Authentication Proxy Configuration
+      Username ${cfg.username}
+      Domain ${cfg.domain}
+      Password ${cfg.password}
+      ${optionalString (cfg.netbios_hostname != "") "Workstation ${cfg.netbios_hostname}"}
+      ${concatMapStrings (entry: "Proxy ${entry}\n") cfg.proxy}
+      ${optionalString (cfg.noproxy != []) "NoProxy ${concatStringsSep ", " cfg.noproxy}"}
+
+      ${concatMapStrings (port: ''
+        Listen ${toString port}
+      '') cfg.port}
+
+      ${cfg.extraConfig}
+    '';
+
+in
+
+{
+
+  options.services.cntlm = {
+
+    enable = mkEnableOption "cntlm, which starts a local proxy";
+
+    username = mkOption {
+      description = ''
+        Proxy account name, without the possibility to include domain name ('at' sign is interpreted literally).
+      '';
+    };
+
+    domain = mkOption {
+      description = ''Proxy account domain/workgroup name.'';
+    };
+
+    password = mkOption {
+      default = "/etc/cntlm.password";
+      type = types.str;
+      description = ''Proxy account password. Note: use chmod 0600 on /etc/cntlm.password for security.'';
+    };
+
+    netbios_hostname = mkOption {
+      type = types.str;
+      default = "";
+      description = ''
+        The hostname of your machine.
+      '';
+    };
+
+    proxy = mkOption {
+      description = ''
+        A list of NTLM/NTLMv2 authenticating HTTP proxies.
+
+        Parent proxy, which requires authentication. The same as proxy on the command-line, can be used more than  once  to  specify  unlimited
+        number  of  proxies.  Should  one proxy fail, cntlm automatically moves on to the next one. The connect request fails only if the whole
+        list of proxies is scanned and (for each request) and found to be invalid. Command-line takes precedence over the configuration file.
+      '';
+      example = [ "proxy.example.com:81" ];
+    };
+
+    noproxy = mkOption {
+      description = ''
+        A list of domains where the proxy is skipped.
+      '';
+      default = [];
+      example = [ "*.example.com" "example.com" ];
+    };
+
+    port = mkOption {
+      default = [3128];
+      description = "Specifies on which ports the cntlm daemon listens.";
+    };
+
+    extraConfig = mkOption {
+      type = types.lines;
+      default = "";
+      description = "Additional config appended to the end of the generated <filename>cntlm.conf</filename>.";
+    };
+
+    configText = mkOption {
+       type = types.lines;
+       default = "";
+       description = "Verbatim contents of <filename>cntlm.conf</filename>.";
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    systemd.services.cntlm = {
+      description = "CNTLM is an NTLM / NTLM Session Response / NTLMv2 authenticating HTTP proxy";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        User = "cntlm";
+        ExecStart = ''
+          ${pkgs.cntlm}/bin/cntlm -U cntlm -c ${configFile} -v -f
+        '';
+      };
+    };
+
+    users.users.cntlm = {
+      name = "cntlm";
+      description = "cntlm system-wide daemon";
+      isSystemUser = true;
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/connman.nix b/nixpkgs/nixos/modules/services/networking/connman.nix
new file mode 100644
index 000000000000..6ccc2dffb267
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/connman.nix
@@ -0,0 +1,161 @@
+{ config, lib, pkgs, ... }:
+
+with pkgs;
+with lib;
+
+let
+  cfg = config.services.connman;
+  configFile = pkgs.writeText "connman.conf" ''
+    [General]
+    NetworkInterfaceBlacklist=${concatStringsSep "," cfg.networkInterfaceBlacklist}
+
+    ${cfg.extraConfig}
+  '';
+  enableIwd = cfg.wifi.backend == "iwd";
+in {
+
+  imports = [
+    (mkRenamedOptionModule [ "networking" "connman" ] [ "services" "connman" ])
+  ];
+
+  ###### interface
+
+  options = {
+
+    services.connman = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to use ConnMan for managing your network connections.
+        '';
+      };
+
+      enableVPN = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to enable ConnMan VPN service.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = ''
+        '';
+        description = ''
+          Configuration lines appended to the generated connman configuration file.
+        '';
+      };
+
+      networkInterfaceBlacklist = mkOption {
+        type = with types; listOf str;
+        default = [ "vmnet" "vboxnet" "virbr" "ifb" "ve" ];
+        description = ''
+          Default blacklisted interfaces, this includes NixOS containers interfaces (ve).
+        '';
+      };
+
+      wifi = {
+        backend = mkOption {
+          type = types.enum [ "wpa_supplicant" "iwd" ];
+          default = "wpa_supplicant";
+          description = ''
+            Specify the Wi-Fi backend used.
+            Currently supported are <option>wpa_supplicant</option> or <option>iwd</option>.
+          '';
+        };
+      };
+
+      extraFlags = mkOption {
+        type = with types; listOf str;
+        default = [ ];
+        example = [ "--nodnsproxy" ];
+        description = ''
+          Extra flags to pass to connmand
+        '';
+      };
+
+      package = mkOption {
+        type = types.path;
+        description = "The connman package / build flavor";
+        default = connman;
+        example = literalExample "pkgs.connmanFull";
+      };
+
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    assertions = [{
+      assertion = !config.networking.useDHCP;
+      message = "You can not use services.connman with networking.useDHCP";
+    }{
+      # TODO: connman seemingly can be used along network manager and
+      # connmanFull supports this - so this should be worked out somehow
+      assertion = !config.networking.networkmanager.enable;
+      message = "You can not use services.connman with networking.networkmanager";
+    }];
+
+    environment.systemPackages = [ cfg.package ];
+
+    systemd.services.connman = {
+      description = "Connection service";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "syslog.target" ] ++ optional enableIwd "iwd.service";
+      requires = optional enableIwd "iwd.service";
+      serviceConfig = {
+        Type = "dbus";
+        BusName = "net.connman";
+        Restart = "on-failure";
+        ExecStart = toString ([
+          "${cfg.package}/sbin/connmand"
+          "--config=${configFile}"
+          "--nodaemon"
+        ] ++ optional enableIwd "--wifi=iwd_agent"
+          ++ cfg.extraFlags);
+        StandardOutput = "null";
+      };
+    };
+
+    systemd.services.connman-vpn = mkIf cfg.enableVPN {
+      description = "ConnMan VPN service";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "syslog.target" ];
+      before = [ "connman" ];
+      serviceConfig = {
+        Type = "dbus";
+        BusName = "net.connman.vpn";
+        ExecStart = "${cfg.package}/sbin/connman-vpnd -n";
+        StandardOutput = "null";
+      };
+    };
+
+    systemd.services.net-connman-vpn = mkIf cfg.enableVPN {
+      description = "D-BUS Service";
+      serviceConfig = {
+        Name = "net.connman.vpn";
+        before = [ "connman" ];
+        ExecStart = "${cfg.package}/sbin/connman-vpnd -n";
+        User = "root";
+        SystemdService = "connman-vpn.service";
+      };
+    };
+
+    networking = {
+      useDHCP = false;
+      wireless = {
+        enable = mkIf (!enableIwd) true;
+        iwd = mkIf enableIwd {
+          enable = true;
+        };
+      };
+      networkmanager.enable = false;
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/consul.nix b/nixpkgs/nixos/modules/services/networking/consul.nix
new file mode 100644
index 000000000000..f7d2afead06c
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/consul.nix
@@ -0,0 +1,254 @@
+{ config, lib, pkgs, utils, ... }:
+
+with lib;
+let
+
+  dataDir = "/var/lib/consul";
+  cfg = config.services.consul;
+
+  configOptions = {
+    data_dir = dataDir;
+    ui = cfg.webUi;
+  } // cfg.extraConfig;
+
+  configFiles = [ "/etc/consul.json" "/etc/consul-addrs.json" ]
+    ++ cfg.extraConfigFiles;
+
+  devices = attrValues (filterAttrs (_: i: i != null) cfg.interface);
+  systemdDevices = forEach devices
+    (i: "sys-subsystem-net-devices-${utils.escapeSystemdPath i}.device");
+in
+{
+  options = {
+
+    services.consul = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enables the consul daemon.
+        '';
+      };
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.consul;
+        defaultText = "pkgs.consul";
+        description = ''
+          The package used for the Consul agent and CLI.
+        '';
+      };
+
+
+      webUi = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enables the web interface on the consul http port.
+        '';
+      };
+
+      leaveOnStop = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          If enabled, causes a leave action to be sent when closing consul.
+          This allows a clean termination of the node, but permanently removes
+          it from the cluster. You probably don't want this option unless you
+          are running a node which going offline in a permanent / semi-permanent
+          fashion.
+        '';
+      };
+
+      interface = {
+
+        advertise = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          description = ''
+            The name of the interface to pull the advertise_addr from.
+          '';
+        };
+
+        bind = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          description = ''
+            The name of the interface to pull the bind_addr from.
+          '';
+        };
+
+      };
+
+      forceIpv4 = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether we should force the interfaces to only pull ipv4 addresses.
+        '';
+      };
+
+      dropPrivileges = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether the consul agent should be run as a non-root consul user.
+        '';
+      };
+
+      extraConfig = mkOption {
+        default = { };
+        description = ''
+          Extra configuration options which are serialized to json and added
+          to the config.json file.
+        '';
+      };
+
+      extraConfigFiles = mkOption {
+        default = [ ];
+        type = types.listOf types.str;
+        description = ''
+          Additional configuration files to pass to consul
+          NOTE: These will not trigger the service to be restarted when altered.
+        '';
+      };
+
+      alerts = {
+        enable = mkEnableOption "consul-alerts";
+
+        package = mkOption {
+          description = "Package to use for consul-alerts.";
+          default = pkgs.consul-alerts;
+          defaultText = "pkgs.consul-alerts";
+          type = types.package;
+        };
+
+        listenAddr = mkOption {
+          description = "Api listening address.";
+          default = "localhost:9000";
+          type = types.str;
+        };
+
+        consulAddr = mkOption {
+          description = "Consul api listening adddress";
+          default = "localhost:8500";
+          type = types.str;
+        };
+
+        watchChecks = mkOption {
+          description = "Whether to enable check watcher.";
+          default = true;
+          type = types.bool;
+        };
+
+        watchEvents = mkOption {
+          description = "Whether to enable event watcher.";
+          default = true;
+          type = types.bool;
+        };
+      };
+
+    };
+
+  };
+
+  config = mkIf cfg.enable (
+    mkMerge [{
+
+      users.users.consul = {
+        description = "Consul agent daemon user";
+        uid = config.ids.uids.consul;
+        # The shell is needed for health checks
+        shell = "/run/current-system/sw/bin/bash";
+      };
+
+      environment = {
+        etc."consul.json".text = builtins.toJSON configOptions;
+        # We need consul.d to exist for consul to start
+        etc."consul.d/dummy.json".text = "{ }";
+        systemPackages = [ cfg.package ];
+      };
+
+      systemd.services.consul = {
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ] ++ systemdDevices;
+        bindsTo = systemdDevices;
+        restartTriggers = [ config.environment.etc."consul.json".source ]
+          ++ mapAttrsToList (_: d: d.source)
+            (filterAttrs (n: _: hasPrefix "consul.d/" n) config.environment.etc);
+
+        serviceConfig = {
+          ExecStart = "@${cfg.package}/bin/consul consul agent -config-dir /etc/consul.d"
+            + concatMapStrings (n: " -config-file ${n}") configFiles;
+          ExecReload = "${cfg.package}/bin/consul reload";
+          PermissionsStartOnly = true;
+          User = if cfg.dropPrivileges then "consul" else null;
+          Restart = "on-failure";
+          TimeoutStartSec = "infinity";
+        } // (optionalAttrs (cfg.leaveOnStop) {
+          ExecStop = "${cfg.package}/bin/consul leave";
+        });
+
+        path = with pkgs; [ iproute gnugrep gawk consul ];
+        preStart = ''
+          mkdir -m 0700 -p ${dataDir}
+          chown -R consul ${dataDir}
+
+          # Determine interface addresses
+          getAddrOnce () {
+            ip addr show dev "$1" \
+              | grep 'inet${optionalString (cfg.forceIpv4) " "}.*scope global' \
+              | awk -F '[ /\t]*' '{print $3}' | head -n 1
+          }
+          getAddr () {
+            ADDR="$(getAddrOnce $1)"
+            LEFT=60 # Die after 1 minute
+            while [ -z "$ADDR" ]; do
+              sleep 1
+              LEFT=$(expr $LEFT - 1)
+              if [ "$LEFT" -eq "0" ]; then
+                echo "Address lookup timed out"
+                exit 1
+              fi
+              ADDR="$(getAddrOnce $1)"
+            done
+            echo "$ADDR"
+          }
+          echo "{" > /etc/consul-addrs.json
+          delim=" "
+        ''
+        + concatStrings (flip mapAttrsToList cfg.interface (name: i:
+          optionalString (i != null) ''
+            echo "$delim \"${name}_addr\": \"$(getAddr "${i}")\"" >> /etc/consul-addrs.json
+            delim=","
+          ''))
+        + ''
+          echo "}" >> /etc/consul-addrs.json
+        '';
+      };
+    }
+
+    (mkIf (cfg.alerts.enable) {
+      systemd.services.consul-alerts = {
+        wantedBy = [ "multi-user.target" ];
+        after = [ "consul.service" ];
+
+        path = [ cfg.package ];
+
+        serviceConfig = {
+          ExecStart = ''
+            ${cfg.alerts.package}/bin/consul-alerts start \
+              --alert-addr=${cfg.alerts.listenAddr} \
+              --consul-addr=${cfg.alerts.consulAddr} \
+              ${optionalString cfg.alerts.watchChecks "--watch-checks"} \
+              ${optionalString cfg.alerts.watchEvents "--watch-events"}
+          '';
+          User = if cfg.dropPrivileges then "consul" else null;
+          Restart = "on-failure";
+        };
+      };
+    })
+
+  ]);
+}
diff --git a/nixpkgs/nixos/modules/services/networking/coredns.nix b/nixpkgs/nixos/modules/services/networking/coredns.nix
new file mode 100644
index 000000000000..afb2b547a465
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/coredns.nix
@@ -0,0 +1,50 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.coredns;
+  configFile = pkgs.writeText "Corefile" cfg.config;
+in {
+  options.services.coredns = {
+    enable = mkEnableOption "Coredns dns server";
+
+    config = mkOption {
+      default = "";
+      example = ''
+        . {
+          whoami
+        }
+      '';
+      type = types.lines;
+      description = "Verbatim Corefile to use. See <link xlink:href=\"https://coredns.io/manual/toc/#configuration\"/> for details.";
+    };
+
+    package = mkOption {
+      default = pkgs.coredns;
+      defaultText = "pkgs.coredns";
+      type = types.package;
+      description = "Coredns package to use.";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.coredns = {
+      description = "Coredns dns server";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        PermissionsStartOnly = true;
+        LimitNPROC = 512;
+        LimitNOFILE = 1048576;
+        CapabilityBoundingSet = "cap_net_bind_service";
+        AmbientCapabilities = "cap_net_bind_service";
+        NoNewPrivileges = true;
+        DynamicUser = true;
+        ExecStart = "${getBin cfg.package}/bin/coredns -conf=${configFile}";
+        ExecReload = "${pkgs.coreutils}/bin/kill -SIGUSR1 $MAINPID";
+        Restart = "on-failure";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/corerad.nix b/nixpkgs/nixos/modules/services/networking/corerad.nix
new file mode 100644
index 000000000000..5d73c0a0d779
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/corerad.nix
@@ -0,0 +1,85 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.corerad;
+
+  writeTOML = name: x:
+    pkgs.runCommandNoCCLocal name {
+      passAsFile = ["config"];
+      config = builtins.toJSON x;
+      buildInputs = [ pkgs.go-toml ];
+    } "jsontoml < $configPath > $out";
+
+in {
+  meta.maintainers = with maintainers; [ mdlayher ];
+
+  options.services.corerad = {
+    enable = mkEnableOption "CoreRAD IPv6 NDP RA daemon";
+
+    settings = mkOption {
+      type = types.uniq types.attrs;
+      example = literalExample ''
+        {
+          interfaces = [
+            # eth0 is an upstream interface monitoring for IPv6 router advertisements.
+            {
+              name = "eth0";
+              monitor = true;
+            }
+            # eth1 is a downstream interface advertising IPv6 prefixes for SLAAC.
+            {
+              name = "eth1";
+              advertise = true;
+              prefix = [{ prefix = "::/64"; }];
+            }
+          ];
+          # Optionally enable Prometheus metrics.
+          debug = {
+            address = "localhost:9430";
+            prometheus = true;
+          };
+        }
+      '';
+      description = ''
+        Configuration for CoreRAD, see <link xlink:href="https://github.com/mdlayher/corerad/blob/master/internal/config/default.toml"/>
+        for supported values. Ignored if configFile is set.
+      '';
+    };
+
+    configFile = mkOption {
+      type = types.path;
+      example = literalExample "\"\${pkgs.corerad}/etc/corerad/corerad.toml\"";
+      description = "Path to CoreRAD TOML configuration file.";
+    };
+
+    package = mkOption {
+      default = pkgs.corerad;
+      defaultText = literalExample "pkgs.corerad";
+      type = types.package;
+      description = "CoreRAD package to use.";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    # Prefer the config file over settings if both are set.
+    services.corerad.configFile = mkDefault (writeTOML "corerad.toml" cfg.settings);
+
+    systemd.services.corerad = {
+      description = "CoreRAD IPv6 NDP RA daemon";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        LimitNPROC = 512;
+        LimitNOFILE = 1048576;
+        CapabilityBoundingSet = "CAP_NET_ADMIN CAP_NET_RAW";
+        AmbientCapabilities = "CAP_NET_ADMIN CAP_NET_RAW";
+        NoNewPrivileges = true;
+        DynamicUser = true;
+        ExecStart = "${getBin cfg.package}/bin/corerad -c=${cfg.configFile}";
+        Restart = "on-failure";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/coturn.nix b/nixpkgs/nixos/modules/services/networking/coturn.nix
new file mode 100644
index 000000000000..1bfbc307c59d
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/coturn.nix
@@ -0,0 +1,334 @@
+{ config, lib, pkgs, ... }:
+with lib;
+let
+  cfg = config.services.coturn;
+  pidfile = "/run/turnserver/turnserver.pid";
+  configFile = pkgs.writeText "turnserver.conf" ''
+listening-port=${toString cfg.listening-port}
+tls-listening-port=${toString cfg.tls-listening-port}
+alt-listening-port=${toString cfg.alt-listening-port}
+alt-tls-listening-port=${toString cfg.alt-tls-listening-port}
+${concatStringsSep "\n" (map (x: "listening-ip=${x}") cfg.listening-ips)}
+${concatStringsSep "\n" (map (x: "relay-ip=${x}") cfg.relay-ips)}
+min-port=${toString cfg.min-port}
+max-port=${toString cfg.max-port}
+${lib.optionalString cfg.lt-cred-mech "lt-cred-mech"}
+${lib.optionalString cfg.no-auth "no-auth"}
+${lib.optionalString cfg.use-auth-secret "use-auth-secret"}
+${lib.optionalString (cfg.static-auth-secret != null) ("static-auth-secret=${cfg.static-auth-secret}")}
+realm=${cfg.realm}
+${lib.optionalString cfg.no-udp "no-udp"}
+${lib.optionalString cfg.no-tcp "no-tcp"}
+${lib.optionalString cfg.no-tls "no-tls"}
+${lib.optionalString cfg.no-dtls "no-dtls"}
+${lib.optionalString cfg.no-udp-relay "no-udp-relay"}
+${lib.optionalString cfg.no-tcp-relay "no-tcp-relay"}
+${lib.optionalString (cfg.cert != null) "cert=${cfg.cert}"}
+${lib.optionalString (cfg.pkey != null) "pkey=${cfg.pkey}"}
+${lib.optionalString (cfg.dh-file != null) ("dh-file=${cfg.dh-file}")}
+no-stdout-log
+syslog
+pidfile=${pidfile}
+${lib.optionalString cfg.secure-stun "secure-stun"}
+${lib.optionalString cfg.no-cli "no-cli"}
+cli-ip=${cfg.cli-ip}
+cli-port=${toString cfg.cli-port}
+${lib.optionalString (cfg.cli-password != null) ("cli-password=${cfg.cli-password}")}
+${cfg.extraConfig}
+'';
+in {
+  options = {
+    services.coturn = {
+      enable = mkEnableOption "coturn TURN server";
+      listening-port = mkOption {
+        type = types.int;
+        default = 3478;
+        description = ''
+          TURN listener port for UDP and TCP.
+          Note: actually, TLS and DTLS sessions can connect to the
+          "plain" TCP and UDP port(s), too - if allowed by configuration.
+        '';
+      };
+      tls-listening-port = mkOption {
+        type = types.int;
+        default = 5349;
+        description = ''
+          TURN listener port for TLS.
+          Note: actually, "plain" TCP and UDP sessions can connect to the TLS and
+          DTLS port(s), too - if allowed by configuration. The TURN server
+          "automatically" recognizes the type of traffic. Actually, two listening
+          endpoints (the "plain" one and the "tls" one) are equivalent in terms of
+          functionality; but we keep both endpoints to satisfy the RFC 5766 specs.
+          For secure TCP connections, we currently support SSL version 3 and
+          TLS version 1.0, 1.1 and 1.2.
+          For secure UDP connections, we support DTLS version 1.
+        '';
+      };
+      alt-listening-port = mkOption {
+        type = types.int;
+        default = cfg.listening-port + 1;
+        defaultText = "listening-port + 1";
+        description = ''
+          Alternative listening port for UDP and TCP listeners;
+          default (or zero) value means "listening port plus one".
+          This is needed for RFC 5780 support
+          (STUN extension specs, NAT behavior discovery). The TURN Server
+          supports RFC 5780 only if it is started with more than one
+          listening IP address of the same family (IPv4 or IPv6).
+          RFC 5780 is supported only by UDP protocol, other protocols
+          are listening to that endpoint only for "symmetry".
+        '';
+      };
+      alt-tls-listening-port = mkOption {
+        type = types.int;
+        default = cfg.tls-listening-port + 1;
+        defaultText = "tls-listening-port + 1";
+        description = ''
+          Alternative listening port for TLS and DTLS protocols.
+        '';
+      };
+      listening-ips = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "203.0.113.42" "2001:DB8::42" ];
+        description = ''
+          Listener IP addresses of relay server.
+          If no IP(s) specified in the config file or in the command line options,
+          then all IPv4 and IPv6 system IPs will be used for listening.
+        '';
+      };
+      relay-ips = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "203.0.113.42" "2001:DB8::42" ];
+        description = ''
+          Relay address (the local IP address that will be used to relay the
+          packets to the peer).
+          Multiple relay addresses may be used.
+          The same IP(s) can be used as both listening IP(s) and relay IP(s).
+
+          If no relay IP(s) specified, then the turnserver will apply the default
+          policy: it will decide itself which relay addresses to be used, and it
+          will always be using the client socket IP address as the relay IP address
+          of the TURN session (if the requested relay address family is the same
+          as the family of the client socket).
+        '';
+      };
+      min-port = mkOption {
+        type = types.int;
+        default = 49152;
+        description = ''
+          Lower bound of UDP relay endpoints
+        '';
+      };
+      max-port = mkOption {
+        type = types.int;
+        default = 65535;
+        description = ''
+          Upper bound of UDP relay endpoints
+        '';
+      };
+      lt-cred-mech = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Use long-term credential mechanism.
+        '';
+      };
+      no-auth = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          This option is opposite to lt-cred-mech.
+          (TURN Server with no-auth option allows anonymous access).
+          If neither option is defined, and no users are defined,
+          then no-auth is default. If at least one user is defined,
+          in this file or in command line or in usersdb file, then
+          lt-cred-mech is default.
+        '';
+      };
+      use-auth-secret = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          TURN REST API flag.
+          Flag that sets a special authorization option that is based upon authentication secret.
+          This feature can be used with the long-term authentication mechanism, only.
+          This feature purpose is to support "TURN Server REST API", see
+          "TURN REST API" link in the project's page
+          https://github.com/coturn/coturn/
+
+          This option is used with timestamp:
+
+          usercombo -> "timestamp:userid"
+          turn user -> usercombo
+          turn password -> base64(hmac(secret key, usercombo))
+
+          This allows TURN credentials to be accounted for a specific user id.
+          If you don't have a suitable id, the timestamp alone can be used.
+          This option is just turning on secret-based authentication.
+          The actual value of the secret is defined either by option static-auth-secret,
+          or can be found in the turn_secret table in the database.
+        '';
+      };
+      static-auth-secret = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          'Static' authentication secret value (a string) for TURN REST API only.
+          If not set, then the turn server
+          will try to use the 'dynamic' value in turn_secret table
+          in user database (if present). The database-stored  value can be changed on-the-fly
+          by a separate program, so this is why that other mode is 'dynamic'.
+        '';
+      };
+      realm = mkOption {
+        type = types.str;
+        default = config.networking.hostName;
+        example = "example.com";
+        description = ''
+          The default realm to be used for the users when no explicit
+          origin/realm relationship was found in the database, or if the TURN
+          server is not using any database (just the commands-line settings
+          and the userdb file). Must be used with long-term credentials
+          mechanism or with TURN REST API.
+        '';
+      };
+      cert = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "/var/lib/acme/example.com/fullchain.pem";
+        description = ''
+          Certificate file in PEM format.
+        '';
+      };
+      pkey = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "/var/lib/acme/example.com/key.pem";
+        description = ''
+          Private key file in PEM format.
+        '';
+      };
+      dh-file = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          Use custom DH TLS key, stored in PEM format in the file.
+        '';
+      };
+      secure-stun = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Require authentication of the STUN Binding request.
+          By default, the clients are allowed anonymous access to the STUN Binding functionality.
+        '';
+      };
+      no-cli = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Turn OFF the CLI support.
+        '';
+      };
+      cli-ip = mkOption {
+        type = types.str;
+        default = "127.0.0.1";
+        description = ''
+          Local system IP address to be used for CLI server endpoint.
+        '';
+      };
+      cli-port = mkOption {
+        type = types.int;
+        default = 5766;
+        description = ''
+          CLI server port.
+        '';
+      };
+      cli-password = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          CLI access password.
+          For the security reasons, it is recommended to use the encrypted
+          for of the password (see the -P command in the turnadmin utility).
+        '';
+      };
+      no-udp = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Disable UDP client listener";
+      };
+      no-tcp = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Disable TCP client listener";
+      };
+      no-tls = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Disable TLS client listener";
+      };
+      no-dtls = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Disable DTLS client listener";
+      };
+      no-udp-relay = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Disable UDP relay endpoints";
+      };
+      no-tcp-relay = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Disable TCP relay endpoints";
+      };
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = "Additional configuration options";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.users.turnserver =
+      { uid = config.ids.uids.turnserver;
+        description = "coturn TURN server user";
+      };
+    users.groups.turnserver =
+      { gid = config.ids.gids.turnserver;
+        members = [ "turnserver" ];
+      };
+
+    systemd.services.coturn = {
+      description = "coturn TURN server";
+      after = [ "network-online.target" ];
+      wants = [ "network-online.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      unitConfig = {
+        Documentation = "man:coturn(1) man:turnadmin(1) man:turnserver(1)";
+      };
+
+      serviceConfig = {
+        Type = "simple";
+        ExecStart = "${pkgs.coturn}/bin/turnserver -c ${configFile}";
+        RuntimeDirectory = "turnserver";
+        User = "turnserver";
+        Group = "turnserver";
+        AmbientCapabilities =
+          mkIf (
+            cfg.listening-port < 1024 ||
+            cfg.alt-listening-port < 1024 ||
+            cfg.tls-listening-port < 1024 ||
+            cfg.alt-tls-listening-port < 1024 ||
+            cfg.min-port < 1024
+          ) "cap_net_bind_service";
+        Restart = "on-abort";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/dante.nix b/nixpkgs/nixos/modules/services/networking/dante.nix
new file mode 100644
index 000000000000..20d4faa1cdb1
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/dante.nix
@@ -0,0 +1,62 @@
+{ config, lib, pkgs, ... }:
+with lib;
+
+let
+  cfg = config.services.dante;
+  confFile = pkgs.writeText "dante-sockd.conf" ''
+    user.privileged: root
+    user.unprivileged: dante
+    logoutput: syslog
+
+    ${cfg.config}
+  '';
+in
+
+{
+  meta = {
+    maintainers = with maintainers; [ arobyn ];
+  };
+
+  options = {
+    services.dante = {
+      enable = mkEnableOption "Dante SOCKS proxy";
+
+      config = mkOption {
+        type        = types.lines;
+        description = ''
+          Contents of Dante's configuration file.
+          NOTE: user.privileged, user.unprivileged and logoutput are set by the service.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    assertions = [
+      { assertion   = cfg.config != "";
+        message     = "please provide Dante configuration file contents";
+      }
+    ];
+
+    users.users.dante = {
+      description   = "Dante SOCKS proxy daemon user";
+      isSystemUser  = true;
+      group         = "dante";
+    };
+    users.groups.dante = {};
+
+    systemd.services.dante = {
+      description   = "Dante SOCKS v4 and v5 compatible proxy server";
+      after         = [ "network-online.target" ];
+      wantedBy      = [ "multi-user.target" ];
+
+      serviceConfig = {
+        Type        = "simple";
+        ExecStart   = "${pkgs.dante}/bin/sockd -f ${confFile}";
+        ExecReload  = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+        # Can crash sometimes; see https://github.com/NixOS/nixpkgs/pull/39005#issuecomment-381828708
+        Restart     = "on-failure";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/ddclient.nix b/nixpkgs/nixos/modules/services/networking/ddclient.nix
new file mode 100644
index 000000000000..053efe712709
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/ddclient.nix
@@ -0,0 +1,209 @@
+{ config, pkgs, lib, ... }:
+
+let
+  cfg = config.services.ddclient;
+  boolToStr = bool: if bool then "yes" else "no";
+  dataDir = "/var/lib/ddclient";
+
+  configText = ''
+    # This file can be used as a template for configFile or is automatically generated by Nix options.
+    cache=${dataDir}/ddclient.cache
+    foreground=YES
+    use=${cfg.use}
+    login=${cfg.username}
+    password=${cfg.password}
+    protocol=${cfg.protocol}
+    ${lib.optionalString (cfg.script != "") "script=${cfg.script}"}
+    ${lib.optionalString (cfg.server != "") "server=${cfg.server}"}
+    ${lib.optionalString (cfg.zone != "")   "zone=${cfg.zone}"}
+    ssl=${boolToStr cfg.ssl}
+    wildcard=YES
+    quiet=${boolToStr cfg.quiet}
+    verbose=${boolToStr cfg.verbose}
+    ${cfg.extraConfig}
+    ${lib.concatStringsSep "," cfg.domains}
+  '';
+
+in
+
+with lib;
+
+{
+
+  imports = [
+    (mkChangedOptionModule [ "services" "ddclient" "domain" ] [ "services" "ddclient" "domains" ]
+      (config:
+        let value = getAttrFromPath [ "services" "ddclient" "domain" ] config;
+        in if value != "" then [ value ] else []))
+    (mkRemovedOptionModule [ "services" "ddclient" "homeDir" ] "")
+  ];
+
+  ###### interface
+
+  options = {
+
+    services.ddclient = with lib.types; {
+
+      enable = mkOption {
+        default = false;
+        type = bool;
+        description = ''
+          Whether to synchronise your machine's IP address with a dynamic DNS provider (e.g. dyndns.org).
+        '';
+      };
+
+      domains = mkOption {
+        default = [ "" ];
+        type = listOf str;
+        description = ''
+          Domain name(s) to synchronize.
+        '';
+      };
+
+      username = mkOption {
+        default = "";
+        type = str;
+        description = ''
+          User name.
+        '';
+      };
+
+      password = mkOption {
+        default = "";
+        type = str;
+        description = ''
+          Password. WARNING: The password becomes world readable in the Nix store.
+        '';
+      };
+
+      interval = mkOption {
+        default = "10min";
+        type = str;
+        description = ''
+          The interval at which to run the check and update.
+          See <command>man 7 systemd.time</command> for the format.
+        '';
+      };
+
+      configFile = mkOption {
+        default = "/etc/ddclient.conf";
+        type = path;
+        description = ''
+          Path to configuration file.
+          When set to the default '/etc/ddclient.conf' it will be populated with the various other options in this module. When it is changed (for example: '/root/nixos/secrets/ddclient.conf') the file read directly to configure ddclient. This is a source of impurity.
+          The purpose of this is to avoid placing secrets into the store.
+        '';
+        example = "/root/nixos/secrets/ddclient.conf";
+      };
+
+      protocol = mkOption {
+        default = "dyndns2";
+        type = str;
+        description = ''
+          Protocol to use with dynamic DNS provider (see https://sourceforge.net/p/ddclient/wiki/protocols).
+        '';
+      };
+
+      server = mkOption {
+        default = "";
+        type = str;
+        description = ''
+          Server address.
+        '';
+      };
+
+      ssl = mkOption {
+        default = true;
+        type = bool;
+        description = ''
+          Whether to use to use SSL/TLS to connect to dynamic DNS provider.
+        '';
+      };
+
+
+      quiet = mkOption {
+        default = false;
+        type = bool;
+        description = ''
+          Print no messages for unnecessary updates.
+        '';
+      };
+
+      script = mkOption {
+        default = "";
+        type = str;
+        description = ''
+          script as required by some providers.
+        '';
+      };
+
+      use = mkOption {
+        default = "web, web=checkip.dyndns.com/, web-skip='Current IP Address: '";
+        type = str;
+        description = ''
+          Method to determine the IP address to send to the dynamic DNS provider.
+        '';
+      };
+
+      verbose = mkOption {
+        default = true;
+        type = bool;
+        description = ''
+          Print verbose information.
+        '';
+      };
+
+      zone = mkOption {
+        default = "";
+        type = str;
+        description = ''
+          zone as required by some providers.
+        '';
+      };
+
+      extraConfig = mkOption {
+        default = "";
+        type = lines;
+        description = ''
+          Extra configuration. Contents will be added verbatim to the configuration file.
+        '';
+      };
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.ddclient.enable {
+    environment.etc."ddclient.conf" = {
+      enable = cfg.configFile == "/etc/ddclient.conf";
+      mode = "0600";
+      text = configText;
+    };
+
+    systemd.services.ddclient = {
+      description = "Dynamic DNS Client";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+      restartTriggers = [ config.environment.etc."ddclient.conf".source ];
+
+      serviceConfig = rec {
+        DynamicUser = true;
+        RuntimeDirectory = StateDirectory;
+        StateDirectory = builtins.baseNameOf dataDir;
+        Type = "oneshot";
+        ExecStartPre = "!${lib.getBin pkgs.coreutils}/bin/install -m666 ${cfg.configFile} /run/${RuntimeDirectory}/ddclient.conf";
+        ExecStart = "${lib.getBin pkgs.ddclient}/bin/ddclient -file /run/${RuntimeDirectory}/ddclient.conf";
+      };
+    };
+
+    systemd.timers.ddclient = {
+      description = "Run ddclient";
+      wantedBy = [ "timers.target" ];
+      timerConfig = {
+        OnBootSec = cfg.interval;
+        OnUnitInactiveSec = cfg.interval;
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/dhcpcd.nix b/nixpkgs/nixos/modules/services/networking/dhcpcd.nix
new file mode 100644
index 000000000000..0507b739d499
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/dhcpcd.nix
@@ -0,0 +1,225 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  dhcpcd = if !config.boot.isContainer then pkgs.dhcpcd else pkgs.dhcpcd.override { udev = null; };
+
+  cfg = config.networking.dhcpcd;
+
+  interfaces = attrValues config.networking.interfaces;
+
+  enableDHCP = config.networking.dhcpcd.enable &&
+        (config.networking.useDHCP || any (i: i.useDHCP == true) interfaces);
+
+  # Don't start dhcpcd on explicitly configured interfaces or on
+  # interfaces that are part of a bridge, bond or sit device.
+  ignoredInterfaces =
+    map (i: i.name) (filter (i: if i.useDHCP != null then !i.useDHCP else i.ipv4.addresses != [ ]) interfaces)
+    ++ mapAttrsToList (i: _: i) config.networking.sits
+    ++ concatLists (attrValues (mapAttrs (n: v: v.interfaces) config.networking.bridges))
+    ++ flatten (concatMap (i: attrNames (filterAttrs (_: config: config.type != "internal") i.interfaces)) (attrValues config.networking.vswitches))
+    ++ concatLists (attrValues (mapAttrs (n: v: v.interfaces) config.networking.bonds))
+    ++ config.networking.dhcpcd.denyInterfaces;
+
+  arrayAppendOrNull = a1: a2: if a1 == null && a2 == null then null
+    else if a1 == null then a2 else if a2 == null then a1
+      else a1 ++ a2;
+
+  # If dhcp is disabled but explicit interfaces are enabled,
+  # we need to provide dhcp just for those interfaces.
+  allowInterfaces = arrayAppendOrNull cfg.allowInterfaces
+    (if !config.networking.useDHCP && enableDHCP then
+      map (i: i.name) (filter (i: i.useDHCP == true) interfaces) else null);
+
+  # Config file adapted from the one that ships with dhcpcd.
+  dhcpcdConf = pkgs.writeText "dhcpcd.conf"
+    ''
+      # Inform the DHCP server of our hostname for DDNS.
+      hostname
+
+      # A list of options to request from the DHCP server.
+      option domain_name_servers, domain_name, domain_search, host_name
+      option classless_static_routes, ntp_servers, interface_mtu
+
+      # A ServerID is required by RFC2131.
+      # Commented out because of many non-compliant DHCP servers in the wild :(
+      #require dhcp_server_identifier
+
+      # A hook script is provided to lookup the hostname if not set by
+      # the DHCP server, but it should not be run by default.
+      nohook lookup-hostname
+
+      # Ignore peth* devices; on Xen, they're renamed physical
+      # Ethernet cards used for bridging.  Likewise for vif* and tap*
+      # (Xen) and virbr* and vnet* (libvirt).
+      denyinterfaces ${toString ignoredInterfaces} lo peth* vif* tap* tun* virbr* vnet* vboxnet* sit*
+
+      # Use the list of allowed interfaces if specified
+      ${optionalString (allowInterfaces != null) "allowinterfaces ${toString allowInterfaces}"}
+
+      # Immediately fork to background if specified, otherwise wait for IP address to be assigned
+      ${{
+        background = "background";
+        any = "waitip";
+        ipv4 = "waitip 4";
+        ipv6 = "waitip 6";
+        both = "waitip 4\nwaitip 6";
+        if-carrier-up = "";
+      }.${cfg.wait}}
+
+      ${cfg.extraConfig}
+    '';
+
+  exitHook = pkgs.writeText "dhcpcd.exit-hook"
+    ''
+      if [ "$reason" = BOUND -o "$reason" = REBOOT ]; then
+          # Restart ntpd.  We need to restart it to make sure that it
+          # will actually do something: if ntpd cannot resolve the
+          # server hostnames in its config file, then it will never do
+          # anything ever again ("couldn't resolve ..., giving up on
+          # it"), so we silently lose time synchronisation. This also
+          # applies to openntpd.
+          /run/current-system/systemd/bin/systemctl try-reload-or-restart ntpd.service openntpd.service chronyd.service || true
+      fi
+
+      ${cfg.runHook}
+    '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    networking.dhcpcd.enable = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Whether to enable dhcpcd for device configuration. This is mainly to
+        explicitly disable dhcpcd (for example when using networkd).
+      '';
+    };
+
+    networking.dhcpcd.persistent = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+          Whenever to leave interfaces configured on dhcpcd daemon
+          shutdown. Set to true if you have your root or store mounted
+          over the network or this machine accepts SSH connections
+          through DHCP interfaces and clients should be notified when
+          it shuts down.
+      '';
+    };
+
+    networking.dhcpcd.denyInterfaces = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      description = ''
+         Disable the DHCP client for any interface whose name matches
+         any of the shell glob patterns in this list. The purpose of
+         this option is to blacklist virtual interfaces such as those
+         created by Xen, libvirt, LXC, etc.
+      '';
+    };
+
+    networking.dhcpcd.allowInterfaces = mkOption {
+      type = types.nullOr (types.listOf types.str);
+      default = null;
+      description = ''
+         Enable the DHCP client for any interface whose name matches
+         any of the shell glob patterns in this list. Any interface not
+         explicitly matched by this pattern will be denied. This pattern only
+         applies when non-null.
+      '';
+    };
+
+    networking.dhcpcd.extraConfig = mkOption {
+      type = types.lines;
+      default = "";
+      description = ''
+         Literal string to append to the config file generated for dhcpcd.
+      '';
+    };
+
+    networking.dhcpcd.runHook = mkOption {
+      type = types.lines;
+      default = "";
+      example = "if [[ $reason =~ BOUND ]]; then echo $interface: Routers are $new_routers - were $old_routers; fi";
+      description = ''
+         Shell code that will be run after all other hooks. See
+         `man dhcpcd-run-hooks` for details on what is possible.
+      '';
+    };
+
+    networking.dhcpcd.wait = mkOption {
+      type = types.enum [ "background" "any" "ipv4" "ipv6" "both" "if-carrier-up" ];
+      default = "any";
+      description = ''
+        This option specifies when the dhcpcd service will fork to background.
+        If set to "background", dhcpcd will fork to background immediately.
+        If set to "ipv4" or "ipv6", dhcpcd will wait for the corresponding IP
+        address to be assigned. If set to "any", dhcpcd will wait for any type
+        (IPv4 or IPv6) to be assigned. If set to "both", dhcpcd will wait for
+        both an IPv4 and an IPv6 address before forking.
+        The option "if-carrier-up" is equivalent to "any" if either ethernet
+        is plugged nor WiFi is powered, and to "background" otherwise.
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf enableDHCP {
+
+    systemd.services.dhcpcd = let
+      cfgN = config.networking;
+      hasDefaultGatewaySet = (cfgN.defaultGateway != null && cfgN.defaultGateway.address != "")
+                          && (!cfgN.enableIPv6 || (cfgN.defaultGateway6 != null && cfgN.defaultGateway6.address != ""));
+    in
+      { description = "DHCP Client";
+
+        wantedBy = [ "multi-user.target" ] ++ optional (!hasDefaultGatewaySet) "network-online.target";
+        wants = [ "network.target" "systemd-udev-settle.service" ];
+        before = [ "network-online.target" ];
+        after = [ "systemd-udev-settle.service" ];
+
+        restartTriggers = [ exitHook ];
+
+        # Stopping dhcpcd during a reconfiguration is undesirable
+        # because it brings down the network interfaces configured by
+        # dhcpcd.  So do a "systemctl restart" instead.
+        stopIfChanged = false;
+
+        path = [ dhcpcd pkgs.nettools pkgs.openresolv ];
+
+        unitConfig.ConditionCapability = "CAP_NET_ADMIN";
+
+        serviceConfig =
+          { Type = "forking";
+            PIDFile = "/run/dhcpcd.pid";
+            ExecStart = "@${dhcpcd}/sbin/dhcpcd dhcpcd --quiet ${optionalString cfg.persistent "--persistent"} --config ${dhcpcdConf}";
+            ExecReload = "${dhcpcd}/sbin/dhcpcd --rebind";
+            Restart = "always";
+          };
+      };
+
+    environment.systemPackages = [ dhcpcd ];
+
+    environment.etc."dhcpcd.exit-hook".source = exitHook;
+
+    powerManagement.resumeCommands = mkIf config.systemd.services.dhcpcd.enable
+      ''
+        # Tell dhcpcd to rebind its interfaces if it's running.
+        /run/current-system/systemd/bin/systemctl reload dhcpcd.service
+      '';
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/dhcpd.nix b/nixpkgs/nixos/modules/services/networking/dhcpd.nix
new file mode 100644
index 000000000000..67f7d8118870
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/dhcpd.nix
@@ -0,0 +1,214 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg4 = config.services.dhcpd4;
+  cfg6 = config.services.dhcpd6;
+
+  writeConfig = cfg: pkgs.writeText "dhcpd.conf"
+    ''
+      default-lease-time 600;
+      max-lease-time 7200;
+      authoritative;
+      ddns-update-style interim;
+      log-facility local1; # see dhcpd.nix
+
+      ${cfg.extraConfig}
+
+      ${lib.concatMapStrings
+          (machine: ''
+            host ${machine.hostName} {
+              hardware ethernet ${machine.ethernetAddress};
+              fixed-address ${machine.ipAddress};
+            }
+          '')
+          cfg.machines
+      }
+    '';
+
+  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}
+        chown dhcpd:nogroup ${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";
+        };
+    };
+  };
+
+  machineOpts = { ... }: {
+
+    options = {
+
+      hostName = mkOption {
+        type = types.str;
+        example = "foo";
+        description = ''
+          Hostname which is assigned statically to the machine.
+        '';
+      };
+
+      ethernetAddress = mkOption {
+        type = types.str;
+        example = "00:16:76:9a:32:1d";
+        description = ''
+          MAC address of the machine.
+        '';
+      };
+
+      ipAddress = mkOption {
+        type = types.str;
+        example = "192.168.1.10";
+        description = ''
+          IP address of the machine.
+        '';
+      };
+
+    };
+  };
+
+  dhcpConfig = postfix: {
+
+    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.
+      '';
+    };
+
+    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.
+      '';
+    };
+
+    interfaces = mkOption {
+      type = types.listOf types.str;
+      default = ["eth0"];
+      description = ''
+        The interfaces on which the DHCP server should listen.
+      '';
+    };
+
+    machines = mkOption {
+      type = with types; listOf (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.
+      '';
+    };
+
+  };
+
+in
+
+{
+
+  imports = [
+    (mkRenamedOptionModule [ "services" "dhcpd" ] [ "services" "dhcpd4" ])
+  ];
+
+  ###### interface
+
+  options = {
+
+    services.dhcpd4 = dhcpConfig "4";
+    services.dhcpd6 = dhcpConfig "6";
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf (cfg4.enable || cfg6.enable) {
+
+    users = {
+      users.dhcpd = {
+        uid = config.ids.uids.dhcpd;
+        description = "DHCP daemon user";
+      };
+    };
+
+    systemd.services = dhcpdService "4" cfg4 // dhcpdService "6" cfg6;
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/dnscache.nix b/nixpkgs/nixos/modules/services/networking/dnscache.nix
new file mode 100644
index 000000000000..d06032daecc7
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/dnscache.nix
@@ -0,0 +1,108 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.dnscache;
+
+  dnscache-root = pkgs.runCommand "dnscache-root" { preferLocalBuild = true; } ''
+    mkdir -p $out/{servers,ip}
+
+    ${concatMapStrings (ip: ''
+      touch "$out/ip/"${lib.escapeShellArg ip}
+    '') cfg.clientIps}
+
+    ${concatStrings (mapAttrsToList (host: ips: ''
+      ${concatMapStrings (ip: ''
+        echo ${lib.escapeShellArg ip} >> "$out/servers/"${lib.escapeShellArg host}
+      '') ips}
+    '') cfg.domainServers)}
+
+    # if a list of root servers was not provided in config, copy it
+    # over. (this is also done by dnscache-conf, but we 'rm -rf
+    # /var/lib/dnscache/root' below & replace it wholesale with this,
+    # so we have to ensure servers/@ exists ourselves.)
+    if [ ! -e $out/servers/@ ]; then
+      # symlink does not work here, due chroot
+      cp ${pkgs.djbdns}/etc/dnsroots.global $out/servers/@;
+    fi
+  '';
+
+in {
+
+  ###### interface
+
+  options = {
+    services.dnscache = {
+
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = "Whether to run the dnscache caching dns server.";
+      };
+
+      ip = mkOption {
+        default = "0.0.0.0";
+        type = types.str;
+        description = "IP address on which to listen for connections.";
+      };
+
+      clientIps = mkOption {
+        default = [ "127.0.0.1" ];
+        type = types.listOf types.str;
+        description = "Client IP addresses (or prefixes) from which to accept connections.";
+        example = ["192.168" "172.23.75.82"];
+      };
+
+      domainServers = mkOption {
+        default = { };
+        type = types.attrsOf (types.listOf types.str);
+        description = ''
+          Table of {hostname: server} pairs to use as authoritative servers for hosts (and subhosts).
+          If entry for @ is not specified predefined list of root servers is used.
+        '';
+        example = literalExample ''
+          {
+            "@" = ["8.8.8.8" "8.8.4.4"];
+            "example.com" = ["192.168.100.100"];
+          }
+        '';
+      };
+
+      forwardOnly = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Whether to treat root servers (for @) as caching
+          servers, requesting addresses the same way a client does. This is
+          needed if you want to use e.g. Google DNS as your upstream DNS.
+        '';
+      };
+
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf config.services.dnscache.enable {
+    environment.systemPackages = [ pkgs.djbdns ];
+    users.users.dnscache.isSystemUser = true;
+
+    systemd.services.dnscache = {
+      description = "djbdns dnscache server";
+      wantedBy = [ "multi-user.target" ];
+      path = with pkgs; [ bash daemontools djbdns ];
+      preStart = ''
+        rm -rf /var/lib/dnscache
+        dnscache-conf dnscache dnscache /var/lib/dnscache ${config.services.dnscache.ip}
+        rm -rf /var/lib/dnscache/root
+        ln -sf ${dnscache-root} /var/lib/dnscache/root
+      '';
+      script = ''
+        cd /var/lib/dnscache/
+        ${optionalString cfg.forwardOnly "export FORWARDONLY=1"}
+        exec ./run
+      '';
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/dnscrypt-proxy2.nix b/nixpkgs/nixos/modules/services/networking/dnscrypt-proxy2.nix
new file mode 100644
index 000000000000..28691e838277
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/dnscrypt-proxy2.nix
@@ -0,0 +1,62 @@
+{ config, lib, pkgs, ... }: with lib;
+
+let
+  cfg = config.services.dnscrypt-proxy2;
+in
+
+{
+  options.services.dnscrypt-proxy2 = {
+    enable = mkEnableOption "dnscrypt-proxy2";
+
+    settings = mkOption {
+      description = ''
+        Attrset that is converted and passed as TOML config file.
+        For available params, see: <link xlink:href="https://github.com/DNSCrypt/dnscrypt-proxy/blob/master/dnscrypt-proxy/example-dnscrypt-proxy.toml"/>
+      '';
+      example = literalExample ''
+        {
+          sources.public-resolvers = {
+            urls = [ "https://download.dnscrypt.info/resolvers-list/v2/public-resolvers.md" ];
+            cache_file = "public-resolvers.md";
+            minisign_key = "RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3";
+            refresh_delay = 72;
+          };
+        }
+      '';
+      type = types.attrs;
+      default = {};
+    };
+
+    configFile = mkOption {
+      description = ''
+        Path to TOML config file. See: <link xlink:href="https://github.com/DNSCrypt/dnscrypt-proxy/blob/master/dnscrypt-proxy/example-dnscrypt-proxy.toml"/>
+        If this option is set, it will override any configuration done in options.services.dnscrypt-proxy2.settings.
+      '';
+      example = "/etc/dnscrypt-proxy/dnscrypt-proxy.toml";
+      type = types.path;
+      default = pkgs.runCommand "dnscrypt-proxy.toml" {
+        json = builtins.toJSON cfg.settings;
+        passAsFile = [ "json" ];
+      } ''
+        ${pkgs.remarshal}/bin/json2toml < $jsonPath > $out
+      '';
+      defaultText = literalExample "TOML file generated from services.dnscrypt-proxy2.settings";
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    networking.nameservers = lib.mkDefault [ "127.0.0.1" ];
+
+    systemd.services.dnscrypt-proxy2 = {
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        AmbientCapabilities = "CAP_NET_BIND_SERVICE";
+        DynamicUser = true;
+        ExecStart = "${pkgs.dnscrypt-proxy2}/bin/dnscrypt-proxy -config ${cfg.configFile}";
+        Restart = "always";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/dnscrypt-wrapper.nix b/nixpkgs/nixos/modules/services/networking/dnscrypt-wrapper.nix
new file mode 100644
index 000000000000..b9333cd19a2a
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/dnscrypt-wrapper.nix
@@ -0,0 +1,281 @@
+{ config, lib, pkgs, ... }:
+with lib;
+
+let
+  cfg     = config.services.dnscrypt-wrapper;
+  dataDir = "/var/lib/dnscrypt-wrapper";
+
+  mkPath = path: default:
+    if path != null
+      then toString path
+      else default;
+
+  publicKey = mkPath cfg.providerKey.public "${dataDir}/public.key";
+  secretKey = mkPath cfg.providerKey.secret "${dataDir}/secret.key";
+
+  daemonArgs = with cfg; [
+    "--listen-address=${address}:${toString port}"
+    "--resolver-address=${upstream.address}:${toString upstream.port}"
+    "--provider-name=${providerName}"
+    "--provider-publickey-file=${publicKey}"
+    "--provider-secretkey-file=${secretKey}"
+    "--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=${publicKey} \
+        --provider-secretkey-file=${secretKey} \
+        --cert-file-expire-days=${toString cfg.keys.expiration}
+    }
+
+    cd ${dataDir}
+
+    # generate provider keypair (first run only)
+    ${optionalString (cfg.providerKey.public == null || cfg.providerKey.secret == null) ''
+      if [ ! -f ${publicKey} ] || [ ! -f ${secretKey} ]; 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 | 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
+      echo "certificate soon to become invalid; backing up old cert"
+      mkdir -p oldkeys
+      mv -v ${cfg.providerName}.key oldkeys/${cfg.providerName}-$(date +%F-%T).key
+      mv -v ${cfg.providerName}.crt oldkeys/${cfg.providerName}-$(date +%F-%T).crt
+      systemctl restart dnscrypt-wrapper
+    fi
+  '';
+
+
+  # This is the fork of the original dnscrypt-proxy maintained by Dyne.org.
+  # dnscrypt-proxy2 doesn't provide the `--test` feature that is needed to
+  # correctly implement key rotation of dnscrypt-wrapper ephemeral keys.
+  dnscrypt-proxy1 = pkgs.callPackage
+    ({ stdenv, fetchFromGitHub, autoreconfHook
+    , pkgconfig, libsodium, ldns, openssl, systemd }:
+
+    stdenv.mkDerivation rec {
+      pname = "dnscrypt-proxy";
+      version = "2019-08-20";
+
+      src = fetchFromGitHub {
+        owner = "dyne";
+        repo = "dnscrypt-proxy";
+        rev = "07ac3825b5069adc28e2547c16b1d983a8ed8d80";
+        sha256 = "0c4mq741q4rpmdn09agwmxap32kf0vgfz7pkhcdc5h54chc3g3xy";
+      };
+
+      configureFlags = optional stdenv.isLinux "--with-systemd";
+
+      nativeBuildInputs = [ autoreconfHook pkgconfig ];
+
+      # <ldns/ldns.h> depends on <openssl/ssl.h>
+      buildInputs = [ libsodium openssl.dev ldns ] ++ optional stdenv.isLinux systemd;
+
+      postInstall = ''
+        # Previous versions required libtool files to load plugins; they are
+        # now strictly optional.
+        rm $out/lib/dnscrypt-proxy/*.la
+      '';
+
+      meta = {
+        description = "A tool for securing communications between a client and a DNS resolver";
+        homepage = "https://github.com/dyne/dnscrypt-proxy";
+        license = licenses.isc;
+        maintainers = with maintainers; [ rnhmjoj ];
+        platforms = platforms.linux;
+      };
+    }) { };
+
+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>.
+      '';
+    };
+
+    providerKey.public = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      example = "/etc/secrets/public.key";
+      description = ''
+        The filepath to the provider public key. If not given a new
+        provider key pair will be generated on the first run.
+      '';
+    };
+
+    providerKey.secret = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      example = "/etc/secrets/secret.key";
+      description = ''
+        The filepath to the provider secret key. If not given a new
+        provider key pair will be generated on the first run.
+      '';
+    };
+
+    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;
+      isSystemUser = true;
+    };
+    users.groups.dnscrypt-wrapper = { };
+
+    security.polkit.extraConfig = ''
+      // Allow dnscrypt-wrapper user to restart dnscrypt-wrapper.service
+      polkit.addRule(function(action, subject) {
+          if (action.id == "org.freedesktop.systemd1.manage-units" &&
+              action.lookup("unit") == "dnscrypt-wrapper.service" &&
+              subject.user == "dnscrypt-wrapper") {
+              return polkit.Result.YES;
+          }
+        });
+    '';
+
+    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-proxy1 gawk ];
+      script = rotateKeys;
+      serviceConfig.User = "dnscrypt-wrapper";
+    };
+
+
+    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;
+      };
+    };
+
+    assertions = with cfg; [
+      { assertion = (providerKey.public == null && providerKey.secret == null) ||
+                    (providerKey.secret != null && providerKey.public != null);
+        message = "The secret and public provider key must be set together.";
+      }
+    ];
+
+  };
+
+  meta.maintainers = with lib.maintainers; [ rnhmjoj ];
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/dnsdist.nix b/nixpkgs/nixos/modules/services/networking/dnsdist.nix
new file mode 100644
index 000000000000..8249da69bc1a
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/dnsdist.nix
@@ -0,0 +1,60 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.dnsdist;
+  configFile = pkgs.writeText "dndist.conf" ''
+    setLocal('${cfg.listenAddress}:${toString cfg.listenPort}')
+    ${cfg.extraConfig}
+    '';
+in {
+  options = {
+    services.dnsdist = {
+      enable = mkEnableOption "dnsdist domain name server";
+
+      listenAddress = mkOption {
+        type = types.str;
+        description = "Listen IP Address";
+        default = "0.0.0.0";
+      };
+      listenPort = mkOption {
+        type = types.int;
+        description = "Listen port";
+        default = 53;
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = ''
+        '';
+        description = ''
+          Extra lines to be added verbatim to dnsdist.conf.
+        '';
+      };
+    };
+  };
+
+  config = mkIf config.services.dnsdist.enable {
+    systemd.services.dnsdist = {
+      description = "dnsdist load balancer";
+      wantedBy = [ "multi-user.target" ];
+      after = ["network.target"];
+
+      serviceConfig = {
+        Restart="on-failure";
+        RestartSec="1";
+        DynamicUser = true;
+        StartLimitInterval="0";
+        PrivateDevices=true;
+        AmbientCapabilities="CAP_NET_BIND_SERVICE";
+        CapabilityBoundingSet="CAP_NET_BIND_SERVICE";
+        ExecStart = "${pkgs.dnsdist}/bin/dnsdist --supervised --disable-syslog --config ${configFile}";
+        ProtectHome=true;
+        RestrictAddressFamilies="AF_UNIX AF_INET AF_INET6";
+        LimitNOFILE="16384";
+        TasksMax="8192";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/dnsmasq.nix b/nixpkgs/nixos/modules/services/networking/dnsmasq.nix
new file mode 100644
index 000000000000..377d7bc57058
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/dnsmasq.nix
@@ -0,0 +1,128 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.dnsmasq;
+  dnsmasq = pkgs.dnsmasq;
+  stateDir = "/var/lib/dnsmasq";
+
+  dnsmasqConf = pkgs.writeText "dnsmasq.conf" ''
+    dhcp-leasefile=${stateDir}/dnsmasq.leases
+    ${optionalString cfg.resolveLocalQueries ''
+      conf-file=/etc/dnsmasq-conf.conf
+      resolv-file=/etc/dnsmasq-resolv.conf
+    ''}
+    ${flip concatMapStrings cfg.servers (server: ''
+      server=${server}
+    '')}
+    ${cfg.extraConfig}
+  '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.dnsmasq = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to run dnsmasq.
+        '';
+      };
+
+      resolveLocalQueries = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether dnsmasq should resolve local queries (i.e. add 127.0.0.1 to
+          /etc/resolv.conf).
+        '';
+      };
+
+      servers = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "8.8.8.8" "8.8.4.4" ];
+        description = ''
+          The DNS servers which dnsmasq should query.
+        '';
+      };
+
+      alwaysKeepRunning = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          If enabled, systemd will always respawn dnsmasq even if shut down manually. The default, disabled, will only restart it on error.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Extra configuration directives that should be added to
+          <literal>dnsmasq.conf</literal>.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    networking.nameservers =
+      optional cfg.resolveLocalQueries "127.0.0.1";
+
+    services.dbus.packages = [ dnsmasq ];
+
+    users.users.dnsmasq = {
+      uid = config.ids.uids.dnsmasq;
+      description = "Dnsmasq daemon user";
+    };
+
+    networking.resolvconf = mkIf cfg.resolveLocalQueries {
+      useLocalResolver = mkDefault true;
+
+      extraConfig = ''
+        dnsmasq_conf=/etc/dnsmasq-conf.conf
+        dnsmasq_resolv=/etc/dnsmasq-resolv.conf
+      '';
+    };
+
+    systemd.services.dnsmasq = {
+        description = "Dnsmasq Daemon";
+        after = [ "network.target" "systemd-resolved.service" ];
+        wantedBy = [ "multi-user.target" ];
+        path = [ dnsmasq ];
+        preStart = ''
+          mkdir -m 755 -p ${stateDir}
+          touch ${stateDir}/dnsmasq.leases
+          chown -R dnsmasq ${stateDir}
+          touch /etc/dnsmasq-{conf,resolv}.conf
+          dnsmasq --test
+        '';
+        serviceConfig = {
+          Type = "dbus";
+          BusName = "uk.org.thekelleys.dnsmasq";
+          ExecStart = "${dnsmasq}/bin/dnsmasq -k --enable-dbus --user=dnsmasq -C ${dnsmasqConf}";
+          ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+          PrivateTmp = true;
+          ProtectSystem = true;
+          ProtectHome = true;
+          Restart = if cfg.alwaysKeepRunning then "always" else "on-failure";
+        };
+        restartTriggers = [ config.environment.etc.hosts.source ];
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/ejabberd.nix b/nixpkgs/nixos/modules/services/networking/ejabberd.nix
new file mode 100644
index 000000000000..a5af25b983b9
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/ejabberd.nix
@@ -0,0 +1,157 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.ejabberd;
+
+  ctlcfg = pkgs.writeText "ejabberdctl.cfg" ''
+    ERL_EPMD_ADDRESS=127.0.0.1
+    ${cfg.ctlConfig}
+  '';
+
+  ectl = ''${cfg.package}/bin/ejabberdctl ${optionalString (cfg.configFile != null) "--config ${cfg.configFile}"} --ctl-config "${ctlcfg}" --spool "${cfg.spoolDir}" --logs "${cfg.logsDir}"'';
+
+  dumps = lib.escapeShellArgs cfg.loadDumps;
+
+in {
+
+  ###### interface
+
+  options = {
+
+    services.ejabberd = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to enable ejabberd server";
+      };
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.ejabberd;
+        defaultText = "pkgs.ejabberd";
+        description = "ejabberd server package to use";
+      };
+
+      user = mkOption {
+        type = types.str;
+        default = "ejabberd";
+        description = "User under which ejabberd is ran";
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "ejabberd";
+        description = "Group under which ejabberd is ran";
+      };
+
+      spoolDir = mkOption {
+        type = types.path;
+        default = "/var/lib/ejabberd";
+        description = "Location of the spooldir of ejabberd";
+      };
+
+      logsDir = mkOption {
+        type = types.path;
+        default = "/var/log/ejabberd";
+        description = "Location of the logfile directory of ejabberd";
+      };
+
+      configFile = mkOption {
+        type = types.nullOr types.path;
+        description = "Configuration file for ejabberd in YAML format";
+        default = null;
+      };
+
+      ctlConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = "Configuration of ejabberdctl";
+      };
+
+      loadDumps = mkOption {
+        type = types.listOf types.path;
+        default = [];
+        description = "Configuration dumps that should be loaded on the first startup";
+        example = literalExample "[ ./myejabberd.dump ]";
+      };
+
+      imagemagick = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Add ImageMagick to server's path; allows for image thumbnailing";
+      };
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ cfg.package ];
+
+    users.users = optionalAttrs (cfg.user == "ejabberd") {
+      ejabberd = {
+        group = cfg.group;
+        home = cfg.spoolDir;
+        createHome = true;
+        uid = config.ids.uids.ejabberd;
+      };
+    };
+
+    users.groups = optionalAttrs (cfg.group == "ejabberd") {
+      ejabberd.gid = config.ids.gids.ejabberd;
+    };
+
+    systemd.services.ejabberd = {
+      description = "ejabberd server";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+      path = [ pkgs.findutils pkgs.coreutils ] ++ lib.optional cfg.imagemagick pkgs.imagemagick;
+
+      serviceConfig = {
+        User = cfg.user;
+        Group = cfg.group;
+        ExecStart = "${ectl} foreground";
+        ExecStop = "${ectl} stop";
+        ExecReload = "${ectl} reload_config";
+      };
+
+      preStart = ''
+        if [ -z "$(ls -A '${cfg.spoolDir}')" ]; then
+          touch "${cfg.spoolDir}/.firstRun"
+        fi
+      '';
+
+      postStart = ''
+        while ! ${ectl} status >/dev/null 2>&1; do
+          if ! kill -0 "$MAINPID"; then exit 1; fi
+          sleep 0.1
+        done
+
+        if [ -e "${cfg.spoolDir}/.firstRun" ]; then
+          rm "${cfg.spoolDir}/.firstRun"
+          for src in ${dumps}; do
+            find "$src" -type f | while read dump; do
+              echo "Loading configuration dump at $dump"
+              ${ectl} load "$dump"
+            done
+          done
+        fi
+      '';
+    };
+
+    systemd.tmpfiles.rules = [
+      "d '${cfg.logsDir}' 0750 ${cfg.user} ${cfg.group} -"
+      "d '${cfg.spoolDir}' 0700 ${cfg.user} ${cfg.group} -"
+    ];
+
+    security.pam.services.ejabberd = {};
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/epmd.nix b/nixpkgs/nixos/modules/services/networking/epmd.nix
new file mode 100644
index 000000000000..692b75e4f086
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/epmd.nix
@@ -0,0 +1,56 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.epmd;
+
+in
+
+{
+  ###### interface
+  options.services.epmd = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether to enable socket activation for Erlang Port Mapper Daemon (epmd),
+        which acts as a name server on all hosts involved in distributed
+        Erlang computations.
+      '';
+    };
+    package = mkOption {
+      type = types.package;
+      default = pkgs.erlang;
+      description = ''
+        The Erlang package to use to get epmd binary. That way you can re-use
+        an Erlang runtime that is already installed for other purposes.
+      '';
+    };
+  };
+
+  ###### implementation
+  config = mkIf cfg.enable {
+    systemd.sockets.epmd = rec {
+      description = "Erlang Port Mapper Daemon Activation Socket";
+      wantedBy = [ "sockets.target" ];
+      before = wantedBy;
+      socketConfig = {
+        ListenStream = "4369";
+        Accept = "false";
+      };
+    };
+
+    systemd.services.epmd = {
+      description = "Erlang Port Mapper Daemon";
+      after = [ "network.target" ];
+      requires = [ "epmd.socket" ];
+
+      serviceConfig = {
+        DynamicUser = true;
+        ExecStart = "${cfg.package}/bin/epmd -systemd";
+        Type = "notify";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/ergo.nix b/nixpkgs/nixos/modules/services/networking/ergo.nix
new file mode 100644
index 000000000000..c52de30dc361
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/ergo.nix
@@ -0,0 +1,141 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.services.ergo;
+
+  inherit (lib) mkEnableOption mkIf mkOption optionalString types;
+
+  configFile = pkgs.writeText "ergo.conf" (''
+ergo {
+  directory = "${cfg.dataDir}"
+  node {
+    mining = false
+  }
+  wallet.secretStorage.secretDir = "${cfg.dataDir}/wallet/keystore"
+}
+
+scorex {
+  network {
+    bindAddress = "${cfg.listen.ip}:${toString cfg.listen.port}"
+  }
+'' + optionalString (cfg.api.keyHash != null) ''
+ restApi {
+    apiKeyHash = "${cfg.api.keyHash}"
+    bindAddress = "${cfg.api.listen.ip}:${toString cfg.api.listen.port}"
+ }
+'' + ''
+}
+'');
+
+in {
+
+  options = {
+
+    services.ergo = {
+      enable = mkEnableOption "Ergo service";
+
+      dataDir = mkOption {
+        type = types.path;
+        default = "/var/lib/ergo";
+        description = "The data directory for the Ergo node.";
+      };
+
+      listen = {
+        ip = mkOption {
+          type = types.str;
+          default = "0.0.0.0";
+          description = "IP address on which the Ergo node should listen.";
+        };
+
+        port = mkOption {
+          type = types.port;
+          default = 9006;
+          description = "Listen port for the Ergo node.";
+        };
+      };
+
+      api = {
+       keyHash = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "324dcf027dd4a30a932c441f365a25e86b173defa4b8e58948253471b81b72cf";
+        description = "Hex-encoded Blake2b256 hash of an API key as a 64-chars long Base16 string.";
+       };
+
+       listen = {
+        ip = mkOption {
+          type = types.str;
+          default = "0.0.0.0";
+          description = "IP address that the Ergo node API should listen on if <option>api.keyHash</option> is defined.";
+          };
+
+        port = mkOption {
+          type = types.port;
+          default = 9052;
+          description = "Listen port for the API endpoint if <option>api.keyHash</option> is defined.";
+        };
+       };
+      };
+
+      testnet = mkOption {
+         type = types.bool;
+         default = false;
+         description = "Connect to testnet network instead of the default mainnet.";
+      };
+
+      user = mkOption {
+        type = types.str;
+        default = "ergo";
+        description = "The user as which to run the Ergo node.";
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = cfg.user;
+        description = "The group as which to run the Ergo node.";
+      };
+
+      openFirewall = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Open ports in the firewall for the Ergo node as well as the API.";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    systemd.tmpfiles.rules = [
+      "d '${cfg.dataDir}' 0770 '${cfg.user}' '${cfg.group}' - -"
+    ];
+
+    systemd.services.ergo = {
+      description = "ergo server";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network-online.target" ];
+      serviceConfig = {
+        User = cfg.user;
+        Group = cfg.group;
+        ExecStart = ''${pkgs.ergo}/bin/ergo \
+                      ${optionalString (!cfg.testnet)
+                      "--mainnet"} \
+                      -c ${configFile}'';
+      };
+    };
+
+    networking.firewall = mkIf cfg.openFirewall {
+      allowedTCPPorts = [ cfg.listen.port ] ++ [ cfg.api.listen.port ];
+    };
+
+    users.users.${cfg.user} = {
+      name = cfg.user;
+      group = cfg.group;
+      description = "Ergo daemon user";
+      home = cfg.dataDir;
+      isSystemUser = true;
+    };
+
+    users.groups.${cfg.group} = {};
+
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/eternal-terminal.nix b/nixpkgs/nixos/modules/services/networking/eternal-terminal.nix
new file mode 100644
index 000000000000..a2e5b30dc0f0
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/eternal-terminal.nix
@@ -0,0 +1,95 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.eternal-terminal;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.eternal-terminal = {
+
+      enable = mkEnableOption "Eternal Terminal server";
+
+      port = mkOption {
+        default = 2022;
+        type = types.int;
+        description = ''
+          The port the server should listen on. Will use the server's default (2022) if not specified.
+
+          Make sure to open this port in the firewall if necessary.
+        '';
+      };
+
+      verbosity = mkOption {
+        default = 0;
+        type = types.enum (lib.range 0 9);
+        description = ''
+          The verbosity level (0-9).
+        '';
+      };
+
+      silent = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          If enabled, disables all logging.
+        '';
+      };
+
+      logSize = mkOption {
+        default = 20971520;
+        type = types.int;
+        description = ''
+          The maximum log size.
+        '';
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    # We need to ensure the et package is fully installed because
+    # the (remote) et client runs the `etterminal` binary when it
+    # connects.
+    environment.systemPackages = [ pkgs.eternal-terminal ];
+
+    systemd.services = {
+      eternal-terminal = {
+        description = "Eternal Terminal server.";
+        wantedBy = [ "multi-user.target" ];
+        after = [ "syslog.target" "network.target" ];
+        serviceConfig = {
+          Type = "forking";
+          ExecStart = "${pkgs.eternal-terminal}/bin/etserver --daemon --cfgfile=${pkgs.writeText "et.cfg" ''
+            ; et.cfg : Config file for Eternal Terminal
+            ;
+
+            [Networking]
+            port = ${toString cfg.port}
+
+            [Debug]
+            verbose = ${toString cfg.verbosity}
+            silent = ${if cfg.silent then "1" else "0"}
+            logsize = ${toString cfg.logSize}
+          ''}";
+          Restart = "on-failure";
+          KillMode = "process";
+        };
+      };
+    };
+  };
+
+  meta = {
+    maintainers = with lib.maintainers; [ pingiun ];
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/fakeroute.nix b/nixpkgs/nixos/modules/services/networking/fakeroute.nix
new file mode 100644
index 000000000000..7916ad4098a7
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/fakeroute.nix
@@ -0,0 +1,65 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.fakeroute;
+  routeConf = pkgs.writeText "route.conf" (concatStringsSep "\n" cfg.route);
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.fakeroute = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable the fakeroute service.
+        '';
+      };
+
+      route = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [
+          "216.102.187.130"
+          "4.0.1.122"
+          "198.116.142.34"
+          "63.199.8.242"
+        ];
+        description = ''
+         Fake route that will appear after the real
+         one to any host running a traceroute.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    systemd.services.fakeroute = {
+      description = "Fakeroute Daemon";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        Type = "forking";
+        User = "root";
+        ExecStart = "${pkgs.fakeroute}/bin/fakeroute -f ${routeConf}";
+      };
+    };
+
+  };
+
+  meta.maintainers = with lib.maintainers; [ rnhmjoj ];
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/ferm.nix b/nixpkgs/nixos/modules/services/networking/ferm.nix
new file mode 100644
index 000000000000..07338ccf4d9c
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/ferm.nix
@@ -0,0 +1,63 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.ferm;
+
+  configFile = pkgs.stdenv.mkDerivation {
+    name = "ferm.conf";
+    text = cfg.config;
+    preferLocalBuild = true;
+    buildCommand = ''
+      echo -n "$text" > $out
+      ${cfg.package}/bin/ferm --noexec $out
+    '';
+  };
+in {
+  options = {
+    services.ferm = {
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Whether to enable Ferm Firewall.
+          *Warning*: Enabling this service WILL disable the existing NixOS
+          firewall! Default firewall rules provided by packages are not
+          considered at the moment.
+        '';
+      };
+      config = mkOption {
+        description = "Verbatim ferm.conf configuration.";
+        default = "";
+        defaultText = "empty firewall, allows any traffic";
+        type = types.lines;
+      };
+      package = mkOption {
+        description = "The ferm package.";
+        type = types.package;
+        default = pkgs.ferm;
+        defaultText = "pkgs.ferm";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.firewall.enable = false;
+    systemd.services.ferm = {
+      description = "Ferm Firewall";
+      after = [ "ipset.target" ];
+      before = [ "network-pre.target" ];
+      wants = [ "network-pre.target" ];
+      wantedBy = [ "multi-user.target" ];
+      reloadIfChanged = true;
+      serviceConfig = {
+        Type="oneshot";
+        RemainAfterExit = "yes";
+        ExecStart = "${cfg.package}/bin/ferm ${configFile}";
+        ExecReload = "${cfg.package}/bin/ferm ${configFile}";
+        ExecStop = "${cfg.package}/bin/ferm -F ${configFile}";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/firefox/sync-server.nix b/nixpkgs/nixos/modules/services/networking/firefox/sync-server.nix
new file mode 100644
index 000000000000..6842aa735617
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/firefox/sync-server.nix
@@ -0,0 +1,183 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.firefox.syncserver;
+
+  defaultDbLocation = "/var/db/firefox-sync-server/firefox-sync-server.db";
+  defaultSqlUri = "sqlite:///${defaultDbLocation}";
+
+  syncServerIni = pkgs.writeText "syncserver.ini" ''
+    [DEFAULT]
+    overrides = ${cfg.privateConfig}
+
+    [server:main]
+    use = egg:gunicorn
+    host = ${cfg.listen.address}
+    port = ${toString cfg.listen.port}
+
+    [app:main]
+    use = egg:syncserver
+
+    [syncserver]
+    public_url = ${cfg.publicUrl}
+    ${optionalString (cfg.sqlUri != "") "sqluri = ${cfg.sqlUri}"}
+    allow_new_users = ${boolToString cfg.allowNewUsers}
+
+    [browserid]
+    backend = tokenserver.verifiers.LocalVerifier
+    audiences = ${removeSuffix "/" cfg.publicUrl}
+  '';
+
+  user = "syncserver";
+  group = "syncserver";
+in
+
+{
+  meta.maintainers = with lib.maintainers; [ nadrieril ];
+
+  options = {
+    services.firefox.syncserver = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable a Firefox Sync Server, this give the opportunity to
+          Firefox users to store all synchronized data on their own server. To use this
+          server, Firefox users should visit the <option>about:config</option>, and
+          replicate the following change
+
+          <screen>
+          services.sync.tokenServerURI: http://localhost:5000/token/1.0/sync/1.5
+          </screen>
+
+          where <option>http://localhost:5000/</option> corresponds to the
+          public url of the server.
+        '';
+      };
+
+      listen.address = mkOption {
+        type = types.str;
+        default = "127.0.0.1";
+        example = "0.0.0.0";
+        description = ''
+          Address on which the sync server listen to.
+        '';
+      };
+
+      listen.port = mkOption {
+        type = types.int;
+        default = 5000;
+        description = ''
+          Port on which the sync server listen to.
+        '';
+      };
+
+      publicUrl = mkOption {
+        type = types.str;
+        default = "http://localhost:5000/";
+        example = "http://sync.example.com/";
+        description = ''
+          Public URL with which firefox users can use to access the sync server.
+        '';
+      };
+
+      allowNewUsers = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to allow new-user signups on the server. Only request by
+          existing accounts will be honored.
+        '';
+      };
+
+      sqlUri = mkOption {
+        type = types.str;
+        default = defaultSqlUri;
+        example = "postgresql://scott:tiger@localhost/test";
+        description = ''
+          The location of the database. This URL is composed of
+          <option>dialect[+driver]://user:password@host/dbname[?key=value..]</option>,
+          where <option>dialect</option> is a database name such as
+          <option>mysql</option>, <option>oracle</option>, <option>postgresql</option>,
+          etc., and <option>driver</option> the name of a DBAPI, such as
+          <option>psycopg2</option>, <option>pyodbc</option>, <option>cx_oracle</option>,
+          etc. The <link
+          xlink:href="http://docs.sqlalchemy.org/en/rel_0_9/core/engines.html#database-urls">
+          SQLAlchemy documentation</link> provides more examples and describe the syntax of
+          the expected URL.
+        '';
+      };
+
+      privateConfig = mkOption {
+        type = types.str;
+        default = "/etc/firefox/syncserver-secret.ini";
+        description = ''
+          The private config file is used to extend the generated config with confidential
+          information, such as the <option>syncserver.sqlUri</option> setting if it contains a
+          password, and the <option>syncserver.secret</option> setting is used by the server to
+          generate cryptographically-signed authentication tokens.
+
+          If this file does not exists, then it is created with a generated
+          <option>syncserver.secret</option> settings.
+       '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    systemd.services.syncserver = {
+      after = [ "network.target" ];
+      description = "Firefox Sync Server";
+      wantedBy = [ "multi-user.target" ];
+      path = [
+        pkgs.coreutils
+        (pkgs.python.withPackages (ps: [ pkgs.syncserver ps.gunicorn ]))
+      ];
+
+      serviceConfig = {
+        User = user;
+        Group = group;
+        PermissionsStartOnly = true;
+      };
+
+      preStart = ''
+        if ! test -e ${cfg.privateConfig}; then
+          mkdir -p $(dirname ${cfg.privateConfig})
+          echo  > ${cfg.privateConfig} '[syncserver]'
+          chmod 600 ${cfg.privateConfig}
+          echo >> ${cfg.privateConfig} "secret = $(head -c 20 /dev/urandom | sha1sum | tr -d ' -')"
+        fi
+        chmod 600 ${cfg.privateConfig}
+        chmod 755 $(dirname ${cfg.privateConfig})
+        chown ${user}:${group} ${cfg.privateConfig}
+
+      '' + optionalString (cfg.sqlUri == defaultSqlUri) ''
+        if ! test -e $(dirname ${defaultDbLocation}); then
+          mkdir -m 700 -p $(dirname ${defaultDbLocation})
+          chown ${user}:${group} $(dirname ${defaultDbLocation})
+        fi
+
+        # Move previous database file if it exists
+        oldDb="/var/db/firefox-sync-server.db"
+        if test -f $oldDb; then
+          mv $oldDb ${defaultDbLocation}
+          chown ${user}:${group} ${defaultDbLocation}
+        fi
+      '';
+
+      script = ''
+        gunicorn --paste ${syncServerIni}
+      '';
+    };
+
+    users.users.${user} = {
+      inherit group;
+      isSystemUser = true;
+    };
+
+    users.groups.${group} = {};
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/fireqos.nix b/nixpkgs/nixos/modules/services/networking/fireqos.nix
new file mode 100644
index 000000000000..0b34f0b6b8b4
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/fireqos.nix
@@ -0,0 +1,52 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.fireqos;
+  fireqosConfig = pkgs.writeText "fireqos.conf" "${cfg.config}";
+in {
+  options.services.fireqos = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        If enabled, FireQOS will be launched with the specified
+        configuration given in `config`.
+      '';
+    };
+
+    config = mkOption {
+      type = types.str;
+      default = "";
+      example = ''
+        interface wlp3s0 world-in input rate 10mbit ethernet
+          class web commit 50kbit
+            match tcp ports 80,443
+
+        interface wlp3s0 world-out input rate 10mbit ethernet
+          class web commit 50kbit
+            match tcp ports 80,443
+      '';
+      description = ''
+        The FireQOS configuration goes here.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.fireqos = {
+      description = "FireQOS";
+      after = [ "network.target" ];
+      serviceConfig = {
+        Type = "oneshot";
+        RemainAfterExit = true;
+        ExecStart = "${pkgs.firehol}/bin/fireqos start ${fireqosConfig}";
+        ExecStop = [
+          "${pkgs.firehol}/bin/fireqos stop"
+          "${pkgs.firehol}/bin/fireqos clear_all_qos"
+        ];
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/firewall.nix b/nixpkgs/nixos/modules/services/networking/firewall.nix
new file mode 100644
index 000000000000..cdc3a172ea70
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/firewall.nix
@@ -0,0 +1,585 @@
+/* This module enables a simple firewall.
+
+   The firewall can be customised in arbitrary ways by setting
+   ‘networking.firewall.extraCommands’.  For modularity, the firewall
+   uses several chains:
+
+   - ‘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
+     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).
+
+*/
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.networking.firewall;
+
+  inherit (config.boot.kernelPackages) kernel;
+
+  kernelHasRPFilter = ((kernel.config.isEnabled or (x: false)) "IP_NF_MATCH_RPFILTER") || (kernel.features.netfilterRPFilter or false);
+
+  helpers = import ./helpers.nix { inherit config lib; };
+
+  writeShScript = name: text: let dir = pkgs.writeScriptBin name ''
+    #! ${pkgs.runtimeShell} -e
+    ${text}
+  ''; in "${dir}/bin/${name}";
+
+  defaultInterface = { default = mapAttrs (name: value: cfg.${name}) commonOptions; };
+  allInterfaces = defaultInterface // cfg.interfaces;
+
+  startScript = writeShScript "firewall-start" ''
+    ${helpers}
+
+    # Flush the old firewall rules.  !!! Ideally, updating the
+    # 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; do
+      ip46tables -F "$chain" 2> /dev/null || true
+      ip46tables -X "$chain" 2> /dev/null || true
+    done
+
+
+    # The "nixos-fw-accept" chain just accepts packets.
+    ip46tables -N nixos-fw-accept
+    ip46tables -A nixos-fw-accept -j ACCEPT
+
+
+    # The "nixos-fw-refuse" chain rejects or drops packets.
+    ip46tables -N nixos-fw-refuse
+
+    ${if cfg.rejectPackets then ''
+      # Send a reset for existing TCP connections that we've
+      # somehow forgotten about.  Send ICMP "port unreachable"
+      # for everything else.
+      ip46tables -A nixos-fw-refuse -p tcp ! --syn -j REJECT --reject-with tcp-reset
+      ip46tables -A nixos-fw-refuse -j REJECT
+    '' else ''
+      ip46tables -A nixos-fw-refuse -j DROP
+    ''}
+
+
+    # The "nixos-fw-log-refuse" chain performs logging, then
+    # jumps to the "nixos-fw-refuse" chain.
+    ip46tables -N nixos-fw-log-refuse
+
+    ${optionalString cfg.logRefusedConnections ''
+      ip46tables -A nixos-fw-log-refuse -p tcp --syn -j LOG --log-level info --log-prefix "refused connection: "
+    ''}
+    ${optionalString (cfg.logRefusedPackets && !cfg.logRefusedUnicastsOnly) ''
+      ip46tables -A nixos-fw-log-refuse -m pkttype --pkt-type broadcast \
+        -j LOG --log-level info --log-prefix "refused broadcast: "
+      ip46tables -A nixos-fw-log-refuse -m pkttype --pkt-type multicast \
+        -j LOG --log-level info --log-prefix "refused multicast: "
+    ''}
+    ip46tables -A nixos-fw-log-refuse -m pkttype ! --pkt-type unicast -j nixos-fw-refuse
+    ${optionalString cfg.logRefusedPackets ''
+      ip46tables -A nixos-fw-log-refuse \
+        -j LOG --log-level info --log-prefix "refused packet: "
+    ''}
+    ip46tables -A nixos-fw-log-refuse -j nixos-fw-refuse
+
+
+    # The "nixos-fw" chain does the actual work.
+    ip46tables -N nixos-fw
+
+    # Clean up rpfilter rules
+    ip46tables -t raw -D PREROUTING -j nixos-fw-rpfilter 2> /dev/null || true
+    ip46tables -t raw -F nixos-fw-rpfilter 2> /dev/null || true
+    ip46tables -t raw -X nixos-fw-rpfilter 2> /dev/null || true
+
+    ${optionalString (kernelHasRPFilter && (cfg.checkReversePath != false)) ''
+      # Perform a reverse-path test to refuse spoofers
+      # For now, we just drop, as the raw table doesn't have a log-refuse yet
+      ip46tables -t raw -N nixos-fw-rpfilter 2> /dev/null || true
+      ip46tables -t raw -A nixos-fw-rpfilter -m rpfilter --validmark ${optionalString (cfg.checkReversePath == "loose") "--loose"} -j RETURN
+
+      # Allows this host to act as a DHCP4 client without first having to use APIPA
+      iptables -t raw -A nixos-fw-rpfilter -p udp --sport 67 --dport 68 -j RETURN
+
+      # Allows this host to act as a DHCPv4 server
+      iptables -t raw -A nixos-fw-rpfilter -s 0.0.0.0 -d 255.255.255.255 -p udp --sport 68 --dport 67 -j RETURN
+
+      ${optionalString cfg.logReversePathDrops ''
+        ip46tables -t raw -A nixos-fw-rpfilter -j LOG --log-level info --log-prefix "rpfilter drop: "
+      ''}
+      ip46tables -t raw -A nixos-fw-rpfilter -j DROP
+
+      ip46tables -t raw -A PREROUTING -j nixos-fw-rpfilter
+    ''}
+
+    # Accept all traffic on the trusted interfaces.
+    ${flip concatMapStrings cfg.trustedInterfaces (iface: ''
+      ip46tables -A nixos-fw -i ${iface} -j nixos-fw-accept
+    '')}
+
+    # Accept packets from established or related connections.
+    ip46tables -A nixos-fw -m conntrack --ctstate ESTABLISHED,RELATED -j nixos-fw-accept
+
+    # Accept connections to the allowed TCP ports.
+    ${concatStrings (mapAttrsToList (iface: cfg:
+      concatMapStrings (port:
+        ''
+          ip46tables -A nixos-fw -p tcp --dport ${toString port} -j nixos-fw-accept ${optionalString (iface != "default") "-i ${iface}"}
+        ''
+      ) cfg.allowedTCPPorts
+    ) allInterfaces)}
+
+    # Accept connections to the allowed TCP port ranges.
+    ${concatStrings (mapAttrsToList (iface: cfg:
+      concatMapStrings (rangeAttr:
+        let range = toString rangeAttr.from + ":" + toString rangeAttr.to; in
+        ''
+          ip46tables -A nixos-fw -p tcp --dport ${range} -j nixos-fw-accept ${optionalString (iface != "default") "-i ${iface}"}
+        ''
+      ) cfg.allowedTCPPortRanges
+    ) allInterfaces)}
+
+    # Accept packets on the allowed UDP ports.
+    ${concatStrings (mapAttrsToList (iface: cfg:
+      concatMapStrings (port:
+        ''
+          ip46tables -A nixos-fw -p udp --dport ${toString port} -j nixos-fw-accept ${optionalString (iface != "default") "-i ${iface}"}
+        ''
+      ) cfg.allowedUDPPorts
+    ) allInterfaces)}
+
+    # Accept packets on the allowed UDP port ranges.
+    ${concatStrings (mapAttrsToList (iface: cfg:
+      concatMapStrings (rangeAttr:
+        let range = toString rangeAttr.from + ":" + toString rangeAttr.to; in
+        ''
+          ip46tables -A nixos-fw -p udp --dport ${range} -j nixos-fw-accept ${optionalString (iface != "default") "-i ${iface}"}
+        ''
+      ) cfg.allowedUDPPortRanges
+    ) allInterfaces)}
+
+    # Accept IPv4 multicast.  Not a big security risk since
+    # probably nobody is listening anyway.
+    #iptables -A nixos-fw -d 224.0.0.0/4 -j nixos-fw-accept
+
+    # Optionally respond to ICMPv4 pings.
+    ${optionalString cfg.allowPing ''
+      iptables -w -A nixos-fw -p icmp --icmp-type echo-request ${optionalString (cfg.pingLimit != null)
+        "-m limit ${cfg.pingLimit} "
+      }-j nixos-fw-accept
+    ''}
+
+    ${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}
+
+    # Reject/drop everything else.
+    ip46tables -A nixos-fw -j nixos-fw-log-refuse
+
+
+    # Enable the firewall.
+    ip46tables -A INPUT -j nixos-fw
+  '';
+
+  stopScript = writeShScript "firewall-stop" ''
+    ${helpers}
+
+    # Clean up in case reload fails
+    ip46tables -D INPUT -j nixos-drop 2>/dev/null || true
+
+    # Clean up after added ruleset
+    ip46tables -D INPUT -j nixos-fw 2>/dev/null || true
+
+    ${optionalString (kernelHasRPFilter && (cfg.checkReversePath != false)) ''
+      ip46tables -t raw -D PREROUTING -j nixos-fw-rpfilter 2>/dev/null || true
+    ''}
+
+    ${cfg.extraStopCommands}
+  '';
+
+  reloadScript = writeShScript "firewall-reload" ''
+    ${helpers}
+
+    # Create a unique drop rule
+    ip46tables -D INPUT -j nixos-drop 2>/dev/null || true
+    ip46tables -F nixos-drop 2>/dev/null || true
+    ip46tables -X nixos-drop 2>/dev/null || true
+    ip46tables -N nixos-drop
+    ip46tables -A nixos-drop -j DROP
+
+    # Don't allow traffic to leak out until the script has completed
+    ip46tables -A INPUT -j nixos-drop
+
+    ${cfg.extraStopCommands}
+
+    if ${startScript}; then
+      ip46tables -D INPUT -j nixos-drop 2>/dev/null || true
+    else
+      echo "Failed to reload firewall... Stopping"
+      ${stopScript}
+      exit 1
+    fi
+  '';
+
+  canonicalizePortList =
+    ports: lib.unique (builtins.sort builtins.lessThan ports);
+
+  commonOptions = {
+    allowedTCPPorts = mkOption {
+      type = types.listOf types.port;
+      default = [ ];
+      apply = canonicalizePortList;
+      example = [ 22 80 ];
+      description =
+        ''
+          List of TCP ports on which incoming connections are
+          accepted.
+        '';
+    };
+
+    allowedTCPPortRanges = mkOption {
+      type = types.listOf (types.attrsOf types.port);
+      default = [ ];
+      example = [ { from = 8999; to = 9003; } ];
+      description =
+        ''
+          A range of TCP ports on which incoming connections are
+          accepted.
+        '';
+    };
+
+    allowedUDPPorts = mkOption {
+      type = types.listOf types.port;
+      default = [ ];
+      apply = canonicalizePortList;
+      example = [ 53 ];
+      description =
+        ''
+          List of open UDP ports.
+        '';
+    };
+
+    allowedUDPPortRanges = mkOption {
+      type = types.listOf (types.attrsOf types.port);
+      default = [ ];
+      example = [ { from = 60000; to = 61000; } ];
+      description =
+        ''
+          Range of open UDP ports.
+        '';
+    };
+  };
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    networking.firewall = {
+      enable = mkOption {
+        type = types.bool;
+        default = true;
+        description =
+          ''
+            Whether to enable the firewall.  This is a simple stateful
+            firewall that blocks connection attempts to unauthorised TCP
+            or UDP ports on this machine.  It does not affect packet
+            forwarding.
+          '';
+      };
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.iptables;
+        defaultText = "pkgs.iptables";
+        example = literalExample "pkgs.iptables-nftables-compat";
+        description =
+          ''
+            The iptables package to use for running the firewall service."
+          '';
+      };
+
+      logRefusedConnections = mkOption {
+        type = types.bool;
+        default = true;
+        description =
+          ''
+            Whether to log rejected or dropped incoming connections.
+          '';
+      };
+
+      logRefusedPackets = mkOption {
+        type = types.bool;
+        default = false;
+        description =
+          ''
+            Whether to log all rejected or dropped incoming packets.
+            This tends to give a lot of log messages, so it's mostly
+            useful for debugging.
+          '';
+      };
+
+      logRefusedUnicastsOnly = mkOption {
+        type = types.bool;
+        default = true;
+        description =
+          ''
+            If <option>networking.firewall.logRefusedPackets</option>
+            and this option are enabled, then only log packets
+            specifically directed at this machine, i.e., not broadcasts
+            or multicasts.
+          '';
+      };
+
+      rejectPackets = mkOption {
+        type = types.bool;
+        default = false;
+        description =
+          ''
+            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 (or a TCP RST packet in
+            case of an existing connection).  Rejecting packets makes
+            port scanning somewhat easier.
+          '';
+      };
+
+      trustedInterfaces = mkOption {
+        type = types.listOf types.str;
+        default = [ ];
+        example = [ "enp0s2" ];
+        description =
+          ''
+            Traffic coming in from these interfaces will be accepted
+            unconditionally.  Traffic from the loopback (lo) interface
+            will always be accepted.
+          '';
+      };
+
+      allowPing = mkOption {
+        type = types.bool;
+        default = true;
+        description =
+          ''
+            Whether to respond to incoming ICMPv4 echo requests
+            ("pings").  ICMPv6 pings are always allowed because the
+            larger address space of IPv6 makes network scanning much
+            less effective.
+          '';
+      };
+
+      pingLimit = mkOption {
+        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"
+          '';
+      };
+
+      checkReversePath = mkOption {
+        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.
+
+            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+)
+          '';
+      };
+
+      logReversePathDrops = mkOption {
+        type = types.bool;
+        default = false;
+        description =
+          ''
+            Logs dropped packets failing the reverse path filter test if
+            the option networking.firewall.checkReversePath is enabled.
+          '';
+      };
+
+      connectionTrackingModules = mkOption {
+        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.
+            The complete list of possible values is given in the example.
+
+            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 unless you
+            know what you are doing. Connection tracking is disabled
+            by default.
+
+            Loading of helpers is recommended to be done through the
+            CT target.  More info:
+            https://home.regit.org/netfilter-en/secure-use-of-helpers/
+          '';
+      };
+
+      autoLoadConntrackHelpers = mkOption {
+        type = types.bool;
+        default = false;
+        description =
+          ''
+            Whether to auto-load connection-tracking helpers.
+            See the description at networking.firewall.connectionTrackingModules
+
+            (needs kernel 3.5+)
+          '';
+      };
+
+      extraCommands = mkOption {
+        type = types.lines;
+        default = "";
+        example = "iptables -A INPUT -p icmp -j ACCEPT";
+        description =
+          ''
+            Additional shell commands executed as part of the firewall
+            initialisation script.  These are executed just before the
+            final "reject" firewall rule is added, so they can be used
+            to allow packets that would otherwise be refused.
+          '';
+      };
+
+      extraPackages = mkOption {
+        type = types.listOf types.package;
+        default = [ ];
+        example = literalExample "[ pkgs.ipset ]";
+        description =
+          ''
+            Additional packages to be included in the environment of the system
+            as well as the path of networking.firewall.extraCommands.
+          '';
+      };
+
+      extraStopCommands = mkOption {
+        type = types.lines;
+        default = "";
+        example = "iptables -P INPUT ACCEPT";
+        description =
+          ''
+            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.
+          '';
+      };
+
+      interfaces = mkOption {
+        default = { };
+        type = with types; attrsOf (submodule [ { options = commonOptions; } ]);
+        description =
+          ''
+            Interface-specific open ports.
+          '';
+      };
+    } // commonOptions;
+
+  };
+
+
+  ###### implementation
+
+  # FIXME: Maybe if `enable' is false, the firewall should still be
+  # built but not started by default?
+  config = mkIf cfg.enable {
+
+    networking.firewall.trustedInterfaces = [ "lo" ];
+
+    environment.systemPackages = [ cfg.package ] ++ cfg.extraPackages;
+
+    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 = [
+      # This is approximately "checkReversePath -> kernelHasRPFilter",
+      # but the checkReversePath option can include non-boolean
+      # values.
+      { assertion = cfg.checkReversePath == false || kernelHasRPFilter;
+        message = "This kernel does not support rpfilter"; }
+    ];
+
+    systemd.services.firewall = {
+      description = "Firewall";
+      wantedBy = [ "sysinit.target" ];
+      wants = [ "network-pre.target" ];
+      before = [ "network-pre.target" ];
+      after = [ "systemd-modules-load.service" ];
+
+      path = [ cfg.package ] ++ 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
+      # better have all necessary modules already loaded.
+      unitConfig.ConditionCapability = "CAP_NET_ADMIN";
+      unitConfig.DefaultDependencies = false;
+
+      reloadIfChanged = true;
+
+      serviceConfig = {
+        Type = "oneshot";
+        RemainAfterExit = true;
+        ExecStart = "@${startScript} firewall-start";
+        ExecReload = "@${reloadScript} firewall-reload";
+        ExecStop = "@${stopScript} firewall-stop";
+      };
+    };
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/flannel.nix b/nixpkgs/nixos/modules/services/networking/flannel.nix
new file mode 100644
index 000000000000..4c040112d28d
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/flannel.nix
@@ -0,0 +1,191 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.flannel;
+
+  networkConfig = filterAttrs (n: v: v != null) {
+    Network = cfg.network;
+    SubnetLen = cfg.subnetLen;
+    SubnetMin = cfg.subnetMin;
+    SubnetMax = cfg.subnetMax;
+    Backend = cfg.backend;
+  };
+in {
+  options.services.flannel = {
+    enable = mkEnableOption "flannel";
+
+    package = mkOption {
+      description = "Package to use for flannel";
+      type = types.package;
+      default = pkgs.flannel;
+      defaultText = "pkgs.flannel";
+    };
+
+    publicIp = mkOption {
+      description = ''
+        IP accessible by other nodes for inter-host communication.
+        Defaults to the IP of the interface being used for communication.
+      '';
+      type = types.nullOr types.str;
+      default = null;
+    };
+
+    iface = mkOption {
+      description = ''
+        Interface to use (IP or name) for inter-host communication.
+        Defaults to the interface for the default route on the machine.
+      '';
+      type = types.nullOr types.str;
+      default = null;
+    };
+
+    etcd = {
+      endpoints = mkOption {
+        description = "Etcd endpoints";
+        type = types.listOf types.str;
+        default = ["http://127.0.0.1:2379"];
+      };
+
+      prefix = mkOption {
+        description = "Etcd key prefix";
+        type = types.str;
+        default = "/coreos.com/network";
+      };
+
+      caFile = mkOption {
+        description = "Etcd certificate authority file";
+        type = types.nullOr types.path;
+        default = null;
+      };
+
+      certFile = mkOption {
+        description = "Etcd cert file";
+        type = types.nullOr types.path;
+        default = null;
+      };
+
+      keyFile = mkOption {
+        description = "Etcd key file";
+        type = types.nullOr types.path;
+        default = null;
+      };
+    };
+
+    kubeconfig = mkOption {
+      description = ''
+        Path to kubeconfig to use for storing flannel config using the
+        Kubernetes API
+      '';
+      type = types.nullOr types.path;
+      default = null;
+    };
+
+    network = mkOption {
+      description = " IPv4 network in CIDR format to use for the entire flannel network.";
+      type = types.str;
+    };
+
+    nodeName = mkOption {
+      description = ''
+        Needed when running with Kubernetes as backend as this cannot be auto-detected";
+      '';
+      type = types.nullOr types.str;
+      default = with config.networking; (hostName + optionalString (domain != null) ".${domain}");
+      example = "node1.example.com";
+    };
+
+    storageBackend = mkOption {
+      description = "Determines where flannel stores its configuration at runtime";
+      type = types.enum ["etcd" "kubernetes"];
+      default = "etcd";
+    };
+
+    subnetLen = mkOption {
+      description = ''
+        The size of the subnet allocated to each host. Defaults to 24 (i.e. /24)
+        unless the Network was configured to be smaller than a /24 in which case
+        it is one less than the network.
+      '';
+      type = types.int;
+      default = 24;
+    };
+
+    subnetMin = mkOption {
+      description = ''
+        The beginning of IP range which the subnet allocation should start with.
+        Defaults to the first subnet of Network.
+      '';
+      type = types.nullOr types.str;
+      default = null;
+    };
+
+    subnetMax = mkOption {
+      description = ''
+        The end of IP range which the subnet allocation should start with.
+        Defaults to the last subnet of Network.
+      '';
+      type = types.nullOr types.str;
+      default = null;
+    };
+
+    backend = mkOption {
+      description = "Type of backend to use and specific configurations for that backend.";
+      type = types.attrs;
+      default = {
+        Type = "vxlan";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.flannel = {
+      description = "Flannel Service";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+      environment = {
+        FLANNELD_PUBLIC_IP = cfg.publicIp;
+        FLANNELD_IFACE = cfg.iface;
+      } // optionalAttrs (cfg.storageBackend == "etcd") {
+        FLANNELD_ETCD_ENDPOINTS = concatStringsSep "," cfg.etcd.endpoints;
+        FLANNELD_ETCD_KEYFILE = cfg.etcd.keyFile;
+        FLANNELD_ETCD_CERTFILE = cfg.etcd.certFile;
+        FLANNELD_ETCD_CAFILE = cfg.etcd.caFile;
+        ETCDCTL_CERT_FILE = cfg.etcd.certFile;
+        ETCDCTL_KEY_FILE = cfg.etcd.keyFile;
+        ETCDCTL_CA_FILE = cfg.etcd.caFile;
+        ETCDCTL_PEERS = concatStringsSep "," cfg.etcd.endpoints;
+      } // optionalAttrs (cfg.storageBackend == "kubernetes") {
+        FLANNELD_KUBE_SUBNET_MGR = "true";
+        FLANNELD_KUBECONFIG_FILE = cfg.kubeconfig;
+        NODE_NAME = cfg.nodeName;
+      };
+      path = [ pkgs.iptables ];
+      preStart = ''
+        mkdir -p /run/flannel
+        touch /run/flannel/docker
+      '' + optionalString (cfg.storageBackend == "etcd") ''
+        echo "setting network configuration"
+        until ${pkgs.etcdctl}/bin/etcdctl set /coreos.com/network/config '${builtins.toJSON networkConfig}'
+        do
+          echo "setting network configuration, retry"
+          sleep 1
+        done
+      '';
+      serviceConfig = {
+        ExecStart = "${cfg.package}/bin/flannel";
+        Restart = "always";
+        RestartSec = "10s";
+      };
+    };
+
+    services.etcd.enable = mkDefault (cfg.storageBackend == "etcd" && cfg.etcd.endpoints == ["http://127.0.0.1:2379"]);
+
+    # for some reason, flannel doesn't let you configure this path
+    # see: https://github.com/coreos/flannel/blob/master/Documentation/configuration.md#configuration
+    environment.etc."kube-flannel/net-conf.json" = mkIf (cfg.storageBackend == "kubernetes") {
+      source = pkgs.writeText "net-conf.json" (builtins.toJSON networkConfig);
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/flashpolicyd.nix b/nixpkgs/nixos/modules/services/networking/flashpolicyd.nix
new file mode 100644
index 000000000000..7f25083307c7
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/flashpolicyd.nix
@@ -0,0 +1,85 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.flashpolicyd;
+
+  flashpolicyd = pkgs.stdenv.mkDerivation {
+    name = "flashpolicyd-0.6";
+
+    src = pkgs.fetchurl {
+      name = "flashpolicyd_v0.6.zip";
+      url = "https://download.adobe.com/pub/adobe/devnet/flashplayer/articles/socket_policy_files/flashpolicyd_v0.6.zip";
+      sha256 = "16zk237233npwfq1m4ksy4g5lzy1z9fp95w7pz0cdlpmv0fv9sm3";
+    };
+
+    buildInputs = [ pkgs.unzip pkgs.perl ];
+
+    installPhase = "mkdir $out; cp -pr * $out/; chmod +x $out/*/*.pl";
+  };
+
+  flashpolicydWrapper = pkgs.writeScriptBin "flashpolicyd"
+    ''
+      #! ${pkgs.runtimeShell}
+      exec ${flashpolicyd}/Perl_xinetd/in.flashpolicyd.pl \
+        --file=${pkgs.writeText "flashpolixy.xml" cfg.policy} \
+        2> /dev/null
+    '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.flashpolicyd = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description =
+          ''
+            Whether to enable the Flash Policy server.  This is
+            necessary if you want Flash applications to make
+            connections to your server.
+          '';
+      };
+
+      policy = mkOption {
+        default =
+          ''
+            <?xml version="1.0"?>
+            <!DOCTYPE cross-domain-policy SYSTEM "/xml/dtds/cross-domain-policy.dtd">
+            <cross-domain-policy>
+              <site-control permitted-cross-domain-policies="master-only"/>
+              <allow-access-from domain="*" to-ports="*" />
+            </cross-domain-policy>
+          '';
+        description = "The policy to be served.  The default is to allow connections from any domain to any port.";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    services.xinetd.enable = true;
+
+    services.xinetd.services = singleton
+      { name = "flashpolicy";
+        port = 843;
+        unlisted = true;
+        server = "${flashpolicydWrapper}/bin/flashpolicyd";
+      };
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/freenet.nix b/nixpkgs/nixos/modules/services/networking/freenet.nix
new file mode 100644
index 000000000000..3da3ab0c7df4
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/freenet.nix
@@ -0,0 +1,64 @@
+# NixOS module for Freenet daemon
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.freenet;
+  varDir = "/var/lib/freenet";
+
+in
+
+{
+
+  ### configuration
+
+  options = {
+
+    services.freenet = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Enable the Freenet daemon";
+      };
+
+      nice = mkOption {
+        type = types.int;
+        default = 10;
+        description = "Set the nice level for the Freenet daemon";
+      };
+
+    };
+
+  };
+
+  ### implementation
+
+  config = mkIf cfg.enable {
+
+    systemd.services.freenet = {
+      description = "Freenet daemon";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig.ExecStart = "${pkgs.freenet}/bin/freenet";
+      serviceConfig.User = "freenet";
+      serviceConfig.UMask = "0007";
+      serviceConfig.WorkingDirectory = varDir;
+      serviceConfig.Nice = cfg.nice;
+    };
+
+    users.users.freenet = {
+      group = "freenet";
+      description = "Freenet daemon user";
+      home = varDir;
+      createHome = true;
+      uid = config.ids.uids.freenet;
+    };
+
+    users.groups.freenet.gid = config.ids.gids.freenet;
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/freeradius.nix b/nixpkgs/nixos/modules/services/networking/freeradius.nix
new file mode 100644
index 000000000000..f3fdd576b65c
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/freeradius.nix
@@ -0,0 +1,84 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.freeradius;
+
+  freeradiusService = cfg:
+  {
+    description = "FreeRadius server";
+    wantedBy = ["multi-user.target"];
+    after = ["network.target"];
+    wants = ["network.target"];
+    preStart = ''
+      ${pkgs.freeradius}/bin/radiusd -C -d ${cfg.configDir} -l stdout
+    '';
+
+    serviceConfig = {
+        ExecStart = "${pkgs.freeradius}/bin/radiusd -f -d ${cfg.configDir} -l stdout" +
+                    optionalString cfg.debug " -xx";
+        ExecReload = [
+          "${pkgs.freeradius}/bin/radiusd -C -d ${cfg.configDir} -l stdout"
+          "${pkgs.coreutils}/bin/kill -HUP $MAINPID"
+        ];
+        User = "radius";
+        ProtectSystem = "full";
+        ProtectHome = "on";
+        Restart = "on-failure";
+        RestartSec = 2;
+    };
+  };
+
+  freeradiusConfig = {
+    enable = mkEnableOption "the freeradius server";
+
+    configDir = mkOption {
+      type = types.path;
+      default = "/etc/raddb";
+      description = ''
+        The path of the freeradius server configuration directory.
+      '';
+    };
+
+    debug = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether to enable debug logging for freeradius (-xx
+        option). This should not be left on, since it includes
+        sensitive data such as passwords in the logs.
+      '';
+    };
+
+  };
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+    services.freeradius = freeradiusConfig;
+  };
+
+
+  ###### implementation
+
+  config = mkIf (cfg.enable) {
+
+    users = {
+      users.radius = {
+        /*uid = config.ids.uids.radius;*/
+        description = "Radius daemon user";
+      };
+    };
+
+    systemd.services.freeradius = freeradiusService cfg;
+    warnings = optional cfg.debug "Freeradius debug logging is enabled. This will log passwords in plaintext to the journal!";
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/gale.nix b/nixpkgs/nixos/modules/services/networking/gale.nix
new file mode 100644
index 000000000000..cb954fd836bc
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/gale.nix
@@ -0,0 +1,181 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.gale;
+  # we convert the path to a string to avoid it being copied to the nix store,
+  # otherwise users could read the private key as all files in the store are
+  # world-readable
+  keyPath = toString cfg.keyPath;
+  # ...but we refer to the pubkey file using a path so that we can ensure the
+  # config gets rebuilt if the public key changes (we can assume the private key
+  # will never change without the public key having changed)
+  gpubFile = cfg.keyPath + "/${cfg.domain}.gpub";
+  home = "/var/lib/gale";
+  keysPrepared = cfg.keyPath != null && lib.pathExists cfg.keyPath;
+in
+{
+  options = {
+    services.gale = {
+      enable = mkEnableOption "the Gale messaging daemon";
+
+      user = mkOption {
+        default = "gale";
+        type = types.str;
+        description = "Username for the Gale daemon.";
+      };
+
+      group = mkOption {
+        default = "gale";
+        type = types.str;
+        description = "Group name for the Gale daemon.";
+      };
+
+      setuidWrapper = mkOption {
+        default = null;
+        description = "Configuration for the Gale gksign setuid wrapper.";
+      };
+
+      domain = mkOption {
+        default = "";
+        type = types.str;
+        description = "Domain name for the Gale system.";
+      };
+
+      keyPath = mkOption {
+        default = null;
+        type = types.nullOr types.path;
+        description = ''
+          Directory containing the key pair for this Gale domain.  The expected
+          filename will be taken from the domain option with ".gpri" and ".gpub"
+          appended.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Additional text to be added to <filename>/etc/gale/conf</filename>.
+        '';
+      };
+    };
+  };
+
+  config = mkMerge [
+    (mkIf cfg.enable {
+       assertions = [{
+         assertion = cfg.domain != "";
+         message = "A domain must be set for Gale.";
+       }];
+
+       warnings = mkIf (!keysPrepared) [
+         "You must run gale-install in order to generate a domain key."
+       ];
+
+       system.activationScripts.gale = mkIf cfg.enable (
+         stringAfter [ "users" "groups" ] ''
+           chmod 755 ${home}
+           mkdir -m 0777 -p ${home}/auth/cache
+           mkdir -m 1777 -p ${home}/auth/local # GALE_DOMAIN.gpub
+           mkdir -m 0700 -p ${home}/auth/private # ROOT.gpub
+           mkdir -m 0755 -p ${home}/auth/trusted # ROOT
+           mkdir -m 0700 -p ${home}/.gale
+           mkdir -m 0700 -p ${home}/.gale/auth
+           mkdir -m 0700 -p ${home}/.gale/auth/private # GALE_DOMAIN.gpri
+
+           ln -sf ${pkgs.gale}/etc/gale/auth/trusted/ROOT "${home}/auth/trusted/ROOT"
+           chown ${cfg.user}:${cfg.group} ${home} ${home}/auth ${home}/auth/*
+           chown ${cfg.user}:${cfg.group} ${home}/.gale ${home}/.gale/auth ${home}/.gale/auth/private
+         ''
+       );
+
+       environment = {
+         etc = {
+           "gale/auth".source = home + "/auth"; # symlink /var/lib/gale/auth
+           "gale/conf".text = ''
+             GALE_USER ${cfg.user}
+             GALE_DOMAIN ${cfg.domain}
+             ${cfg.extraConfig}
+           '';
+         };
+
+         systemPackages = [ pkgs.gale ];
+       };
+
+       users.users.${cfg.user} = {
+         description = "Gale daemon";
+         uid = config.ids.uids.gale;
+         group = cfg.group;
+         home = home;
+         createHome = true;
+       };
+
+       users.groups = [{
+         name = cfg.group;
+         gid = config.ids.gids.gale;
+       }];
+    })
+    (mkIf (cfg.enable && keysPrepared) {
+       assertions = [
+         {
+           assertion = cfg.keyPath != null
+                    && lib.pathExists (cfg.keyPath + "/${cfg.domain}.gpub");
+           message = "Couldn't find a Gale public key for ${cfg.domain}.";
+         }
+         {
+           assertion = cfg.keyPath != null
+                    && lib.pathExists (cfg.keyPath + "/${cfg.domain}.gpri");
+           message = "Couldn't find a Gale private key for ${cfg.domain}.";
+         }
+       ];
+
+       services.gale.setuidWrapper = {
+         program = "gksign";
+         source = "${pkgs.gale}/bin/gksign";
+         owner = cfg.user;
+         group = cfg.group;
+         setuid = true;
+         setgid = false;
+       };
+
+       security.wrappers.gksign = cfg.setuidWrapper;
+
+       systemd.services.gale-galed = {
+         description = "Gale messaging daemon";
+         wantedBy = [ "multi-user.target" ];
+         wants = [ "gale-gdomain.service" ];
+         after = [ "network.target" ];
+
+         preStart = ''
+           install -m 0640 -o ${cfg.user} -g ${cfg.group} ${keyPath}/${cfg.domain}.gpri "${home}/.gale/auth/private/"
+           install -m 0644 -o ${cfg.user} -g ${cfg.group} ${gpubFile} "${home}/.gale/auth/private/${cfg.domain}.gpub"
+           install -m 0644 -o ${cfg.user} -g ${cfg.group} ${gpubFile} "${home}/auth/local/${cfg.domain}.gpub"
+         '';
+
+         serviceConfig = {
+           Type = "forking";
+           ExecStart = "@${pkgs.gale}/bin/galed galed";
+           User = cfg.user;
+           Group = cfg.group;
+           PermissionsStartOnly = true;
+         };
+       };
+
+       systemd.services.gale-gdomain = {
+         description = "Gale AKD daemon";
+         wantedBy = [ "multi-user.target" ];
+         requires = [ "gale-galed.service" ];
+         after = [ "gale-galed.service" ];
+
+         serviceConfig = {
+           Type = "forking";
+           ExecStart = "@${pkgs.gale}/bin/gdomain gdomain";
+           User = cfg.user;
+           Group = cfg.group;
+         };
+       };
+    })
+  ];
+}
diff --git a/nixpkgs/nixos/modules/services/networking/gateone.nix b/nixpkgs/nixos/modules/services/networking/gateone.nix
new file mode 100644
index 000000000000..4456a95402ed
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/gateone.nix
@@ -0,0 +1,59 @@
+{ config, lib, pkgs, ...}:
+with lib;
+let
+  cfg = config.services.gateone;
+in
+{
+options = {
+    services.gateone = {
+      enable = mkEnableOption "GateOne server";
+      pidDir = mkOption {
+        default = "/run/gateone";
+        type = types.path;
+        description = ''Path of pid files for GateOne.'';
+      };
+      settingsDir = mkOption {
+        default = "/var/lib/gateone";
+        type = types.path;
+        description = ''Path of configuration files for GateOne.'';
+      };
+    };
+};
+config = mkIf cfg.enable {
+  environment.systemPackages = with pkgs.pythonPackages; [
+    gateone pkgs.openssh pkgs.procps pkgs.coreutils pkgs.cacert];
+
+  users.users.gateone = {
+    description = "GateOne privilege separation user";
+    uid = config.ids.uids.gateone;
+    home = cfg.settingsDir;
+  };
+  users.groups.gateone.gid = config.ids.gids.gateone;
+
+  systemd.services.gateone = with pkgs; {
+    description = "GateOne web-based terminal";
+    path = [ pythonPackages.gateone nix openssh procps coreutils ];
+    preStart = ''
+      if [ ! -d ${cfg.settingsDir} ] ; then
+        mkdir -m 0750 -p ${cfg.settingsDir}
+        chown -R gateone.gateone ${cfg.settingsDir}
+      fi
+      if [ ! -d ${cfg.pidDir} ] ; then
+        mkdir -m 0750 -p ${cfg.pidDir}
+        chown -R gateone.gateone ${cfg.pidDir}
+      fi
+      '';
+    #unitConfig.RequiresMountsFor = "${cfg.settingsDir}";
+    serviceConfig = {
+      ExecStart = ''${pythonPackages.gateone}/bin/gateone --settings_dir=${cfg.settingsDir} --pid_file=${cfg.pidDir}/gateone.pid --gid=${toString config.ids.gids.gateone} --uid=${toString config.ids.uids.gateone}'';
+      User = "gateone";
+      Group = "gateone";
+      WorkingDirectory = cfg.settingsDir;
+    };
+
+    wantedBy = [ "multi-user.target" ];
+    requires = [ "network.target" ];
+  };
+};
+}
+  
diff --git a/nixpkgs/nixos/modules/services/networking/gdomap.nix b/nixpkgs/nixos/modules/services/networking/gdomap.nix
new file mode 100644
index 000000000000..3d829cb69135
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/gdomap.nix
@@ -0,0 +1,29 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  #
+  # interface
+  #
+  options = {
+    services.gdomap = {
+      enable = mkEnableOption "GNUstep Distributed Objects name server";
+   };
+  };
+
+  #
+  # implementation
+  #
+  config = mkIf config.services.gdomap.enable {
+    # NOTE: gdomap runs as root
+    # TODO: extra user for gdomap?
+    systemd.services.gdomap = {
+      description = "gdomap server";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+      path  = [ pkgs.gnustep.base ];
+      serviceConfig.ExecStart = "${pkgs.gnustep.base}/bin/gdomap -f";
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/git-daemon.nix b/nixpkgs/nixos/modules/services/networking/git-daemon.nix
new file mode 100644
index 000000000000..52c895215fbe
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/git-daemon.nix
@@ -0,0 +1,130 @@
+{ config, lib, pkgs, ... }:
+with lib;
+let
+
+  cfg = config.services.gitDaemon;
+
+in
+{
+
+  ###### interface
+
+  options = {
+    services.gitDaemon = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable Git daemon, which allows public hosting of git repositories
+          without any access controls. This is mostly intended for read-only access.
+
+          You can allow write access by setting daemon.receivepack configuration
+          item of the repository to true. This is solely meant for a closed LAN setting
+          where everybody is friendly.
+
+          If you need any access controls, use something else.
+        '';
+      };
+
+      basePath = mkOption {
+        type = types.str;
+        default = "";
+        example = "/srv/git/";
+        description = ''
+          Remap all the path requests as relative to the given path. For example,
+          if you set base-path to /srv/git, then if you later try to pull
+          git://example.com/hello.git, Git daemon will interpret the path as /srv/git/hello.git.
+        '';
+      };
+
+      exportAll = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Publish all directories that look like Git repositories (have the objects
+          and refs subdirectories), even if they do not have the git-daemon-export-ok file.
+
+          If disabled, you need to touch .git/git-daemon-export-ok in each repository
+          you want the daemon to publish.
+
+          Warning: enabling this without a repository whitelist or basePath
+          publishes every git repository you have.
+        '';
+      };
+
+      repositories = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "/srv/git" "/home/user/git/repo2" ];
+        description = ''
+          A whitelist of paths of git repositories, or directories containing repositories
+          all of which would be published. Paths must not end in "/".
+
+          Warning: leaving this empty and enabling exportAll publishes all
+          repositories in your filesystem or basePath if specified.
+        '';
+      };
+
+      listenAddress = mkOption {
+        type = types.str;
+        default = "";
+        example = "example.com";
+        description = "Listen on a specific IP address or hostname.";
+      };
+
+      port = mkOption {
+        type = types.int;
+        default = 9418;
+        description = "Port to listen on.";
+      };
+
+      options = mkOption {
+        type = types.str;
+        default = "";
+        description = "Extra configuration options to be passed to Git daemon.";
+      };
+
+      user = mkOption {
+        type = types.str;
+        default = "git";
+        description = "User under which Git daemon would be running.";
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "git";
+        description = "Group under which Git daemon would be running.";
+      };
+
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    users.users = optionalAttrs (cfg.user == "git") {
+      git = {
+        uid = config.ids.uids.git;
+        description = "Git daemon user";
+      };
+    };
+
+    users.groups = optionalAttrs (cfg.group == "git") {
+      git.gid = config.ids.gids.git;
+    };
+
+    systemd.services.git-daemon = {
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      script = "${pkgs.git}/bin/git daemon --reuseaddr "
+        + (optionalString (cfg.basePath != "") "--base-path=${cfg.basePath} ")
+        + (optionalString (cfg.listenAddress != "") "--listen=${cfg.listenAddress} ")
+        + "--port=${toString cfg.port} --user=${cfg.user} --group=${cfg.group} ${cfg.options} "
+        + "--verbose " + (optionalString cfg.exportAll "--export-all ")  + concatStringsSep " " cfg.repositories;
+    };
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/gnunet.nix b/nixpkgs/nixos/modules/services/networking/gnunet.nix
new file mode 100644
index 000000000000..69d4ed047756
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/gnunet.nix
@@ -0,0 +1,166 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.gnunet;
+
+  homeDir = "/var/lib/gnunet";
+
+  configFile = with cfg; pkgs.writeText "gnunetd.conf"
+    ''
+      [PATHS]
+      SERVICEHOME = ${homeDir}
+
+      [ats]
+      WAN_QUOTA_IN = ${toString load.maxNetDownBandwidth} b
+      WAN_QUOTA_OUT = ${toString load.maxNetUpBandwidth} b
+
+      [datastore]
+      QUOTA = ${toString fileSharing.quota} MB
+
+      [transport-udp]
+      PORT = ${toString udp.port}
+      ADVERTISED_PORT = ${toString udp.port}
+
+      [transport-tcp]
+      PORT = ${toString tcp.port}
+      ADVERTISED_PORT = ${toString tcp.port}
+
+      ${extraOptions}
+    '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.gnunet = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to run the GNUnet daemon.  GNUnet is GNU's anonymous
+          peer-to-peer communication and file sharing framework.
+        '';
+      };
+
+      fileSharing = {
+        quota = mkOption {
+          type = types.int;
+          default = 1024;
+          description = ''
+            Maximum file system usage (in MiB) for file sharing.
+          '';
+        };
+      };
+
+      udp = {
+        port = mkOption {
+          type = types.port;
+          default = 2086;  # assigned by IANA
+          description = ''
+            The UDP port for use by GNUnet.
+          '';
+        };
+      };
+
+      tcp = {
+        port = mkOption {
+          type = types.port;
+          default = 2086;  # assigned by IANA
+          description = ''
+            The TCP port for use by GNUnet.
+          '';
+        };
+      };
+
+      load = {
+        maxNetDownBandwidth = mkOption {
+          type = types.int;
+          default = 50000;
+          description = ''
+            Maximum bandwidth usage (in bits per second) for GNUnet
+            when downloading data.
+          '';
+        };
+
+        maxNetUpBandwidth = mkOption {
+          type = types.int;
+          default = 50000;
+          description = ''
+            Maximum bandwidth usage (in bits per second) for GNUnet
+            when downloading data.
+          '';
+        };
+
+        hardNetUpBandwidth = mkOption {
+          type = types.int;
+          default = 0;
+          description = ''
+            Hard bandwidth limit (in bits per second) when uploading
+            data.
+          '';
+        };
+      };
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.gnunet;
+        defaultText = "pkgs.gnunet";
+        description = "Overridable attribute of the gnunet package to use.";
+        example = literalExample "pkgs.gnunet_git";
+      };
+
+      extraOptions = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Additional options that will be copied verbatim in `gnunet.conf'.
+          See `gnunet.conf(5)' for details.
+        '';
+      };
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.gnunet.enable {
+
+    users.users.gnunet = {
+      group = "gnunet";
+      description = "GNUnet User";
+      home = homeDir;
+      createHome = true;
+      uid = config.ids.uids.gnunet;
+    };
+
+    users.groups.gnunet.gid = config.ids.gids.gnunet;
+
+    # The user tools that talk to `gnunetd' should come from the same source,
+    # so install them globally.
+    environment.systemPackages = [ cfg.package ];
+
+    systemd.services.gnunet = {
+      description = "GNUnet";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      path = [ cfg.package pkgs.miniupnpc ];
+      environment.TMPDIR = "/tmp";
+      serviceConfig.PrivateTmp = true;
+      serviceConfig.ExecStart = "${cfg.package}/lib/gnunet/libexec/gnunet-service-arm -c ${configFile}";
+      serviceConfig.User = "gnunet";
+      serviceConfig.UMask = "0007";
+      serviceConfig.WorkingDirectory = homeDir;
+    };
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/go-neb.nix b/nixpkgs/nixos/modules/services/networking/go-neb.nix
new file mode 100644
index 000000000000..991ae38f30a5
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/go-neb.nix
@@ -0,0 +1,53 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.go-neb;
+
+  configFile = pkgs.writeText "config.yml" (builtins.toJSON cfg.config);
+in {
+  options.services.go-neb = {
+    enable = mkEnableOption "Extensible matrix bot written in Go";
+
+    bindAddress = mkOption {
+      type = types.str;
+      description = "Port (and optionally address) to listen on.";
+      default = ":4050";
+    };
+
+    baseUrl = mkOption {
+      type = types.str;
+      description = "Public-facing endpoint that can receive webhooks.";
+    };
+
+    config = mkOption {
+      type = types.uniq types.attrs;
+      description = ''
+        Your <filename>config.yaml</filename> as a Nix attribute set.
+        See <link xlink:href="https://github.com/matrix-org/go-neb/blob/master/config.sample.yaml">config.sample.yaml</link>
+        for possible options.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.go-neb = {
+      description = "Extensible matrix bot written in Go";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      environment = {
+        BASE_URL = cfg.baseUrl;
+        BIND_ADDRESS = cfg.bindAddress;
+        CONFIG_FILE = configFile;
+      };
+
+      serviceConfig = {
+        ExecStart = "${pkgs.go-neb}/bin/go-neb";
+        DynamicUser = true;
+      };
+    };
+  };
+
+  meta.maintainers = with maintainers; [ hexa maralorn ];
+}
diff --git a/nixpkgs/nixos/modules/services/networking/go-shadowsocks2.nix b/nixpkgs/nixos/modules/services/networking/go-shadowsocks2.nix
new file mode 100644
index 000000000000..afbd7ea27c65
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/go-shadowsocks2.nix
@@ -0,0 +1,30 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.services.go-shadowsocks2.server;
+in {
+  options.services.go-shadowsocks2.server = {
+    enable = mkEnableOption "go-shadowsocks2 server";
+
+    listenAddress = mkOption {
+      type = types.str;
+      description = "Server listen address or URL";
+      example = "ss://AEAD_CHACHA20_POLY1305:your-password@:8488";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.go-shadowsocks2-server = {
+      description = "go-shadowsocks2 server";
+
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        ExecStart = "${pkgs.go-shadowsocks2}/bin/go-shadowsocks2 -s '${cfg.listenAddress}'";
+        DynamicUser = true;
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/gogoclient.nix b/nixpkgs/nixos/modules/services/networking/gogoclient.nix
new file mode 100644
index 000000000000..99455b183144
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/gogoclient.nix
@@ -0,0 +1,85 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let cfg = config.services.gogoclient;
+in
+
+{
+
+  ###### interface
+
+  options = {
+    services.gogoclient = {
+      enable = mkOption {
+        default = false;
+        type =  types.bool;
+        description = ''
+          Enable the gogoCLIENT IPv6 tunnel.
+        '';
+      };
+      autorun = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to automatically start the tunnel.
+        '';
+      };
+
+      username = mkOption {
+        default = "";
+        description = ''
+          Your Gateway6 login name, if any.
+        '';
+      };
+
+      password = mkOption {
+        default = "";
+        type = types.str;
+        description = ''
+          Path to a file (as a string), containing your gogoNET password, if any.
+        '';
+      };
+
+      server = mkOption {
+        default = "anonymous.freenet6.net";
+        example = "broker.freenet6.net";
+        description = "The Gateway6 server to be used.";
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    boot.kernelModules = [ "tun" ];
+
+    networking.enableIPv6 = true;
+
+    systemd.services.gogoclient = {
+      description = "ipv6 tunnel";
+
+      after = [ "network.target" ];
+      requires = [ "network.target" ];
+
+      unitConfig.RequiresMountsFor = "/var/lib/gogoc";
+
+      script = let authMethod = if cfg.password == "" then "anonymous" else "any"; in ''
+        mkdir -p -m 700 /var/lib/gogoc
+        cat ${pkgs.gogoclient}/share/${pkgs.gogoclient.name}/gogoc.conf.sample | \
+          ${pkgs.gnused}/bin/sed \
+            -e "s|^userid=|&${cfg.username}|" \
+            -e "s|^passwd=|&${optionalString (cfg.password != "") "$(cat ${cfg.password})"}|" \
+            -e "s|^server=.*|server=${cfg.server}|" \
+            -e "s|^auth_method=.*|auth_method=${authMethod}|" \
+            -e "s|^#log_file=|log_file=1|" > /var/lib/gogoc/gogoc.conf
+        cd /var/lib/gogoc
+        exec ${pkgs.gogoclient}/bin/gogoc -y -f /var/lib/gogoc/gogoc.conf
+      '';
+    } // optionalAttrs cfg.autorun {
+      wantedBy = [ "multi-user.target" ];
+    };
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/gvpe.nix b/nixpkgs/nixos/modules/services/networking/gvpe.nix
new file mode 100644
index 000000000000..92e87cd4640d
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/gvpe.nix
@@ -0,0 +1,124 @@
+# GNU Virtual Private Ethernet
+
+{config, pkgs, lib, ...}:
+
+let
+  inherit (lib) mkOption mkIf;
+
+  cfg = config.services.gvpe;
+
+  finalConfig = if cfg.configFile != null then
+    cfg.configFile
+  else if cfg.configText != null then
+    pkgs.writeTextFile {
+      name = "gvpe.conf";
+      text = cfg.configText;
+    }
+  else
+    throw "You must either specify contents of the config file or the config file itself for GVPE";
+
+  ifupScript = if cfg.ipAddress == null || cfg.subnet == null then
+     throw "Specify IP address and subnet (with mask) for GVPE"
+   else if cfg.nodename == null then
+     throw "You must set node name for GVPE"
+   else
+   (pkgs.writeTextFile {
+    name = "gvpe-if-up";
+    text = ''
+      #! /bin/sh
+
+      export PATH=$PATH:${pkgs.iproute}/sbin
+
+      ip link set $IFNAME up
+      ip address add ${cfg.ipAddress} dev $IFNAME
+      ip route add ${cfg.subnet} dev $IFNAME
+
+      ${cfg.customIFSetup}
+    '';
+    executable = true;
+  });
+in
+
+{
+  options = {
+    services.gvpe = {
+      enable = lib.mkEnableOption "gvpe";
+
+      nodename = mkOption {
+        default = null;
+        description =''
+          GVPE node name
+        '';
+      };
+      configText = mkOption {
+        default = null;
+        example = ''
+          tcp-port = 655
+          udp-port = 655
+          mtu = 1480
+          ifname = vpn0
+
+          node = alpha
+          hostname = alpha.example.org
+          connect = always
+          enable-udp = true
+          enable-tcp = true
+          on alpha if-up = if-up-0
+          on alpha pid-file = /var/gvpe/gvpe.pid
+        '';
+        description = ''
+          GVPE config contents
+        '';
+      };
+      configFile = mkOption {
+        default = null;
+        example = "/root/my-gvpe-conf";
+        description = ''
+          GVPE config file, if already present
+        '';
+      };
+      ipAddress = mkOption {
+        default = null;
+        description = ''
+          IP address to assign to GVPE interface
+        '';
+      };
+      subnet = mkOption {
+        default = null;
+        example = "10.0.0.0/8";
+        description = ''
+          IP subnet assigned to GVPE network
+        '';
+      };
+      customIFSetup = mkOption {
+        default = "";
+        description = ''
+          Additional commands to apply in ifup script
+        '';
+      };
+    };
+  };
+  config = mkIf cfg.enable {
+    systemd.services.gvpe = {
+      description = "GNU Virtual Private Ethernet node";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      preStart = ''
+        mkdir -p /var/gvpe
+        mkdir -p /var/gvpe/pubkey
+        chown root /var/gvpe
+        chmod 700 /var/gvpe
+        cp ${finalConfig} /var/gvpe/gvpe.conf
+        cp ${ifupScript} /var/gvpe/if-up
+      '';
+
+      script = "${pkgs.gvpe}/sbin/gvpe -c /var/gvpe -D ${cfg.nodename} "
+        + " ${cfg.nodename}.pid-file=/var/gvpe/gvpe.pid"
+        + " ${cfg.nodename}.if-up=if-up"
+        + " &> /var/log/gvpe";
+
+      serviceConfig.Restart = "always";
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/hans.nix b/nixpkgs/nixos/modules/services/networking/hans.nix
new file mode 100644
index 000000000000..8334dc68d623
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/hans.nix
@@ -0,0 +1,145 @@
+# NixOS module for hans, ip over icmp daemon
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.hans;
+
+  hansUser = "hans";
+
+in
+{
+
+  ### configuration
+
+  options = {
+
+    services.hans = {
+      clients = mkOption {
+        default = {};
+        description = ''
+          Each attribute of this option defines a systemd service that
+          runs hans. Many or none may be defined.
+          The name of each service is
+          <literal>hans-<replaceable>name</replaceable></literal>
+          where <replaceable>name</replaceable> is the name of the
+          corresponding attribute name.
+        '';
+        example = literalExample ''
+        {
+          foo = {
+            server = "192.0.2.1";
+            extraConfig = "-v";
+          }
+        }
+        '';
+        type = types.attrsOf (types.submodule (
+        {
+          options = {
+            server = mkOption {
+              type = types.str;
+              default = "";
+              description = "IP address of server running hans";
+              example = "192.0.2.1";
+            };
+
+            extraConfig = mkOption {
+              type = types.str;
+              default = "";
+              description = "Additional command line parameters";
+              example = "-v";
+            };
+
+            passwordFile = mkOption {
+              type = types.str;
+              default = "";
+              description = "File that containts password";
+            };
+
+          };
+        }));
+      };
+
+      server = {
+        enable = mkOption {
+          type = types.bool;
+          default = false;
+          description = "enable hans server";
+        };
+
+        ip = mkOption {
+          type = types.str;
+          default = "";
+          description = "The assigned ip range";
+          example = "198.51.100.0";
+        };
+
+        respondToSystemPings = mkOption {
+          type = types.bool;
+          default = false;
+          description = "Force hans respond to ordinary pings";
+        };
+
+        extraConfig = mkOption {
+          type = types.str;
+          default = "";
+          description = "Additional command line parameters";
+          example = "-v";
+        };
+
+        passwordFile = mkOption {
+          type = types.str;
+          default = "";
+          description = "File that containts password";
+        };
+      };
+
+    };
+  };
+
+  ### implementation
+
+  config = mkIf (cfg.server.enable || cfg.clients != {}) {
+    boot.kernel.sysctl = optionalAttrs cfg.server.respondToSystemPings {
+      "net.ipv4.icmp_echo_ignore_all" = 1;
+    };
+
+    boot.kernelModules = [ "tun" ];
+
+    systemd.services =
+    let
+      createHansClientService = name: cfg:
+      {
+        description = "hans client - ${name}";
+        after = [ "network.target" ];
+        wantedBy = [ "multi-user.target" ];
+        script = "${pkgs.hans}/bin/hans -f -u ${hansUser} ${cfg.extraConfig} -c ${cfg.server} ${optionalString (cfg.passwordFile != "") "-p $(cat \"${cfg.passwordFile}\")"}";
+        serviceConfig = {
+          RestartSec = "30s";
+          Restart = "always";
+        };
+      };
+    in
+    listToAttrs (
+      mapAttrsToList
+        (name: value: nameValuePair "hans-${name}" (createHansClientService name value))
+        cfg.clients
+    ) // {
+      hans = mkIf (cfg.server.enable) {
+        description = "hans, ip over icmp server daemon";
+        after = [ "network.target" ];
+        wantedBy = [ "multi-user.target" ];
+        script = "${pkgs.hans}/bin/hans -f -u ${hansUser} ${cfg.server.extraConfig} -s ${cfg.server.ip} ${optionalString cfg.server.respondToSystemPings "-r"} ${optionalString (cfg.server.passwordFile != "") "-p $(cat \"${cfg.server.passwordFile}\")"}";
+      };
+    };
+
+    users.users.${hansUser} = {
+      description = "Hans daemon user";
+      isSystemUser = true;
+    };
+  };
+
+  meta.maintainers = with maintainers; [ gnidorah ];
+}
diff --git a/nixpkgs/nixos/modules/services/networking/haproxy.nix b/nixpkgs/nixos/modules/services/networking/haproxy.nix
new file mode 100644
index 000000000000..e9d72b35499d
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/haproxy.nix
@@ -0,0 +1,112 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.services.haproxy;
+
+  haproxyCfg = pkgs.writeText "haproxy.conf" ''
+    global
+      # needed for hot-reload to work without dropping packets in multi-worker mode
+      stats socket /run/haproxy/haproxy.sock mode 600 expose-fd listeners level user
+
+    ${cfg.config}
+  '';
+
+in
+with lib;
+{
+  options = {
+    services.haproxy = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable HAProxy, the reliable, high performance TCP/HTTP
+          load balancer.
+        '';
+      };
+
+      user = mkOption {
+        type = types.str;
+        default = "haproxy";
+        description = "User account under which haproxy runs.";
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "haproxy";
+        description = "Group account under which haproxy runs.";
+      };
+
+      config = mkOption {
+        type = types.nullOr types.lines;
+        default = null;
+        description = ''
+          Contents of the HAProxy configuration file,
+          <filename>haproxy.conf</filename>.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    assertions = [{
+      assertion = cfg.config != null;
+      message = "You must provide services.haproxy.config.";
+    }];
+
+    # configuration file indirection is needed to support reloading
+    environment.etc."haproxy.cfg".source = haproxyCfg;
+
+    systemd.services.haproxy = {
+      description = "HAProxy";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        User = cfg.user;
+        Group = cfg.group;
+        Type = "notify";
+        ExecStartPre = [
+          # when the master process receives USR2, it reloads itself using exec(argv[0]),
+          # so we create a symlink there and update it before reloading
+          "${pkgs.coreutils}/bin/ln -sf ${pkgs.haproxy}/sbin/haproxy /run/haproxy/haproxy"
+          # when running the config test, don't be quiet so we can see what goes wrong
+          "/run/haproxy/haproxy -c -f ${haproxyCfg}"
+        ];
+        ExecStart = "/run/haproxy/haproxy -Ws -f /etc/haproxy.cfg -p /run/haproxy/haproxy.pid";
+        # support reloading
+        ExecReload = [
+          "${pkgs.haproxy}/sbin/haproxy -c -f ${haproxyCfg}"
+          "${pkgs.coreutils}/bin/ln -sf ${pkgs.haproxy}/sbin/haproxy /run/haproxy/haproxy"
+          "${pkgs.coreutils}/bin/kill -USR2 $MAINPID"
+        ];
+        KillMode = "mixed";
+        SuccessExitStatus = "143";
+        Restart = "always";
+        RuntimeDirectory = "haproxy";
+        # upstream hardening options
+        NoNewPrivileges = true;
+        ProtectHome = true;
+        ProtectSystem = "strict";
+        ProtectKernelTunables = true;
+        ProtectKernelModules = true;
+        ProtectControlGroups = true;
+        SystemCallFilter= "~@cpu-emulation @keyring @module @obsolete @raw-io @reboot @swap @sync";
+        # needed in case we bind to port < 1024
+        AmbientCapabilities = "CAP_NET_BIND_SERVICE";
+      };
+    };
+
+    users.users = optionalAttrs (cfg.user == "haproxy") {
+      haproxy = {
+        group = cfg.group;
+        isSystemUser = true;
+      };
+    };
+
+    users.groups = optionalAttrs (cfg.group == "haproxy") {
+      haproxy = {};
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/helpers.nix b/nixpkgs/nixos/modules/services/networking/helpers.nix
new file mode 100644
index 000000000000..d7d42de0e3a8
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/helpers.nix
@@ -0,0 +1,11 @@
+{ config, lib, ... }: ''
+  # Helper command to manipulate both the IPv4 and IPv6 tables.
+  ip46tables() {
+    iptables -w "$@"
+    ${
+      lib.optionalString config.networking.enableIPv6 ''
+        ip6tables -w "$@"
+      ''
+    }
+  }
+''
diff --git a/nixpkgs/nixos/modules/services/networking/heyefi.nix b/nixpkgs/nixos/modules/services/networking/heyefi.nix
new file mode 100644
index 000000000000..fc2b5a848578
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/heyefi.nix
@@ -0,0 +1,82 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.heyefi;
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.heyefi = {
+
+      enable = mkEnableOption "heyefi";
+
+      cardMacaddress = mkOption {
+        default = "";
+        description = ''
+          An Eye-Fi card MAC address.
+          '';
+      };
+
+      uploadKey = mkOption {
+        default = "";
+        description = ''
+          An Eye-Fi card's upload key.
+          '';
+      };
+
+      uploadDir = mkOption {
+        example = "/home/username/pictures";
+        description = ''
+          The directory to upload the files to.
+          '';
+      };
+
+      user = mkOption {
+        default = "root";
+        description = ''
+          heyefi will be run under this user (user must exist,
+          this can be your user name).
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    systemd.services.heyefi =
+      {
+        description = "heyefi service";
+        after = [ "network.target" ];
+        wantedBy = [ "multi-user.target" ];
+        serviceConfig = {
+          User = "${cfg.user}";
+          Restart = "always";
+          ExecStart = "${pkgs.heyefi}/bin/heyefi";
+        };
+
+      };
+
+    environment.etc."heyefi/heyefi.config".text =
+      ''
+        # /etc/heyefi/heyefi.conf: DO NOT EDIT -- this file has been generated automatically.
+        cards = [["${config.services.heyefi.cardMacaddress}","${config.services.heyefi.uploadKey}"]]
+        upload_dir = "${toString config.services.heyefi.uploadDir}"
+      '';
+
+    environment.systemPackages = [ pkgs.heyefi ];
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/hostapd.nix b/nixpkgs/nixos/modules/services/networking/hostapd.nix
new file mode 100644
index 000000000000..5d73038363a9
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/hostapd.nix
@@ -0,0 +1,218 @@
+{ config, lib, pkgs, utils, ... }:
+
+# TODO:
+#
+# asserts
+#   ensure that the nl80211 module is loaded/compiled in the kernel
+#   wpa_supplicant and hostapd on the same wireless interface doesn't make any sense
+
+with lib;
+
+let
+
+  cfg = config.services.hostapd;
+
+  escapedInterface = utils.escapeSystemdPath cfg.interface;
+
+  configFile = pkgs.writeText "hostapd.conf" ''
+    interface=${cfg.interface}
+    driver=${cfg.driver}
+    ssid=${cfg.ssid}
+    hw_mode=${cfg.hwMode}
+    channel=${toString cfg.channel}
+    ${optionalString (cfg.countryCode != null) ''country_code=${cfg.countryCode}''}
+    ${optionalString (cfg.countryCode != null) ''ieee80211d=1''}
+
+    # logging (debug level)
+    logger_syslog=-1
+    logger_syslog_level=${toString cfg.logLevel}
+    logger_stdout=-1
+    logger_stdout_level=${toString cfg.logLevel}
+
+    ctrl_interface=/run/hostapd
+    ctrl_interface_group=${cfg.group}
+
+    ${optionalString cfg.wpa ''
+      wpa=2
+      wpa_passphrase=${cfg.wpaPassphrase}
+    ''}
+    ${optionalString cfg.noScan "noscan=1"}
+
+    ${cfg.extraConfig}
+  '' ;
+
+in
+
+{
+  ###### interface
+
+  options = {
+
+    services.hostapd = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable putting a wireless interface into infrastructure mode,
+          allowing other wireless devices to associate with the wireless
+          interface and do wireless networking. A simple access point will
+          <option>enable hostapd.wpa</option>,
+          <option>hostapd.wpaPassphrase</option>, and
+          <option>hostapd.ssid</option>, as well as DHCP on the wireless
+          interface to provide IP addresses to the associated stations, and
+          NAT (from the wireless interface to an upstream interface).
+        '';
+      };
+
+      interface = mkOption {
+        default = "";
+        example = "wlp2s0";
+        description = ''
+          The interfaces <command>hostapd</command> will use.
+        '';
+      };
+
+      noScan = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Do not scan for overlapping BSSs in HT40+/- mode.
+          Caution: turning this on will violate regulatory requirements!
+        '';
+      };
+
+      driver = mkOption {
+        default = "nl80211";
+        example = "hostapd";
+        type = types.str;
+        description = ''
+          Which driver <command>hostapd</command> will use.
+          Most applications will probably use the default.
+        '';
+      };
+
+      ssid = mkOption {
+        default = "nixos";
+        example = "mySpecialSSID";
+        type = types.str;
+        description = "SSID to be used in IEEE 802.11 management frames.";
+      };
+
+      hwMode = mkOption {
+        default = "g";
+        type = types.enum [ "a" "b" "g" ];
+        description = ''
+          Operation mode.
+          (a = IEEE 802.11a, b = IEEE 802.11b, g = IEEE 802.11g).
+        '';
+      };
+
+      channel = mkOption {
+        default = 7;
+        example = 11;
+        type = types.int;
+        description = ''
+          Channel number (IEEE 802.11)
+          Please note that some drivers do not use this value from
+          <command>hostapd</command> and the channel will need to be configured
+          separately with <command>iwconfig</command>.
+        '';
+      };
+
+      group = mkOption {
+        default = "wheel";
+        example = "network";
+        type = types.str;
+        description = ''
+          Members of this group can control <command>hostapd</command>.
+        '';
+      };
+
+      wpa = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Enable WPA (IEEE 802.11i/D3.0) to authenticate with the access point.
+        '';
+      };
+
+      wpaPassphrase = mkOption {
+        default = "my_sekret";
+        example = "any_64_char_string";
+        type = types.str;
+        description = ''
+          WPA-PSK (pre-shared-key) passphrase. Clients will need this
+          passphrase to associate with this access point.
+          Warning: This passphrase will get put into a world-readable file in
+          the Nix store!
+        '';
+      };
+
+      logLevel = mkOption {
+        default = 2;
+        type = types.int;
+        description = ''
+          Levels (minimum value for logged events):
+          0 = verbose debugging
+          1 = debugging
+          2 = informational messages
+          3 = notification
+          4 = warning
+        '';
+      };
+
+      countryCode = mkOption {
+        default = null;
+        example = "US";
+        type = with types; nullOr str;
+        description = ''
+          Country code (ISO/IEC 3166-1). Used to set regulatory domain.
+          Set as needed to indicate country in which device is operating.
+          This can limit available channels and transmit power.
+          These two octets are used as the first two octets of the Country String
+          (dot11CountryString).
+          If set this enables IEEE 802.11d. This advertises the countryCode and
+          the set of allowed channels and transmit power levels based on the
+          regulatory limits.
+        '';
+      };
+
+      extraConfig = mkOption {
+        default = "";
+        example = ''
+          auth_algo=0
+          ieee80211n=1
+          ht_capab=[HT40-][SHORT-GI-40][DSSS_CCK-40]
+          '';
+        type = types.lines;
+        description = "Extra configuration options to put in hostapd.conf.";
+      };
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages =  [ pkgs.hostapd ];
+
+    services.udev.packages = optional (cfg.countryCode != null) [ pkgs.crda ];
+
+    systemd.services.hostapd =
+      { description = "hostapd wireless AP";
+
+        path = [ pkgs.hostapd ];
+        after = [ "sys-subsystem-net-devices-${escapedInterface}.device" ];
+        bindsTo = [ "sys-subsystem-net-devices-${escapedInterface}.device" ];
+        requiredBy = [ "network-link-${cfg.interface}.service" ];
+        wantedBy = [ "multi-user.target" ];
+
+        serviceConfig =
+          { ExecStart = "${pkgs.hostapd}/bin/hostapd ${configFile}";
+            Restart = "always";
+          };
+      };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/htpdate.nix b/nixpkgs/nixos/modules/services/networking/htpdate.nix
new file mode 100644
index 000000000000..6954e5b060c4
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/htpdate.nix
@@ -0,0 +1,80 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  inherit (pkgs) htpdate;
+
+  cfg = config.services.htpdate;
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.htpdate = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable htpdate daemon.
+        '';
+      };
+
+      extraOptions = mkOption {
+        type = types.str;
+        default = "";
+        description = ''
+          Additional command line arguments to pass to htpdate.
+        '';
+      };
+
+      servers = mkOption {
+        type = types.listOf types.str;
+        default = [ "www.google.com" ];
+        description = ''
+          HTTP servers to use for time synchronization.
+        '';
+      };
+
+      proxy = mkOption {
+        type = types.str;
+        default = "";
+        example = "127.0.0.1:8118";
+        description = ''
+          HTTP proxy used for requests.
+        '';
+      };
+
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    systemd.services.htpdate = {
+      description = "htpdate daemon";
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        Type = "forking";
+        PIDFile = "/run/htpdate.pid";
+        ExecStart = concatStringsSep " " [
+          "${htpdate}/bin/htpdate"
+          "-D -u nobody"
+          "-a -s"
+          "-l"
+          "${optionalString (cfg.proxy != "") "-P ${cfg.proxy}"}"
+          "${cfg.extraOptions}"
+          "${concatStringsSep " " cfg.servers}"
+        ];
+      };
+    };
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/hylafax/default.nix b/nixpkgs/nixos/modules/services/networking/hylafax/default.nix
new file mode 100644
index 000000000000..d8ffa3fc04d2
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/hylafax/default.nix
@@ -0,0 +1,31 @@
+{ config, lib, pkgs, ... }:
+
+{
+
+  imports = [
+    ./options.nix
+    ./systemd.nix
+  ];
+
+  config = lib.modules.mkIf config.services.hylafax.enable {
+    environment.systemPackages = [ pkgs.hylafaxplus ];
+    users.users.uucp = {
+      uid = config.ids.uids.uucp;
+      group = "uucp";
+      description = "Unix-to-Unix CoPy system";
+      isSystemUser = true;
+      inherit (config.users.users.nobody) home;
+    };
+    assertions = [{
+      assertion = config.services.hylafax.modems != {};
+      message = ''
+        HylaFAX cannot be used without modems.
+        Please define at least one modem with
+        <option>config.services.hylafax.modems</option>.
+      '';
+    }];
+  };
+
+  meta.maintainers = [ lib.maintainers.yarny ];
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/hylafax/faxq-default.nix b/nixpkgs/nixos/modules/services/networking/hylafax/faxq-default.nix
new file mode 100644
index 000000000000..9b634650cf79
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/hylafax/faxq-default.nix
@@ -0,0 +1,12 @@
+{ ... }:
+
+# see man:hylafax-config(5)
+
+{
+
+  ModemGroup = [ ''"any:0:.*"'' ];
+  ServerTracing = "0x78701";
+  SessionTracing = "0x78701";
+  UUCPLockDir = "/var/lock";
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/hylafax/faxq-wait.sh b/nixpkgs/nixos/modules/services/networking/hylafax/faxq-wait.sh
new file mode 100755
index 000000000000..8c39e9d20c18
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/hylafax/faxq-wait.sh
@@ -0,0 +1,29 @@
+#! @shell@ -e
+
+# skip this if there are no modems at all
+if ! stat -t "@spoolAreaPath@"/etc/config.* >/dev/null 2>&1
+then
+  exit 0
+fi
+
+echo "faxq started, waiting for modem(s) to initialize..."
+
+for i in `seq @timeoutSec@0 -1 0`  # gracefully timeout
+do
+  sleep 0.1
+  # done if status files exist, but don't mention initialization
+  if \
+    stat -t "@spoolAreaPath@"/status/* >/dev/null 2>&1 \
+    && \
+    ! grep --silent --ignore-case 'initializing server' \
+    "@spoolAreaPath@"/status/*
+  then
+    echo "modem(s) apparently ready"
+    exit 0
+  fi
+  # if i reached 0, modems probably failed to initialize
+  if test $i -eq 0
+  then
+    echo "warning: modem initialization timed out"
+  fi
+done
diff --git a/nixpkgs/nixos/modules/services/networking/hylafax/hfaxd-default.nix b/nixpkgs/nixos/modules/services/networking/hylafax/hfaxd-default.nix
new file mode 100644
index 000000000000..8999dae57f41
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/hylafax/hfaxd-default.nix
@@ -0,0 +1,10 @@
+{ ... }:
+
+# see man:hfaxd(8)
+
+{
+
+  ServerTracing = "0x91";
+  XferLogFile = "/clientlog";
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/hylafax/modem-default.nix b/nixpkgs/nixos/modules/services/networking/hylafax/modem-default.nix
new file mode 100644
index 000000000000..7529b5b0aafd
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/hylafax/modem-default.nix
@@ -0,0 +1,22 @@
+{ pkgs, ... }:
+
+# see man:hylafax-config(5)
+
+{
+
+  TagLineFont = "etc/LiberationSans-25.pcf";
+  TagLineLocale = ''en_US.UTF-8'';
+
+  AdminGroup = "root";  # groups that can change server config
+  AnswerRotary = "fax";  # don't accept anything else but faxes
+  LogFileMode = "0640";
+  PriorityScheduling = true;
+  RecvFileMode = "0640";
+  ServerTracing = "0x78701";
+  SessionTracing = "0x78701";
+  UUCPLockDir = "/var/lock";
+
+  SendPageCmd = ''${pkgs.coreutils}/bin/false'';  # prevent pager transmit
+  SendUUCPCmd = ''${pkgs.coreutils}/bin/false'';  # prevent UUCP transmit
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/hylafax/options.nix b/nixpkgs/nixos/modules/services/networking/hylafax/options.nix
new file mode 100644
index 000000000000..4ac6d3fa8432
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/hylafax/options.nix
@@ -0,0 +1,375 @@
+{ config, lib, pkgs, ... }:
+
+let
+
+  inherit (lib.options) literalExample mkEnableOption mkOption;
+  inherit (lib.types) bool enum int lines loaOf nullOr path str submodule;
+  inherit (lib.modules) mkDefault mkIf mkMerge;
+
+  commonDescr = ''
+    Values can be either strings or integers
+    (which will be added to the config file verbatimly)
+    or lists thereof
+    (which will be translated to multiple
+    lines with the same configuration key).
+    Boolean values are translated to "Yes" or "No".
+    The default contains some reasonable
+    configuration to yield an operational system.
+  '';
+
+  str1 = lib.types.addCheck str (s: s!="");  # non-empty string
+  int1 = lib.types.addCheck int (i: i>0);  # positive integer
+
+  configAttrType =
+    # Options in HylaFAX configuration files can be
+    # booleans, strings, integers, or list thereof
+    # representing multiple config directives with the same key.
+    # This type definition resolves all
+    # those types into a list of strings.
+    let
+      inherit (lib.types) attrsOf coercedTo listOf;
+      innerType = coercedTo bool (x: if x then "Yes" else "No")
+        (coercedTo int (toString) str);
+    in
+      attrsOf (coercedTo innerType lib.singleton (listOf innerType));
+
+  cfg = config.services.hylafax;
+
+  modemConfigOptions = { name, config, ... }: {
+    options = {
+      name = mkOption {
+        type = str1;
+        example = "ttyS1";
+        description = ''
+          Name of modem device,
+          will be searched for in <filename>/dev</filename>.
+        '';
+      };
+      type = mkOption {
+        type = str1;
+        example = "cirrus";
+        description = ''
+          Name of modem configuration file,
+          will be searched for in <filename>config</filename>
+          in the spooling area directory.
+        '';
+      };
+      config = mkOption {
+        type = configAttrType;
+        example = {
+          AreaCode = "49";
+          LocalCode = "30";
+          FAXNumber = "123456";
+          LocalIdentifier = "LostInBerlin";
+        };
+        description = ''
+          Attribute set of values for the given modem.
+          ${commonDescr}
+          Options defined here override options in
+          <option>commonModemConfig</option> for this modem.
+        '';
+      };
+    };
+    config.name = mkDefault name;
+    config.config.Include = [ "config/${config.type}" ];
+  };
+
+  defaultConfig =
+    let
+      inherit (config.security) wrapperDir;
+      inherit (config.services.mail.sendmailSetuidWrapper) program;
+      mkIfDefault = cond: value: mkIf cond (mkDefault value);
+      noWrapper = config.services.mail.sendmailSetuidWrapper==null;
+      # If a sendmail setuid wrapper exists,
+      # we add the path to the default configuration file.
+      # Otherwise, we use `false` to provoke
+      # an error if hylafax tries to use it.
+      c.sendmailPath = mkMerge [
+        (mkIfDefault noWrapper ''${pkgs.coreutils}/bin/false'')
+        (mkIfDefault (!noWrapper) ''${wrapperDir}/${program}'')
+      ];
+      importDefaultConfig = file:
+        lib.attrsets.mapAttrs
+        (lib.trivial.const mkDefault)
+        (import file { inherit pkgs; });
+      c.commonModemConfig = importDefaultConfig ./modem-default.nix;
+      c.faxqConfig = importDefaultConfig ./faxq-default.nix;
+      c.hfaxdConfig = importDefaultConfig ./hfaxd-default.nix;
+    in
+      c;
+
+  localConfig =
+    let
+      c.hfaxdConfig.UserAccessFile = cfg.userAccessFile;
+      c.faxqConfig = lib.attrsets.mapAttrs
+        (lib.trivial.const (v: mkIf (v!=null) v))
+        {
+          AreaCode = cfg.areaCode;
+          CountryCode = cfg.countryCode;
+          LongDistancePrefix = cfg.longDistancePrefix;
+          InternationalPrefix = cfg.internationalPrefix;
+        };
+      c.commonModemConfig = c.faxqConfig;
+    in
+      c;
+
+in
+
+
+{
+
+
+  options.services.hylafax = {
+
+    enable = mkEnableOption ''HylaFAX server'';
+
+    autostart = mkOption {
+      type = bool;
+      default = true;
+      example = false;
+      description = ''
+        Autostart the HylaFAX queue manager at system start.
+        If this is <literal>false</literal>, the queue manager
+        will still be started if there are pending
+        jobs or if a user tries to connect to it.
+      '';
+    };
+
+    countryCode = mkOption {
+      type = nullOr str1;
+      default = null;
+      example = "49";
+      description = ''Country code for server and all modems.'';
+    };
+
+    areaCode = mkOption {
+      type = nullOr str1;
+      default = null;
+      example = "30";
+      description = ''Area code for server and all modems.'';
+    };
+
+    longDistancePrefix = mkOption {
+      type = nullOr str;
+      default = null;
+      example = "0";
+      description = ''Long distance prefix for server and all modems.'';
+    };
+
+    internationalPrefix = mkOption {
+      type = nullOr str;
+      default = null;
+      example = "00";
+      description = ''International prefix for server and all modems.'';
+    };
+
+    spoolAreaPath = mkOption {
+      type = path;
+      default = "/var/spool/fax";
+      description = ''
+        The spooling area will be created/maintained
+        at the location given here.
+      '';
+    };
+
+    userAccessFile = mkOption {
+      type = path;
+      default = "/etc/hosts.hfaxd";
+      description = ''
+        The <filename>hosts.hfaxd</filename>
+        file entry in the spooling area
+        will be symlinked to the location given here.
+        This file must exist and be
+        readable only by the <literal>uucp</literal> user.
+        See hosts.hfaxd(5) for details.
+        This configuration permits access for all users:
+        <literal>
+          environment.etc."hosts.hfaxd" = {
+            mode = "0600";
+            user = "uucp";
+            text = ".*";
+          };
+        </literal>
+        Note that host-based access can be controlled with
+        <option>config.systemd.sockets.hylafax-hfaxd.listenStreams</option>;
+        by default, only 127.0.0.1 is permitted to connect.
+      '';
+    };
+
+    sendmailPath = mkOption {
+      type = path;
+      example = literalExample "''${pkgs.postfix}/bin/sendmail";
+      # '' ;  # fix vim
+      description = ''
+        Path to <filename>sendmail</filename> program.
+        The default uses the local sendmail wrapper
+        (see <option>config.services.mail.sendmailSetuidWrapper</option>),
+        otherwise the <filename>false</filename>
+        binary to cause an error if used.
+      '';
+    };
+
+    hfaxdConfig = mkOption {
+      type = configAttrType;
+      example.RecvqProtection = "0400";
+      description = ''
+        Attribute set of lines for the global
+        hfaxd config file <filename>etc/hfaxd.conf</filename>.
+        ${commonDescr}
+      '';
+    };
+
+    faxqConfig = mkOption {
+      type = configAttrType;
+      example = {
+        InternationalPrefix = "00";
+        LongDistancePrefix = "0";
+      };
+      description = ''
+        Attribute set of lines for the global
+        faxq config file <filename>etc/config</filename>.
+        ${commonDescr}
+      '';
+    };
+
+    commonModemConfig = mkOption {
+      type = configAttrType;
+      example = {
+        InternationalPrefix = "00";
+        LongDistancePrefix = "0";
+      };
+      description = ''
+        Attribute set of default values for
+        modem config files <filename>etc/config.*</filename>.
+        ${commonDescr}
+        Think twice before changing
+        paths of fax-processing scripts.
+      '';
+    };
+
+    modems = mkOption {
+      type = loaOf (submodule [ modemConfigOptions ]);
+      default = {};
+      example.ttyS1 = {
+        type = "cirrus";
+        config = {
+          FAXNumber = "123456";
+          LocalIdentifier = "Smith";
+        };
+      };
+      description = ''
+        Description of installed modems.
+        At least on modem must be defined
+        to enable the HylaFAX server.
+      '';
+    };
+
+    spoolExtraInit = mkOption {
+      type = lines;
+      default = "";
+      example = ''chmod 0755 .  # everyone may read my faxes'';
+      description = ''
+        Additional shell code that is executed within the
+        spooling area directory right after its setup.
+      '';
+    };
+
+    faxcron.enable.spoolInit = mkEnableOption ''
+      Purge old files from the spooling area with
+      <filename>faxcron</filename>
+      each time the spooling area is initialized.
+    '';
+    faxcron.enable.frequency = mkOption {
+      type = nullOr str1;
+      default = null;
+      example = "daily";
+      description = ''
+        Purge old files from the spooling area with
+        <filename>faxcron</filename> with the given frequency
+        (see systemd.time(7)).
+      '';
+    };
+    faxcron.infoDays = mkOption {
+      type = int1;
+      default = 30;
+      description = ''
+        Set the expiration time for data in the
+        remote machine information directory in days.
+      '';
+    };
+    faxcron.logDays = mkOption {
+      type = int1;
+      default = 30;
+      description = ''
+        Set the expiration time for
+        session trace log files in days.
+      '';
+    };
+    faxcron.rcvDays = mkOption {
+      type = int1;
+      default = 7;
+      description = ''
+        Set the expiration time for files in
+        the received facsimile queue in days.
+      '';
+    };
+
+    faxqclean.enable.spoolInit = mkEnableOption ''
+      Purge old files from the spooling area with
+      <filename>faxqclean</filename>
+      each time the spooling area is initialized.
+    '';
+    faxqclean.enable.frequency = mkOption {
+      type = nullOr str1;
+      default = null;
+      example = "daily";
+      description = ''
+        Purge old files from the spooling area with
+        <filename>faxcron</filename> with the given frequency
+        (see systemd.time(7)).
+      '';
+    };
+    faxqclean.archiving = mkOption {
+      type = enum [ "never" "as-flagged" "always" ];
+      default = "as-flagged";
+      example = "always";
+      description = ''
+        Enable or suppress job archiving:
+        <literal>never</literal> disables job archiving,
+        <literal>as-flagged</literal> archives jobs that
+        have been flagged for archiving by sendfax,
+        <literal>always</literal> forces archiving of all jobs.
+        See also sendfax(1) and faxqclean(8).
+      '';
+    };
+    faxqclean.doneqMinutes = mkOption {
+      type = int1;
+      default = 15;
+      example = literalExample ''24*60'';
+      description = ''
+        Set the job
+        age threshold (in minutes) that controls how long
+        jobs may reside in the doneq directory.
+      '';
+    };
+    faxqclean.docqMinutes = mkOption {
+      type = int1;
+      default = 60;
+      example = literalExample ''24*60'';
+      description = ''
+        Set the document
+        age threshold (in minutes) that controls how long
+        unreferenced files may reside in the docq directory.
+      '';
+    };
+
+  };
+
+
+  config.services.hylafax =
+    mkIf
+    (config.services.hylafax.enable)
+    (mkMerge [ defaultConfig localConfig ])
+  ;
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/hylafax/spool.sh b/nixpkgs/nixos/modules/services/networking/hylafax/spool.sh
new file mode 100755
index 000000000000..31e930e8c597
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/hylafax/spool.sh
@@ -0,0 +1,111 @@
+#! @shell@ -e
+
+# The following lines create/update the HylaFAX spool directory:
+# Subdirectories/files with persistent data are kept,
+# other directories/files are removed/recreated,
+# mostly from the template spool
+# directory in the HylaFAX package.
+
+# This block explains how the spool area is
+# derived from the spool template in the HylaFAX package:
+#
+#                  + capital letter: directory; file otherwise
+#                  + P/p: persistent directory
+#                  + F/f: directory with symlinks per entry
+#                  + T/t: temporary data
+#                  + S/s: single symlink into package
+#                  |
+#                  | + u: change ownership to uucp:uucp
+#                  | + U: ..also change access mode to user-only
+#                  | |
+# archive          P U
+# bin              S
+# client           T u  (client connection info)
+# config           S
+# COPYRIGHT        s
+# dev              T u  (maybe some FIFOs)
+# docq             P U
+# doneq            P U
+# etc              F    contains customized config files!
+# etc/hosts.hfaxd  f
+# etc/xferfaxlog   f
+# info             P u  (database of called devices)
+# log              P u  (communication logs)
+# pollq            P U
+# recvq            P u
+# sendq            P U
+# status           T u  (modem status info files)
+# tmp              T U
+
+
+shopt -s dotglob  # if bash sees "*", it also includes dot files
+lnsym () { ln --symbol "$@" ; }
+lnsymfrc () { ln --symbolic --force "$@" ; }
+cprd () { cp --remove-destination "$@" ; }
+update () { install --owner=@faxuser@ --group=@faxgroup@ "$@" ; }
+
+
+## create/update spooling area
+
+update --mode=0750 -d "@spoolAreaPath@"
+cd "@spoolAreaPath@"
+
+persist=(archive docq doneq info log pollq recvq sendq)
+
+# remove entries that don't belong here
+touch dummy  # ensure "*" resolves to something
+for k in *
+do
+  keep=0
+  for j in "${persist[@]}" xferfaxlog clientlog faxcron.lastrun
+  do
+    if test "$k" == "$j"
+    then
+      keep=1
+      break
+    fi
+  done
+  if test "$keep" == "0"
+  then
+    rm --recursive "$k"
+  fi
+done
+
+# create persistent data directories (unless they exist already)
+update --mode=0700 -d "${persist[@]}"
+chmod 0755 info log recvq
+
+# create ``xferfaxlog``, ``faxcron.lastrun``, ``clientlog``
+touch clientlog faxcron.lastrun xferfaxlog
+chown @faxuser@:@faxgroup@ clientlog faxcron.lastrun xferfaxlog
+
+# create symlinks for frozen directories/files
+lnsym --target-directory=. "@hylafax@"/spool/{COPYRIGHT,bin,config}
+
+# create empty temporary directories
+update --mode=0700 -d client dev status
+update -d tmp
+
+
+## create and fill etc
+
+install -d "@spoolAreaPath@/etc"
+cd "@spoolAreaPath@/etc"
+
+# create symlinks to all files in template's etc
+lnsym --target-directory=. "@hylafax@/spool/etc"/*
+
+# set LOCKDIR in setup.cache
+sed --regexp-extended 's|^(UUCP_LOCKDIR=).*$|\1'"'@lockPath@'|g" --in-place setup.cache
+
+# etc/{xferfaxlog,lastrun} are stored in the spool root
+lnsymfrc --target-directory=. ../xferfaxlog
+lnsymfrc --no-target-directory ../faxcron.lastrun lastrun
+
+# etc/hosts.hfaxd is provided by the NixOS configuration
+lnsymfrc --no-target-directory "@userAccessFile@" hosts.hfaxd
+
+# etc/config and etc/config.${DEVID} must be copied:
+# hfaxd reads these file after locking itself up in a chroot
+cprd --no-target-directory "@globalConfigPath@" config
+cprd --target-directory=. "@modemConfigPath@"/*
diff --git a/nixpkgs/nixos/modules/services/networking/hylafax/systemd.nix b/nixpkgs/nixos/modules/services/networking/hylafax/systemd.nix
new file mode 100644
index 000000000000..b9b9b9dca4f0
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/hylafax/systemd.nix
@@ -0,0 +1,249 @@
+{ config, lib, pkgs, ... }:
+
+
+let
+
+  inherit (lib) mkIf mkMerge;
+  inherit (lib) concatStringsSep optionalString;
+
+  cfg = config.services.hylafax;
+  mapModems = lib.forEach (lib.attrValues cfg.modems);
+
+  mkConfigFile = name: conf:
+    # creates hylafax config file,
+    # makes sure "Include" is listed *first*
+    let
+      mkLines = conf:
+        (lib.concatLists
+        (lib.flip lib.mapAttrsToList conf
+        (k: map (v: ''${k}: ${v}'')
+      )));
+      include = mkLines { Include = conf.Include or []; };
+      other = mkLines ( conf // { Include = []; } );
+    in
+      pkgs.writeText ''hylafax-config${name}''
+      (concatStringsSep "\n" (include ++ other));
+
+  globalConfigPath = mkConfigFile "" cfg.faxqConfig;
+
+  modemConfigPath =
+    let
+      mkModemConfigFile = { config, name, ... }:
+        mkConfigFile ''.${name}''
+        (cfg.commonModemConfig // config);
+      mkLine = { name, type, ... }@modem: ''
+        # check if modem config file exists:
+        test -f "${pkgs.hylafaxplus}/spool/config/${type}"
+        ln \
+          --symbolic \
+          --no-target-directory \
+          "${mkModemConfigFile modem}" \
+          "$out/config.${name}"
+      '';
+    in
+      pkgs.runCommand "hylafax-config-modems" { preferLocalBuild = true; }
+      ''mkdir --parents "$out/" ${concatStringsSep "\n" (mapModems mkLine)}'';
+
+  setupSpoolScript = pkgs.substituteAll {
+    name = "hylafax-setup-spool.sh";
+    src = ./spool.sh;
+    isExecutable = true;
+    inherit (pkgs.stdenv) shell;
+    hylafax = pkgs.hylafaxplus;
+    faxuser = "uucp";
+    faxgroup = "uucp";
+    lockPath = "/var/lock";
+    inherit globalConfigPath modemConfigPath;
+    inherit (cfg) sendmailPath spoolAreaPath userAccessFile;
+  };
+
+  waitFaxqScript = pkgs.substituteAll {
+    # This script checks the modems status files
+    # and waits until all modems report readiness.
+    name = "hylafax-faxq-wait-start.sh";
+    src = ./faxq-wait.sh;
+    isExecutable = true;
+    timeoutSec = toString 10;
+    inherit (pkgs.stdenv) shell;
+    inherit (cfg) spoolAreaPath;
+  };
+
+  sockets.hylafax-hfaxd = {
+    description = "HylaFAX server socket";
+    documentation = [ "man:hfaxd(8)" ];
+    wantedBy = [ "multi-user.target" ];
+    listenStreams = [ "127.0.0.1:4559" ];
+    socketConfig.FreeBind = true;
+    socketConfig.Accept = true;
+  };
+
+  paths.hylafax-faxq = {
+    description = "HylaFAX queue manager sendq watch";
+    documentation = [ "man:faxq(8)" "man:sendq(5)" ];
+    wantedBy = [ "multi-user.target" ];
+    pathConfig.PathExistsGlob = [ ''${cfg.spoolAreaPath}/sendq/q*'' ];
+  };
+
+  timers = mkMerge [
+    (
+      mkIf (cfg.faxcron.enable.frequency!=null)
+      { hylafax-faxcron.timerConfig.Persistent = true; }
+    )
+    (
+      mkIf (cfg.faxqclean.enable.frequency!=null)
+      { hylafax-faxqclean.timerConfig.Persistent = true; }
+    )
+  ];
+
+  hardenService =
+    # Add some common systemd service hardening settings,
+    # but allow each service (here) to override
+    # settings by explicitely setting those to `null`.
+    # More hardening would be nice but makes
+    # customizing hylafax setups very difficult.
+    # If at all, it should only be added along
+    # with some options to customize it.
+    let
+      hardening = {
+        PrivateDevices = true;  # breaks /dev/tty...
+        PrivateNetwork = true;
+        PrivateTmp = true;
+        ProtectControlGroups = true;
+        #ProtectHome = true;  # breaks custom spool dirs
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        #ProtectSystem = "strict";  # breaks custom spool dirs
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+      };
+      filter = key: value: (value != null) || ! (lib.hasAttr key hardening);
+      apply = service: lib.filterAttrs filter (hardening // (service.serviceConfig or {}));
+    in
+      service: service // { serviceConfig = apply service; };
+
+  services.hylafax-spool = {
+    description = "HylaFAX spool area preparation";
+    documentation = [ "man:hylafax-server(4)" ];
+    script = ''
+      ${setupSpoolScript}
+      cd "${cfg.spoolAreaPath}"
+      ${cfg.spoolExtraInit}
+      if ! test -f "${cfg.spoolAreaPath}/etc/hosts.hfaxd"
+      then
+        echo hosts.hfaxd is missing
+        exit 1
+      fi
+    '';
+    serviceConfig.ExecStop = ''${setupSpoolScript}'';
+    serviceConfig.RemainAfterExit = true;
+    serviceConfig.Type = "oneshot";
+    unitConfig.RequiresMountsFor = [ cfg.spoolAreaPath ];
+  };
+
+  services.hylafax-faxq = {
+    description = "HylaFAX queue manager";
+    documentation = [ "man:faxq(8)" ];
+    requires = [ "hylafax-spool.service" ];
+    after = [ "hylafax-spool.service" ];
+    wants = mapModems ( { name, ... }: ''hylafax-faxgetty@${name}.service'' );
+    wantedBy = mkIf cfg.autostart [ "multi-user.target" ];
+    serviceConfig.Type = "forking";
+    serviceConfig.ExecStart = ''${pkgs.hylafaxplus}/spool/bin/faxq -q "${cfg.spoolAreaPath}"'';
+    # This delays the "readiness" of this service until
+    # all modems are initialized (or a timeout is reached).
+    # Otherwise, sending a fax with the fax service
+    # stopped will always yield a failed send attempt:
+    # The fax service is started when the job is created with
+    # `sendfax`, but modems need some time to initialize.
+    serviceConfig.ExecStartPost = [ ''${waitFaxqScript}'' ];
+    # faxquit fails if the pipe is already gone
+    # (e.g. the service is already stopping)
+    serviceConfig.ExecStop = ''-${pkgs.hylafaxplus}/spool/bin/faxquit -q "${cfg.spoolAreaPath}"'';
+    # disable some systemd hardening settings
+    serviceConfig.PrivateDevices = null;
+    serviceConfig.RestrictRealtime = null;
+  };
+
+  services."hylafax-hfaxd@" = {
+    description = "HylaFAX server";
+    documentation = [ "man:hfaxd(8)" ];
+    after = [ "hylafax-faxq.service" ];
+    requires = [ "hylafax-faxq.service" ];
+    serviceConfig.StandardInput = "socket";
+    serviceConfig.StandardOutput = "socket";
+    serviceConfig.ExecStart = ''${pkgs.hylafaxplus}/spool/bin/hfaxd -q "${cfg.spoolAreaPath}" -d -I'';
+    unitConfig.RequiresMountsFor = [ cfg.userAccessFile ];
+    # disable some systemd hardening settings
+    serviceConfig.PrivateDevices = null;
+    serviceConfig.PrivateNetwork = null;
+  };
+
+  services.hylafax-faxcron = rec {
+    description = "HylaFAX spool area maintenance";
+    documentation = [ "man:faxcron(8)" ];
+    after = [ "hylafax-spool.service" ];
+    requires = [ "hylafax-spool.service" ];
+    wantedBy = mkIf cfg.faxcron.enable.spoolInit requires;
+    startAt = mkIf (cfg.faxcron.enable.frequency!=null) cfg.faxcron.enable.frequency;
+    serviceConfig.ExecStart = concatStringsSep " " [
+      ''${pkgs.hylafaxplus}/spool/bin/faxcron''
+      ''-q "${cfg.spoolAreaPath}"''
+      ''-info ${toString cfg.faxcron.infoDays}''
+      ''-log  ${toString cfg.faxcron.logDays}''
+      ''-rcv  ${toString cfg.faxcron.rcvDays}''
+    ];
+  };
+
+  services.hylafax-faxqclean = rec {
+    description = "HylaFAX spool area queue cleaner";
+    documentation = [ "man:faxqclean(8)" ];
+    after = [ "hylafax-spool.service" ];
+    requires = [ "hylafax-spool.service" ];
+    wantedBy = mkIf cfg.faxqclean.enable.spoolInit requires;
+    startAt = mkIf (cfg.faxqclean.enable.frequency!=null) cfg.faxqclean.enable.frequency;
+    serviceConfig.ExecStart = concatStringsSep " " [
+      ''${pkgs.hylafaxplus}/spool/bin/faxqclean''
+      ''-q "${cfg.spoolAreaPath}"''
+      ''-v''
+      (optionalString (cfg.faxqclean.archiving!="never") ''-a'')
+      (optionalString (cfg.faxqclean.archiving=="always")  ''-A'')
+      ''-j ${toString (cfg.faxqclean.doneqMinutes*60)}''
+      ''-d ${toString (cfg.faxqclean.docqMinutes*60)}''
+    ];
+  };
+
+  mkFaxgettyService = { name, ... }:
+    lib.nameValuePair ''hylafax-faxgetty@${name}'' rec {
+      description = "HylaFAX faxgetty for %I";
+      documentation = [ "man:faxgetty(8)" ];
+      bindsTo = [ "dev-%i.device" ];
+      requires = [ "hylafax-spool.service" ];
+      after = bindsTo ++ requires;
+      before = [ "hylafax-faxq.service" "getty.target" ];
+      unitConfig.StopWhenUnneeded = true;
+      unitConfig.AssertFileNotEmpty = ''${cfg.spoolAreaPath}/etc/config.%I'';
+      serviceConfig.UtmpIdentifier = "%I";
+      serviceConfig.TTYPath = "/dev/%I";
+      serviceConfig.Restart = "always";
+      serviceConfig.KillMode = "process";
+      serviceConfig.IgnoreSIGPIPE = false;
+      serviceConfig.ExecStart = ''-${pkgs.hylafaxplus}/spool/bin/faxgetty -q "${cfg.spoolAreaPath}" /dev/%I'';
+      # faxquit fails if the pipe is already gone
+      # (e.g. the service is already stopping)
+      serviceConfig.ExecStop = ''-${pkgs.hylafaxplus}/spool/bin/faxquit -q "${cfg.spoolAreaPath}" %I'';
+      # disable some systemd hardening settings
+      serviceConfig.PrivateDevices = null;
+      serviceConfig.RestrictRealtime = null;
+    };
+
+  modemServices =
+    lib.listToAttrs (mapModems mkFaxgettyService);
+
+in
+
+{
+  config.systemd = mkIf cfg.enable {
+    inherit sockets timers paths;
+    services = lib.mapAttrs (lib.const hardenService) (services // modemServices);
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/i2p.nix b/nixpkgs/nixos/modules/services/networking/i2p.nix
new file mode 100644
index 000000000000..3b6010531f13
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/i2p.nix
@@ -0,0 +1,34 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.i2p;
+  homeDir = "/var/lib/i2p";
+in {
+  ###### interface
+  options.services.i2p.enable = mkEnableOption "I2P router";
+
+  ###### implementation
+  config = mkIf cfg.enable {
+    users.users.i2p = {
+      group = "i2p";
+      description = "i2p User";
+      home = homeDir;
+      createHome = true;
+      uid = config.ids.uids.i2p;
+    };
+    users.groups.i2p.gid = config.ids.gids.i2p;
+    systemd.services.i2p = {
+      description = "I2P router with administration interface for hidden services";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        User = "i2p";
+        WorkingDirectory = homeDir;
+        Restart = "on-abort";
+        ExecStart = "${pkgs.i2p}/bin/i2prouter-plain";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/i2pd.nix b/nixpkgs/nixos/modules/services/networking/i2pd.nix
new file mode 100644
index 000000000000..93a21fd4c97e
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/i2pd.nix
@@ -0,0 +1,684 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.i2pd;
+
+  homeDir = "/var/lib/i2pd";
+
+  strOpt = k: v: k + " = " + v;
+  boolOpt = k: v: k + " = " + boolToString v;
+  intOpt = k: v: k + " = " + toString v;
+  lstOpt = k: xs: k + " = " + concatStringsSep "," xs;
+  optionalNullString = o: s: optional (s != null) (strOpt o s);
+  optionalNullBool = o: b: optional (b != null) (boolOpt o b);
+  optionalNullInt = o: i: optional (i != null) (intOpt o i);
+  optionalEmptyList = o: l: optional ([] != l) (lstOpt o l);
+
+  mkEnableTrueOption = name: mkEnableOption name // { default = true; };
+
+  mkEndpointOpt = name: addr: port: {
+    enable = mkEnableOption name;
+    name = mkOption {
+      type = types.str;
+      default = name;
+      description = "The endpoint name.";
+    };
+    address = mkOption {
+      type = types.str;
+      default = addr;
+      description = "Bind address for ${name} endpoint.";
+    };
+    port = mkOption {
+      type = types.int;
+      default = port;
+      description = "Bind port for ${name} endoint.";
+    };
+  };
+
+  i2cpOpts = name: {
+    length = mkOption {
+      type = types.int;
+      description = "Guaranteed minimum hops for ${name} tunnels.";
+      default = 3;
+    };
+    quantity = mkOption {
+      type = types.int;
+      description = "Number of simultaneous ${name} tunnels.";
+      default = 5;
+    };
+  };
+
+  mkKeyedEndpointOpt = name: addr: port: keyloc:
+    (mkEndpointOpt name addr port) // {
+      keys = mkOption {
+        type = with types; nullOr str;
+        default = keyloc;
+        description = ''
+          File to persist ${lib.toUpper name} keys.
+        '';
+      };
+      inbound = i2cpOpts name;
+      outbound = i2cpOpts name;
+      latency.min = mkOption {
+        type = with types; nullOr int;
+        description = "Min latency for tunnels.";
+        default = null;
+      };
+      latency.max = mkOption {
+        type = with types; nullOr int;
+        description = "Max latency for tunnels.";
+        default = null;
+      };
+    };
+
+  commonTunOpts = name: {
+    outbound = i2cpOpts name;
+    inbound = i2cpOpts name;
+    crypto.tagsToSend = mkOption {
+      type = types.int;
+      description = "Number of ElGamal/AES tags to send.";
+      default = 40;
+    };
+    destination = mkOption {
+      type = types.str;
+      description = "Remote endpoint, I2P hostname or b32.i2p address.";
+    };
+    keys = mkOption {
+      type = types.str;
+      default = name + "-keys.dat";
+      description = "Keyset used for tunnel identity.";
+    };
+  } // mkEndpointOpt name "127.0.0.1" 0;
+
+  sec = name: "\n[" + name + "]";
+  notice = "# DO NOT EDIT -- this file has been generated automatically.";
+  i2pdConf = let
+    opts = [
+      notice
+      (strOpt "loglevel" cfg.logLevel)
+      (boolOpt "logclftime" cfg.logCLFTime)
+      (boolOpt "ipv4" cfg.enableIPv4)
+      (boolOpt "ipv6" cfg.enableIPv6)
+      (boolOpt "notransit" cfg.notransit)
+      (boolOpt "floodfill" cfg.floodfill)
+      (intOpt "netid" cfg.netid)
+    ] ++ (optionalNullInt "bandwidth" cfg.bandwidth)
+      ++ (optionalNullInt "port" cfg.port)
+      ++ (optionalNullString "family" cfg.family)
+      ++ (optionalNullString "datadir" cfg.dataDir)
+      ++ (optionalNullInt "share" cfg.share)
+      ++ (optionalNullBool "ssu" cfg.ssu)
+      ++ (optionalNullBool "ntcp" cfg.ntcp)
+      ++ (optionalNullString "ntcpproxy" cfg.ntcpProxy)
+      ++ (optionalNullString "ifname" cfg.ifname)
+      ++ (optionalNullString "ifname4" cfg.ifname4)
+      ++ (optionalNullString "ifname6" cfg.ifname6)
+      ++ [
+      (sec "limits")
+      (intOpt "transittunnels" cfg.limits.transittunnels)
+      (intOpt "coresize" cfg.limits.coreSize)
+      (intOpt "openfiles" cfg.limits.openFiles)
+      (intOpt "ntcphard" cfg.limits.ntcpHard)
+      (intOpt "ntcpsoft" cfg.limits.ntcpSoft)
+      (intOpt "ntcpthreads" cfg.limits.ntcpThreads)
+      (sec "upnp")
+      (boolOpt "enabled" cfg.upnp.enable)
+      (sec "precomputation")
+      (boolOpt "elgamal" cfg.precomputation.elgamal)
+      (sec "reseed")
+      (boolOpt "verify" cfg.reseed.verify)
+    ] ++ (optionalNullString "file" cfg.reseed.file)
+      ++ (optionalEmptyList "urls" cfg.reseed.urls)
+      ++ (optionalNullString "floodfill" cfg.reseed.floodfill)
+      ++ (optionalNullString "zipfile" cfg.reseed.zipfile)
+      ++ (optionalNullString "proxy" cfg.reseed.proxy)
+      ++ [
+      (sec "trust")
+      (boolOpt "enabled" cfg.trust.enable)
+      (boolOpt "hidden" cfg.trust.hidden)
+    ] ++ (optionalEmptyList "routers" cfg.trust.routers)
+      ++ (optionalNullString "family" cfg.trust.family)
+      ++ [
+      (sec "websockets")
+      (boolOpt "enabled" cfg.websocket.enable)
+      (strOpt "address" cfg.websocket.address)
+      (intOpt "port" cfg.websocket.port)
+      (sec "exploratory")
+      (intOpt "inbound.length" cfg.exploratory.inbound.length)
+      (intOpt "inbound.quantity" cfg.exploratory.inbound.quantity)
+      (intOpt "outbound.length" cfg.exploratory.outbound.length)
+      (intOpt "outbound.quantity" cfg.exploratory.outbound.quantity)
+      (sec "ntcp2")
+      (boolOpt "enabled" cfg.ntcp2.enable)
+      (boolOpt "published" cfg.ntcp2.published)
+      (intOpt "port" cfg.ntcp2.port)
+      (sec "addressbook")
+      (strOpt "defaulturl" cfg.addressbook.defaulturl)
+    ] ++ (optionalEmptyList "subscriptions" cfg.addressbook.subscriptions)
+      ++ (flip map
+      (collect (proto: proto ? port && proto ? address) cfg.proto)
+      (proto: let protoOpts = [
+        (sec proto.name)
+        (boolOpt "enabled" proto.enable)
+        (strOpt "address" proto.address)
+        (intOpt "port" proto.port)
+        ] ++ (if proto ? keys then optionalNullString "keys" proto.keys else [])
+        ++ (if proto ? auth then optionalNullBool "auth" proto.auth else [])
+        ++ (if proto ? user then optionalNullString "user" proto.user else [])
+        ++ (if proto ? pass then optionalNullString "pass" proto.pass else [])
+        ++ (if proto ? strictHeaders then optionalNullBool "strictheaders" proto.strictHeaders else [])
+        ++ (if proto ? hostname then optionalNullString "hostname" proto.hostname else [])
+        ++ (if proto ? outproxy then optionalNullString "outproxy" proto.outproxy else [])
+        ++ (if proto ? outproxyPort then optionalNullInt "outproxyport" proto.outproxyPort else [])
+        ++ (if proto ? outproxyEnable then optionalNullBool "outproxy.enabled" proto.outproxyEnable else []);
+        in (concatStringsSep "\n" protoOpts)
+      ));
+  in
+    pkgs.writeText "i2pd.conf" (concatStringsSep "\n" opts);
+
+  tunnelConf = let opts = [
+    notice
+    (flip map
+      (collect (tun: tun ? port && tun ? destination) cfg.outTunnels)
+      (tun: let outTunOpts = [
+        (sec tun.name)
+        "type = client"
+        (intOpt "port" tun.port)
+        (strOpt "destination" tun.destination)
+        ] ++ (if tun ? destinationPort then optionalNullInt "destinationport" tun.destinationPort else [])
+        ++ (if tun ? keys then
+            optionalNullString "keys" tun.keys else [])
+        ++ (if tun ? address then
+            optionalNullString "address" tun.address else [])
+        ++ (if tun ? inbound.length then
+            optionalNullInt "inbound.length" tun.inbound.length else [])
+        ++ (if tun ? inbound.quantity then
+            optionalNullInt "inbound.quantity" tun.inbound.quantity else [])
+        ++ (if tun ? outbound.length then
+            optionalNullInt "outbound.length" tun.outbound.length else [])
+        ++ (if tun ? outbound.quantity then
+            optionalNullInt "outbound.quantity" tun.outbound.quantity else [])
+        ++ (if tun ? crypto.tagsToSend then
+            optionalNullInt "crypto.tagstosend" tun.crypto.tagsToSend else []);
+        in concatStringsSep "\n" outTunOpts))
+    (flip map
+      (collect (tun: tun ? port && tun ? address) cfg.inTunnels)
+      (tun: let inTunOpts = [
+        (sec tun.name)
+        "type = server"
+        (intOpt "port" tun.port)
+        (strOpt "host" tun.address)
+      ] ++ (if tun ? destination then
+            optionalNullString "destination" tun.destination else [])
+        ++ (if tun ? keys then
+            optionalNullString "keys" tun.keys else [])
+        ++ (if tun ? inPort then
+            optionalNullInt "inport" tun.inPort else [])
+        ++ (if tun ? accessList then
+            optionalEmptyList "accesslist" tun.accessList else []);
+        in concatStringsSep "\n" inTunOpts))];
+    in pkgs.writeText "i2pd-tunnels.conf" opts;
+
+  i2pdSh = pkgs.writeScriptBin "i2pd" ''
+    #!/bin/sh
+    exec ${pkgs.i2pd}/bin/i2pd \
+      ${if cfg.address == null then "" else "--host="+cfg.address} \
+      --service \
+      --conf=${i2pdConf} \
+      --tunconf=${tunnelConf}
+  '';
+
+in
+
+{
+
+  imports = [
+    (mkRenamedOptionModule [ "services" "i2pd" "extIp" ] [ "services" "i2pd" "address" ])
+  ];
+
+  ###### interface
+
+  options = {
+
+    services.i2pd = {
+
+      enable = mkEnableOption "I2Pd daemon" // {
+        description = ''
+          Enables I2Pd as a running service upon activation.
+          Please read http://i2pd.readthedocs.io/en/latest/ for further
+          configuration help.
+        '';
+      };
+
+      logLevel = mkOption {
+        type = types.enum ["debug" "info" "warn" "error"];
+        default = "error";
+        description = ''
+          The log level. <command>i2pd</command> defaults to "info"
+          but that generates copious amounts of log messages.
+
+          We default to "error" which is similar to the default log
+          level of <command>tor</command>.
+        '';
+      };
+
+      logCLFTime = mkEnableOption "Full CLF-formatted date and time to log";
+
+      address = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          Your external IP or hostname.
+        '';
+      };
+
+      family = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          Specify a family the router belongs to.
+        '';
+      };
+
+      dataDir = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          Alternative path to storage of i2pd data (RI, keys, peer profiles, ...)
+        '';
+      };
+
+      share = mkOption {
+        type = types.int;
+        default = 100;
+        description = ''
+          Limit of transit traffic from max bandwidth in percents.
+        '';
+      };
+
+      ifname = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          Network interface to bind to.
+        '';
+      };
+
+      ifname4 = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          IPv4 interface to bind to.
+        '';
+      };
+
+      ifname6 = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          IPv6 interface to bind to.
+        '';
+      };
+
+      ntcpProxy = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          Proxy URL for NTCP transport.
+        '';
+      };
+
+      ntcp = mkEnableTrueOption "ntcp";
+      ssu = mkEnableTrueOption "ssu";
+
+      notransit = mkEnableOption "notransit" // {
+        description = ''
+          Tells the router to not accept transit tunnels during startup.
+        '';
+      };
+
+      floodfill = mkEnableOption "floodfill" // {
+        description = ''
+          If the router is declared to be unreachable and needs introduction nodes.
+        '';
+      };
+
+      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.
+           If not set, <command>i2pd</command> defaults to 32KBps.
+        '';
+      };
+
+      port = mkOption {
+        type = with types; nullOr int;
+        default = null;
+        description = ''
+          I2P listen port. If no one is given the router will pick between 9111 and 30777.
+        '';
+      };
+
+      enableIPv4 = mkEnableTrueOption "IPv4 connectivity";
+      enableIPv6 = mkEnableOption "IPv6 connectivity";
+      nat = mkEnableTrueOption "NAT bypass";
+
+      upnp.enable = mkEnableOption "UPnP service discovery";
+      upnp.name = mkOption {
+        type = types.str;
+        default = "I2Pd";
+        description = ''
+          Name i2pd appears in UPnP forwardings list.
+        '';
+      };
+
+      precomputation.elgamal = mkEnableTrueOption "Precomputed ElGamal tables" // {
+        description = ''
+          Whenever to use precomputated tables for ElGamal.
+          <command>i2pd</command> defaults to <literal>false</literal>
+          to save 64M of memory (and looses some performance).
+
+          We default to <literal>true</literal> as that is what most
+          users want anyway.
+        '';
+      };
+
+      reseed.verify = mkEnableOption "SU3 signature verification";
+
+      reseed.file = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          Full path to SU3 file to reseed from.
+        '';
+      };
+
+      reseed.urls = mkOption {
+        type = with types; listOf str;
+        default = [];
+        description = ''
+          Reseed URLs.
+        '';
+      };
+
+      reseed.floodfill = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          Path to router info of floodfill to reseed from.
+        '';
+      };
+
+      reseed.zipfile = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          Path to local .zip file to reseed from.
+        '';
+      };
+
+      reseed.proxy = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          URL for reseed proxy, supports http/socks.
+        '';
+      };
+
+     addressbook.defaulturl = mkOption {
+        type = types.str;
+        default = "http://joajgazyztfssty4w2on5oaqksz6tqoxbduy553y34mf4byv6gpq.b32.i2p/export/alive-hosts.txt";
+        description = ''
+          AddressBook subscription URL for initial setup
+        '';
+      };
+     addressbook.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
+        '';
+      };
+
+      trust.enable = mkEnableOption "Explicit trust options";
+
+      trust.family = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          Router Familiy to trust for first hops.
+        '';
+      };
+
+      trust.routers = mkOption {
+        type = with types; listOf str;
+        default = [];
+        description = ''
+          Only connect to the listed routers.
+        '';
+      };
+
+      trust.hidden = mkEnableOption "Router concealment";
+
+      websocket = mkEndpointOpt "websockets" "127.0.0.1" 7666;
+
+      exploratory.inbound = i2cpOpts "exploratory";
+      exploratory.outbound = i2cpOpts "exploratory";
+
+      ntcp2.enable = mkEnableTrueOption "NTCP2.";
+      ntcp2.published = mkEnableOption "NTCP2 publication";
+      ntcp2.port = mkOption {
+        type = types.int;
+        default = 0;
+        description = ''
+          Port to listen for incoming NTCP2 connections (0=auto).
+        '';
+      };
+
+      limits.transittunnels = mkOption {
+        type = types.int;
+        default = 2500;
+        description = ''
+          Maximum number of active transit sessions.
+        '';
+      };
+
+      limits.coreSize = mkOption {
+        type = types.int;
+        default = 0;
+        description = ''
+          Maximum size of corefile in Kb (0 - use system limit).
+        '';
+      };
+
+      limits.openFiles = mkOption {
+        type = types.int;
+        default = 0;
+        description = ''
+          Maximum number of open files (0 - use system default).
+        '';
+      };
+
+      limits.ntcpHard = mkOption {
+        type = types.int;
+        default = 0;
+        description = ''
+          Maximum number of active transit sessions.
+        '';
+      };
+
+      limits.ntcpSoft = mkOption {
+        type = types.int;
+        default = 0;
+        description = ''
+          Threshold to start probabalistic backoff with ntcp sessions (default: use system limit).
+        '';
+      };
+
+      limits.ntcpThreads = mkOption {
+        type = types.int;
+        default = 1;
+        description = ''
+          Maximum number of threads used by NTCP DH worker.
+        '';
+      };
+
+      proto.http = (mkEndpointOpt "http" "127.0.0.1" 7070) // {
+
+        auth = mkEnableOption "Webconsole authentication";
+
+        user = mkOption {
+          type = types.str;
+          default = "i2pd";
+          description = ''
+            Username for webconsole access
+          '';
+        };
+
+        pass = mkOption {
+          type = types.str;
+          default = "i2pd";
+          description = ''
+            Password for webconsole access.
+          '';
+        };
+
+        strictHeaders = mkOption {
+          type = with types; nullOr bool;
+          default = null;
+          description = ''
+            Enable strict host checking on WebUI.
+          '';
+        };
+
+        hostname = mkOption {
+          type = with types; nullOr str;
+          default = null;
+          description = ''
+            Expected hostname for WebUI.
+          '';
+        };
+      };
+
+      proto.httpProxy = (mkKeyedEndpointOpt "httpproxy" "127.0.0.1" 4444 "httpproxy-keys.dat")
+      // {
+        outproxy = mkOption {
+          type = with types; nullOr str;
+          default = null;
+          description = "Upstream outproxy bind address.";
+        };
+      };
+      proto.socksProxy = (mkKeyedEndpointOpt "socksproxy" "127.0.0.1" 4447 "socksproxy-keys.dat")
+      // {
+        outproxyEnable = mkEnableOption "SOCKS outproxy";
+        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;
+
+      outTunnels = mkOption {
+        default = {};
+        type = with types; attrsOf (submodule (
+          { name, ... }: {
+            options = {
+              destinationPort = mkOption {
+                type = with types; nullOr int;
+                default = null;
+                description = "Connect to particular port at destination.";
+              };
+            } // commonTunOpts name;
+            config = {
+              name = mkDefault name;
+            };
+          }
+        ));
+        description = ''
+          Connect to someone as a client and establish a local accept endpoint
+        '';
+      };
+
+      inTunnels = mkOption {
+        default = {};
+        type = with types; attrsOf (submodule (
+          { name, ... }: {
+            options = {
+              inPort = mkOption {
+                type = types.int;
+                default = 0;
+                description = "Service port. Default to the tunnel's listen port.";
+              };
+              accessList = mkOption {
+                type = with types; listOf str;
+                default = [];
+                description = "I2P nodes that are allowed to connect to this service.";
+              };
+            } // commonTunOpts name;
+            config = {
+              name = mkDefault name;
+            };
+          }
+        ));
+        description = ''
+          Serve something on I2P network at port and delegate requests to address inPort.
+        '';
+      };
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    users.users.i2pd = {
+      group = "i2pd";
+      description = "I2Pd User";
+      home = homeDir;
+      createHome = true;
+      uid = config.ids.uids.i2pd;
+    };
+
+    users.groups.i2pd.gid = config.ids.gids.i2pd;
+
+    systemd.services.i2pd = {
+      description = "Minimal I2P router";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig =
+      {
+        User = "i2pd";
+        WorkingDirectory = homeDir;
+        Restart = "on-abort";
+        ExecStart = "${i2pdSh}/bin/i2pd";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/iodine.nix b/nixpkgs/nixos/modules/services/networking/iodine.nix
new file mode 100644
index 000000000000..46051d7044b5
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/iodine.nix
@@ -0,0 +1,197 @@
+# NixOS module for iodine, ip over dns daemon
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.iodine;
+
+  iodinedUser = "iodined";
+
+  /* is this path made unreadable by ProtectHome = true ? */
+  isProtected = x: hasPrefix "/root" x || hasPrefix "/home" x;
+in
+{
+  imports = [
+    (mkRenamedOptionModule [ "services" "iodined" "enable" ] [ "services" "iodine" "server" "enable" ])
+    (mkRenamedOptionModule [ "services" "iodined" "domain" ] [ "services" "iodine" "server" "domain" ])
+    (mkRenamedOptionModule [ "services" "iodined" "ip" ] [ "services" "iodine" "server" "ip" ])
+    (mkRenamedOptionModule [ "services" "iodined" "extraConfig" ] [ "services" "iodine" "server" "extraConfig" ])
+    (mkRemovedOptionModule [ "services" "iodined" "client" ] "")
+  ];
+
+  ### configuration
+
+  options = {
+
+    services.iodine = {
+      clients = mkOption {
+        default = {};
+        description = ''
+          Each attribute of this option defines a systemd service that
+          runs iodine. Many or none may be defined.
+          The name of each service is
+          <literal>iodine-<replaceable>name</replaceable></literal>
+          where <replaceable>name</replaceable> is the name of the
+          corresponding attribute name.
+        '';
+        example = literalExample ''
+          {
+            foo = {
+              server = "tunnel.mdomain.com";
+              relay = "8.8.8.8";
+              extraConfig = "-v";
+            }
+          }
+        '';
+        type = types.attrsOf (
+          types.submodule (
+            {
+              options = {
+                server = mkOption {
+                  type = types.str;
+                  default = "";
+                  description = "Hostname of server running iodined";
+                  example = "tunnel.mydomain.com";
+                };
+
+                relay = mkOption {
+                  type = types.str;
+                  default = "";
+                  description = "DNS server to use as an intermediate relay to the iodined server";
+                  example = "8.8.8.8";
+                };
+
+                extraConfig = mkOption {
+                  type = types.str;
+                  default = "";
+                  description = "Additional command line parameters";
+                  example = "-l 192.168.1.10 -p 23";
+                };
+
+                passwordFile = mkOption {
+                  type = types.str;
+                  default = "";
+                  description = "Path to a file containing the password.";
+                };
+              };
+            }
+          )
+        );
+      };
+
+      server = {
+        enable = mkOption {
+          type = types.bool;
+          default = false;
+          description = "enable iodined server";
+        };
+
+        ip = mkOption {
+          type = types.str;
+          default = "";
+          description = "The assigned ip address or ip range";
+          example = "172.16.10.1/24";
+        };
+
+        domain = mkOption {
+          type = types.str;
+          default = "";
+          description = "Domain or subdomain of which nameservers point to us";
+          example = "tunnel.mydomain.com";
+        };
+
+        extraConfig = mkOption {
+          type = types.str;
+          default = "";
+          description = "Additional command line parameters";
+          example = "-l 192.168.1.10 -p 23";
+        };
+
+        passwordFile = mkOption {
+          type = types.str;
+          default = "";
+          description = "File that contains password";
+        };
+      };
+
+    };
+  };
+
+  ### implementation
+
+  config = mkIf (cfg.server.enable || cfg.clients != {}) {
+    environment.systemPackages = [ pkgs.iodine ];
+    boot.kernelModules = [ "tun" ];
+
+    systemd.services =
+      let
+        createIodineClientService = name: cfg:
+          {
+            description = "iodine client - ${name}";
+            after = [ "network.target" ];
+            wantedBy = [ "multi-user.target" ];
+            script = "exec ${pkgs.iodine}/bin/iodine -f -u ${iodinedUser} ${cfg.extraConfig} ${optionalString (cfg.passwordFile != "") "< \"${builtins.toString cfg.passwordFile}\""} ${cfg.relay} ${cfg.server}";
+            serviceConfig = {
+              RestartSec = "30s";
+              Restart = "always";
+
+              # hardening :
+              # Filesystem access
+              ProtectSystem = "strict";
+              ProtectHome = if isProtected cfg.passwordFile then "read-only" else "true" ;
+              PrivateTmp = true;
+              ReadWritePaths = "/dev/net/tun";
+              PrivateDevices = false;
+              ProtectKernelTunables = true;
+              ProtectKernelModules = true;
+              ProtectControlGroups = true;
+              # Caps
+              NoNewPrivileges = true;
+              # Misc.
+              LockPersonality = true;
+              RestrictRealtime = true;
+              PrivateMounts = true;
+              MemoryDenyWriteExecute = true;
+            };
+          };
+      in
+        listToAttrs (
+          mapAttrsToList
+            (name: value: nameValuePair "iodine-${name}" (createIodineClientService name value))
+            cfg.clients
+        ) // {
+          iodined = mkIf (cfg.server.enable) {
+            description = "iodine, ip over dns server daemon";
+            after = [ "network.target" ];
+            wantedBy = [ "multi-user.target" ];
+            script = "exec ${pkgs.iodine}/bin/iodined -f -u ${iodinedUser} ${cfg.server.extraConfig} ${optionalString (cfg.server.passwordFile != "") "< \"${builtins.toString cfg.server.passwordFile}\""} ${cfg.server.ip} ${cfg.server.domain}";
+            serviceConfig = {
+              # Filesystem access
+              ProtectSystem = "strict";
+              ProtectHome = if isProtected cfg.server.passwordFile then "read-only" else "true" ;
+              PrivateTmp = true;
+              ReadWritePaths = "/dev/net/tun";
+              PrivateDevices = false;
+              ProtectKernelTunables = true;
+              ProtectKernelModules = true;
+              ProtectControlGroups = true;
+              # Caps
+              NoNewPrivileges = true;
+              # Misc.
+              LockPersonality = true;
+              RestrictRealtime = true;
+              PrivateMounts = true;
+              MemoryDenyWriteExecute = true;
+            };
+          };
+        };
+
+    users.users.${iodinedUser} = {
+      uid = config.ids.uids.iodined;
+      description = "Iodine daemon user";
+    };
+    users.groups.iodined.gid = config.ids.gids.iodined;
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/iperf3.nix b/nixpkgs/nixos/modules/services/networking/iperf3.nix
new file mode 100644
index 000000000000..0fe378b225d7
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/iperf3.nix
@@ -0,0 +1,97 @@
+{ config, lib, pkgs, ... }: with lib;
+let
+  cfg = config.services.iperf3;
+
+  api = {
+    enable = mkEnableOption "iperf3 network throughput testing server";
+    port = mkOption {
+      type        = types.ints.u16;
+      default     = 5201;
+      description = "Server port to listen on for iperf3 client requsts.";
+    };
+    affinity = mkOption {
+      type        = types.nullOr types.ints.unsigned;
+      default     = null;
+      description = "CPU affinity for the process.";
+    };
+    bind = mkOption {
+      type        = types.nullOr types.str;
+      default     = null;
+      description = "Bind to the specific interface associated with the given address.";
+    };
+    openFirewall = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Open ports in the firewall for iperf3.";
+    };
+    verbose = mkOption {
+      type        = types.bool;
+      default     = false;
+      description = "Give more detailed output.";
+    };
+    forceFlush = mkOption {
+      type        = types.bool;
+      default     = false;
+      description = "Force flushing output at every interval.";
+    };
+    debug = mkOption {
+      type        = types.bool;
+      default     = false;
+      description = "Emit debugging output.";
+    };
+    rsaPrivateKey = mkOption {
+      type        = types.nullOr types.path;
+      default     = null;
+      description = "Path to the RSA private key (not password-protected) used to decrypt authentication credentials from the client.";
+    };
+    authorizedUsersFile = mkOption {
+      type        = types.nullOr types.path;
+      default     = null;
+      description = "Path to the configuration file containing authorized users credentials to run iperf tests.";
+    };
+    extraFlags = mkOption {
+      type        = types.listOf types.str;
+      default     = [ ];
+      description = "Extra flags to pass to iperf3(1).";
+    };
+  };
+
+  imp = {
+
+    networking.firewall = mkIf cfg.openFirewall {
+      allowedTCPPorts = [ cfg.port ];
+    };
+
+    systemd.services.iperf3 = {
+      description = "iperf3 daemon";
+      unitConfig.Documentation = "man:iperf3(1) https://iperf.fr/iperf-doc.php";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+
+      serviceConfig = {
+        Restart = "on-failure";
+        RestartSec = 2;
+        DynamicUser = true;
+        PrivateDevices = true;
+        CapabilityBoundingSet = "";
+        NoNewPrivileges = true;
+        ExecStart = ''
+          ${pkgs.iperf3}/bin/iperf \
+            --server \
+            --port ${toString cfg.port} \
+            ${optionalString (cfg.affinity != null) "--affinity ${toString cfg.affinity}"} \
+            ${optionalString (cfg.bind != null) "--bind ${cfg.bind}"} \
+            ${optionalString (cfg.rsaPrivateKey != null) "--rsa-private-key-path ${cfg.rsaPrivateKey}"} \
+            ${optionalString (cfg.authorizedUsersFile != null) "--authorized-users-path ${cfg.authorizedUsersFile}"} \
+            ${optionalString cfg.verbose "--verbose"} \
+            ${optionalString cfg.debug "--debug"} \
+            ${optionalString cfg.forceFlush "--forceflush"} \
+            ${escapeShellArgs cfg.extraFlags}
+        '';
+      };
+    };
+  };
+in {
+  options.services.iperf3 = api;
+  config = mkIf cfg.enable imp;
+}
diff --git a/nixpkgs/nixos/modules/services/networking/ircd-hybrid/builder.sh b/nixpkgs/nixos/modules/services/networking/ircd-hybrid/builder.sh
new file mode 100644
index 000000000000..38312210df25
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/ircd-hybrid/builder.sh
@@ -0,0 +1,31 @@
+source $stdenv/setup
+
+doSub() {
+    local src=$1
+    local dst=$2
+    mkdir -p $(dirname $dst)
+    substituteAll $src $dst
+}
+
+subDir=/
+for i in $scripts; do
+    if test "$(echo $i | cut -c1-2)" = "=>"; then
+        subDir=$(echo $i | cut -c3-)
+    else
+        dst=$out/$subDir/$(stripHash $i | sed 's/\.in//')
+        doSub $i $dst
+        chmod +x $dst # !!!
+    fi
+done
+
+subDir=/
+for i in $substFiles; do
+    if test "$(echo $i | cut -c1-2)" = "=>"; then
+        subDir=$(echo $i | cut -c3-)
+    else
+        dst=$out/$subDir/$(stripHash $i | sed 's/\.in//')
+        doSub $i $dst
+    fi
+done
+
+mkdir -p $out/bin
diff --git a/nixpkgs/nixos/modules/services/networking/ircd-hybrid/control.in b/nixpkgs/nixos/modules/services/networking/ircd-hybrid/control.in
new file mode 100644
index 000000000000..312dfaada329
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/ircd-hybrid/control.in
@@ -0,0 +1,26 @@
+#! @shell@ -e
+
+# Make sure that the environment is deterministic.
+export PATH=@coreutils@/bin
+
+if test "$1" = "start"; then
+	if ! @procps@/bin/pgrep ircd; then
+	if @ipv6Enabled@; then 
+		while ! @iproute@/sbin/ip addr | 
+			@gnugrep@/bin/grep inet6 | 
+			@gnugrep@/bin/grep global; do
+			sleep 1;
+		done;
+	fi;
+	rm -rf /home/ircd
+	mkdir -p /home/ircd
+	chown ircd: /home/ircd
+	cd /home/ircd
+    env - HOME=/homeless-shelter $extraEnv \
+        @su@/bin/su ircd --shell=/bin/sh -c ' @ircdHybrid@/bin/ircd -configfile @out@/conf/ircd.conf </dev/null -logfile /home/ircd/ircd.log' 2>&1 >/var/log/ircd-hybrid.out
+	fi;
+fi
+
+if test "$1" = "stop" ; then 
+	@procps@/bin/pkill ircd;
+fi;
diff --git a/nixpkgs/nixos/modules/services/networking/ircd-hybrid/default.nix b/nixpkgs/nixos/modules/services/networking/ircd-hybrid/default.nix
new file mode 100644
index 000000000000..91d0bf437d69
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/ircd-hybrid/default.nix
@@ -0,0 +1,125 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.ircdHybrid;
+
+  ircdService = pkgs.stdenv.mkDerivation rec {
+    name = "ircd-hybrid-service";
+    scripts = [ "=>/bin" ./control.in ];
+    substFiles = [ "=>/conf" ./ircd.conf ];
+    inherit (pkgs) ircdHybrid coreutils su iproute gnugrep procps;
+
+    ipv6Enabled = boolToString config.networking.enableIPv6;
+
+    inherit (cfg) serverName sid description adminEmail
+            extraPort;
+
+    cryptoSettings =
+      (optionalString (cfg.rsaKey != null) "rsa_private_key_file = \"${cfg.rsaKey}\";\n") +
+      (optionalString (cfg.certificate != null) "ssl_certificate_file = \"${cfg.certificate}\";\n");
+
+    extraListen = map (ip: "host = \""+ip+"\";\nport = 6665 .. 6669, "+extraPort+"; ") cfg.extraIPs;
+
+    builder = ./builder.sh;
+  };
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.ircdHybrid = {
+
+      enable = mkEnableOption "IRCD";
+
+      serverName = mkOption {
+        default = "hades.arpa";
+        description = "
+          IRCD server name.
+        ";
+      };
+
+      sid = mkOption {
+        default = "0NL";
+        description = "
+          IRCD server unique ID in a net of servers.
+        ";
+      };
+
+      description = mkOption {
+        default = "Hybrid-7 IRC server.";
+        description = "
+          IRCD server description.
+        ";
+      };
+
+      rsaKey = mkOption {
+        default = null;
+        example = literalExample "/root/certificates/irc.key";
+        description = "
+          IRCD server RSA key.
+        ";
+      };
+
+      certificate = mkOption {
+        default = null;
+        example = literalExample "/root/certificates/irc.pem";
+        description = "
+          IRCD server SSL certificate. There are some limitations - read manual.
+        ";
+      };
+
+      adminEmail = mkOption {
+        default = "<bit-bucket@example.com>";
+        example = "<name@domain.tld>";
+        description = "
+          IRCD server administrator e-mail.
+        ";
+      };
+
+      extraIPs = mkOption {
+        default = [];
+        example = ["127.0.0.1"];
+        description = "
+          Extra IP's to bind.
+        ";
+      };
+
+      extraPort = mkOption {
+        default = "7117";
+        description = "
+          Extra port to avoid filtering.
+        ";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.ircdHybrid.enable {
+
+    users.users.ircd =
+      { description = "IRCD owner";
+        group = "ircd";
+        uid = config.ids.uids.ircd;
+      };
+
+    users.groups.ircd.gid = config.ids.gids.ircd;
+
+    systemd.services.ircd-hybrid = {
+      description = "IRCD Hybrid server";
+      after = [ "started networking" ];
+      wantedBy = [ "multi-user.target" ];
+      script = "${ircdService}/bin/control start";
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/ircd-hybrid/ircd.conf b/nixpkgs/nixos/modules/services/networking/ircd-hybrid/ircd.conf
new file mode 100644
index 000000000000..17ef203840af
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/ircd-hybrid/ircd.conf
@@ -0,0 +1,1051 @@
+/* doc/example.conf - ircd-hybrid-7 Example configuration file
+ * Copyright (C) 2000-2006 Hybrid Development Team
+ *
+ * Written by ejb, wcampbel, db, leeh and others
+ * Other example configurations can be found in the source dir under
+ * etc/.
+ *
+ * $Id: example.conf 639 2006-06-01 14:12:21Z michael $
+ */
+
+/* IMPORTANT NOTES:
+ *
+ * auth {} blocks MUST be specified in order of precedence.  The first one
+ * that matches a user will be used.  So place spoofs first, then specials,
+ * then general access.
+ *
+ * Shell style (#), C++ style (//) and C style comments are supported.
+ *
+ * Files may be included by either:
+ *        .include "filename"
+ *        .include <filename>
+ *
+ * Times/durations are written as:
+ *        12 hours 30 minutes 1 second
+ *        
+ * Valid units of time:
+ *        month, week, day, hour, minute, second
+ *
+ * Valid units of size:
+ *        megabyte/mbyte/mb, kilobyte/kbyte/kb, byte
+ *
+ * Sizes and times may be singular or plural.  
+ */ 
+
+/* EFNET NOTE:
+ *
+ * This config file is NOT suitable for EFNet.  EFNet admins should use
+ * example.efnet.conf
+ */
+ 
+/*
+ * serverinfo {}:  contains information about the server. (OLD M:)
+ */
+serverinfo {
+	/*
+	 * name: the name of our server.  This cannot be changed at runtime.
+	 */
+	name = "@serverName@";
+
+	/*
+	 * sid: a server's unique ID.  This is three characters long and must
+	 * be in the form [0-9][A-Z0-9][A-Z0-9].  The first character must be
+	 * a digit, followed by 2 alpha-numerical letters.
+	 * NOTE: The letters must be capitalized.  This cannot be changed at runtime.
+	 */
+	sid = "@sid@";
+
+	/*
+	 * description: the description of the server.  '[' and ']' may not
+	 * be used here for compatibility with older servers.
+	 */
+	description = "@description@";
+
+	/*
+	 * network info: the name and description of the network this server
+	 * is on.  Shown in the 005 reply and used with serverhiding.
+	 */
+	network_name = "JustIRCNetwork";
+	network_desc = "This is My Network";
+
+	/*
+	 * hub: allow this server to act as a hub and have multiple servers
+	 * connected to it.  This may not be changed if there are active
+	 * LazyLink servers.
+	 */
+	hub = no;
+
+	/*
+	 * vhost: the IP to bind to when we connect outward to ipv4 servers.
+	 * This should be an ipv4 IP only, or "* for INADDR_ANY.
+	 */
+	#vhost = "192.169.0.1";
+
+	/*
+	 * vhost6: the IP to bind to when we connect outward to ipv6 servers.
+	 * This should be an ipv6 IP only, or "* for INADDR_ANY.
+	 */
+	#vhost6 = "3ffe:80e8:546::2";
+
+	/* max_clients: the maximum number of clients allowed to connect */
+	max_clients = 512;
+
+	/*
+	 * rsa key: the path to the file containing our rsa key for cryptlink.
+	 *
+	 * Example command to store a 2048 bit RSA keypair in
+	 * rsa.key, and the public key in rsa.pub:
+	 * 
+	 * 	openssl genrsa -out rsa.key 2048
+	 *	openssl rsa -in rsa.key -pubout -out rsa.pub
+	 *	chown <ircd-user>.<ircd.group> rsa.key rsa.pub
+	 *	chmod 0600 rsa.key
+	 *	chmod 0644 rsa.pub
+	 */
+	#rsa_private_key_file = "/usr/local/ircd/etc/rsa.key";
+
+	/*
+	 * ssl certificate: the path to the file containing our ssl certificate
+	 * for encrypted client connection.
+	 *
+	 * This assumes your private RSA key is stored in rsa.key. You
+	 * MUST have an RSA key in order to generate the certificate
+	 *
+	 *	openssl req -new -days 365 -x509 -key rsa.key -out cert.pem
+	 *
+	 * See http://www.openssl.org/docs/HOWTO/certificates.txt
+	 *
+	 * Please use the following values when generating the cert
+	 *
+	 *	Organization Name: Network Name
+	 *	Organization Unit Name: changme.someirc.net
+	 *	Common Name: irc.someirc.net
+	 *	E-mail: you@domain.com
+	 */
+	#ssl_certificate_file = "/usr/local/ircd/etc/cert.pem";
+
+	@cryptoSettings@
+};
+
+/*
+ * admin {}:  contains admin information about the server. (OLD A:)
+ */
+admin {
+	name = "Anonymous Hero";
+	description = "Main Server Administrator";
+	email = "@adminEmail@";
+};
+
+/*
+ * log {}:  contains information about logfiles.
+ */
+log {
+	/* Do you want to enable logging to ircd.log? */
+	use_logging = yes;
+
+	/*
+	 * logfiles: the logfiles to use for user connects, /oper uses,
+	 * and failed /oper.  These files must exist for logging to be used.
+	 */
+	fname_userlog = "/home/ircd/logs/userlog";
+	fname_operlog = "/home/ircd/logs/operlog";
+	fname_killlog = "/home/ircd/logs/kill";
+	fname_klinelog = "/home/ircd/logs/kline";
+	fname_glinelog = "/home/ircd/logs/gline";
+
+	/*
+	 * log_level: the amount of detail to log in ircd.log.  The
+	 * higher, the more information is logged.  May be changed
+	 * once the server is running via /quote SET LOG.  Either:
+	 * L_CRIT, L_ERROR, L_WARN, L_NOTICE, L_TRACE, L_INFO or L_DEBUG
+	 */
+	log_level = L_INFO;
+};
+
+/*
+ * class {}:  contains information about classes for users (OLD Y:)
+ */
+class {
+	/* name: the name of the class.  classes are text now */
+	name = "users";
+
+	/*
+	 * ping_time: how often a client must reply to a PING from the
+	 * server before they are dropped.
+	 */
+	ping_time = 90 seconds;
+
+	/*
+	 * number_per_ip: how many local users are allowed to connect
+	 * from one IP  (optional)
+	 */
+	number_per_ip = 10;
+
+	/*
+	 * max_local: how many local users are allowed to connect
+	 * from one ident@host  (optional)
+	 */
+	max_local = 50;
+
+	/*
+	 * max_global: network-wide limit of users per ident@host  (optional)
+	 */
+	max_global = 50;
+
+	/*
+	 * max_number: the maximum number of users allowed in this class (optional)
+	 */
+	max_number = 10000;
+
+	/*
+	 * the following lines are optional and allow you to define
+	 * how many users can connect from one /NN subnet
+	 */
+	/*cidr_bitlen_ipv4 = 24;
+	 *cidr_bitlen_ipv6 = 120;
+	 *number_per_cidr = 16;*/
+
+	/*
+	 * sendq: the amount of data allowed in a clients queue before
+	 * they are dropped.
+	 */
+	sendq = 100 kbytes;
+};
+
+class {
+	name = "opers";
+	ping_time = 90 seconds;
+	number_per_ip = 10;
+	max_number = 100;
+	sendq = 100kbytes;
+};
+
+class {
+	name = "server";
+	ping_time = 90 seconds;
+
+	/*
+	 * ping_warning: how fast a server must reply to a PING before
+	 * a warning to opers is generated.
+	 */
+	ping_warning = 15 seconds;
+
+	/*
+	 * connectfreq: only used in server classes.  Specifies the delay
+	 * between autoconnecting to servers.
+	 */
+	connectfreq = 5 minutes;
+
+	/* max number: the amount of servers to autoconnect to */
+	max_number = 1;
+
+	/* sendq: servers need a higher sendq as they send more data */
+	sendq = 2 megabytes;
+};
+
+/*
+ * listen {}:  contains information about the ports ircd listens on (OLD P:)
+ */
+listen {
+	/*
+	 * port: the specific port to listen on.  If no host is specified
+	 * before, it will listen on all available IPs.
+	 *
+	 * Ports are separated via a comma, a range may be specified using ".."
+	 */
+	
+	/* port: listen on all available IPs, ports 6665 to 6669 */
+	port = 6665 .. 6669;
+
+	/*
+	 * Listen on 192.168.0.1/6697 with ssl enabled and hidden from STATS P
+	 * unless you are an administrator.
+	 *
+	 * NOTE: The "flags" directive has to come before "port".  Always!
+	 */
+	#flags = hidden, ssl;
+	#host = "192.168.0.1";
+	#port = 6697;
+
+	/*
+	 * host: set a specific IP/host the ports after the line will listen 
+	 * on.  This may be ipv4 or ipv6.
+	 */
+	#host = "1.2.3.4";
+	#port = 7000, 7001;
+
+	#host = "3ffe:1234:a:b:c::d";
+	#port = 7002;
+	
+	@extraListen@
+};
+
+auth {
+	user = "*@*";
+	class = "users";
+	#flags = need_ident;
+};
+
+/*
+ * operator {}:  defines ircd operators. (OLD O:)
+ *
+ * ircd-hybrid no longer supports local operators, privileges are
+ * controlled via flags.
+ */
+operator {
+	/* name: the name of the oper */
+	/* NOTE: operator "opername"{} is also supported */
+	name = "god";
+
+	/*
+	 * user: the user@host required for this operator.  CIDR is not
+	 * supported.  Multiple user="" lines are supported.
+	 */
+	user = "*god@*";
+	user = "*@127.0.0.1";
+
+	/*
+	 * password: the password required to oper.  By default this will
+	 * need to be encrypted using 'mkpasswd'.  MD5 is supported.
+	 */
+	password = "iamoperator";
+
+	/*
+	 * encrypted: controls whether the oper password above has been
+	 * encrypted.  (OLD CRYPT_OPER_PASSWORD now optional per operator)
+	 */
+	encrypted = no;
+
+	/*
+	 * rsa_public_key_file: the public key for this oper when using Challenge.
+	 * A password should not be defined when this is used, see 
+	 * doc/challenge.txt for more information.
+	 */
+#	rsa_public_key_file = "/usr/local/ircd/etc/oper.pub";
+
+	/* class: the class the oper joins when they successfully /oper */
+	class = "opers";
+
+	/*
+	 * umodes: default usermodes opers get when they /oper.  If defined,
+	 * it will override oper_umodes settings in general {}.
+	 * Available usermodes:
+	 *
+	 * +b - bots         - See bot and drone flooding notices
+	 * +c - cconn        - Client connection/quit notices
+	 * +D - deaf         - Don't receive channel messages
+	 * +d - debug        - See debugging notices
+	 * +f - full         - See I: line full notices
+	 * +G - softcallerid - Server Side Ignore for users not on your channels
+	 * +g - callerid     - Server Side Ignore (for privmsgs etc)
+	 * +i - invisible    - Not shown in NAMES or WHO unless you share a
+	 *                     a channel
+	 * +k - skill        - See server generated KILL messages
+	 * +l - locops       - See LOCOPS messages
+	 * +n - nchange      - See client nick changes
+	 * +r - rej          - See rejected client notices
+	 * +s - servnotice   - See general server notices
+	 * +u - unauth       - See unauthorized client notices
+	 * +w - wallop       - See server generated WALLOPS
+	 * +x - external     - See remote server connection and split notices
+	 * +y - spy          - See LINKS, STATS, TRACE notices etc.
+	 * +z - operwall     - See oper generated WALLOPS
+	 */
+#	umodes = locops, servnotice, operwall, wallop;
+
+	/*
+	 * privileges: controls the activities and commands an oper is 
+	 * allowed to do on the server.  All options default to no.
+	 * Available options:
+	 *
+	 * global_kill:  allows remote users to be /KILL'd (OLD 'O' flag)
+	 * remote:       allows remote SQUIT and CONNECT   (OLD 'R' flag)
+	 * remoteban:    allows remote KLINE/UNKLINE
+	 * kline:        allows KILL, KLINE and DLINE      (OLD 'K' flag)
+	 * unkline:      allows UNKLINE and UNDLINE        (OLD 'U' flag)
+	 * gline:        allows GLINE                      (OLD 'G' flag)
+	 * xline:         allows XLINE                     (OLD 'X' flag)
+	 * operwall:     allows OPERWALL
+	 * nick_changes: allows oper to see nickchanges    (OLD 'N' flag)
+	 *               via usermode +n
+	 * rehash:       allows oper to REHASH config      (OLD 'H' flag)
+	 * die:          allows DIE and RESTART            (OLD 'D' flag)
+	 * admin:        gives admin privileges.  admins
+	 *               may (un)load modules and see the
+	 *               real IPs of servers.
+	 * hidden_admin: same as 'admin', but noone can recognize you as
+	 *               being an admin
+	 * hidden_oper:  not shown in /stats p (except for other operators)
+	 */
+	/* You can either use
+	 * die = yes;
+	 * rehash = yes;
+	 *
+	 * or in a flags statement i.e.
+	 * flags = die, rehash;
+	 *
+	 * You can also negate a flag with ~ i.e.
+	 * flags = ~remote;
+	 *
+	 */
+	flags = global_kill, remote, kline, unkline, xline,
+		die, rehash, nick_changes, admin, operwall;
+};
+
+/*
+ * shared {}: users that are allowed to remote kline (OLD U:)
+ *
+ * NOTE: This can be effectively used for remote klines.
+ *       Please note that there is no password authentication
+ *       for users setting remote klines.  You must also be
+ *       /oper'd in order to issue a remote kline.
+ */
+shared {
+	/*
+	 * name: the server the user must be on to set klines.  If this is not
+	 * specified, the user will be allowed to kline from all servers.
+	 */
+	name = "irc2.some.server";
+
+	/*
+	 * user: the user@host mask that is allowed to set klines.  If this is
+	 * not specified, all users on the server above will be allowed to set
+	 * a remote kline.
+	 */
+	user = "oper@my.host.is.spoofed";
+
+	/*
+	 * type: list of what to share, options are as follows:
+	 *	kline	- allow oper/server to kline
+	 *	tkline	- allow temporary klines
+	 *	unkline	- allow oper/server to unkline
+	 *	xline	- allow oper/server to xline
+	 * 	txline	- allow temporary xlines
+	 *	unxline	- allow oper/server to unxline
+	 *	resv	- allow oper/server to resv
+	 * 	tresv	- allow temporary resvs
+	 *	unresv	- allow oper/server to unresv
+	 *      locops  - allow oper/server to locops - only used for servers that cluster
+	 *	all	- allow oper/server to do all of the above (default)
+	 */
+	type = kline, unkline, resv;
+};
+
+/*
+ * kill {}:  users that are not allowed to connect (OLD K:)
+ * Oper issued klines will be added to the specified kline config
+ */
+kill {
+	user = "bad@*.hacked.edu";
+	reason = "Obviously hacked account";
+};
+
+kill {
+	user = "^O[[:alpha:]]?[[:digit:]]+(x\.o|\.xo)$@^[[:alnum:]]{4}\.evilnet.org$";
+	type = regex;
+};
+
+/*
+ * deny {}:  IPs that are not allowed to connect (before DNS/ident lookup)
+ * Oper issued dlines will be added to the specified dline config
+ */
+deny {
+	ip = "10.0.1.0/24";
+	reason = "Reconnecting vhosted bots";
+};
+
+/*
+ * exempt {}: IPs that are exempt from deny {} and Dlines. (OLD d:)
+ */
+exempt {
+	ip = "192.168.0.0/16";
+};
+
+/*
+ * resv {}:  nicks and channels users may not use/join (OLD Q:)
+ */
+resv {
+	/* reason: the reason for the proceeding resv's */
+	reason = "There are no services on this network";
+
+	/* resv: the nicks and channels users may not join/use */
+	nick = "nickserv";
+	nick = "chanserv";
+	channel = "#services";
+
+	/* resv: wildcard masks are also supported in nicks only */
+	reason = "Clone bots";
+	nick = "clone*";
+};
+
+/*
+ * gecos {}:  The X: replacement, used for banning users based on
+ * their "realname".
+ */
+gecos {
+	name = "*sex*";
+	reason = "Possible spambot";
+};
+
+gecos {
+	name = "sub7server";
+	reason = "Trojan drone";
+};
+
+gecos {
+	name = "*http*";
+	reason = "Spambot";
+};
+
+gecos {
+	name = "^\[J[0o]hn Do[3e]\]-[0-9]{2,5}$";
+	type = regex;
+};
+
+/*
+ * channel {}:  The channel block contains options pertaining to channels
+ */
+channel {
+	/*
+	 * disable_fake_channels: this option, if set to 'yes', will
+	 * disallow clients to create or join channels that have one
+	 * of the following ASCII characters in their name:
+	 *
+	 *   2 | bold
+	 *   3 | mirc color
+         *  15 | plain text
+	 *  22 | reverse
+	 *  31 | underline
+	 * 160 | non-breaking space
+	 */
+	disable_fake_channels = yes;
+
+	/*
+	 * restrict_channels: reverse channel RESVs logic, only reserved
+	 * channels are allowed
+	 */
+	restrict_channels = no;
+
+	/*
+	 * disable_local_channels: prevent users from joining &channels.
+	 */
+	disable_local_channels = no;
+
+	/*
+	 * use_invex: Enable/disable channel mode +I, a n!u@h list of masks
+	 * that can join a +i channel without an invite.
+	 */
+	use_invex = yes;
+
+	/*
+	 * use_except: Enable/disable channel mode +e, a n!u@h list of masks
+	 * that can join a channel through a ban (+b).
+	 */
+	use_except = yes;
+
+	/*
+	 * use_knock: Allows users to request an invite to a channel that
+	 * is locked somehow (+ikl).  If the channel is +p or you are banned
+	 * the knock will not be sent.
+	 */
+	use_knock = yes;
+
+	/*
+	 * knock_delay: The amount of time a user must wait between issuing
+	 * the knock command.
+	 */
+	knock_delay = 1 minutes;
+
+	/*
+	 * knock_delay_channel: How often a knock to any specific channel
+	 * is permitted, regardless of the user sending the knock.
+	 */
+	knock_delay_channel = 1 minute;
+
+	/*
+	 * burst_topicwho: enable sending of who set topic on topicburst
+	 * default is yes
+	 */
+	burst_topicwho = yes;
+
+	/*
+	 * max_chans_per_user: The maximum number of channels a user can
+	 * join/be on.
+	 */
+	max_chans_per_user = 25;
+
+	/* quiet_on_ban: stop banned people talking in channels. */
+	quiet_on_ban = yes;
+
+	/* max_bans: maximum number of +b/e/I modes in a channel */
+	max_bans = 1000;
+
+	/*
+	 * how many joins in how many seconds constitute a flood, use 0 to
+	 * disable. +b opers will be notified (changeable via /set)
+	 */
+	join_flood_count = 100;
+	join_flood_time = 10 seconds;
+
+	/*
+	 * splitcode: The ircd will now check splitmode every few seconds.
+	 *
+	 * Either split users or split servers can activate splitmode, but
+	 * both conditions must be met for the ircd to deactivate splitmode.
+	 * 
+	 * You may force splitmode to be permanent by /quote set splitmode on
+	 */
+
+	/*
+	 * default_split_user_count: when the usercount is lower than this level,
+	 * consider ourselves split.  This must be set for automatic splitmode.
+	 */
+	default_split_user_count = 0;
+
+	/*
+	 * default_split_server_count: when the servercount is lower than this,
+	 * consider ourselves split.  This must be set for automatic splitmode.
+	 */
+	default_split_server_count = 0;
+
+	/* split no create: disallow users creating channels on split. */
+	no_create_on_split = yes;
+
+	/* split: no join: disallow users joining channels at all on a split */
+	no_join_on_split = no;
+};
+
+/*
+ * serverhide {}:  The serverhide block contains the options regarding
+ * serverhiding
+ */
+serverhide {
+	/*
+	 * flatten_links: this option will show all servers in /links appear
+	 * that they are linked to this current server
+	 */
+	flatten_links = no;
+
+	/*
+	 * links_delay: how often to update the links file when it is
+	 * flattened.
+	 */
+	links_delay = 5 minutes;
+
+	/*
+	 * hidden: hide this server from a /links output on servers that
+	 * support it.  This allows hub servers to be hidden etc.
+	 */
+	hidden = no;
+
+	/*
+	 * disable_hidden: prevent servers hiding themselves from a
+	 * /links output.
+	 */
+	disable_hidden = no;
+
+	/*
+	 * hide_servers: hide remote servernames everywhere and instead use
+	 * hidden_name and network_desc.
+	 */
+	hide_servers = no;
+
+	/*
+	 * Use this as the servername users see if hide_servers = yes.
+	 */
+	hidden_name = "*.hidden.com";
+
+	/*
+	 * hide_server_ips: If this is disabled, opers will be unable to see servers
+	 * ips and will be shown a masked ip, admins will be shown the real ip.
+	 *
+	 * If this is enabled, nobody can see a servers ip.  *This is a kludge*, it
+	 * has the side effect of hiding the ips everywhere, including logfiles.
+	 *
+	 * We recommend you leave this disabled, and just take care with who you
+	 * give admin=yes; to.
+	 */
+	hide_server_ips = no;
+};
+
+/*
+ * general {}:  The general block contains many of the options that were once
+ * compiled in options in config.h.  The general block is read at start time.
+ */
+general {
+	/*
+	 * gline_min_cidr: the minimum required length of a CIDR bitmask
+	 * for IPv4 based glines
+	 */
+	gline_min_cidr = 16;
+
+	/*
+	 * gline_min_cidr6: the minimum required length of a CIDR bitmask
+	 * for IPv6 based glines
+	 */
+	gline_min_cidr6 = 48;
+
+	/*
+	 * Whether to automatically set mode +i on connecting users.
+	 */
+	invisible_on_connect = yes;
+
+	/*
+	 * If you don't explicitly specify burst_away in your connect blocks, then
+	 * they will default to the burst_away value below.
+	 */
+	burst_away = no;
+
+	/*
+	 * Show "actually using host <ip>" on /whois when possible.
+	 */
+	use_whois_actually = yes;
+
+	/*
+	 * Max time from the nickname change that still causes KILL
+	 * automatically to switch for the current nick of that user. (seconds)
+	 */
+	kill_chase_time_limit = 90;
+
+	/*
+	 * If hide_spoof_ips is disabled, opers will be allowed to see the real IP of spoofed
+	 * users in /trace etc.  If this is defined they will be shown a masked IP.
+	 */
+	hide_spoof_ips = yes;
+
+	/*
+	 * Ignore bogus timestamps from other servers.  Yes, this will desync
+	 * the network, but it will allow chanops to resync with a valid non TS 0
+	 *
+	 * This should be enabled network wide, or not at all.
+	 */
+	ignore_bogus_ts = no;
+
+	/*
+	 * disable_auth: completely disable ident lookups; if you enable this,
+	 * be careful of what you set need_ident to in your auth {} blocks
+	 */
+	disable_auth = no;
+
+	/* disable_remote_commands: disable users doing commands on remote servers */
+	disable_remote_commands = no;
+
+	/*
+	 * tkline_expire_notices: enables or disables temporary kline/xline
+	 * expire notices.
+	 */
+	tkline_expire_notices = no;
+
+	/*
+	 * default_floodcount: the default value of floodcount that is configurable
+	 * via /quote set floodcount.  This is the amount of lines a user
+	 * may send to any other user/channel in one second.
+	 */
+	default_floodcount = 10;
+
+	/*
+	 * failed_oper_notice: send a notice to all opers on the server when 
+	 * someone tries to OPER and uses the wrong password, host or ident.
+	 */
+	failed_oper_notice = yes;
+
+	/*
+	 * dots_in_ident: the amount of '.' characters permitted in an ident
+	 * reply before the user is rejected.
+	 */
+	dots_in_ident = 2;
+
+	/*
+	 * dot_in_ip6_addr: ircd-hybrid-6.0 and earlier will disallow hosts 
+	 * without a '.' in them.  This will add one to the end.  Only needed
+	 * for older servers.
+	 */
+	dot_in_ip6_addr = no;
+
+	/*
+	 * min_nonwildcard: the minimum non wildcard characters in k/d/g lines
+	 * placed via the server.  klines hand placed are exempt from limits.
+	 * wildcard chars: '.' ':' '*' '?' '@' '!' '#'
+	 */
+	min_nonwildcard = 4;
+
+	/*
+	 * min_nonwildcard_simple: the minimum non wildcard characters in 
+	 * gecos bans.  wildcard chars: '*' '?' '#'
+	 */
+	min_nonwildcard_simple = 3;
+
+	/* max_accept: maximum allowed /accept's for +g usermode */
+	max_accept = 20;
+
+	/* anti_nick_flood: enable the nickflood control code */
+	anti_nick_flood = yes;
+
+	/* nick flood: the nick changes allowed in the specified period */
+	max_nick_time = 20 seconds;
+	max_nick_changes = 5;
+
+	/*
+	 * anti_spam_exit_message_time: the minimum time a user must be connected
+	 * before custom quit messages are allowed.
+	 */
+	anti_spam_exit_message_time = 5 minutes;
+
+	/*
+	 * ts delta: the time delta allowed between server clocks before
+	 * a warning is given, or before the link is dropped.  all servers
+	 * should run ntpdate/rdate to keep clocks in sync
+	 */
+	ts_warn_delta = 30 seconds;
+	ts_max_delta = 5 minutes;
+
+	/*
+	 * kline_with_reason: show the user the reason why they are k/d/glined 
+	 * on exit.  May give away who set k/dline when set via tcm.
+	 */
+	kline_with_reason = yes;
+
+	/*
+	 * kline_reason: show this message to users on channel
+	 * instead of the oper reason.
+	 */
+	kline_reason = "Connection closed";
+
+	/*
+	 * reject_hold_time: wait this amount of time before disconnecting
+	 * a rejected client. Use 0 to disable.
+	 */
+	reject_hold_time = 0;
+
+	/*
+	 * warn_no_nline: warn opers about servers that try to connect but
+	 * we don't have a connect {} block for.  Twits with misconfigured 
+	 * servers can get really annoying with this enabled.
+	 */
+	warn_no_nline = yes;
+
+	/*
+	 * stats_e_disabled: set this to 'yes' to disable "STATS e" for both
+	 * operators and administrators.  Doing so is a good idea in case
+	 * there are any exempted (exempt{}) server IPs you don't want to
+	 * see leaked.
+	 */
+	stats_e_disabled = no;
+
+	/* stats_o_oper only: make stats o (opers) oper only */
+	stats_o_oper_only = yes;
+
+	/* stats_P_oper_only: make stats P (ports) oper only */
+	stats_P_oper_only = yes;
+
+	/*
+	 * stats i oper only: make stats i (auth {}) oper only. set to:
+	 *     yes:    show users no auth blocks, made oper only.
+	 *     masked: show users first matching auth block
+	 *     no:     show users all auth blocks.
+	 */
+	stats_i_oper_only = yes;
+
+	/*
+	 * stats_k_oper_only: make stats k/K (klines) oper only.  set to:
+	 *     yes:    show users no auth blocks, made oper only
+	 *     masked: show users first matching auth block
+	 *     no:     show users all auth blocks.
+	 */
+	stats_k_oper_only = yes;
+
+	/*
+	 * caller_id_wait: time between notifying a +g user that somebody
+	 * is messaging them.
+	 */
+	caller_id_wait = 1 minute;
+
+	/*
+	 * opers_bypass_callerid: allows operators to bypass +g and message
+	 * anyone who has it set (useful if you use services).
+	 */
+	opers_bypass_callerid = no;
+
+	/*
+	 * pace_wait_simple: time between use of less intensive commands
+	 * (ADMIN, HELP, (L)USERS, VERSION, remote WHOIS)
+	 */
+	pace_wait_simple = 1 second;
+
+	/*
+	 * pace_wait: time between more intensive commands
+	 * (INFO, LINKS, LIST, MAP, MOTD, STATS, WHO, wildcard WHOIS, WHOWAS)
+	 */
+	pace_wait = 10 seconds;
+
+	/*
+	 * short_motd: send clients a notice telling them to read the motd
+	 * instead of forcing a motd to clients who may simply ignore it.
+	 */
+	short_motd = no;
+
+	/*
+	 * ping_cookie: require clients to respond exactly to a ping command,
+	 * can help block certain types of drones and FTP PASV mode spoofing.
+	 */
+	ping_cookie = no;
+
+	/* no_oper_flood: increase flood limits for opers. */
+	no_oper_flood = yes;
+
+	/*
+	 * true_no_oper_flood: completely eliminate flood limits for opers
+	 * and for clients with can_flood = yes in their auth {} blocks
+	 */
+	true_no_oper_flood = yes;
+
+	/* oper_pass_resv: allow opers to over-ride RESVs on nicks/channels */
+	oper_pass_resv = yes;
+
+	/*
+	 * idletime: the maximum amount of time a user may idle before
+	 * they are disconnected
+	 */
+	idletime = 0;
+
+	/* REMOVE ME.  The following line checks you've been reading. */
+	#havent_read_conf = 1;
+
+	/*
+	 * max_targets: the maximum amount of targets in a single 
+	 * PRIVMSG/NOTICE.  Set to 999 NOT 0 for unlimited.
+	 */
+	max_targets = 4;
+
+	/*
+	 * client_flood: maximum amount of data in a clients queue before
+	 * they are dropped for flooding.
+	 */
+	client_flood = 2560 bytes;
+
+	/*
+	 * message_locale: the default message locale
+	 * Use "standard" for the compiled in defaults.
+	 * To install the translated messages, go into messages/ in the
+	 * source directory and run `make install'.
+	 */
+	message_locale = "standard";
+
+	/*
+	 * usermodes configurable: a list of usermodes for the options below
+	 *
+	 * +b - bots         - See bot and drone flooding notices
+	 * +c - cconn        - Client connection/quit notices
+	 * +D - deaf         - Don't receive channel messages
+	 * +d - debug        - See debugging notices
+	 * +f - full         - See I: line full notices
+	 * +G - softcallerid - Server Side Ignore for users not on your channels
+	 * +g - callerid     - Server Side Ignore (for privmsgs etc)
+	 * +i - invisible    - Not shown in NAMES or WHO unless you share a 
+	 *                     a channel
+	 * +k - skill        - See server generated KILL messages
+	 * +l - locops       - See LOCOPS messages
+	 * +n - nchange      - See client nick changes
+	 * +r - rej          - See rejected client notices
+	 * +s - servnotice   - See general server notices
+	 * +u - unauth       - See unauthorized client notices
+	 * +w - wallop       - See server generated WALLOPS
+	 * +x - external     - See remote server connection and split notices
+	 * +y - spy          - See LINKS, STATS, TRACE notices etc.
+	 * +z - operwall     - See oper generated WALLOPS
+	 */
+
+	/* oper_only_umodes: usermodes only opers may set */
+	oper_only_umodes = bots, cconn, debug, full, skill, nchange, 
+			   rej, spy, external, operwall, locops, unauth;
+
+	/* oper_umodes: default usermodes opers get when they /oper */
+	oper_umodes = bots, locops, servnotice, operwall, wallop;
+
+	/*
+	 * servlink_path: path to 'servlink' program used by ircd to handle
+	 * encrypted/compressed server <-> server links.
+	 *
+	 * only define if servlink is not in same directory as ircd itself.
+	 */
+	#servlink_path = "/usr/local/ircd/bin/servlink";
+
+	/*
+	 * default_cipher_preference: default cipher to use for cryptlink when none is
+	 * specified in connect block.
+	 */
+	#default_cipher_preference = "BF/168";
+
+	/*
+	 * use_egd: if your system does not have *random devices yet you
+	 * want to use OpenSSL and encrypted links, enable this.  Beware -
+	 * EGD is *very* CPU intensive when gathering data for its pool
+	 */
+#	use_egd = yes;
+
+	/*
+	 * egdpool_path: path to EGD pool. Not necessary for OpenSSL >= 0.9.7
+	 * which automatically finds the path.
+	 */
+#	egdpool_path = "/run/egd-pool";
+
+
+	/*
+	 * compression_level: level of compression for compressed links between
+	 * servers.  
+	 *
+	 * values are between: 1 (least compression, fastest)
+	 *                and: 9 (most compression, slowest).
+	 */
+#	compression_level = 6;
+
+	/*
+	 * throttle_time: the minimum amount of time between connections from
+	 * the same ip.  exempt {} blocks are excluded from this throttling.
+	 * Offers protection against flooders who reconnect quickly.  
+	 * Set to 0 to disable.
+	 */
+	throttle_time = 10;
+};
+
+glines {
+	/* enable: enable glines, network wide temp klines */
+	enable = yes;
+
+	/*
+	 * duration: the amount of time a gline will remain on your
+	 * server before expiring
+	 */
+	duration = 1 day;
+
+	/*
+	 * logging: which types of rules you want to log when triggered
+	 * (choose reject or block)
+	 */
+	logging = reject, block;
+
+	/*
+	 * NOTE: gline ACLs can cause a desync of glines throughout the
+	 * network, meaning some servers may have a gline triggered, and
+	 * others may not. Also, you only need insert rules for glines
+	 * that you want to block and/or reject. If you want to accept and
+	 * propagate the gline, do NOT put a rule for it.
+	 */
+
+	/* user@host for rule to apply to */
+	user = "god@I.still.hate.packets";
+	/* server for rule to apply to */
+	name = "hades.arpa";
+
+	/*
+	 * action: action to take when a matching gline is found. options are:
+	 *  reject	- do not apply the gline locally
+	 *  block	- do not propagate the gline
+	 */
+	action = reject, block;
+
+	user = "god@*";
+	name = "*";
+	action = block;
+};
+
diff --git a/nixpkgs/nixos/modules/services/networking/iwd.nix b/nixpkgs/nixos/modules/services/networking/iwd.nix
new file mode 100644
index 000000000000..6be67a8b96f4
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/iwd.nix
@@ -0,0 +1,29 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.networking.wireless.iwd;
+in {
+  options.networking.wireless.iwd.enable = mkEnableOption "iwd";
+
+  config = mkIf cfg.enable {
+    assertions = [{
+      assertion = !config.networking.wireless.enable;
+      message = ''
+        Only one wireless daemon is allowed at the time: networking.wireless.enable and networking.wireless.iwd.enable are mutually exclusive.
+      '';
+    }];
+
+    # for iwctl
+    environment.systemPackages =  [ pkgs.iwd ];
+
+    services.dbus.packages = [ pkgs.iwd ];
+
+    systemd.packages = [ pkgs.iwd ];
+
+    systemd.services.iwd.wantedBy = [ "multi-user.target" ];
+  };
+
+  meta.maintainers = with lib.maintainers; [ mic92 dtzWill ];
+}
diff --git a/nixpkgs/nixos/modules/services/networking/keepalived/default.nix b/nixpkgs/nixos/modules/services/networking/keepalived/default.nix
new file mode 100644
index 000000000000..c9ac2ee25990
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/keepalived/default.nix
@@ -0,0 +1,303 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.keepalived;
+
+  keepalivedConf = pkgs.writeText "keepalived.conf" ''
+    global_defs {
+      ${optionalString cfg.enableScriptSecurity "enable_script_security"}
+      ${snmpGlobalDefs}
+      ${cfg.extraGlobalDefs}
+    }
+
+    ${vrrpScriptStr}
+    ${vrrpInstancesStr}
+    ${cfg.extraConfig}
+  '';
+
+  snmpGlobalDefs = with cfg.snmp; optionalString enable (
+    optionalString (socket != null) "snmp_socket ${socket}\n"
+    + optionalString enableKeepalived "enable_snmp_keepalived\n"
+    + optionalString enableChecker "enable_snmp_checker\n"
+    + optionalString enableRfc "enable_snmp_rfc\n"
+    + optionalString enableRfcV2 "enable_snmp_rfcv2\n"
+    + optionalString enableRfcV3 "enable_snmp_rfcv3\n"
+    + optionalString enableTraps "enable_traps"
+  );
+
+  vrrpScriptStr = concatStringsSep "\n" (map (s:
+    ''
+      vrrp_script ${s.name} {
+        script "${s.script}"
+        interval ${toString s.interval}
+        fall ${toString s.fall}
+        rise ${toString s.rise}
+        timeout ${toString s.timeout}
+        weight ${toString s.weight}
+        user ${s.user} ${optionalString (s.group != null) s.group}
+
+        ${s.extraConfig}
+      }
+    ''
+  ) vrrpScripts);
+
+  vrrpInstancesStr = concatStringsSep "\n" (map (i:
+    ''
+      vrrp_instance ${i.name} {
+        interface ${i.interface}
+        state ${i.state}
+        virtual_router_id ${toString i.virtualRouterId}
+        priority ${toString i.priority}
+        ${optionalString i.noPreempt "nopreempt"}
+
+        ${optionalString i.useVmac (
+          "use_vmac" + optionalString (i.vmacInterface != null) " ${i.vmacInterface}"
+        )}
+        ${optionalString i.vmacXmitBase "vmac_xmit_base"}
+
+        ${optionalString (i.unicastSrcIp != null) "unicast_src_ip ${i.unicastSrcIp}"}
+        unicast_peer {
+          ${concatStringsSep "\n" i.unicastPeers}
+        }
+
+        virtual_ipaddress {
+          ${concatMapStringsSep "\n" virtualIpLine i.virtualIps}
+        }
+
+        ${optionalString (builtins.length i.trackScripts > 0) ''
+          track_script {
+            ${concatStringsSep "\n" i.trackScripts}
+          }
+        ''}
+
+        ${optionalString (builtins.length i.trackInterfaces > 0) ''
+          track_interface {
+            ${concatStringsSep "\n" i.trackInterfaces}
+          }
+        ''}
+
+        ${i.extraConfig}
+      }
+    ''
+  ) vrrpInstances);
+
+  virtualIpLine = (ip:
+    ip.addr
+    + optionalString (notNullOrEmpty ip.brd) " brd ${ip.brd}"
+    + optionalString (notNullOrEmpty ip.dev) " dev ${ip.dev}"
+    + optionalString (notNullOrEmpty ip.scope) " scope ${ip.scope}"
+    + optionalString (notNullOrEmpty ip.label) " label ${ip.label}"
+  );
+
+  notNullOrEmpty = s: !(s == null || s == "");
+
+  vrrpScripts = mapAttrsToList (name: config:
+    {
+      inherit name;
+    } // config
+  ) cfg.vrrpScripts;
+
+  vrrpInstances = mapAttrsToList (iName: iConfig:
+    {
+      name = iName;
+    } // iConfig
+  ) cfg.vrrpInstances;
+
+  vrrpInstanceAssertions = i: [
+    { assertion = i.interface != "";
+      message = "services.keepalived.vrrpInstances.${i.name}.interface option cannot be empty.";
+    }
+    { assertion = i.virtualRouterId >= 0 && i.virtualRouterId <= 255;
+      message = "services.keepalived.vrrpInstances.${i.name}.virtualRouterId must be an integer between 0..255.";
+    }
+    { assertion = i.priority >= 0 && i.priority <= 255;
+      message = "services.keepalived.vrrpInstances.${i.name}.priority must be an integer between 0..255.";
+    }
+    { assertion = i.vmacInterface == null || i.useVmac;
+      message = "services.keepalived.vrrpInstances.${i.name}.vmacInterface has no effect when services.keepalived.vrrpInstances.${i.name}.useVmac is not set.";
+    }
+    { assertion = !i.vmacXmitBase || i.useVmac;
+      message = "services.keepalived.vrrpInstances.${i.name}.vmacXmitBase has no effect when services.keepalived.vrrpInstances.${i.name}.useVmac is not set.";
+    }
+  ] ++ flatten (map (virtualIpAssertions i.name) i.virtualIps)
+    ++ flatten (map (vrrpScriptAssertion i.name) i.trackScripts);
+
+  virtualIpAssertions = vrrpName: ip: [
+    { assertion = ip.addr != "";
+      message = "The 'addr' option for an services.keepalived.vrrpInstances.${vrrpName}.virtualIps entry cannot be empty.";
+    }
+  ];
+
+  vrrpScriptAssertion = vrrpName: scriptName: {
+    assertion = builtins.hasAttr scriptName cfg.vrrpScripts;
+    message = "services.keepalived.vrrpInstances.${vrrpName} trackscript ${scriptName} is not defined in services.keepalived.vrrpScripts.";
+  };
+
+  pidFile = "/run/keepalived.pid";
+
+in
+{
+
+  options = {
+    services.keepalived = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable Keepalived.
+        '';
+      };
+
+      enableScriptSecurity = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Don't run scripts configured to be run as root if any part of the path is writable by a non-root user.
+        '';
+      };
+
+      snmp = {
+
+        enable = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Whether to enable the builtin AgentX subagent.
+          '';
+        };
+
+        socket = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          description = ''
+            Socket to use for connecting to SNMP master agent. If this value is
+            set to null, keepalived's default will be used, which is
+            unix:/var/agentx/master, unless using a network namespace, when the
+            default is udp:localhost:705.
+          '';
+        };
+
+        enableKeepalived = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Enable SNMP handling of vrrp element of KEEPALIVED MIB.
+          '';
+        };
+
+        enableChecker = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Enable SNMP handling of checker element of KEEPALIVED MIB.
+          '';
+        };
+
+        enableRfc = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Enable SNMP handling of RFC2787 and RFC6527 VRRP MIBs.
+          '';
+        };
+
+        enableRfcV2 = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Enable SNMP handling of RFC2787 VRRP MIB.
+          '';
+        };
+
+        enableRfcV3 = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Enable SNMP handling of RFC6527 VRRP MIB.
+          '';
+        };
+
+        enableTraps = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Enable SNMP traps.
+          '';
+        };
+
+      };
+
+      vrrpScripts = mkOption {
+        type = types.attrsOf (types.submodule (import ./vrrp-script-options.nix {
+          inherit lib;
+        }));
+        default = {};
+        description = "Declarative vrrp script config";
+      };
+
+      vrrpInstances = mkOption {
+        type = types.attrsOf (types.submodule (import ./vrrp-instance-options.nix {
+          inherit lib;
+        }));
+        default = {};
+        description = "Declarative vhost config";
+      };
+
+      extraGlobalDefs = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Extra lines to be added verbatim to the 'global_defs' block of the
+          configuration file
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Extra lines to be added verbatim to the configuration file.
+        '';
+      };
+
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    assertions = flatten (map vrrpInstanceAssertions vrrpInstances);
+
+    systemd.timers.keepalived-boot-delay = {
+      description = "Keepalive Daemon delay to avoid instant transition to MASTER state";
+      after = [ "network.target" "network-online.target" "syslog.target" ];
+      requires = [ "network-online.target" ];
+      wantedBy = [ "multi-user.target" ];
+      timerConfig = {
+        OnActiveSec = "5s";
+        Unit = "keepalived.service";
+      };
+    };
+
+    systemd.services.keepalived = {
+      description = "Keepalive Daemon (LVS and VRRP)";
+      after = [ "network.target" "network-online.target" "syslog.target" ];
+      wants = [ "network-online.target" ];
+      serviceConfig = {
+        Type = "forking";
+        PIDFile = pidFile;
+        KillMode = "process";
+        ExecStart = "${pkgs.keepalived}/sbin/keepalived"
+          + " -f ${keepalivedConf}"
+          + " -p ${pidFile}"
+          + optionalString cfg.snmp.enable " --snmp";
+        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+        Restart = "always";
+        RestartSec = "1s";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/keepalived/virtual-ip-options.nix b/nixpkgs/nixos/modules/services/networking/keepalived/virtual-ip-options.nix
new file mode 100644
index 000000000000..1b8889b1b472
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/keepalived/virtual-ip-options.nix
@@ -0,0 +1,50 @@
+{ lib } :
+
+with lib;
+{
+  options = {
+
+    addr = mkOption {
+      type = types.str;
+      description = ''
+        IP address, optionally with a netmask: IPADDR[/MASK]
+      '';
+    };
+
+    brd = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        The broadcast address on the interface.
+      '';
+    };
+
+    dev = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        The name of the device to add the address to.
+      '';
+    };
+
+    scope = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        The scope of the area where this address is valid.
+      '';
+    };
+
+    label = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        Each address may be tagged with a label string. In order to preserve
+        compatibility with Linux-2.0 net aliases, this string must coincide with
+        the name of the device or must be prefixed with the device name followed
+        by colon.
+      '';
+    };
+
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/keepalived/vrrp-instance-options.nix b/nixpkgs/nixos/modules/services/networking/keepalived/vrrp-instance-options.nix
new file mode 100644
index 000000000000..85b9bc337726
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/keepalived/vrrp-instance-options.nix
@@ -0,0 +1,135 @@
+{ lib } :
+
+with lib;
+{
+  options = {
+
+    interface = mkOption {
+      type = types.str;
+      description = ''
+        Interface for inside_network, bound by vrrp.
+      '';
+    };
+
+    state = mkOption {
+      type = types.enum [ "MASTER" "BACKUP" ];
+      default = "BACKUP";
+      description = ''
+        Initial state. As soon as the other machine(s) come up, an election will
+        be held and the machine with the highest "priority" will become MASTER.
+        So the entry here doesn't matter a whole lot.
+      '';
+    };
+
+    virtualRouterId = mkOption {
+      type = types.int;
+      description = ''
+        Arbitrary unique number 0..255. Used to differentiate multiple instances
+        of vrrpd running on the same NIC (and hence same socket).
+      '';
+    };
+
+    priority = mkOption {
+      type = types.int;
+      default = 100;
+      description = ''
+        For electing MASTER, highest priority wins. To be MASTER, make 50 more
+        than other machines.
+      '';
+    };
+
+    noPreempt = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        VRRP will normally preempt a lower priority machine when a higher
+        priority machine comes online. "nopreempt" allows the lower priority
+        machine to maintain the master role, even when a higher priority machine
+        comes back online. NOTE: For this to work, the initial state of this
+        entry must be BACKUP.
+      '';
+    };
+
+    useVmac = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Use VRRP Virtual MAC.
+      '';
+    };
+
+    vmacInterface = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+         Name of the vmac interface to use. keepalived will come up with a name
+         if you don't specify one.
+      '';
+    };
+
+    vmacXmitBase = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Send/Recv VRRP messages from base interface instead of VMAC interface.
+      '';
+    };
+
+    unicastSrcIp = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+         Default IP for binding vrrpd is the primary IP on interface. If you
+         want to hide location of vrrpd, use this IP as src_addr for unicast
+         vrrp packets.
+      '';
+    };
+
+    unicastPeers = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      description = ''
+        Do not send VRRP adverts over VRRP multicast group. Instead it sends
+        adverts to the following list of ip addresses using unicast design
+        fashion. It can be cool to use VRRP FSM and features in a networking
+        environment where multicast is not supported! IP Addresses specified can
+        IPv4 as well as IPv6.
+      '';
+    };
+
+    virtualIps = mkOption {
+      type = types.listOf (types.submodule (import ./virtual-ip-options.nix {
+        inherit lib;
+      }));
+      default = [];
+      example = literalExample ''
+        TODO: Example
+      '';
+      description = "Declarative vhost config";
+    };
+
+    trackScripts = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      example = [ "chk_cmd1" "chk_cmd2" ];
+      description = "List of script names to invoke for health tracking.";
+    };
+
+    trackInterfaces = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      example = [ "eth0" "eth1" ];
+      description = "List of network interfaces to monitor for health tracking.";
+    };
+
+    extraConfig = mkOption {
+      type = types.lines;
+      default = "";
+      description = ''
+        Extra lines to be added verbatim to the vrrp_instance section.
+      '';
+    };
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/keepalived/vrrp-script-options.nix b/nixpkgs/nixos/modules/services/networking/keepalived/vrrp-script-options.nix
new file mode 100644
index 000000000000..a3f794c40a89
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/keepalived/vrrp-script-options.nix
@@ -0,0 +1,64 @@
+{ lib } :
+
+with lib;
+with lib.types;
+{
+  options = {
+
+    script = mkOption {
+      type = str;
+      example = "\${pkgs.curl} -f http://localhost:80";
+      description = "(Path of) Script command to execute followed by args, i.e. cmd [args]...";
+    };
+
+    interval = mkOption {
+      type = int;
+      default = 1;
+      description = "Seconds between script invocations.";
+    };
+
+    timeout = mkOption {
+      type = int;
+      default = 5;
+      description = "Seconds after which script is considered to have failed.";
+    };
+
+    weight = mkOption {
+      type = int;
+      default = 0;
+      description = "Following a failure, adjust the priority by this weight.";
+    };
+
+    rise = mkOption {
+      type = int;
+      default = 5;
+      description = "Required number of successes for OK transition.";
+    };
+
+    fall = mkOption {
+      type = int;
+      default = 3;
+      description = "Required number of failures for KO transition.";
+    };
+
+    user = mkOption {
+      type = str;
+      default = "keepalived_script";
+      description = "Name of user to run the script under.";
+    };
+
+    group = mkOption {
+      type = nullOr str;
+      default = null;
+      description = "Name of group to run the script under. Defaults to user group.";
+    };
+
+    extraConfig = mkOption {
+      type = lines;
+      default = "";
+      description = "Extra lines to be added verbatim to the vrrp_script section.";
+    };
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/keybase.nix b/nixpkgs/nixos/modules/services/networking/keybase.nix
new file mode 100644
index 000000000000..495102cb7eee
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/keybase.nix
@@ -0,0 +1,47 @@
+{ config, lib, pkgs, ... }:
+with lib;
+let
+  cfg = config.services.keybase;
+
+in {
+
+  ###### interface
+
+  options = {
+
+    services.keybase = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to start the Keybase service.";
+      };
+
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    # Upstream: https://github.com/keybase/client/blob/master/packaging/linux/systemd/keybase.service
+    systemd.user.services.keybase = {
+      description = "Keybase service";
+      unitConfig.ConditionUser = "!@system";
+      environment.KEYBASE_SERVICE_TYPE = "systemd";
+      serviceConfig = {
+        Type = "notify";
+        EnvironmentFile = [
+          "-%E/keybase/keybase.autogen.env"
+          "-%E/keybase/keybase.env"
+        ];
+        ExecStart = "${pkgs.keybase}/bin/keybase service";
+        Restart = "on-failure";
+        PrivateTmp = true;
+      };
+      wantedBy = [ "default.target" ];
+    };
+
+    environment.systemPackages = [ pkgs.keybase ];
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/kippo.nix b/nixpkgs/nixos/modules/services/networking/kippo.nix
new file mode 100644
index 000000000000..553415a2f329
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/kippo.nix
@@ -0,0 +1,117 @@
+# NixOS module for kippo honeypot ssh server
+# See all the options for configuration details.
+#
+# Default port is 2222. Recommend using something like this for port redirection to default SSH port:
+# networking.firewall.extraCommands = ''
+#      iptables -t nat -A PREROUTING -i IN_IFACE -p tcp --dport 22 -j REDIRECT --to-port 2222'';
+#
+# Lastly: use this service at your own risk. I am working on a way to run this inside a VM.
+{ config, lib, pkgs, ... }:
+with lib;
+let
+  cfg = config.services.kippo;
+in
+{
+  options = {
+    services.kippo = {
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''Enable the kippo honeypot ssh server.'';
+      };
+      port = mkOption {
+        default = 2222;
+        type = types.int;
+        description = ''TCP port number for kippo to bind to.'';
+      };
+      hostname = mkOption {
+        default = "nas3";
+        type = types.str;
+        description = ''Hostname for kippo to present to SSH login'';
+      };
+      varPath = mkOption {
+        default = "/var/lib/kippo";
+        type = types.path;
+        description = ''Path of read/write files needed for operation and configuration.'';
+      };
+      logPath = mkOption {
+        default = "/var/log/kippo";
+        type = types.path;
+        description = ''Path of log files needed for operation and configuration.'';
+      };
+      pidPath = mkOption {
+        default = "/run/kippo";
+        type = types.path;
+        description = ''Path of pid files needed for operation.'';
+      };
+      extraConfig = mkOption {
+        default = "";
+        type = types.lines;
+        description = ''Extra verbatim configuration added to the end of kippo.cfg.'';
+      };
+    };
+
+  };
+  config = mkIf cfg.enable {
+    environment.systemPackages = with pkgs.pythonPackages; [
+      python pkgs.kippo.twisted pycrypto pyasn1 ];
+
+    environment.etc."kippo.cfg".text = ''
+        # Automatically generated by NixOS.
+        # See ${pkgs.kippo}/src/kippo.cfg for details.
+        [honeypot]
+        log_path = ${cfg.logPath}
+        download_path = ${cfg.logPath}/dl
+        filesystem_file = ${cfg.varPath}/honeyfs
+        filesystem_file = ${cfg.varPath}/fs.pickle
+        data_path = ${cfg.varPath}/data
+        txtcmds_path = ${cfg.varPath}/txtcmds
+        public_key = ${cfg.varPath}/keys/public.key
+        private_key = ${cfg.varPath}/keys/private.key
+        ssh_port = ${toString cfg.port}
+        hostname = ${cfg.hostname}
+        ${cfg.extraConfig}
+    '';
+
+    users.users.kippo = {
+      description = "kippo web server privilege separation user";
+      uid = 108; # why does config.ids.uids.kippo give an error?
+    };
+    users.groups.kippo.gid = 108;
+
+    systemd.services.kippo = with pkgs; {
+      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.kippo.twisted}/lib/python2.7/site-packages/:.";
+      preStart = ''
+        if [ ! -d ${cfg.varPath}/ ] ; then
+            mkdir -p ${cfg.logPath}/tty
+            mkdir -p ${cfg.logPath}/dl
+            mkdir -p ${cfg.varPath}/keys
+            cp ${pkgs.kippo}/src/honeyfs ${cfg.varPath} -r
+            cp ${pkgs.kippo}/src/fs.pickle ${cfg.varPath}/fs.pickle
+            cp ${pkgs.kippo}/src/data ${cfg.varPath} -r
+            cp ${pkgs.kippo}/src/txtcmds ${cfg.varPath} -r
+
+            chmod u+rw ${cfg.varPath} -R
+            chown kippo.kippo ${cfg.varPath} -R
+            chown kippo.kippo ${cfg.logPath} -R
+            chmod u+rw ${cfg.logPath} -R
+        fi
+        if [ ! -d ${cfg.pidPath}/ ] ; then
+            mkdir -p ${cfg.pidPath}
+            chmod u+rw ${cfg.pidPath}
+            chown kippo.kippo ${cfg.pidPath}
+        fi
+      '';
+
+      serviceConfig.ExecStart = "${pkgs.kippo.twisted}/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/nixpkgs/nixos/modules/services/networking/knot.nix b/nixpkgs/nixos/modules/services/networking/knot.nix
new file mode 100644
index 000000000000..12ff89fe8492
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/knot.nix
@@ -0,0 +1,117 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.knot;
+
+  configFile = pkgs.writeTextFile {
+    name = "knot.conf";
+    text = (concatMapStringsSep "\n" (file: "include: ${file}") cfg.keyFiles) + "\n" +
+           cfg.extraConfig;
+    checkPhase = lib.optionalString (cfg.keyFiles == []) ''
+      ${cfg.package}/bin/knotc --config=$out conf-check
+    '';
+  };
+
+  socketFile = "/run/knot/knot.sock";
+
+  knot-cli-wrappers = pkgs.stdenv.mkDerivation {
+    name = "knot-cli-wrappers";
+    buildInputs = [ pkgs.makeWrapper ];
+    buildCommand = ''
+      mkdir -p $out/bin
+      makeWrapper ${cfg.package}/bin/knotc "$out/bin/knotc" \
+        --add-flags "--config=${configFile}" \
+        --add-flags "--socket=${socketFile}"
+      makeWrapper ${cfg.package}/bin/keymgr "$out/bin/keymgr" \
+        --add-flags "--config=${configFile}"
+      for executable in kdig khost kjournalprint knsec3hash knsupdate kzonecheck
+      do
+        ln -s "${cfg.package}/bin/$executable" "$out/bin/$executable"
+      done
+      mkdir -p "$out/share"
+      ln -s '${cfg.package}/share/man' "$out/share/"
+    '';
+  };
+in {
+  options = {
+    services.knot = {
+      enable = mkEnableOption "Knot authoritative-only DNS server";
+
+      extraArgs = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          List of additional command line paramters for knotd
+        '';
+      };
+
+      keyFiles = mkOption {
+        type = types.listOf types.path;
+        default = [];
+        description = ''
+          A list of files containing additional configuration
+          to be included using the include directive. This option
+          allows to include configuration like TSIG keys without
+          exposing them to the nix store readable to any process.
+          Note that using this option will also disable configuration
+          checks at build time.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Extra lines to be added verbatim to knot.conf
+        '';
+      };
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.knot-dns;
+        defaultText = "pkgs.knot-dns";
+        description = ''
+          Which Knot DNS package to use
+        '';
+      };
+    };
+  };
+
+  config = mkIf config.services.knot.enable {
+    users.users.knot = {
+      isSystemUser = true;
+      group = "knot";
+      description = "Knot daemon user";
+    };
+
+    users.groups.knot.gid = null;
+    systemd.services.knot = {
+      unitConfig.Documentation = "man:knotd(8) man:knot.conf(5) man:knotc(8) https://www.knot-dns.cz/docs/${cfg.package.version}/html/";
+      description = cfg.package.meta.description;
+      wantedBy = [ "multi-user.target" ];
+      wants = [ "network.target" ];
+      after = ["network.target" ];
+
+      serviceConfig = {
+        Type = "notify";
+        ExecStart = "${cfg.package}/bin/knotd --config=${configFile} --socket=${socketFile} ${concatStringsSep " " cfg.extraArgs}";
+        ExecReload = "${knot-cli-wrappers}/bin/knotc reload";
+        CapabilityBoundingSet = "CAP_NET_BIND_SERVICE CAP_SETPCAP";
+        AmbientCapabilities = "CAP_NET_BIND_SERVICE CAP_SETPCAP";
+        NoNewPrivileges = true;
+        User = "knot";
+        RuntimeDirectory = "knot";
+        StateDirectory = "knot";
+        StateDirectoryMode = "0700";
+        PrivateDevices = true;
+        RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6";
+        SystemCallArchitectures = "native";
+        Restart = "on-abort";
+      };
+    };
+
+    environment.systemPackages = [ knot-cli-wrappers ];
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/kresd.nix b/nixpkgs/nixos/modules/services/networking/kresd.nix
new file mode 100644
index 000000000000..c5a84eebd46f
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/kresd.nix
@@ -0,0 +1,145 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.kresd;
+
+  # Convert systemd-style address specification to kresd config line(s).
+  # On Nix level we don't attempt to precisely validate the address specifications.
+  mkListen = kind: addr: let
+    al_v4 = builtins.match "([0-9.]\+):([0-9]\+)" addr;
+    al_v6 = builtins.match "\\[(.\+)]:([0-9]\+)" addr;
+    al_portOnly = builtins.match "()([0-9]\+)" addr;
+    al = findFirst (a: a != null)
+      (throw "services.kresd.*: incorrect address specification '${addr}'")
+      [ al_v4 al_v6 al_portOnly ];
+    port = last al;
+    addrSpec = if al_portOnly == null then "'${head al}'" else "{'::', '127.0.0.1'}";
+    in # freebind is set for compatibility with earlier kresd services;
+       # it could be configurable, for example.
+      ''
+        net.listen(${addrSpec}, ${port}, { kind = '${kind}', freebind = true })
+      '';
+
+  configFile = pkgs.writeText "kresd.conf" (
+    optionalString (cfg.listenDoH != []) ''
+      modules.load('http')
+    ''
+    + concatMapStrings (mkListen "dns") cfg.listenPlain
+    + concatMapStrings (mkListen "tls") cfg.listenTLS
+    + concatMapStrings (mkListen "doh") cfg.listenDoH
+    + cfg.extraConfig
+  );
+
+  package = if cfg.listenDoH == []
+    then pkgs.knot-resolver # never force `extraFeatures = false`
+    else pkgs.knot-resolver.override { extraFeatures = true; };
+in {
+  meta.maintainers = [ maintainers.vcunat /* upstream developer */ ];
+
+  imports = [
+    (mkChangedOptionModule [ "services" "kresd" "interfaces" ] [ "services" "kresd" "listenPlain" ]
+      (config:
+        let value = getAttrFromPath [ "services" "kresd" "interfaces" ] config;
+        in map
+          (iface: if elem ":" (stringToCharacters iface) then "[${iface}]:53" else "${iface}:53") # Syntax depends on being IPv6 or IPv4.
+          value
+      )
+    )
+    (mkRemovedOptionModule [ "services" "kresd" "cacheDir" ] "Please use (bind-)mounting instead.")
+  ];
+
+  ###### 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/knot-resolver/control/1</literal>
+        and give commands interactively to kresd@1.service.
+      '';
+    };
+    extraConfig = mkOption {
+      type = types.lines;
+      default = "";
+      description = ''
+        Extra lines to be added verbatim to the generated configuration file.
+      '';
+    };
+    listenPlain = mkOption {
+      type = with types; listOf str;
+      default = [ "[::1]:53" "127.0.0.1:53" ];
+      example = [ "53" ];
+      description = ''
+        What addresses and ports the server should listen on.
+        For detailed syntax see ListenStream in man systemd.socket.
+      '';
+    };
+    listenTLS = mkOption {
+      type = with types; listOf str;
+      default = [];
+      example = [ "198.51.100.1:853" "[2001:db8::1]:853" "853" ];
+      description = ''
+        Addresses and ports on which kresd should provide DNS over TLS (see RFC 7858).
+        For detailed syntax see ListenStream in man systemd.socket.
+      '';
+    };
+    listenDoH = mkOption {
+      type = with types; listOf str;
+      default = [];
+      example = [ "198.51.100.1:443" "[2001:db8::1]:443" "443" ];
+      description = ''
+        Addresses and ports on which kresd should provide DNS over HTTPS (see RFC 8484).
+        For detailed syntax see ListenStream in man systemd.socket.
+      '';
+    };
+    instances = mkOption {
+      type = types.ints.unsigned;
+      default = 1;
+      description = ''
+        The number of instances to start.  They will be called kresd@{1,2,...}.service.
+        Knot Resolver uses no threads, so this is the way to scale.
+        You can dynamically start/stop them at will, so this is just system default.
+      '';
+    };
+    # TODO: perhaps options for more common stuff like cache size or forwarding
+  };
+
+  ###### implementation
+  config = mkIf cfg.enable {
+    environment.etc."knot-resolver/kresd.conf".source = configFile; # not required
+
+    users.users.knot-resolver =
+      { isSystemUser = true;
+        group = "knot-resolver";
+        description = "Knot-resolver daemon user";
+      };
+    users.groups.knot-resolver.gid = null;
+
+    systemd.packages = [ package ]; # the units are patched inside the package a bit
+
+    systemd.targets.kresd = { # configure units started by default
+      wantedBy = [ "multi-user.target" ];
+      wants = [ "kres-cache-gc.service" ]
+        ++ map (i: "kresd@${toString i}.service") (range 1 cfg.instances);
+    };
+    systemd.services."kresd@".serviceConfig = {
+      ExecStart = "${package}/bin/kresd --noninteractive "
+        + "-c ${package}/lib/knot-resolver/distro-preconfig.lua -c ${configFile}";
+      # Ensure correct ownership in case UID or GID changes.
+      CacheDirectory = "knot-resolver";
+      CacheDirectoryMode = "0750";
+    };
+
+    environment.etc."tmpfiles.d/knot-resolver.conf".source =
+      "${package}/lib/tmpfiles.d/knot-resolver.conf";
+
+    # Try cleaning up the previously default location of cache file.
+    # Note that /var/cache/* should always be safe to remove.
+    # TODO: remove later, probably between 20.09 and 21.03
+    systemd.tmpfiles.rules = [ "R /var/cache/kresd" ];
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/lambdabot.nix b/nixpkgs/nixos/modules/services/networking/lambdabot.nix
new file mode 100644
index 000000000000..b7c8bd008fe1
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/lambdabot.nix
@@ -0,0 +1,82 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.lambdabot;
+
+  rc = builtins.toFile "script.rc" cfg.script;
+
+in
+
+{
+
+  ### configuration
+
+  options = {
+
+    services.lambdabot = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Enable the Lambdabot IRC bot";
+      };
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.lambdabot;
+        defaultText = "pkgs.lambdabot";
+        description = "Used lambdabot package";
+      };
+
+      script = mkOption {
+        type = types.str;
+        default = "";
+        description = "Lambdabot script";
+      };
+
+    };
+
+  };
+
+  ### implementation
+
+  config = mkIf cfg.enable {
+
+    systemd.services.lambdabot = {
+      description = "Lambdabot daemon";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      # Workaround for https://github.com/lambdabot/lambdabot/issues/117
+      script = ''
+        mkdir -p ~/.lambdabot
+        cd ~/.lambdabot
+        mkfifo /run/lambdabot/offline
+        (
+          echo 'rc ${rc}'
+          while true; do
+            cat /run/lambdabot/offline
+          done
+        ) | ${cfg.package}/bin/lambdabot
+      '';
+      serviceConfig = {
+        User = "lambdabot";
+        RuntimeDirectory = [ "lambdabot" ];
+      };
+    };
+
+    users.users.lambdabot = {
+      group = "lambdabot";
+      description = "Lambdabot daemon user";
+      home = "/var/lib/lambdabot";
+      createHome = true;
+      uid = config.ids.uids.lambdabot;
+    };
+
+    users.groups.lambdabot.gid = config.ids.gids.lambdabot;
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/libreswan.nix b/nixpkgs/nixos/modules/services/networking/libreswan.nix
new file mode 100644
index 000000000000..280158b89f61
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/libreswan.nix
@@ -0,0 +1,129 @@
+{ 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))
+                  (imap0 (i: v: {ind = i; 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}"
+        "${pkgs.nssTools}"
+        "${pkgs.iptables}"
+        "${pkgs.nettools}"
+      ];
+
+      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/nixpkgs/nixos/modules/services/networking/lldpd.nix b/nixpkgs/nixos/modules/services/networking/lldpd.nix
new file mode 100644
index 000000000000..d5de9c45d84b
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/lldpd.nix
@@ -0,0 +1,39 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.lldpd;
+
+in
+
+{
+  options.services.lldpd = {
+    enable = mkEnableOption "Link Layer Discovery Protocol Daemon";
+
+    extraArgs = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      example = [ "-c" "-k" "-I eth0" ];
+      description = "List of command line parameters for lldpd";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.users._lldpd = {
+      description = "lldpd user";
+      group = "_lldpd";
+      home = "/run/lldpd";
+      isSystemUser = true;
+    };
+    users.groups._lldpd = {};
+
+    environment.systemPackages = [ pkgs.lldpd ];
+    systemd.packages = [ pkgs.lldpd ];
+
+    systemd.services.lldpd = {
+      wantedBy = [ "multi-user.target" ];
+      environment.LLDPD_OPTIONS = concatStringsSep " " cfg.extraArgs;
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/logmein-hamachi.nix b/nixpkgs/nixos/modules/services/networking/logmein-hamachi.nix
new file mode 100644
index 000000000000..11cbdda2f845
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/logmein-hamachi.nix
@@ -0,0 +1,50 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.logmein-hamachi;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.logmein-hamachi.enable = mkOption {
+      type = types.bool;
+      default = false;
+      description =
+        ''
+          Whether to enable LogMeIn Hamachi, a proprietary
+          (closed source) commercial VPN software.
+        '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    systemd.services.logmein-hamachi = {
+      description = "LogMeIn Hamachi Daemon";
+
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+
+      serviceConfig = {
+        Type = "forking";
+        ExecStart = "${pkgs.logmein-hamachi}/bin/hamachid";
+      };
+    };
+
+    environment.systemPackages = [ pkgs.logmein-hamachi ];
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/magic-wormhole-mailbox-server.nix b/nixpkgs/nixos/modules/services/networking/magic-wormhole-mailbox-server.nix
new file mode 100644
index 000000000000..09d357cd2b6e
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/magic-wormhole-mailbox-server.nix
@@ -0,0 +1,28 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.magic-wormhole-mailbox-server;
+  dataDir = "/var/lib/magic-wormhole-mailbox-server;";
+  python = pkgs.python3.withPackages (py: [ py.magic-wormhole-mailbox-server py.twisted ]);
+in
+{
+  options.services.magic-wormhole-mailbox-server = {
+    enable = mkEnableOption "Enable Magic Wormhole Mailbox Server";
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.magic-wormhole-mailbox-server = {
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        DynamicUser = true;
+        ExecStart = "${python}/bin/twistd --nodaemon wormhole-mailbox";
+        WorkingDirectory = dataDir;
+        StateDirectory = baseNameOf dataDir;
+      };
+    };
+
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/mailpile.nix b/nixpkgs/nixos/modules/services/networking/mailpile.nix
new file mode 100644
index 000000000000..b79ee11d17db
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/mailpile.nix
@@ -0,0 +1,72 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.mailpile;
+
+  hostname = cfg.hostname;
+  port = cfg.port;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.mailpile = {
+      enable = mkEnableOption "Mailpile the mail client";
+
+      hostname = mkOption {
+        default = "localhost";
+        description = "Listen to this hostname or ip.";
+      };
+      port = mkOption {
+        default = "33411";
+        description = "Listen on this port.";
+      };
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.mailpile.enable {
+
+    users.users.mailpile =
+      { uid = config.ids.uids.mailpile;
+        description = "Mailpile user";
+        createHome = true;
+        home = "/var/lib/mailpile";
+      };
+
+    users.groups.mailpile =
+      { gid = config.ids.gids.mailpile;
+      };
+
+    systemd.services.mailpile =
+      {
+        description = "Mailpile server.";
+        after = [ "network.target" ];
+        wantedBy = [ "multi-user.target" ];
+        serviceConfig = {
+          User = "mailpile";
+          ExecStart = "${pkgs.mailpile}/bin/mailpile --www ${hostname}:${port} --wait";
+          # mixed - first send SIGINT to main process,
+          # then after 2min send SIGKILL to whole group if neccessary
+          KillMode = "mixed";
+          KillSignal = "SIGINT";  # like Ctrl+C - safe mailpile shutdown
+          TimeoutSec = 120;  # wait 2min untill SIGKILL
+        };
+        environment.MAILPILE_HOME = "/var/lib/mailpile/.local/share/Mailpile";
+      };
+
+    environment.systemPackages = [ pkgs.mailpile ];
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/matterbridge.nix b/nixpkgs/nixos/modules/services/networking/matterbridge.nix
new file mode 100644
index 000000000000..b8b4f37c84a8
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/matterbridge.nix
@@ -0,0 +1,120 @@
+{ options, config, pkgs, lib, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.matterbridge;
+
+  matterbridgeConfToml =
+    if cfg.configPath == null then
+      pkgs.writeText "matterbridge.toml" (cfg.configFile)
+    else
+      cfg.configPath;
+
+in
+
+{
+  options = {
+    services.matterbridge = {
+      enable = mkEnableOption "Matterbridge chat platform bridge";
+
+      configPath = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        example = "/etc/nixos/matterbridge.toml";
+        description = ''
+          The path to the matterbridge configuration file.
+        '';
+      };
+
+      configFile = mkOption {
+        type = types.str;
+        example = ''
+          # WARNING: as this file contains credentials, do not use this option!
+          # It is kept only for backwards compatibility, and would cause your
+          # credentials to be in the nix-store, thus with the world-readable
+          # permission bits.
+          # Use services.matterbridge.configPath instead.
+
+          [irc]
+              [irc.freenode]
+              Server="irc.freenode.net:6667"
+              Nick="matterbot"
+
+          [mattermost]
+              [mattermost.work]
+               # Do not prefix it with http:// or https://
+               Server="yourmattermostserver.domain"
+               Team="yourteam"
+               Login="yourlogin"
+               Password="yourpass"
+               PrefixMessagesWithNick=true
+
+          [[gateway]]
+          name="gateway1"
+          enable=true
+              [[gateway.inout]]
+              account="irc.freenode"
+              channel="#testing"
+
+              [[gateway.inout]]
+              account="mattermost.work"
+              channel="off-topic"
+        '';
+        description = ''
+          WARNING: THIS IS INSECURE, as your password will end up in
+          <filename>/nix/store</filename>, thus publicly readable. Use
+          <literal>services.matterbridge.configPath</literal> instead.
+
+          The matterbridge configuration file in the TOML file format.
+        '';
+      };
+      user = mkOption {
+        type = types.str;
+        default = "matterbridge";
+        description = ''
+          User which runs the matterbridge service.
+        '';
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "matterbridge";
+        description = ''
+          Group which runs the matterbridge service.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    warnings = optional options.services.matterbridge.configFile.isDefined
+      "The option services.matterbridge.configFile is insecure and should be replaced with services.matterbridge.configPath";
+
+    users.users = optionalAttrs (cfg.user == "matterbridge")
+      { matterbridge = {
+          group = "matterbridge";
+          isSystemUser = true;
+        };
+      };
+
+    users.groups = optionalAttrs (cfg.group == "matterbridge")
+      { matterbridge = { };
+      };
+
+    systemd.services.matterbridge = {
+      description = "Matterbridge chat platform bridge";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+
+      serviceConfig = {
+        User = cfg.user;
+        Group = cfg.group;
+        ExecStart = "${pkgs.matterbridge}/bin/matterbridge -conf ${matterbridgeConfToml}";
+        Restart = "always";
+        RestartSec = "10";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/minidlna.nix b/nixpkgs/nixos/modules/services/networking/minidlna.nix
new file mode 100644
index 000000000000..c580ba47dad3
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/minidlna.nix
@@ -0,0 +1,193 @@
+# Module for MiniDLNA, a simple DLNA server.
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.minidlna;
+  port = 8200;
+in
+
+{
+  ###### interface
+  options = {
+    services.minidlna.enable = mkOption {
+      type = types.bool;
+      default = false;
+      description =
+        ''
+          Whether to enable MiniDLNA, a simple DLNA server.  It serves
+          media files such as video and music to DLNA client devices
+          such as televisions and media players.
+        '';
+    };
+
+    services.minidlna.mediaDirs = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      example = [ "/data/media" "V,/home/alice/video" ];
+      description =
+        ''
+          Directories to be scanned for media files.  The prefixes
+          <literal>A,</literal>, <literal>V,</literal> and
+          <literal>P,</literal> restrict a directory to audio, video
+          or image files.  The directories must be accessible to the
+          <literal>minidlna</literal> user account.
+        '';
+    };
+
+    services.minidlna.friendlyName = mkOption {
+      type = types.str;
+      default = "${config.networking.hostName} MiniDLNA";
+      defaultText = "$HOSTNAME MiniDLNA";
+      example = "rpi3";
+      description =
+        ''
+          Name that the DLNA server presents to clients.
+        '';
+    };
+
+    services.minidlna.rootContainer = mkOption {
+      type = types.str;
+      default = ".";
+      example = "B";
+      description =
+        ''
+          Use a different container as the root of the directory tree presented
+          to clients. The possible values are:
+          - "." - standard container
+          - "B" - "Browse Directory"
+          - "M" - "Music"
+          - "P" - "Pictures"
+          - "V" - "Video"
+          - Or, you can specify the ObjectID of your desired root container
+            (eg. 1$F for Music/Playlists)
+          If you specify "B" and the client device is audio-only then
+          "Music/Folders" will be used as root.
+         '';
+    };
+
+    services.minidlna.loglevel = mkOption {
+      type = types.str;
+      default = "warn";
+      example = "general,artwork,database,inotify,scanner,metadata,http,ssdp,tivo=warn";
+      description =
+        ''
+          Defines the type of messages that should be logged, and down to
+          which level of importance they should be considered.
+
+          The possible types are “artwork”, “database”, “general”, “http”,
+          “inotify”, “metadata”, “scanner”, “ssdp” and “tivo”.
+
+          The levels are “off”, “fatal”, “error”, “warn”, “info” and
+          “debug”, listed here in order of decreasing importance.  “off”
+          turns off logging messages entirely, “fatal” logs the most
+          critical messages only, and so on down to “debug” that logs every
+          single messages.
+
+          The types are comma-separated, followed by an equal sign (‘=’),
+          followed by a level that applies to the preceding types. This can
+          be repeated, separating each of these constructs with a comma.
+
+          Defaults to “general,artwork,database,inotify,scanner,metadata,
+          http,ssdp,tivo=warn” which logs every type of message at the
+          “warn” level.
+        '';
+    };
+
+    services.minidlna.announceInterval = mkOption {
+      type = types.int;
+      default = 895;
+      description =
+        ''
+          The interval between announces (in seconds).
+
+          By default miniDLNA will announce its presence on the network
+          approximately every 15 minutes.
+
+          Many people prefer shorter announce intervals (e.g. 60 seconds)
+          on their home networks, especially when DLNA clients are
+          started on demand.
+        '';
+    };
+
+    services.minidlna.config = mkOption {
+      type = types.lines;
+      description =
+      ''
+        The contents of MiniDLNA's configuration file.
+        When the service is activated, a basic template is generated
+        from the current options opened here.
+      '';
+    };
+
+    services.minidlna.extraConfig = mkOption {
+      type = types.lines;
+      default = "";
+      example = ''
+        # Not exhaustive example
+        # Support for streaming .jpg and .mp3 files to a TiVo supporting HMO.
+        enable_tivo=no
+        # SSDP notify interval, in seconds.
+        notify_interval=10
+        # maximum number of simultaneous connections
+        # note: many clients open several simultaneous connections while
+        # streaming
+        max_connections=50
+        # set this to yes to allow symlinks that point outside user-defined
+        # media_dirs.
+        wide_links=yes
+      '';
+      description =
+      ''
+        Extra minidlna options not yet opened for configuration here
+        (strict_dlna, model_number, model_name, etc...).  This is appended
+        to the current service already provided.
+      '';
+    };
+  };
+
+  ###### implementation
+  config = mkIf cfg.enable {
+    services.minidlna.config =
+      ''
+        port=${toString port}
+        friendly_name=${cfg.friendlyName}
+        db_dir=/var/cache/minidlna
+        log_level=${cfg.loglevel}
+        inotify=yes
+        root_container=${cfg.rootContainer}
+        ${concatMapStrings (dir: ''
+          media_dir=${dir}
+        '') cfg.mediaDirs}
+        notify_interval=${toString cfg.announceInterval}
+        ${cfg.extraConfig}
+      '';
+
+    users.users.minidlna = {
+      description = "MiniDLNA daemon user";
+      group = "minidlna";
+      uid = config.ids.uids.minidlna;
+    };
+
+    users.groups.minidlna.gid = config.ids.gids.minidlna;
+
+    systemd.services.minidlna =
+      { description = "MiniDLNA Server";
+
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ];
+
+        serviceConfig =
+          { User = "minidlna";
+            Group = "minidlna";
+            CacheDirectory = "minidlna";
+            RuntimeDirectory = "minidlna";
+            PIDFile = "/run/minidlna/pid";
+            ExecStart =
+              "${pkgs.minidlna}/sbin/minidlnad -S -P /run/minidlna/pid" +
+              " -f ${pkgs.writeText "minidlna.conf" cfg.config}";
+          };
+      };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/miniupnpd.nix b/nixpkgs/nixos/modules/services/networking/miniupnpd.nix
new file mode 100644
index 000000000000..c095d9948546
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/miniupnpd.nix
@@ -0,0 +1,79 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.miniupnpd;
+  configFile = pkgs.writeText "miniupnpd.conf" ''
+    ext_ifname=${cfg.externalInterface}
+    enable_natpmp=${if cfg.natpmp then "yes" else "no"}
+    enable_upnp=${if cfg.upnp then "yes" else "no"}
+
+    ${concatMapStrings (range: ''
+      listening_ip=${range}
+    '') cfg.internalIPs}
+
+    ${cfg.appendConfig}
+  '';
+in
+{
+  options = {
+    services.miniupnpd = {
+      enable = mkEnableOption "MiniUPnP daemon";
+
+      externalInterface = mkOption {
+        type = types.str;
+        description = ''
+          Name of the external interface.
+        '';
+      };
+
+      internalIPs = mkOption {
+        type = types.listOf types.str;
+        example = [ "192.168.1.1/24" "enp1s0" ];
+        description = ''
+          The IP address ranges to listen on.
+        '';
+      };
+
+      natpmp = mkEnableOption "NAT-PMP support";
+
+      upnp = mkOption {
+        default = true;
+        type = types.bool;
+        description = ''
+          Whether to enable UPNP support.
+        '';
+      };
+
+      appendConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Configuration lines appended to the MiniUPnP config.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    networking.firewall.extraCommands = ''
+      ${pkgs.bash}/bin/bash -x ${pkgs.miniupnpd}/etc/miniupnpd/iptables_init.sh -i ${cfg.externalInterface}
+    '';
+
+    networking.firewall.extraStopCommands = ''
+      ${pkgs.bash}/bin/bash -x ${pkgs.miniupnpd}/etc/miniupnpd/iptables_removeall.sh -i ${cfg.externalInterface}
+    '';
+
+    systemd.services.miniupnpd = {
+      description = "MiniUPnP daemon";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        ExecStart = "${pkgs.miniupnpd}/bin/miniupnpd -f ${configFile}";
+        PIDFile = "/run/miniupnpd.pid";
+        Type = "forking";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/miredo.nix b/nixpkgs/nixos/modules/services/networking/miredo.nix
new file mode 100644
index 000000000000..2c8393fb5b41
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/miredo.nix
@@ -0,0 +1,92 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.miredo;
+  pidFile = "/run/miredo.pid";
+  miredoConf = pkgs.writeText "miredo.conf" ''
+    InterfaceName ${cfg.interfaceName}
+    ServerAddress ${cfg.serverAddress}
+    ${optionalString (cfg.bindAddress != null) "BindAddress ${cfg.bindAddress}"}
+    ${optionalString (cfg.bindPort != null) "BindPort ${cfg.bindPort}"}
+  '';
+in
+{
+
+  ###### interface
+
+  options = {
+
+    services.miredo = {
+
+      enable = mkEnableOption "the Miredo IPv6 tunneling service";
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.miredo;
+        defaultText = "pkgs.miredo";
+        description = ''
+          The package to use for the miredo daemon's binary.
+        '';
+      };
+
+      serverAddress = mkOption {
+        default = "teredo.remlab.net";
+        type = types.str;
+        description = ''
+          The hostname or primary IPv4 address of the Teredo server.
+          This setting is required if Miredo runs as a Teredo client.
+          "teredo.remlab.net" is an experimental service for testing only.
+          Please use another server for production and/or large scale deployments.
+        '';
+      };
+
+      interfaceName = mkOption {
+        default = "teredo";
+        type = types.str;
+        description = ''
+          Name of the network tunneling interface.
+        '';
+      };
+
+      bindAddress = mkOption {
+        default = null;
+        type = types.nullOr types.str;
+        description = ''
+          Depending on the local firewall/NAT rules, you might need to force
+          Miredo to use a fixed UDP port and or IPv4 address.
+        '';
+      };
+
+      bindPort = mkOption {
+        default = null;
+        type = types.nullOr types.str;
+        description = ''
+          Depending on the local firewall/NAT rules, you might need to force
+          Miredo to use a fixed UDP port and or IPv4 address.
+        '';
+      };
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    systemd.services.miredo = {
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+      description = "Teredo IPv6 Tunneling Daemon";
+      serviceConfig = {
+        Restart = "always";
+        RestartSec = "5s";
+        ExecStart = "${cfg.package}/bin/miredo -c ${miredoConf} -p ${pidFile} -f";
+        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+      };
+    };
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/mjpg-streamer.nix b/nixpkgs/nixos/modules/services/networking/mjpg-streamer.nix
new file mode 100644
index 000000000000..dbc35e2e71c0
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/mjpg-streamer.nix
@@ -0,0 +1,80 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.mjpg-streamer;
+
+in {
+
+  options = {
+
+    services.mjpg-streamer = {
+
+      enable = mkEnableOption "mjpg-streamer webcam streamer";
+
+      inputPlugin = mkOption {
+        type = types.str;
+        default = "input_uvc.so";
+        description = ''
+          Input plugin. See plugins documentation for more information.
+        '';
+      };
+
+      outputPlugin = mkOption {
+        type = types.str;
+        default = "output_http.so -w @www@ -n -p 5050";
+        description = ''
+          Output plugin. <literal>@www@</literal> is substituted for default mjpg-streamer www directory.
+          See plugins documentation for more information.
+        '';
+      };
+
+      user = mkOption {
+        type = types.str;
+        default = "mjpg-streamer";
+        description = "mjpg-streamer user name.";
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "video";
+        description = "mjpg-streamer group name.";
+      };
+
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    users.users = optionalAttrs (cfg.user == "mjpg-streamer") {
+      mjpg-streamer = {
+        uid = config.ids.uids.mjpg-streamer;
+        group = cfg.group;
+      };
+    };
+
+    systemd.services.mjpg-streamer = {
+      description = "mjpg-streamer webcam streamer";
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        User = cfg.user;
+        Group = cfg.group;
+        Restart = "on-failure";
+        RestartSec = 1;
+      };
+
+      script = ''
+        IPLUGIN="${cfg.inputPlugin}"
+        OPLUGIN="${cfg.outputPlugin}"
+        OPLUGIN="''${OPLUGIN//@www@/${pkgs.mjpg-streamer}/share/mjpg-streamer/www}"
+        exec ${pkgs.mjpg-streamer}/bin/mjpg_streamer -i "$IPLUGIN" -o "$OPLUGIN"
+      '';
+    };
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/monero.nix b/nixpkgs/nixos/modules/services/networking/monero.nix
new file mode 100644
index 000000000000..97af29978397
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/monero.nix
@@ -0,0 +1,238 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg     = config.services.monero;
+  dataDir = "/var/lib/monero";
+
+  listToConf = option: list:
+    concatMapStrings (value: "${option}=${value}\n") list;
+
+  login = (cfg.rpc.user != null && cfg.rpc.password != null);
+
+  configFile = with cfg; pkgs.writeText "monero.conf" ''
+    log-file=/dev/stdout
+    data-dir=${dataDir}
+
+    ${optionalString mining.enable ''
+      start-mining=${mining.address}
+      mining-threads=${toString mining.threads}
+    ''}
+
+    rpc-bind-ip=${rpc.address}
+    rpc-bind-port=${toString rpc.port}
+    ${optionalString login ''
+      rpc-login=${rpc.user}:${rpc.password}
+    ''}
+    ${optionalString rpc.restricted ''
+      restricted-rpc=1
+    ''}
+
+    limit-rate-up=${toString limits.upload}
+    limit-rate-down=${toString limits.download}
+    max-concurrency=${toString limits.threads}
+    block-sync-size=${toString limits.syncSize}
+
+    ${listToConf "add-peer" extraNodes}
+    ${listToConf "add-priority-node" priorityNodes}
+    ${listToConf "add-exclusive-node" exclusiveNodes}
+
+    ${extraConfig}
+  '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.monero = {
+
+      enable = mkEnableOption "Monero node daemon";
+
+      mining.enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to mine moneroj.
+        '';
+      };
+
+      mining.address = mkOption {
+        type = types.str;
+        default = "";
+        description = ''
+          Monero address where to send mining rewards.
+        '';
+      };
+
+      mining.threads = mkOption {
+        type = types.addCheck types.int (x: x>=0);
+        default = 0;
+        description = ''
+          Number of threads used for mining.
+          Set to <literal>0</literal> to use all available.
+        '';
+      };
+
+      rpc.user = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          User name for RPC connections.
+        '';
+      };
+
+      rpc.password = mkOption {
+        type = types.str;
+        default = null;
+        description = ''
+          Password for RPC connections.
+        '';
+      };
+
+      rpc.address = mkOption {
+        type = types.str;
+        default = "127.0.0.1";
+        description = ''
+          IP address the RPC server will bind to.
+        '';
+      };
+
+      rpc.port = mkOption {
+        type = types.int;
+        default = 18081;
+        description = ''
+          Port the RPC server will bind to.
+        '';
+      };
+
+      rpc.restricted = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to restrict RPC to view only commands.
+        '';
+      };
+
+      limits.upload = mkOption {
+        type = types.addCheck types.int (x: x>=-1);
+        default = -1;
+        description = ''
+          Limit of the upload rate in kB/s.
+          Set to <literal>-1</literal> to leave unlimited.
+        '';
+      };
+
+      limits.download = mkOption {
+        type = types.addCheck types.int (x: x>=-1);
+        default = -1;
+        description = ''
+          Limit of the download rate in kB/s.
+          Set to <literal>-1</literal> to leave unlimited.
+        '';
+      };
+
+      limits.threads = mkOption {
+        type = types.addCheck types.int (x: x>=0);
+        default = 0;
+        description = ''
+          Maximum number of threads used for a parallel job.
+          Set to <literal>0</literal> to leave unlimited.
+        '';
+      };
+
+      limits.syncSize = mkOption {
+        type = types.addCheck types.int (x: x>=0);
+        default = 0;
+        description = ''
+          Maximum number of blocks to sync at once.
+          Set to <literal>0</literal> for adaptive.
+        '';
+      };
+
+      extraNodes = mkOption {
+        type = types.listOf types.str;
+        default = [ ];
+        description = ''
+          List of additional peer IP addresses to add to the local list.
+        '';
+      };
+
+      priorityNodes = mkOption {
+        type = types.listOf types.str;
+        default = [ ];
+        description = ''
+          List of peer IP addresses to connect to and
+          attempt to keep the connection open.
+        '';
+      };
+
+      exclusiveNodes = mkOption {
+        type = types.listOf types.str;
+        default = [ ];
+        description = ''
+          List of peer IP addresses to connect to *only*.
+          If given the other peer options will be ignored.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Extra lines to be added verbatim to monerod configuration.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    users.users.monero = {
+      uid  = config.ids.uids.monero;
+      description = "Monero daemon user";
+      home = dataDir;
+      createHome = true;
+    };
+
+    users.groups.monero = {
+      gid = config.ids.gids.monero;
+    };
+
+    systemd.services.monero = {
+      description = "monero daemon";
+      after    = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        User  = "monero";
+        Group = "monero";
+        ExecStart = "${pkgs.monero}/bin/monerod --config-file=${configFile} --non-interactive";
+        Restart = "always";
+        SuccessExitStatus = [ 0 1 ];
+      };
+    };
+
+    assertions = singleton {
+      assertion = cfg.mining.enable -> cfg.mining.address != "";
+      message   = ''
+       You need a Monero address to receive mining rewards:
+       specify one using option monero.mining.address.
+      '';
+    };
+
+  };
+
+  meta.maintainers = with lib.maintainers; [ rnhmjoj ];
+
+}
+
diff --git a/nixpkgs/nixos/modules/services/networking/morty.nix b/nixpkgs/nixos/modules/services/networking/morty.nix
new file mode 100644
index 000000000000..e3a6444c1163
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/morty.nix
@@ -0,0 +1,97 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.morty;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.morty = {
+
+      enable = mkEnableOption
+        "Morty proxy server. See https://github.com/asciimoo/morty";
+
+      ipv6 = mkOption {
+        type = types.bool;
+        default = true;
+        description = "Allow IPv6 HTTP requests?";
+        defaultText = "Allow IPv6 HTTP requests.";
+      };
+
+      key = mkOption {
+        type = types.str;
+        default = "";
+        description = "HMAC url validation key (hexadecimal encoded).
+	Leave blank to disable. Without validation key, anyone can
+	submit proxy requests. Leave blank to disable.";
+        defaultText = "No HMAC url validation. Generate with echo -n somevalue | openssl dgst -sha1 -hmac somekey";
+      };
+
+      timeout = mkOption {
+        type = types.int;
+        default = 2;
+        description = "Request timeout in seconds.";
+        defaultText = "A resource now gets 2 seconds to respond.";
+      };
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.morty;
+        defaultText = "pkgs.morty";
+        description = "morty package to use.";
+      };
+
+      port = mkOption {
+        type = types.int;
+        default = 3000;
+        description = "Listing port";
+      };
+
+      listenAddress = mkOption {
+        type = types.str;
+        default = "127.0.0.1";
+        description = "The address on which the service listens";
+        defaultText = "127.0.0.1 (localhost)";
+      };
+
+    };
+
+  };
+
+  ###### Service definition
+
+  config = mkIf config.services.morty.enable {
+
+    users.users.morty =
+      { description = "Morty user";
+        createHome = true;
+        home = "/var/lib/morty";
+        isSystemUser = true;
+      };
+
+    systemd.services.morty =
+      {
+        description = "Morty sanitizing proxy server.";
+        after = [ "network.target" ];
+        wantedBy = [ "multi-user.target" ];
+        serviceConfig = {
+          User = "morty";
+          ExecStart = ''${cfg.package}/bin/morty              \
+	    -listen ${cfg.listenAddress}:${toString cfg.port} \
+	    ${optionalString cfg.ipv6 "-ipv6"}                \
+	    ${optionalString (cfg.key != "") "-key " + cfg.key} \
+	  '';
+        };
+      };
+    environment.systemPackages = [ cfg.package ];
+
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/mosquitto.nix b/nixpkgs/nixos/modules/services/networking/mosquitto.nix
new file mode 100644
index 000000000000..d2feb93e2b72
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/mosquitto.nix
@@ -0,0 +1,231 @@
+{ config, lib, pkgs, ...}:
+
+with lib;
+
+let
+  cfg = config.services.mosquitto;
+
+  listenerConf = optionalString cfg.ssl.enable ''
+    listener ${toString cfg.ssl.port} ${cfg.ssl.host}
+    cafile ${cfg.ssl.cafile}
+    certfile ${cfg.ssl.certfile}
+    keyfile ${cfg.ssl.keyfile}
+  '';
+
+  passwordConf = optionalString cfg.checkPasswords ''
+    password_file ${cfg.dataDir}/passwd
+  '';
+
+  mosquittoConf = pkgs.writeText "mosquitto.conf" ''
+    acl_file ${aclFile}
+    persistence true
+    allow_anonymous ${boolToString cfg.allowAnonymous}
+    bind_address ${cfg.host}
+    port ${toString cfg.port}
+    ${passwordConf}
+    ${listenerConf}
+    ${cfg.extraConf}
+  '';
+
+  userAcl = (concatStringsSep "\n\n" (mapAttrsToList (n: c:
+    "user ${n}\n" + (concatStringsSep "\n" c.acl)) cfg.users
+  ));
+
+  aclFile = pkgs.writeText "mosquitto.acl" ''
+    ${cfg.aclExtraConf}
+    ${userAcl}
+  '';
+
+in
+
+{
+
+  ###### Interface
+
+  options = {
+    services.mosquitto = {
+      enable = mkEnableOption "the MQTT Mosquitto broker";
+
+      host = mkOption {
+        default = "127.0.0.1";
+        example = "0.0.0.0";
+        type = types.str;
+        description = ''
+          Host to listen on without SSL.
+        '';
+      };
+
+      port = mkOption {
+        default = 1883;
+        example = 1883;
+        type = types.int;
+        description = ''
+          Port on which to listen without SSL.
+        '';
+      };
+
+      ssl = {
+        enable = mkEnableOption "SSL listener";
+
+        cafile = mkOption {
+          type = types.nullOr types.path;
+          default = null;
+          description = "Path to PEM encoded CA certificates.";
+        };
+
+        certfile = mkOption {
+          type = types.nullOr types.path;
+          default = null;
+          description = "Path to PEM encoded server certificate.";
+        };
+
+        keyfile = mkOption {
+          type = types.nullOr types.path;
+          default = null;
+          description = "Path to PEM encoded server key.";
+        };
+
+        host = mkOption {
+          default = "0.0.0.0";
+          example = "localhost";
+          type = types.str;
+          description = ''
+            Host to listen on with SSL.
+          '';
+        };
+
+        port = mkOption {
+          default = 8883;
+          example = 8883;
+          type = types.int;
+          description = ''
+            Port on which to listen with SSL.
+          '';
+        };
+      };
+
+      dataDir = mkOption {
+        default = "/var/lib/mosquitto";
+        type = types.path;
+        description = ''
+          The data directory.
+        '';
+      };
+
+      users = mkOption {
+        type = types.attrsOf (types.submodule {
+          options = {
+            password = mkOption {
+              type = with types; uniq (nullOr str);
+              default = null;
+              description = ''
+                Specifies the (clear text) password for the MQTT User.
+              '';
+            };
+
+            hashedPassword = mkOption {
+              type = with types; uniq (nullOr str);
+              default = null;
+              description = ''
+                Specifies the hashed password for the MQTT User.
+                <option>hashedPassword</option> overrides <option>password</option>.
+                To generate hashed password install <literal>mosquitto</literal>
+                package and use <literal>mosquitto_passwd</literal>.
+              '';
+            };
+
+            acl = mkOption {
+              type = types.listOf types.str;
+              example = [ "topic read A/B" "topic A/#" ];
+              description = ''
+                Control client access to topics on the broker.
+              '';
+            };
+          };
+        });
+        example = { john = { password = "123456"; acl = [ "topic readwrite john/#" ]; }; };
+        description = ''
+          A set of users and their passwords and ACLs.
+        '';
+      };
+
+      allowAnonymous = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Allow clients to connect without authentication.
+        '';
+      };
+
+      checkPasswords = mkOption {
+        default = false;
+        example = true;
+        type = types.bool;
+        description = ''
+          Refuse connection when clients provide incorrect passwords.
+        '';
+      };
+
+      extraConf = mkOption {
+        default = "";
+        type = types.lines;
+        description = ''
+          Extra config to append to `mosquitto.conf` file.
+        '';
+      };
+
+      aclExtraConf = mkOption {
+        default = "";
+        type = types.lines;
+        description = ''
+          Extra config to prepend to the ACL file.
+        '';
+      };
+
+    };
+  };
+
+
+  ###### Implementation
+
+  config = mkIf cfg.enable {
+
+    systemd.services.mosquitto = {
+      description = "Mosquitto MQTT Broker Daemon";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+      serviceConfig = {
+        Type = "notify";
+        NotifyAccess = "main";
+        User = "mosquitto";
+        Group = "mosquitto";
+        RuntimeDirectory = "mosquitto";
+        WorkingDirectory = cfg.dataDir;
+        Restart = "on-failure";
+        ExecStart = "${pkgs.mosquitto}/bin/mosquitto -c ${mosquittoConf}";
+        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+      };
+      preStart = ''
+        rm -f ${cfg.dataDir}/passwd
+        touch ${cfg.dataDir}/passwd
+      '' + concatStringsSep "\n" (
+        mapAttrsToList (n: c:
+          if c.hashedPassword != null then
+            "echo '${n}:${c.hashedPassword}' >> ${cfg.dataDir}/passwd"
+          else optionalString (c.password != null)
+            "${pkgs.mosquitto}/bin/mosquitto_passwd -b ${cfg.dataDir}/passwd ${n} '${c.password}'"
+        ) cfg.users);
+    };
+
+    users.users.mosquitto = {
+      description = "Mosquitto MQTT Broker Daemon owner";
+      group = "mosquitto";
+      uid = config.ids.uids.mosquitto;
+      home = cfg.dataDir;
+      createHome = true;
+    };
+
+    users.groups.mosquitto.gid = config.ids.gids.mosquitto;
+
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/mstpd.nix b/nixpkgs/nixos/modules/services/networking/mstpd.nix
new file mode 100644
index 000000000000..5d1fc4a65427
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/mstpd.nix
@@ -0,0 +1,33 @@
+{ config, lib, pkgs, ... }:
+let
+  cfg = config.services.mstpd;
+in
+with lib;
+{
+  options.services.mstpd = {
+    
+    enable = mkOption {
+      default = false;
+      type = types.bool;
+      description = ''
+        Whether to enable the multiple spanning tree protocol daemon.
+      '';
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.mstpd ];
+
+    systemd.services.mstpd = {
+      description = "Multiple Spanning Tree Protocol Daemon";
+      wantedBy = [ "network.target" ];
+      unitConfig.ConditionCapability = "CAP_NET_ADMIN";
+      serviceConfig = {
+        Type = "forking";
+        ExecStart = "@${pkgs.mstpd}/bin/mstpd mstpd";
+        PIDFile = "/run/mstpd.pid";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/mtprotoproxy.nix b/nixpkgs/nixos/modules/services/networking/mtprotoproxy.nix
new file mode 100644
index 000000000000..d896f227b82c
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/mtprotoproxy.nix
@@ -0,0 +1,110 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.mtprotoproxy;
+
+  configOpts = {
+    PORT = cfg.port;
+    USERS = cfg.users;
+    SECURE_ONLY = cfg.secureOnly;
+  } // lib.optionalAttrs (cfg.adTag != null) { AD_TAG = cfg.adTag; }
+    // cfg.extraConfig;
+
+  convertOption = opt:
+    if isString opt || isInt opt then
+      builtins.toJSON opt
+    else if isBool opt then
+      if opt then "True" else "False"
+    else if isList opt then
+      "[" + concatMapStringsSep "," convertOption opt + "]"
+    else if isAttrs opt then
+      "{" + concatStringsSep "," (mapAttrsToList (name: opt: "${builtins.toJSON name}: ${convertOption opt}") opt) + "}"
+    else
+      throw "Invalid option type";
+
+  configFile = pkgs.writeText "config.py" (concatStringsSep "\n" (mapAttrsToList (name: opt: "${name} = ${convertOption opt}") configOpts));
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.mtprotoproxy = {
+
+      enable = mkEnableOption "mtprotoproxy";
+
+      port = mkOption {
+        type = types.int;
+        default = 3256;
+        description = ''
+          TCP port to accept mtproto connections on.
+        '';
+      };
+
+      users = mkOption {
+        type = types.attrsOf types.str;
+        example = {
+          tg = "00000000000000000000000000000000";
+          tg2 = "0123456789abcdef0123456789abcdef";
+        };
+        description = ''
+          Allowed users and their secrets. A secret is a 32 characters long hex string.
+        '';
+      };
+
+      secureOnly = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Don't allow users to connect in non-secure mode (without random padding).
+        '';
+      };
+
+      adTag = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        # Taken from mtproxyproto's repo.
+        example = "3c09c680b76ee91a4c25ad51f742267d";
+        description = ''
+          Tag for advertising that can be obtained from @MTProxybot.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.attrs;
+        default = {};
+        example = {
+          STATS_PRINT_PERIOD = 600;
+        };
+        description = ''
+          Extra configuration options for mtprotoproxy.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    systemd.services.mtprotoproxy = {
+      description = "MTProto Proxy Daemon";
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        ExecStart = "${pkgs.mtprotoproxy}/bin/mtprotoproxy ${configFile}";
+        DynamicUser = true;
+      };
+    };
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/mullvad-vpn.nix b/nixpkgs/nixos/modules/services/networking/mullvad-vpn.nix
new file mode 100644
index 000000000000..cc98414257ca
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/mullvad-vpn.nix
@@ -0,0 +1,43 @@
+{ config, lib, pkgs, ... }:
+let
+  cfg = config.services.mullvad-vpn;
+in
+with lib;
+{
+  options.services.mullvad-vpn.enable = mkOption {
+    type = types.bool;
+    default = false;
+    description = ''
+      This option enables Mullvad VPN daemon.
+    '';
+  };
+
+  config = mkIf cfg.enable {
+    boot.kernelModules = [ "tun" ];
+
+    systemd.services.mullvad-daemon = {
+      description = "Mullvad VPN daemon";
+      wantedBy = [ "multi-user.target" ];
+      wants = [ "network.target" ];
+      after = [
+        "network-online.target"
+        "NetworkManager.service"
+        "systemd-resolved.service"
+      ];
+      path = [
+        pkgs.iproute
+        # Needed for ping
+        "/run/wrappers"
+      ];
+      serviceConfig = {
+        StartLimitBurst = 5;
+        StartLimitIntervalSec = 20;
+        ExecStart = "${pkgs.mullvad-vpn}/bin/mullvad-daemon -v --disable-stdout-timestamps";
+        Restart = "always";
+        RestartSec = 1;
+      };
+    };
+  };
+
+  meta.maintainers = [ maintainers.xfix ];
+}
diff --git a/nixpkgs/nixos/modules/services/networking/murmur.nix b/nixpkgs/nixos/modules/services/networking/murmur.nix
new file mode 100644
index 000000000000..3054ae1b201f
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/murmur.nix
@@ -0,0 +1,270 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.murmur;
+  forking = cfg.logFile != null;
+  configFile = pkgs.writeText "murmurd.ini" ''
+    database=/var/lib/murmur/murmur.sqlite
+    dbDriver=QSQLITE
+
+    autobanAttempts=${toString cfg.autobanAttempts}
+    autobanTimeframe=${toString cfg.autobanTimeframe}
+    autobanTime=${toString cfg.autobanTime}
+
+    logfile=${optionalString (cfg.logFile != null) cfg.logFile}
+    ${optionalString forking "pidfile=/run/murmur/murmurd.pid"}
+
+    welcometext="${cfg.welcometext}"
+    port=${toString cfg.port}
+
+    ${if cfg.hostName == "" then "" else "host="+cfg.hostName}
+    ${if cfg.password == "" then "" else "serverpassword="+cfg.password}
+
+    bandwidth=${toString cfg.bandwidth}
+    users=${toString cfg.users}
+
+    textmessagelength=${toString cfg.textMsgLength}
+    imagemessagelength=${toString cfg.imgMsgLength}
+    allowhtml=${boolToString cfg.allowHtml}
+    logdays=${toString cfg.logDays}
+    bonjour=${boolToString cfg.bonjour}
+    sendversion=${boolToString cfg.sendVersion}
+
+    ${if cfg.registerName     == "" then "" else "registerName="+cfg.registerName}
+    ${if cfg.registerPassword == "" then "" else "registerPassword="+cfg.registerPassword}
+    ${if cfg.registerUrl      == "" then "" else "registerUrl="+cfg.registerUrl}
+    ${if cfg.registerHostname == "" then "" else "registerHostname="+cfg.registerHostname}
+
+    certrequired=${boolToString cfg.clientCertRequired}
+    ${if cfg.sslCert == "" then "" else "sslCert="+cfg.sslCert}
+    ${if cfg.sslKey  == "" then "" else "sslKey="+cfg.sslKey}
+    ${if cfg.sslCa   == "" then "" else "sslCA="+cfg.sslCa}
+
+    ${cfg.extraConfig}
+  '';
+in
+{
+  imports = [
+    (mkRenamedOptionModule [ "services" "murmur" "welcome" ] [ "services" "murmur" "welcometext" ])
+    (mkRemovedOptionModule [ "services" "murmur" "pidfile" ] "Hardcoded to /run/murmur/murmurd.pid now")
+  ];
+
+  options = {
+    services.murmur = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "If enabled, start the Murmur Mumble server.";
+      };
+
+      autobanAttempts = mkOption {
+        type = types.int;
+        default = 10;
+        description = ''
+          Number of attempts a client is allowed to make in
+          <literal>autobanTimeframe</literal> seconds, before being
+          banned for <literal>autobanTime</literal>.
+        '';
+      };
+
+      autobanTimeframe = mkOption {
+        type = types.int;
+        default = 120;
+        description = ''
+          Timeframe in which a client can connect without being banned
+          for repeated attempts (in seconds).
+        '';
+      };
+
+      autobanTime = mkOption {
+        type = types.int;
+        default = 300;
+        description = "The amount of time an IP ban lasts (in seconds).";
+      };
+
+      logFile = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        example = "/var/log/murmur/murmurd.log";
+        description = "Path to the log file for Murmur daemon. Empty means log to journald.";
+      };
+
+      welcometext = mkOption {
+        type = types.str;
+        default = "";
+        description = "Welcome message for connected clients.";
+      };
+
+      port = mkOption {
+        type = types.int;
+        default = 64738;
+        description = "Ports to bind to (UDP and TCP).";
+      };
+
+      hostName = mkOption {
+        type = types.str;
+        default = "";
+        description = "Host to bind to. Defaults binding on all addresses.";
+      };
+
+      password = mkOption {
+        type = types.str;
+        default = "";
+        description = "Required password to join server, if specified.";
+      };
+
+      bandwidth = mkOption {
+        type = types.int;
+        default = 72000;
+        description = ''
+          Maximum bandwidth (in bits per second) that clients may send
+          speech at.
+        '';
+      };
+
+      users = mkOption {
+        type = types.int;
+        default = 100;
+        description = "Maximum number of concurrent clients allowed.";
+      };
+
+      textMsgLength = mkOption {
+        type = types.int;
+        default = 5000;
+        description = "Max length of text messages. Set 0 for no limit.";
+      };
+
+      imgMsgLength = mkOption {
+        type = types.int;
+        default = 131072;
+        description = "Max length of image messages. Set 0 for no limit.";
+      };
+
+      allowHtml = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Allow HTML in client messages, comments, and channel
+          descriptions.
+        '';
+      };
+
+      logDays = mkOption {
+        type = types.int;
+        default = 31;
+        description = ''
+          How long to store RPC logs for in the database. Set 0 to
+          keep logs forever, or -1 to disable DB logging.
+        '';
+      };
+
+      bonjour = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable Bonjour auto-discovery, which allows clients over
+          your LAN to automatically discover Murmur servers.
+        '';
+      };
+
+      sendVersion = mkOption {
+        type = types.bool;
+        default = true;
+        description = "Send Murmur version in UDP response.";
+      };
+
+      registerName = mkOption {
+        type = types.str;
+        default = "";
+        description = ''
+          Public server registration name, and also the name of the
+          Root channel. Even if you don't publicly register your
+          server, you probably still want to set this.
+        '';
+      };
+
+      registerPassword = mkOption {
+        type = types.str;
+        default = "";
+        description = ''
+          Public server registry password, used authenticate your
+          server to the registry to prevent impersonation; required for
+          subsequent registry updates.
+        '';
+      };
+
+      registerUrl = mkOption {
+        type = types.str;
+        default = "";
+        description = "URL website for your server.";
+      };
+
+      registerHostname = mkOption {
+        type = types.str;
+        default = "";
+        description = ''
+          DNS hostname where your server can be reached. This is only
+          needed if you want your server to be accessed by its
+          hostname and not IP - but the name *must* resolve on the
+          internet properly.
+        '';
+      };
+
+      clientCertRequired = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Require clients to authenticate via certificates.";
+      };
+
+      sslCert = mkOption {
+        type = types.str;
+        default = "";
+        description = "Path to your SSL certificate.";
+      };
+
+      sslKey = mkOption {
+        type = types.str;
+        default = "";
+        description = "Path to your SSL key.";
+      };
+
+      sslCa = mkOption {
+        type = types.str;
+        default = "";
+        description = "Path to your SSL CA certificate.";
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = "Extra configuration to put into murmur.ini.";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.users.murmur = {
+      description     = "Murmur Service user";
+      home            = "/var/lib/murmur";
+      createHome      = true;
+      uid             = config.ids.uids.murmur;
+    };
+
+    systemd.services.murmur = {
+      description = "Murmur Chat Service";
+      wantedBy    = [ "multi-user.target" ];
+      after       = [ "network-online.target "];
+
+      serviceConfig = {
+        # murmurd doesn't fork when logging to the console.
+        Type      = if forking then "forking" else "simple";
+        PIDFile   = mkIf forking "/run/murmur/murmurd.pid";
+        RuntimeDirectory = mkIf forking "murmur";
+        User      = "murmur";
+        ExecStart = "${pkgs.murmur}/bin/murmurd -ini ${configFile}";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/mxisd.nix b/nixpkgs/nixos/modules/services/networking/mxisd.nix
new file mode 100644
index 000000000000..482d6ff456b1
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/mxisd.nix
@@ -0,0 +1,127 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  isMa1sd =
+    package:
+    lib.hasPrefix "ma1sd" package.name;
+
+  isMxisd =
+    package:
+    lib.hasPrefix "mxisd" package.name;
+
+  cfg = config.services.mxisd;
+
+  server = optionalAttrs (cfg.server.name != null) { inherit (cfg.server) name; }
+        // optionalAttrs (cfg.server.port != null) { inherit (cfg.server) port; };
+
+  baseConfig = {
+    matrix.domain = cfg.matrix.domain;
+    key.path = "${cfg.dataDir}/signing.key";
+    storage = {
+      provider.sqlite.database = if isMa1sd cfg.package
+                                 then "${cfg.dataDir}/ma1sd.db"
+                                 else "${cfg.dataDir}/mxisd.db";
+    };
+  } // optionalAttrs (server != {}) { inherit server; };
+
+  # merges baseConfig and extraConfig into a single file
+  fullConfig = recursiveUpdate baseConfig cfg.extraConfig;
+
+  configFile = if isMa1sd cfg.package
+               then pkgs.writeText "ma1sd-config.yaml" (builtins.toJSON fullConfig)
+               else pkgs.writeText "mxisd-config.yaml" (builtins.toJSON fullConfig);
+
+in {
+  options = {
+    services.mxisd = {
+      enable = mkEnableOption "matrix federated identity server";
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.mxisd;
+        defaultText = "pkgs.mxisd";
+        description = "The mxisd/ma1sd package to use";
+      };
+
+      dataDir = mkOption {
+        type = types.str;
+        default = "/var/lib/mxisd";
+        description = "Where data mxisd/ma1sd uses resides";
+      };
+
+      extraConfig = mkOption {
+        type = types.attrs;
+        default = {};
+        description = "Extra options merged into the mxisd/ma1sd configuration";
+      };
+
+      matrix = {
+
+        domain = mkOption {
+          type = types.str;
+          description = ''
+            the domain of the matrix homeserver
+          '';
+        };
+
+      };
+
+      server = {
+
+        name = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          description = ''
+            Public hostname of mxisd/ma1sd, if different from the Matrix domain.
+          '';
+        };
+
+        port = mkOption {
+          type = types.nullOr types.int;
+          default = null;
+          description = ''
+            HTTP port to listen on (unencrypted)
+          '';
+        };
+
+      };
+
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.users.mxisd =
+      {
+        group = "mxisd";
+        home = cfg.dataDir;
+        createHome = true;
+        shell = "${pkgs.bash}/bin/bash";
+        uid = config.ids.uids.mxisd;
+      };
+
+    users.groups.mxisd =
+      {
+        gid = config.ids.gids.mxisd;
+      };
+
+    systemd.services.mxisd = {
+      description = "a federated identity server for the matrix ecosystem";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = let
+        executable = if isMa1sd cfg.package then "ma1sd" else "mxisd";
+      in {
+        Type = "simple";
+        User = "mxisd";
+        Group = "mxisd";
+        ExecStart = "${cfg.package}/bin/${executable} -c ${configFile}";
+        WorkingDirectory = cfg.dataDir;
+        Restart = "on-failure";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/namecoind.nix b/nixpkgs/nixos/modules/services/networking/namecoind.nix
new file mode 100644
index 000000000000..6ca99e1321bd
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/namecoind.nix
@@ -0,0 +1,199 @@
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg     = config.services.namecoind;
+  dataDir = "/var/lib/namecoind";
+  useSSL  = (cfg.rpc.certificate != null) && (cfg.rpc.key != null);
+  useRPC  = (cfg.rpc.user != null) && (cfg.rpc.password != null);
+
+  listToConf = option: list:
+    concatMapStrings (value :"${option}=${value}\n") list;
+
+  configFile = pkgs.writeText "namecoin.conf" (''
+    server=1
+    daemon=0
+    txindex=1
+    txprevcache=1
+    walletpath=${cfg.wallet}
+    gen=${if cfg.generate then "1" else "0"}
+    ${listToConf "addnode" cfg.extraNodes}
+    ${listToConf "connect" cfg.trustedNodes}
+  '' + optionalString useRPC ''
+    rpcbind=${cfg.rpc.address}
+    rpcport=${toString cfg.rpc.port}
+    rpcuser=${cfg.rpc.user}
+    rpcpassword=${cfg.rpc.password}
+    ${listToConf "rpcallowip" cfg.rpc.allowFrom}
+  '' + optionalString useSSL ''
+    rpcssl=1
+    rpcsslcertificatechainfile=${cfg.rpc.certificate}
+    rpcsslprivatekeyfile=${cfg.rpc.key}
+    rpcsslciphers=TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!3DES:@STRENGTH
+  '');
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.namecoind = {
+
+      enable = mkEnableOption "namecoind, Namecoin client";
+
+      wallet = mkOption {
+        type = types.path;
+        default = "${dataDir}/wallet.dat";
+        description = ''
+          Wallet file. The ownership of the file has to be
+          namecoin:namecoin, and the permissions must be 0640.
+        '';
+      };
+
+      generate = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to generate (mine) Namecoins.
+        '';
+      };
+
+      extraNodes = mkOption {
+        type = types.listOf types.str;
+        default = [ ];
+        description = ''
+          List of additional peer IP addresses to connect to.
+        '';
+      };
+
+      trustedNodes = mkOption {
+        type = types.listOf types.str;
+        default = [ ];
+        description = ''
+          List of the only peer IP addresses to connect to. If specified
+          no other connection will be made.
+        '';
+      };
+
+      rpc.user = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          User name for RPC connections.
+        '';
+      };
+
+      rpc.password = mkOption {
+        type = types.str;
+        default = null;
+        description = ''
+          Password for RPC connections.
+        '';
+      };
+
+      rpc.address = mkOption {
+        type = types.str;
+        default = "0.0.0.0";
+        description = ''
+          IP address the RPC server will bind to.
+        '';
+      };
+
+      rpc.port = mkOption {
+        type = types.int;
+        default = 8332;
+        description = ''
+          Port the RPC server will bind to.
+        '';
+      };
+
+      rpc.certificate = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        example = "/var/lib/namecoind/server.cert";
+        description = ''
+          Certificate file for securing RPC connections.
+        '';
+      };
+
+      rpc.key = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        example = "/var/lib/namecoind/server.pem";
+        description = ''
+          Key file for securing RPC connections.
+        '';
+      };
+
+
+      rpc.allowFrom = mkOption {
+        type = types.listOf types.str;
+        default = [ "127.0.0.1" ];
+        description = ''
+          List of IP address ranges allowed to use the RPC API.
+          Wiledcards (*) can be user to specify a range.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    users.users.namecoin = {
+      uid  = config.ids.uids.namecoin;
+      description = "Namecoin daemon user";
+      home = dataDir;
+      createHome = true;
+    };
+
+    users.groups.namecoin = {
+      gid  = config.ids.gids.namecoin;
+    };
+
+    systemd.services.namecoind = {
+      description = "Namecoind daemon";
+      after    = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        User  = "namecoin";
+        Group = "namecoin";
+        ExecStart  = "${pkgs.namecoind}/bin/namecoind -conf=${configFile} -datadir=${dataDir} -printtoconsole";
+        ExecStop   = "${pkgs.coreutils}/bin/kill -KILL $MAINPID";
+        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+        Nice = "10";
+        PrivateTmp = true;
+        TimeoutStopSec     = "60s";
+        TimeoutStartSec    = "2s";
+        Restart            = "always";
+        StartLimitInterval = "120s";
+        StartLimitBurst    = "5";
+      };
+
+      preStart = optionalString (cfg.wallet != "${dataDir}/wallet.dat")  ''
+        # check wallet file permissions
+        if [ "$(stat --printf '%u' ${cfg.wallet})" != "${toString config.ids.uids.namecoin}" \
+           -o "$(stat --printf '%g' ${cfg.wallet})" != "${toString config.ids.gids.namecoin}" \
+           -o "$(stat --printf '%a' ${cfg.wallet})" != "640" ]; then
+           echo "ERROR: bad ownership or rights on ${cfg.wallet}" >&2
+           exit 1
+        fi
+      '';
+
+    };
+
+  };
+
+  meta.maintainers = with lib.maintainers; [ rnhmjoj ];
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/nat.nix b/nixpkgs/nixos/modules/services/networking/nat.nix
new file mode 100644
index 000000000000..21ae9eb8b6d4
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/nat.nix
@@ -0,0 +1,290 @@
+# This module enables Network Address Translation (NAT).
+# XXX: todo: support multiple upstream links
+# see http://yesican.chsoft.biz/lartc/MultihomedLinuxNetworking.html
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.networking.nat;
+
+  dest = if cfg.externalIP == null then "-j MASQUERADE" else "-j SNAT --to-source ${cfg.externalIP}";
+
+  helpers = import ./helpers.nix { inherit config lib; };
+
+  flushNat = ''
+    ${helpers}
+    ip46tables -w -t nat -D PREROUTING -j nixos-nat-pre 2>/dev/null|| true
+    ip46tables -w -t nat -F nixos-nat-pre 2>/dev/null || true
+    ip46tables -w -t nat -X nixos-nat-pre 2>/dev/null || true
+    ip46tables -w -t nat -D POSTROUTING -j nixos-nat-post 2>/dev/null || true
+    ip46tables -w -t nat -F nixos-nat-post 2>/dev/null || true
+    ip46tables -w -t nat -X nixos-nat-post 2>/dev/null || true
+    ip46tables -w -t nat -D OUTPUT -j nixos-nat-out 2>/dev/null || true
+    ip46tables -w -t nat -F nixos-nat-out 2>/dev/null || true
+    ip46tables -w -t nat -X nixos-nat-out 2>/dev/null || true
+
+    ${cfg.extraStopCommands}
+  '';
+
+  setupNat = ''
+    ${helpers}
+    # Create subchain where we store rules
+    ip46tables -w -t nat -N nixos-nat-pre
+    ip46tables -w -t nat -N nixos-nat-post
+    ip46tables -w -t nat -N nixos-nat-out
+
+    # We can't match on incoming interface in POSTROUTING, so
+    # mark packets coming from the internal interfaces.
+    ${concatMapStrings (iface: ''
+      iptables -w -t nat -A nixos-nat-pre \
+        -i '${iface}' -j MARK --set-mark 1
+    '') cfg.internalInterfaces}
+
+    # NAT the marked packets.
+    ${optionalString (cfg.internalInterfaces != []) ''
+      iptables -w -t nat -A nixos-nat-post -m mark --mark 1 \
+        ${optionalString (cfg.externalInterface != null) "-o ${cfg.externalInterface}"} ${dest}
+    ''}
+
+    # NAT packets coming from the internal IPs.
+    ${concatMapStrings (range: ''
+      iptables -w -t nat -A nixos-nat-post \
+        -s '${range}' ${optionalString (cfg.externalInterface != null) "-o ${cfg.externalInterface}"} ${dest}
+    '') cfg.internalIPs}
+
+    # NAT from external ports to internal ports.
+    ${concatMapStrings (fwd: ''
+      iptables -w -t nat -A nixos-nat-pre \
+        -i ${toString cfg.externalInterface} -p ${fwd.proto} \
+        --dport ${builtins.toString fwd.sourcePort} \
+        -j DNAT --to-destination ${fwd.destination}
+
+      ${concatMapStrings (loopbackip:
+        let
+          m                = builtins.match "([0-9.]+):([0-9-]+)" fwd.destination;
+          destinationIP    = if (m == null) then throw "bad ip:ports `${fwd.destination}'" else elemAt m 0;
+          destinationPorts = if (m == null) then throw "bad ip:ports `${fwd.destination}'" else builtins.replaceStrings ["-"] [":"] (elemAt m 1);
+        in ''
+          # Allow connections to ${loopbackip}:${toString fwd.sourcePort} from the host itself
+          iptables -w -t nat -A nixos-nat-out \
+            -d ${loopbackip} -p ${fwd.proto} \
+            --dport ${builtins.toString fwd.sourcePort} \
+            -j DNAT --to-destination ${fwd.destination}
+
+          # Allow connections to ${loopbackip}:${toString fwd.sourcePort} from other hosts behind NAT
+          iptables -w -t nat -A nixos-nat-pre \
+            -d ${loopbackip} -p ${fwd.proto} \
+            --dport ${builtins.toString fwd.sourcePort} \
+            -j DNAT --to-destination ${fwd.destination}
+
+          iptables -w -t nat -A nixos-nat-post \
+            -d ${destinationIP} -p ${fwd.proto} \
+            --dport ${destinationPorts} \
+            -j SNAT --to-source ${loopbackip}
+        '') fwd.loopbackIPs}
+    '') cfg.forwardPorts}
+
+    ${optionalString (cfg.dmzHost != null) ''
+      iptables -w -t nat -A nixos-nat-pre \
+        -i ${toString cfg.externalInterface} -j DNAT \
+        --to-destination ${cfg.dmzHost}
+    ''}
+
+    ${cfg.extraCommands}
+
+    # Append our chains to the nat tables
+    ip46tables -w -t nat -A PREROUTING -j nixos-nat-pre
+    ip46tables -w -t nat -A POSTROUTING -j nixos-nat-post
+    ip46tables -w -t nat -A OUTPUT -j nixos-nat-out
+  '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    networking.nat.enable = mkOption {
+      type = types.bool;
+      default = false;
+      description =
+        ''
+          Whether to enable Network Address Translation (NAT).
+        '';
+    };
+
+    networking.nat.internalInterfaces = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      example = [ "eth0" ];
+      description =
+        ''
+          The interfaces for which to perform NAT. Packets coming from
+          these interface and destined for the external interface will
+          be rewritten.
+        '';
+    };
+
+    networking.nat.internalIPs = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      example = [ "192.168.1.0/24" ];
+      description =
+        ''
+          The IP address ranges for which to perform NAT.  Packets
+          coming from these addresses (on any interface) and destined
+          for the external interface will be rewritten.
+        '';
+    };
+
+    networking.nat.externalInterface = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      example = "eth1";
+      description =
+        ''
+          The name of the external network interface.
+        '';
+    };
+
+    networking.nat.externalIP = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      example = "203.0.113.123";
+      description =
+        ''
+          The public IP address to which packets from the local
+          network are to be rewritten.  If this is left empty, the
+          IP address associated with the external interface will be
+          used.
+        '';
+    };
+
+    networking.nat.forwardPorts = mkOption {
+      type = with types; listOf (submodule {
+        options = {
+          sourcePort = mkOption {
+            type = types.either types.int (types.strMatching "[[:digit:]]+:[[:digit:]]+");
+            example = 8080;
+            description = "Source port of the external interface; to specify a port range, use a string with a colon (e.g. \"60000:61000\")";
+          };
+
+          destination = mkOption {
+            type = types.str;
+            example = "10.0.0.1:80";
+            description = "Forward connection to destination ip:port; to specify a port range, use ip:start-end";
+          };
+
+          proto = mkOption {
+            type = types.str;
+            default = "tcp";
+            example = "udp";
+            description = "Protocol of forwarded connection";
+          };
+
+          loopbackIPs = mkOption {
+            type = types.listOf types.str;
+            default = [];
+            example = literalExample ''[ "55.1.2.3" ]'';
+            description = "Public IPs for NAT reflection; for connections to `loopbackip:sourcePort' from the host itself and from other hosts behind NAT";
+          };
+        };
+      });
+      default = [];
+      example = [ { sourcePort = 8080; destination = "10.0.0.1:80"; proto = "tcp"; } ];
+      description =
+        ''
+          List of forwarded ports from the external interface to
+          internal destinations by using DNAT.
+        '';
+    };
+
+    networking.nat.dmzHost = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      example = "10.0.0.1";
+      description =
+        ''
+          The local IP address to which all traffic that does not match any
+          forwarding rule is forwarded.
+        '';
+    };
+
+    networking.nat.extraCommands = mkOption {
+      type = types.lines;
+      default = "";
+      example = "iptables -A INPUT -p icmp -j ACCEPT";
+      description =
+        ''
+          Additional shell commands executed as part of the nat
+          initialisation script.
+        '';
+    };
+
+    networking.nat.extraStopCommands = mkOption {
+      type = types.lines;
+      default = "";
+      example = "iptables -D INPUT -p icmp -j ACCEPT || true";
+      description =
+        ''
+          Additional shell commands executed as part of the nat
+          teardown script.
+        '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkMerge [
+    { networking.firewall.extraCommands = mkBefore flushNat; }
+    (mkIf config.networking.nat.enable {
+
+      assertions = [
+        { assertion = (cfg.dmzHost != null)    -> (cfg.externalInterface != null);
+          message = "networking.nat.dmzHost requires networking.nat.externalInterface";
+        }
+        { assertion = (cfg.forwardPorts != []) -> (cfg.externalInterface != null);
+          message = "networking.nat.forwardPorts requires networking.nat.externalInterface";
+        }
+      ];
+
+      environment.systemPackages = [ pkgs.iptables ];
+
+      boot = {
+        kernelModules = [ "nf_nat_ftp" ];
+        kernel.sysctl = {
+          "net.ipv4.conf.all.forwarding" = mkOverride 99 true;
+          "net.ipv4.conf.default.forwarding" = mkOverride 99 true;
+        };
+      };
+
+      networking.firewall = mkIf config.networking.firewall.enable {
+        extraCommands = setupNat;
+        extraStopCommands = flushNat;
+      };
+
+      systemd.services = mkIf (!config.networking.firewall.enable) { nat = {
+        description = "Network Address Translation";
+        wantedBy = [ "network.target" ];
+        after = [ "network-pre.target" "systemd-modules-load.service" ];
+        path = [ pkgs.iptables ];
+        unitConfig.ConditionCapability = "CAP_NET_ADMIN";
+
+        serviceConfig = {
+          Type = "oneshot";
+          RemainAfterExit = true;
+        };
+
+        script = flushNat + setupNat;
+
+        postStop = flushNat;
+      }; };
+    })
+  ];
+}
diff --git a/nixpkgs/nixos/modules/services/networking/ndppd.nix b/nixpkgs/nixos/modules/services/networking/ndppd.nix
new file mode 100644
index 000000000000..77e979a8a424
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/ndppd.nix
@@ -0,0 +1,189 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.ndppd;
+
+  render = s: f: concatStringsSep "\n" (mapAttrsToList f s);
+  prefer = a: b: if a != null then a else b;
+
+  ndppdConf = prefer cfg.configFile (pkgs.writeText "ndppd.conf" ''
+    route-ttl ${toString cfg.routeTTL}
+    ${render cfg.proxies (proxyInterfaceName: proxy: ''
+    proxy ${prefer proxy.interface proxyInterfaceName} {
+      router ${boolToString proxy.router}
+      timeout ${toString proxy.timeout}
+      ttl ${toString proxy.ttl}
+      ${render proxy.rules (ruleNetworkName: rule: ''
+      rule ${prefer rule.network ruleNetworkName} {
+        ${rule.method}${if rule.method == "iface" then " ${rule.interface}" else ""}
+      }'')}
+    }'')}
+  '');
+
+  proxy = types.submodule {
+    options = {
+      interface = mkOption {
+        type = types.nullOr types.str;
+        description = ''
+          Listen for any Neighbor Solicitation messages on this interface,
+          and respond to them according to a set of rules.
+          Defaults to the name of the attrset.
+        '';
+        default = null;
+      };
+      router = mkOption {
+        type = types.bool;
+        description = ''
+          Turns on or off the router flag for Neighbor Advertisement Messages.
+        '';
+        default = true;
+      };
+      timeout = mkOption {
+        type = types.int;
+        description = ''
+          Controls how long to wait for a Neighbor Advertisment Message before
+          invalidating the entry, in milliseconds.
+        '';
+        default = 500;
+      };
+      ttl = mkOption {
+        type = types.int;
+        description = ''
+          Controls how long a valid or invalid entry remains in the cache, in
+          milliseconds.
+        '';
+        default = 30000;
+      };
+      rules = mkOption {
+        type = types.attrsOf rule;
+        description = ''
+          This is a rule that the target address is to match against. If no netmask
+          is provided, /128 is assumed. You may have several rule sections, and the
+          addresses may or may not overlap.
+        '';
+        default = {};
+      };
+    };
+  };
+
+  rule = types.submodule {
+    options = {
+      network = mkOption {
+        type = types.nullOr types.str;
+        description = ''
+          This is the target address is to match against. If no netmask
+          is provided, /128 is assumed. The addresses of serveral rules
+          may or may not overlap.
+          Defaults to the name of the attrset.
+        '';
+        default = null;
+      };
+      method = mkOption {
+        type = types.enum [ "static" "iface" "auto" ];
+        description = ''
+          static: Immediately answer any Neighbor Solicitation Messages
+            (if they match the IP rule).
+          iface: Forward the Neighbor Solicitation Message through the specified
+            interface and only respond if a matching Neighbor Advertisement
+            Message is received.
+          auto: Same as iface, but instead of manually specifying the outgoing
+            interface, check for a matching route in /proc/net/ipv6_route.
+        '';
+        default = "auto";
+      };
+      interface = mkOption {
+        type = types.nullOr types.str;
+        description = "Interface to use when method is iface.";
+        default = null;
+      };
+    };
+  };
+
+in {
+  options.services.ndppd = {
+    enable = mkEnableOption "daemon that proxies NDP (Neighbor Discovery Protocol) messages between interfaces";
+    interface = mkOption {
+      type = types.nullOr types.str;
+      description = ''
+        Interface which is on link-level with router.
+        (Legacy option, use services.ndppd.proxies.&lt;interface&gt;.rules.&lt;network&gt; instead)
+      '';
+      default = null;
+      example = "eth0";
+    };
+    network = mkOption {
+      type = types.nullOr types.str;
+      description = ''
+        Network that we proxy.
+        (Legacy option, use services.ndppd.proxies.&lt;interface&gt;.rules.&lt;network&gt; instead)
+      '';
+      default = null;
+      example = "1111::/64";
+    };
+    configFile = mkOption {
+      type = types.nullOr types.path;
+      description = "Path to configuration file.";
+      default = null;
+    };
+    routeTTL = mkOption {
+      type = types.int;
+      description = ''
+        This tells 'ndppd' how often to reload the route file /proc/net/ipv6_route,
+        in milliseconds.
+      '';
+      default = 30000;
+    };
+    proxies = mkOption {
+      type = types.attrsOf proxy;
+      description = ''
+        This sets up a listener, that will listen for any Neighbor Solicitation
+        messages, and respond to them according to a set of rules.
+      '';
+      default = {};
+      example = literalExample ''
+        {
+          eth0.rules."1111::/64" = {};
+        }
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    warnings = mkIf (cfg.interface != null && cfg.network != null) [ ''
+      The options services.ndppd.interface and services.ndppd.network will probably be removed soon,
+      please use services.ndppd.proxies.<interface>.rules.<network> instead.
+    '' ];
+
+    services.ndppd.proxies = mkIf (cfg.interface != null && cfg.network != null) {
+      ${cfg.interface}.rules.${cfg.network} = {};
+    };
+
+    systemd.services.ndppd = {
+      description = "NDP Proxy Daemon";
+      documentation = [ "man:ndppd(1)" "man:ndppd.conf(5)" ];
+      after = [ "network-pre.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        ExecStart = "${pkgs.ndppd}/bin/ndppd -c ${ndppdConf}";
+
+        # Sandboxing
+        CapabilityBoundingSet = "CAP_NET_RAW CAP_NET_ADMIN";
+        ProtectSystem = "strict";
+        ProtectHome = true;
+        PrivateTmp = true;
+        PrivateDevices = true;
+        ProtectKernelTunables = true;
+        ProtectKernelModules = true;
+        ProtectControlGroups = true;
+        RestrictAddressFamilies = "AF_INET6 AF_PACKET AF_NETLINK";
+        RestrictNamespaces = true;
+        LockPersonality = true;
+        MemoryDenyWriteExecute = true;
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/networkmanager.nix b/nixpkgs/nixos/modules/services/networking/networkmanager.nix
new file mode 100644
index 000000000000..cc789897b29f
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/networkmanager.nix
@@ -0,0 +1,491 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.networking.networkmanager;
+
+  basePackages = with pkgs; [
+    crda
+    modemmanager
+    networkmanager
+    networkmanager-fortisslvpn
+    networkmanager-iodine
+    networkmanager-l2tp
+    networkmanager-openconnect
+    networkmanager-openvpn
+    networkmanager-vpnc
+   ] ++ optional (!delegateWireless && !enableIwd) wpa_supplicant;
+
+  delegateWireless = config.networking.wireless.enable == true && cfg.unmanaged != [];
+
+  enableIwd = cfg.wifi.backend == "iwd";
+
+  configFile = pkgs.writeText "NetworkManager.conf" ''
+    [main]
+    plugins=keyfile
+    dhcp=${cfg.dhcp}
+    dns=${cfg.dns}
+    # If resolvconf is disabled that means that resolv.conf is managed by some other module.
+    rc-manager=${if config.networking.resolvconf.enable then "resolvconf" else "unmanaged"}
+
+    [keyfile]
+    ${optionalString (cfg.unmanaged != [])
+      ''unmanaged-devices=${lib.concatStringsSep ";" cfg.unmanaged}''}
+
+    [logging]
+    level=${cfg.logLevel}
+    audit=${lib.boolToString config.security.audit.enable}
+
+    [connection]
+    ipv6.ip6-privacy=2
+    ethernet.cloned-mac-address=${cfg.ethernet.macAddress}
+    wifi.cloned-mac-address=${cfg.wifi.macAddress}
+    ${optionalString (cfg.wifi.powersave != null)
+      ''wifi.powersave=${if cfg.wifi.powersave then "3" else "2"}''}
+
+    [device]
+    wifi.scan-rand-mac-address=${if cfg.wifi.scanRandMacAddress then "yes" else "no"}
+    wifi.backend=${cfg.wifi.backend}
+
+    ${cfg.extraConfig}
+  '';
+
+  /*
+    [network-manager]
+    Identity=unix-group:networkmanager
+    Action=org.freedesktop.NetworkManager.*
+    ResultAny=yes
+    ResultInactive=no
+    ResultActive=yes
+
+    [modem-manager]
+    Identity=unix-group:networkmanager
+    Action=org.freedesktop.ModemManager*
+    ResultAny=yes
+    ResultInactive=no
+    ResultActive=yes
+  */
+  polkitConf = ''
+    polkit.addRule(function(action, subject) {
+      if (
+        subject.isInGroup("networkmanager")
+        && (action.id.indexOf("org.freedesktop.NetworkManager.") == 0
+            || action.id.indexOf("org.freedesktop.ModemManager")  == 0
+        ))
+          { return polkit.Result.YES; }
+    });
+  '';
+
+  ns = xs: pkgs.writeText "nameservers" (
+    concatStrings (map (s: "nameserver ${s}\n") xs)
+  );
+
+  overrideNameserversScript = pkgs.writeScript "02overridedns" ''
+    #!/bin/sh
+    PATH=${with pkgs; makeBinPath [ gnused gnugrep coreutils ]}
+    tmp=$(mktemp)
+    sed '/nameserver /d' /etc/resolv.conf > $tmp
+    grep 'nameserver ' /etc/resolv.conf | \
+      grep -vf ${ns (cfg.appendNameservers ++ cfg.insertNameservers)} > $tmp.ns
+    cat $tmp ${ns cfg.insertNameservers} $tmp.ns ${ns cfg.appendNameservers} > /etc/resolv.conf
+    rm -f $tmp $tmp.ns
+  '';
+
+  dispatcherTypesSubdirMap = {
+    basic = "";
+    pre-up = "pre-up.d/";
+    pre-down = "pre-down.d/";
+  };
+
+  macAddressOpt = mkOption {
+    type = types.either types.str (types.enum ["permanent" "preserve" "random" "stable"]);
+    default = "preserve";
+    example = "00:11:22:33:44:55";
+    description = ''
+      Set the MAC address of the interface.
+      <variablelist>
+        <varlistentry>
+          <term>"XX:XX:XX:XX:XX:XX"</term>
+          <listitem><para>MAC address of the interface</para></listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><literal>"permanent"</literal></term>
+          <listitem><para>Use the permanent MAC address of the device</para></listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><literal>"preserve"</literal></term>
+          <listitem><para>Don’t change the MAC address of the device upon activation</para></listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><literal>"random"</literal></term>
+          <listitem><para>Generate a randomized value upon each connect</para></listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><literal>"stable"</literal></term>
+          <listitem><para>Generate a stable, hashed MAC address</para></listitem>
+        </varlistentry>
+      </variablelist>
+    '';
+  };
+
+in {
+
+  meta = {
+    maintainers = teams.freedesktop.members;
+  };
+
+  ###### interface
+
+  options = {
+
+    networking.networkmanager = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to use NetworkManager to obtain an IP address and other
+          configuration for all network interfaces that are not manually
+          configured. If enabled, a group <literal>networkmanager</literal>
+          will be created. Add all users that should have permission
+          to change network settings to this group.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Configuration appended to the generated NetworkManager.conf.
+          Refer to
+          <link xlink:href="https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html">
+            https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html
+          </link>
+          or
+          <citerefentry>
+            <refentrytitle>NetworkManager.conf</refentrytitle>
+            <manvolnum>5</manvolnum>
+          </citerefentry>
+          for more information.
+        '';
+      };
+
+      unmanaged = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          List of interfaces that will not be managed by NetworkManager.
+          Interface name can be specified here, but if you need more fidelity,
+          refer to
+          <link xlink:href="https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html#device-spec">
+            https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html#device-spec
+          </link>
+          or the "Device List Format" Appendix of
+          <citerefentry>
+            <refentrytitle>NetworkManager.conf</refentrytitle>
+            <manvolnum>5</manvolnum>
+          </citerefentry>.
+        '';
+      };
+
+      packages = mkOption {
+        type = types.listOf types.package;
+        default = [ ];
+        description = ''
+          Extra packages that provide NetworkManager plugins.
+        '';
+        apply = list: basePackages ++ list;
+      };
+
+      dhcp = mkOption {
+        type = types.enum [ "dhclient" "dhcpcd" "internal" ];
+        default = "internal";
+        description = ''
+          Which program (or internal library) should be used for DHCP.
+        '';
+      };
+
+      logLevel = mkOption {
+        type = types.enum [ "OFF" "ERR" "WARN" "INFO" "DEBUG" "TRACE" ];
+        default = "WARN";
+        description = ''
+          Set the default logging verbosity level.
+        '';
+      };
+
+      appendNameservers = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          A list of name servers that should be appended
+          to the ones configured in NetworkManager or received by DHCP.
+        '';
+      };
+
+      insertNameservers = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          A list of name servers that should be inserted before
+          the ones configured in NetworkManager or received by DHCP.
+        '';
+      };
+
+      ethernet.macAddress = macAddressOpt;
+
+      wifi = {
+        macAddress = macAddressOpt;
+
+        backend = mkOption {
+          type = types.enum [ "wpa_supplicant" "iwd" ];
+          default = "wpa_supplicant";
+          description = ''
+            Specify the Wi-Fi backend used for the device.
+            Currently supported are <option>wpa_supplicant</option> or <option>iwd</option> (experimental).
+          '';
+        };
+
+        powersave = mkOption {
+          type = types.nullOr types.bool;
+          default = null;
+          description = ''
+            Whether to enable Wi-Fi power saving.
+          '';
+        };
+
+        scanRandMacAddress = mkOption {
+          type = types.bool;
+          default = true;
+          description = ''
+            Whether to enable MAC address randomization of a Wi-Fi device
+            during scanning.
+          '';
+        };
+      };
+
+      dns = mkOption {
+        type = types.enum [ "default" "dnsmasq" "unbound" "systemd-resolved" "none" ];
+        default = "default";
+        description = ''
+          Set the DNS (<literal>resolv.conf</literal>) processing mode.
+          </para>
+          <para>
+          A description of these modes can be found in the main section of
+          <link xlink:href="https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html">
+            https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html
+          </link>
+          or in
+          <citerefentry>
+            <refentrytitle>NetworkManager.conf</refentrytitle>
+            <manvolnum>5</manvolnum>
+          </citerefentry>.
+        '';
+      };
+
+      dispatcherScripts = mkOption {
+        type = types.listOf (types.submodule {
+          options = {
+            source = mkOption {
+              type = types.path;
+              description = ''
+                Path to the hook script.
+              '';
+            };
+
+            type = mkOption {
+              type = types.enum (attrNames dispatcherTypesSubdirMap);
+              default = "basic";
+              description = ''
+                Dispatcher hook type. Look up the hooks described at
+                <link xlink:href="https://developer.gnome.org/NetworkManager/stable/NetworkManager.html">https://developer.gnome.org/NetworkManager/stable/NetworkManager.html</link>
+                and choose the type depending on the output folder.
+                You should then filter the event type (e.g., "up"/"down") from within your script.
+              '';
+            };
+          };
+        });
+        default = [];
+        example = literalExample ''
+        [ {
+              source = pkgs.writeText "upHook" '''
+
+                if [ "$2" != "up" ]; then
+                    logger "exit: event $2 != up"
+                    exit
+                fi
+
+                # coreutils and iproute are in PATH too
+                logger "Device $DEVICE_IFACE coming up"
+            ''';
+            type = "basic";
+        } ]'';
+        description = ''
+          A list of scripts which will be executed in response to  network  events.
+        '';
+      };
+
+      enableStrongSwan = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable the StrongSwan plugin.
+          </para><para>
+          If you enable this option the
+          <literal>networkmanager_strongswan</literal> plugin will be added to
+          the <option>networking.networkmanager.packages</option> option
+          so you don't need to to that yourself.
+        '';
+      };
+    };
+  };
+
+  imports = [
+    (mkRenamedOptionModule [ "networking" "networkmanager" "useDnsmasq" ] [ "networking" "networkmanager" "dns" ])
+    (mkRemovedOptionModule ["networking" "networkmanager" "dynamicHosts"] ''
+      This option was removed because allowing (multiple) regular users to
+      override host entries affecting the whole system opens up a huge attack
+      vector. There seem to be very rare cases where this might be useful.
+      Consider setting system-wide host entries using networking.hosts, provide
+      them via the DNS server in your network, or use environment.etc
+      to add a file into /etc/NetworkManager/dnsmasq.d reconfiguring hostsdir.
+    '')
+  ];
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    assertions = [
+      { assertion = config.networking.wireless.enable == true -> cfg.unmanaged != [];
+        message = ''
+          You can not use networking.networkmanager with networking.wireless.
+          Except if you mark some interfaces as <literal>unmanaged</literal> by NetworkManager.
+        '';
+      }
+    ];
+
+    environment.etc = with pkgs; {
+      "NetworkManager/NetworkManager.conf".source = configFile;
+
+      "NetworkManager/VPN/nm-openvpn-service.name".source =
+        "${networkmanager-openvpn}/lib/NetworkManager/VPN/nm-openvpn-service.name";
+
+      "NetworkManager/VPN/nm-vpnc-service.name".source =
+        "${networkmanager-vpnc}/lib/NetworkManager/VPN/nm-vpnc-service.name";
+
+      "NetworkManager/VPN/nm-openconnect-service.name".source =
+        "${networkmanager-openconnect}/lib/NetworkManager/VPN/nm-openconnect-service.name";
+
+      "NetworkManager/VPN/nm-fortisslvpn-service.name".source =
+        "${networkmanager-fortisslvpn}/lib/NetworkManager/VPN/nm-fortisslvpn-service.name";
+
+      "NetworkManager/VPN/nm-l2tp-service.name".source =
+        "${networkmanager-l2tp}/lib/NetworkManager/VPN/nm-l2tp-service.name";
+
+      "NetworkManager/VPN/nm-iodine-service.name".source =
+        "${networkmanager-iodine}/lib/NetworkManager/VPN/nm-iodine-service.name";
+      }
+      // optionalAttrs (cfg.appendNameservers != [] || cfg.insertNameservers != [])
+         {
+           "NetworkManager/dispatcher.d/02overridedns".source = overrideNameserversScript;
+         }
+      // optionalAttrs cfg.enableStrongSwan
+         {
+           "NetworkManager/VPN/nm-strongswan-service.name".source =
+             "${pkgs.networkmanager_strongswan}/lib/NetworkManager/VPN/nm-strongswan-service.name";
+         }
+      // listToAttrs (lib.imap1 (i: s:
+         {
+            name = "NetworkManager/dispatcher.d/${dispatcherTypesSubdirMap.${s.type}}03userscript${lib.fixedWidthNumber 4 i}";
+            value = { mode = "0544"; inherit (s) source; };
+         }) cfg.dispatcherScripts);
+
+    environment.systemPackages = cfg.packages;
+
+    users.groups = {
+      networkmanager.gid = config.ids.gids.networkmanager;
+      nm-openvpn.gid = config.ids.gids.nm-openvpn;
+    };
+
+    users.users = {
+      nm-openvpn = {
+        uid = config.ids.uids.nm-openvpn;
+        extraGroups = [ "networkmanager" ];
+      };
+      nm-iodine = {
+        isSystemUser = true;
+        group = "networkmanager";
+      };
+    };
+
+    systemd.packages = cfg.packages;
+
+    systemd.tmpfiles.rules = [
+      "d /etc/NetworkManager/system-connections 0700 root root -"
+      "d /etc/ipsec.d 0700 root root -"
+      "d /var/lib/NetworkManager-fortisslvpn 0700 root root -"
+
+      "d /var/lib/dhclient 0755 root root -"
+      "d /var/lib/misc 0755 root root -" # for dnsmasq.leases
+    ];
+
+    systemd.services.NetworkManager = {
+      wantedBy = [ "network.target" ];
+      restartTriggers = [ configFile ];
+
+      aliases = [ "dbus-org.freedesktop.NetworkManager.service" ];
+
+      serviceConfig = {
+        StateDirectory = "NetworkManager";
+        StateDirectoryMode = 755; # not sure if this really needs to be 755
+      };
+    };
+
+    systemd.services.NetworkManager-wait-online = {
+      wantedBy = [ "network-online.target" ];
+    };
+
+    systemd.services.ModemManager.aliases = [ "dbus-org.freedesktop.ModemManager1.service" ];
+
+    # override unit as recommended by upstream - see https://github.com/NixOS/nixpkgs/issues/88089
+    # TODO: keep an eye on modem-manager releases as this will eventually be added to the upstream unit
+    systemd.services.ModemManager.serviceConfig.ExecStart = [
+      ""
+      "${pkgs.modemmanager}/sbin/ModemManager --filter-policy=STRICT"
+    ];
+
+    systemd.services.NetworkManager-dispatcher = {
+      wantedBy = [ "network.target" ];
+      restartTriggers = [ configFile ];
+
+      # useful binaries for user-specified hooks
+      path = [ pkgs.iproute pkgs.utillinux pkgs.coreutils ];
+      aliases = [ "dbus-org.freedesktop.nm-dispatcher.service" ];
+    };
+
+    # Turn off NixOS' network management when networking is managed entirely by NetworkManager
+    networking = mkMerge [
+      (mkIf (!delegateWireless) {
+        useDHCP = false;
+      })
+
+      (mkIf cfg.enableStrongSwan {
+        networkmanager.packages = [ pkgs.networkmanager_strongswan ];
+      })
+
+      (mkIf enableIwd {
+        wireless.iwd.enable = true;
+      })
+    ];
+
+    security.polkit.extraConfig = polkitConf;
+
+    services.dbus.packages = cfg.packages
+      ++ optional cfg.enableStrongSwan pkgs.strongswanNM
+      ++ optional (cfg.dns == "dnsmasq") pkgs.dnsmasq;
+
+    services.udev.packages = cfg.packages;
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/nftables.nix b/nixpkgs/nixos/modules/services/networking/nftables.nix
new file mode 100644
index 000000000000..ec9d9753cfe2
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/nftables.nix
@@ -0,0 +1,136 @@
+{ config, pkgs, lib, ... }:
+with lib;
+let
+  cfg = config.networking.nftables;
+in
+{
+  ###### interface
+
+  options = {
+    networking.nftables.enable = mkOption {
+      type = types.bool;
+      default = false;
+      description =
+        ''
+          Whether to enable nftables.  nftables is a Linux-based packet
+          filtering framework intended to replace frameworks like iptables.
+
+          This conflicts with the standard networking firewall, so make sure to
+          disable it before using nftables.
+
+          Note that if you have Docker enabled you will not be able to use
+          nftables without intervention. Docker uses iptables internally to
+          setup NAT for containers. This module disables the ip_tables kernel
+          module, however Docker automatically loads the module. Please see [1]
+          for more information.
+
+          There are other programs that use iptables internally too, such as
+          libvirt.
+
+          [1]: https://github.com/NixOS/nixpkgs/issues/24318#issuecomment-289216273
+        '';
+    };
+    networking.nftables.ruleset = mkOption {
+      type = types.lines;
+      example = ''
+        # Check out https://wiki.nftables.org/ for better documentation.
+        # Table for both IPv4 and IPv6.
+        table inet filter {
+          # Block all incomming connections traffic except SSH and "ping".
+          chain input {
+            type filter hook input priority 0;
+
+            # accept any localhost traffic
+            iifname lo accept
+
+            # accept traffic originated from us
+            ct state {established, related} accept
+
+            # ICMP
+            # routers may also want: mld-listener-query, nd-router-solicit
+            ip6 nexthdr icmpv6 icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept
+            ip protocol icmp icmp type { destination-unreachable, router-advertisement, time-exceeded, parameter-problem } accept
+
+            # allow "ping"
+            ip6 nexthdr icmpv6 icmpv6 type echo-request accept
+            ip protocol icmp icmp type echo-request accept
+
+            # accept SSH connections (required for a server)
+            tcp dport 22 accept
+
+            # count and drop any other traffic
+            counter drop
+          }
+
+          # Allow all outgoing connections.
+          chain output {
+            type filter hook output priority 0;
+            accept
+          }
+
+          chain forward {
+            type filter hook forward priority 0;
+            accept
+          }
+        }
+      '';
+      description =
+        ''
+          The ruleset to be used with nftables.  Should be in a format that
+          can be loaded using "/bin/nft -f".  The ruleset is updated atomically.
+        '';
+    };
+    networking.nftables.rulesetFile = mkOption {
+      type = types.path;
+      default = pkgs.writeTextFile {
+        name = "nftables-rules";
+        text = cfg.ruleset;
+      };
+      description =
+        ''
+          The ruleset file to be used with nftables.  Should be in a format that
+          can be loaded using "nft -f".  The ruleset is updated atomically.
+        '';
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    assertions = [{
+      assertion = config.networking.firewall.enable == false;
+      message = "You can not use nftables with services.networking.firewall.";
+    }];
+    boot.blacklistedKernelModules = [ "ip_tables" ];
+    environment.systemPackages = [ pkgs.nftables ];
+    systemd.services.nftables = {
+      description = "nftables firewall";
+      before = [ "network-pre.target" ];
+      wants = [ "network-pre.target" ];
+      wantedBy = [ "multi-user.target" ];
+      reloadIfChanged = true;
+      serviceConfig = let
+        rulesScript = pkgs.writeScript "nftables-rules" ''
+          #! ${pkgs.nftables}/bin/nft -f
+          flush ruleset
+          include "${cfg.rulesetFile}"
+        '';
+        checkScript = pkgs.writeScript "nftables-check" ''
+          #! ${pkgs.runtimeShell} -e
+          if $(${pkgs.kmod}/bin/lsmod | grep -q ip_tables); then
+            echo "Unload ip_tables before using nftables!" 1>&2
+            exit 1
+          else
+            ${rulesScript}
+          fi
+        '';
+      in {
+        Type = "oneshot";
+        RemainAfterExit = true;
+        ExecStart = checkScript;
+        ExecReload = checkScript;
+        ExecStop = "${pkgs.nftables}/bin/nft flush ruleset";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/nghttpx/backend-params-submodule.nix b/nixpkgs/nixos/modules/services/networking/nghttpx/backend-params-submodule.nix
new file mode 100644
index 000000000000..6523f4b8b9e0
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/nghttpx/backend-params-submodule.nix
@@ -0,0 +1,131 @@
+{ lib, ...}:
+{ options = {
+    proto = lib.mkOption {
+      type        = lib.types.enum [ "h2" "http/1.1" ];
+      default     = "http/1.1";
+      description = ''
+        This option configures the protocol the backend server expects
+        to use.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-b
+        for more detail.
+      '';
+    };
+
+    tls = lib.mkOption {
+      type        = lib.types.bool;
+      default     = false;
+      description = ''
+        This option determines whether nghttpx will negotiate its
+        connection with a backend server using TLS or not. The burden
+        is on the backend server to provide the TLS certificate!
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-b
+        for more detail.
+      '';
+    };
+
+    sni = lib.mkOption {
+      type        = lib.types.nullOr lib.types.str;
+      default     = null;
+      description = ''
+        Override the TLS SNI field value. This value (in nghttpx)
+        defaults to the host value of the backend configuration.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-b
+        for more detail.
+      '';
+    };
+
+    fall = lib.mkOption {
+      type        = lib.types.int;
+      default     = 0;
+      description = ''
+        If nghttpx cannot connect to the backend N times in a row, the
+        backend is assumed to be offline and is excluded from load
+        balancing. If N is 0 the backend is never excluded from load
+        balancing.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-b
+        for more detail.
+      '';
+    };
+
+    rise = lib.mkOption {
+      type        = lib.types.int;
+      default     = 0;
+      description = ''
+        If the backend is excluded from load balancing, nghttpx will
+        periodically attempt to make a connection to the backend. If
+        the connection is successful N times in a row the backend is
+        re-included in load balancing. If N is 0 a backend is never
+        reconsidered for load balancing once it falls.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-b
+        for more detail.
+      '';
+    };
+
+    affinity = lib.mkOption {
+      type        = lib.types.enum [ "ip" "none" ];
+      default     = "none";
+      description = ''
+        If "ip" is given, client IP based session affinity is
+        enabled. If "none" is given, session affinity is disabled.
+
+        Session affinity is enabled (by nghttpx) per-backend
+        pattern. If at least one backend has a non-"none" affinity,
+        then session affinity is enabled for all backend servers
+        sharing the same pattern.
+
+        It is advised to set affinity on all backends explicitly if
+        session affinity is desired. The session affinity may break if
+        one of the backend gets unreachable, or backend settings are
+        reloaded or replaced by API.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-b
+        for more detail.
+      '';
+    };
+
+    dns = lib.mkOption {
+      type        = lib.types.bool;
+      default     = false;
+      description = ''
+        Name resolution of a backends host name is done at start up,
+        or configuration reload. If "dns" is true, name resolution
+        takes place dynamically.
+
+        This is useful if a backends address changes frequently. If
+        "dns" is true, name resolution of a backend's host name at
+        start up, or configuration reload is skipped.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-b
+        for more detail.
+      '';
+    };
+
+    redirect-if-not-tls = lib.mkOption {
+      type        = lib.types.bool;
+      default     = false;
+      description = ''
+        If true, a backend match requires the frontend connection be
+        TLS encrypted. If it is not, nghttpx responds to the request
+        with a 308 status code and https URI the client should use
+        instead in the Location header.
+
+        The port number in the redirect URI is 443 by default and can
+        be changed using 'services.nghttpx.redirect-https-port'
+        option.
+
+        If at least one backend has "redirect-if-not-tls" set to true,
+        this feature is enabled for all backend servers with the same
+        pattern. It is advised to set "redirect-if-no-tls" parameter
+        to all backends explicitly if this feature is desired.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-b
+        for more detail.
+      '';
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/nghttpx/backend-submodule.nix b/nixpkgs/nixos/modules/services/networking/nghttpx/backend-submodule.nix
new file mode 100644
index 000000000000..eb559e926e76
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/nghttpx/backend-submodule.nix
@@ -0,0 +1,50 @@
+{ lib, ... }:
+{ options = {
+    server = lib.mkOption {
+      type =
+        lib.types.either
+          (lib.types.submodule (import ./server-options.nix))
+          (lib.types.path);
+      example = {
+        host = "127.0.0.1";
+        port = 8888;
+      };
+      default = {
+        host = "127.0.0.1";
+        port = 80;
+      };
+      description = ''
+        Backend server location specified as either a host:port pair
+        or a unix domain docket.
+      '';
+    };
+
+    patterns = lib.mkOption {
+      type    = lib.types.listOf lib.types.str;
+      example = [
+        "*.host.net/v1/"
+        "host.org/v2/mypath"
+        "/somepath"
+      ];
+      default     = [];
+      description = ''
+        List of nghttpx backend patterns.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-b
+        for more information on the pattern syntax and nghttpxs behavior.
+      '';
+    };
+
+    params = lib.mkOption {
+      type    = lib.types.nullOr (lib.types.submodule (import ./backend-params-submodule.nix));
+      example = {
+        proto = "h2";
+        tls   = true;
+      };
+      default     = null;
+      description = ''
+        Parameters to configure a backend.
+      '';
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/nghttpx/default.nix b/nixpkgs/nixos/modules/services/networking/nghttpx/default.nix
new file mode 100644
index 000000000000..881a2670f5db
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/nghttpx/default.nix
@@ -0,0 +1,118 @@
+{config, pkgs, lib, ...}:
+let
+  cfg = config.services.nghttpx;
+
+  # renderHost :: Either ServerOptions Path -> String
+  renderHost = server:
+    if builtins.isString server
+    then "unix://${server}"
+    else "${server.host},${builtins.toString server.port}";
+
+  # Filter out submodule parameters whose value is null or false or is
+  # the key _module.
+  #
+  # filterParams :: ParamsSubmodule -> ParamsSubmodule
+  filterParams = p:
+    lib.filterAttrs
+      (n: v: ("_module" != n) && (null != v) && (false != v))
+      (lib.optionalAttrs (null != p) p);
+
+  # renderBackend :: BackendSubmodule -> String
+  renderBackend = backend:
+    let
+      host = renderHost backend.server;
+      patterns = lib.concatStringsSep ":" backend.patterns;
+
+      # Render a set of backend parameters, this is somewhat
+      # complicated because nghttpx backend patterns can be entirely
+      # omitted and the params may be given as a mixed collection of
+      # 'key=val' pairs or atoms (e.g: 'proto=h2;tls')
+      params =
+        lib.mapAttrsToList
+          (n: v:
+            if builtins.isBool v
+            then n
+            else if builtins.isString v
+            then "${n}=${v}"
+            else "${n}=${builtins.toString v}")
+          (filterParams backend.params);
+
+      # NB: params are delimited by a ";" which is the same delimiter
+      # to separate the host;[pattern];[params] sections of a backend
+      sections =
+        builtins.filter (e: "" != e) ([
+          host
+          patterns
+        ]++params);
+      formattedSections = lib.concatStringsSep ";" sections;
+    in
+      "backend=${formattedSections}";
+
+  # renderFrontend :: FrontendSubmodule -> String
+  renderFrontend = frontend:
+    let
+      host   = renderHost frontend.server;
+      params0 =
+        lib.mapAttrsToList
+          (n: v: if builtins.isBool v then n else v)
+          (filterParams frontend.params);
+
+      # NB: nghttpx doesn't accept "tls", you must omit "no-tls" for
+      # the default behavior of turning on TLS.
+      params1 = lib.remove "tls" params0;
+          
+      sections          = [ host] ++ params1;
+      formattedSections = lib.concatStringsSep ";" sections;
+    in
+      "frontend=${formattedSections}";
+
+  configurationFile = pkgs.writeText "nghttpx.conf" ''
+    ${lib.optionalString (null != cfg.tls) ("private-key-file="+cfg.tls.key)}
+    ${lib.optionalString (null != cfg.tls) ("certificate-file="+cfg.tls.crt)}
+
+    user=nghttpx
+
+    ${lib.concatMapStringsSep "\n" renderFrontend cfg.frontends}
+    ${lib.concatMapStringsSep "\n" renderBackend  cfg.backends}
+
+    backlog=${builtins.toString cfg.backlog}
+    backend-address-family=${cfg.backend-address-family}
+
+    workers=${builtins.toString cfg.workers}
+    rlimit-nofile=${builtins.toString cfg.rlimit-nofile}
+
+    ${lib.optionalString cfg.single-thread "single-thread=yes"}
+    ${lib.optionalString cfg.single-process "single-process=yes"}
+
+    ${cfg.extraConfig}
+  '';
+in
+{ imports = [
+    ./nghttpx-options.nix
+  ];
+  
+  config = lib.mkIf cfg.enable {
+
+    users.groups.nghttpx = { };
+    users.users.nghttpx = {
+      group = config.users.groups.nghttpx.name;
+      isSystemUser = true;
+    };
+      
+
+    systemd.services = {
+      nghttpx = {
+        wantedBy = [ "multi-user.target" ];
+        after    = [ "network.target" ];
+        script   = ''
+          ${pkgs.nghttp2}/bin/nghttpx --conf=${configurationFile}
+        '';
+
+        serviceConfig = {
+          Restart    = "on-failure";
+          RestartSec = 60;
+        };
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/nghttpx/frontend-params-submodule.nix b/nixpkgs/nixos/modules/services/networking/nghttpx/frontend-params-submodule.nix
new file mode 100644
index 000000000000..33c8572bd14f
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/nghttpx/frontend-params-submodule.nix
@@ -0,0 +1,64 @@
+{ lib, ...}:
+{ options = {
+    tls = lib.mkOption {
+      type        = lib.types.enum [ "tls" "no-tls" ];
+      default     = "tls";
+      description = ''
+        Enable or disable TLS. If true (enabled) the key and
+        certificate must be configured for nghttpx.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-f
+        for more detail.
+      '';
+    };
+
+    sni-fwd = lib.mkOption {
+      type    = lib.types.bool;
+      default = false;
+      description = ''
+        When performing a match to select a backend server, SNI host
+        name received from the client is used instead of the request
+        host. See --backend option about the pattern match.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-f
+        for more detail.
+      '';
+    };
+
+    api = lib.mkOption {
+      type        = lib.types.bool;
+      default     = false;
+      description = ''
+        Enable API access for this frontend. This enables you to
+        dynamically modify nghttpx at run-time therefore this feature
+        is disabled by default and should be turned on with care.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-f
+        for more detail.
+      '';
+    };
+
+    healthmon = lib.mkOption {
+      type        = lib.types.bool;
+      default     = false;
+      description = ''
+        Make this frontend a health monitor endpoint. Any request
+        received on this frontend is responded to with a 200 OK.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-f
+        for more detail.
+      '';
+    };
+
+    proxyproto = lib.mkOption {
+      type        = lib.types.bool;
+      default     = false;
+      description = ''
+        Accept PROXY protocol version 1 on frontend connection.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-f
+        for more detail.
+      '';
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/nghttpx/frontend-submodule.nix b/nixpkgs/nixos/modules/services/networking/nghttpx/frontend-submodule.nix
new file mode 100644
index 000000000000..887ef4502131
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/nghttpx/frontend-submodule.nix
@@ -0,0 +1,36 @@
+{ lib, ... }:
+{ options = {
+    server = lib.mkOption {
+      type =
+        lib.types.either
+          (lib.types.submodule (import ./server-options.nix))
+          (lib.types.path);
+      example = {
+        host = "127.0.0.1";
+        port = 8888;
+      };
+      default = {
+        host = "127.0.0.1";
+        port = 80;
+      };
+      description = ''
+        Frontend server interface binding specification as either a
+        host:port pair or a unix domain docket.
+
+        NB: a host of "*" listens on all interfaces and includes IPv6
+        addresses.
+      '';
+    };
+
+    params = lib.mkOption {
+      type    = lib.types.nullOr (lib.types.submodule (import ./frontend-params-submodule.nix));
+      example = {
+        tls   = "tls";
+      };
+      default     = null;
+      description = ''
+        Parameters to configure a backend.
+      '';
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/nghttpx/nghttpx-options.nix b/nixpkgs/nixos/modules/services/networking/nghttpx/nghttpx-options.nix
new file mode 100644
index 000000000000..51f1d081b971
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/nghttpx/nghttpx-options.nix
@@ -0,0 +1,142 @@
+{ lib, ... }:
+{ options.services.nghttpx = {
+    enable = lib.mkEnableOption "nghttpx";
+
+    frontends = lib.mkOption {
+      type        = lib.types.listOf (lib.types.submodule (import ./frontend-submodule.nix));
+      description = ''
+        A list of frontend listener specifications.
+      '';
+      example = [
+        { server = {
+            host = "*";
+            port = 80;
+          };
+
+          params = {
+            tls = "no-tls";
+          };
+        }
+      ];
+    };
+
+    backends  = lib.mkOption {
+      type = lib.types.listOf (lib.types.submodule (import ./backend-submodule.nix));
+      description = ''
+        A list of backend specifications.
+      '';
+      example = [
+        { server = {
+            host = "172.16.0.22";
+            port = 8443;
+          };
+          patterns = [ "/" ];
+          params   = {
+            proto               = "http/1.1";
+            redirect-if-not-tls = true;
+          };
+        }
+      ];
+    };
+
+    tls = lib.mkOption {
+      type        = lib.types.nullOr (lib.types.submodule (import ./tls-submodule.nix));
+      default     = null;
+      description = ''
+        TLS certificate and key paths. Note that this does not enable
+        TLS for a frontend listener, to do so, a frontend
+        specification must set <literal>params.tls</literal> to true.
+      '';
+      example = {
+        key = "/etc/ssl/keys/server.key";
+        crt = "/etc/ssl/certs/server.crt";
+      };
+    };
+
+    extraConfig = lib.mkOption {
+      type        = lib.types.lines;
+      default     = "";
+      description = ''
+        Extra configuration options to be appended to the generated
+        configuration file.
+      '';
+    };
+
+    single-process = lib.mkOption {
+      type        = lib.types.bool;
+      default     = false;
+      description = ''
+        Run this program in a single process mode for debugging
+        purpose. Without this option, nghttpx creates at least 2
+        processes: master and worker processes. If this option is
+        used, master and worker are unified into a single
+        process. nghttpx still spawns additional process if neverbleed
+        is used. In the single process mode, the signal handling
+        feature is disabled.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx--single-process
+      '';
+    };
+
+    backlog = lib.mkOption {
+      type        = lib.types.int;
+      default     = 65536;
+      description = ''
+        Listen backlog size.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx--backlog
+      '';
+    };
+
+    backend-address-family = lib.mkOption {
+      type = lib.types.enum [
+        "auto"
+        "IPv4"
+        "IPv6"
+      ];
+      default = "auto";
+      description = ''
+        Specify address family of backend connections. If "auto" is
+        given, both IPv4 and IPv6 are considered. If "IPv4" is given,
+        only IPv4 address is considered. If "IPv6" is given, only IPv6
+        address is considered.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx--backend-address-family
+      '';
+    };
+
+    workers = lib.mkOption {
+      type        = lib.types.int;
+      default     = 1;
+      description = ''
+        Set the number of worker threads.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-n
+      '';
+    };
+
+    single-thread = lib.mkOption {
+      type        = lib.types.bool;
+      default     = false;
+      description = ''
+        Run everything in one thread inside the worker process. This
+        feature is provided for better debugging experience, or for
+        the platforms which lack thread support. If threading is
+        disabled, this option is always enabled.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx--single-thread
+      '';
+    };
+
+    rlimit-nofile = lib.mkOption {
+      type        = lib.types.int;
+      default     = 0;
+      description = ''
+        Set maximum number of open files (RLIMIT_NOFILE) to &lt;N&gt;. If 0
+        is given, nghttpx does not set the limit.
+
+        Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx--rlimit-nofile
+      '';
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/nghttpx/server-options.nix b/nixpkgs/nixos/modules/services/networking/nghttpx/server-options.nix
new file mode 100644
index 000000000000..ef23bfd793c5
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/nghttpx/server-options.nix
@@ -0,0 +1,18 @@
+{ lib, ... }:
+{ options = {
+    host = lib.mkOption {
+      type        = lib.types.str;
+      example     = "127.0.0.1";
+      description = ''
+        Server host address.
+      '';
+    };
+    port = lib.mkOption {
+      type        = lib.types.int;
+      example     = 5088;
+      description = ''
+        Server host port.
+      '';
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/nghttpx/tls-submodule.nix b/nixpkgs/nixos/modules/services/networking/nghttpx/tls-submodule.nix
new file mode 100644
index 000000000000..8f3cdaae2c81
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/nghttpx/tls-submodule.nix
@@ -0,0 +1,21 @@
+{lib, ...}:
+{ options = {
+    key = lib.mkOption {
+      type        = lib.types.str;
+      example     = "/etc/ssl/keys/mykeyfile.key";
+      default     = "/etc/ssl/keys/server.key";
+      description = ''
+        Path to the TLS key file.
+      '';
+    };
+
+    crt = lib.mkOption {
+      type        = lib.types.str;
+      example     = "/etc/ssl/certs/mycert.crt";
+      default     = "/etc/ssl/certs/server.crt";
+      description = ''
+        Path to the TLS certificate file.
+      '';
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/ngircd.nix b/nixpkgs/nixos/modules/services/networking/ngircd.nix
new file mode 100644
index 000000000000..4b2fa7795922
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/ngircd.nix
@@ -0,0 +1,59 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.ngircd;
+
+  configFile = pkgs.stdenv.mkDerivation {
+    name = "ngircd.conf";
+
+    text = cfg.config;
+
+    preferLocalBuild = true;
+
+    buildCommand = ''
+      echo -n "$text" > $out
+      ${cfg.package}/sbin/ngircd --config $out --configtest
+    '';
+  };
+in {
+  options = {
+    services.ngircd = {
+      enable = mkEnableOption "the ngircd IRC server";
+
+      config = mkOption {
+        description = "The ngircd configuration (see ngircd.conf(5)).";
+
+        type = types.lines;
+      };
+
+      package = mkOption {
+        description = "The ngircd package.";
+
+        type = types.package;
+
+        default = pkgs.ngircd;
+        defaultText = "pkgs.ngircd";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    #!!! TODO: Use ExecReload (see https://github.com/NixOS/nixpkgs/issues/1988)
+    systemd.services.ngircd = {
+      description = "The ngircd IRC server";
+
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig.ExecStart = "${cfg.package}/sbin/ngircd --config ${configFile} --nodaemon";
+
+      serviceConfig.User = "ngircd";
+    };
+
+    users.users.ngircd = {
+      uid = config.ids.uids.ngircd;
+      description = "ngircd user.";
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/nix-serve.nix b/nixpkgs/nixos/modules/services/networking/nix-serve.nix
new file mode 100644
index 000000000000..347d87b3f385
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/nix-serve.nix
@@ -0,0 +1,81 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.services.nix-serve;
+in
+{
+  options = {
+    services.nix-serve = {
+      enable = mkEnableOption "nix-serve, the standalone Nix binary cache server";
+
+      port = mkOption {
+        type = types.int;
+        default = 5000;
+        description = ''
+          Port number where nix-serve will listen on.
+        '';
+      };
+
+      bindAddress = mkOption {
+        type = types.str;
+        default = "0.0.0.0";
+        description = ''
+          IP address where nix-serve will bind its listening socket.
+        '';
+      };
+
+      secretKeyFile = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          The path to the file used for signing derivation data.
+          Generate with:
+
+          ```
+          nix-store --generate-binary-cache-key key-name secret-key-file public-key-file
+          ```
+
+          Make sure user `nix-serve` has read access to the private key file.
+
+          For more details see <citerefentry><refentrytitle>nix-store</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
+        '';
+      };
+
+      extraParams = mkOption {
+        type = types.separatedString " ";
+        default = "";
+        description = ''
+          Extra command line parameters for nix-serve.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.nix-serve = {
+      description = "nix-serve binary cache server";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      path = [ config.nix.package.out pkgs.bzip2.bin ];
+      environment.NIX_REMOTE = "daemon";
+      environment.NIX_SECRET_KEY_FILE = cfg.secretKeyFile;
+
+      serviceConfig = {
+        Restart = "always";
+        RestartSec = "5s";
+        ExecStart = "${pkgs.nix-serve}/bin/nix-serve " +
+          "--listen ${cfg.bindAddress}:${toString cfg.port} ${cfg.extraParams}";
+        User = "nix-serve";
+        Group = "nogroup";
+      };
+    };
+
+    users.users.nix-serve = {
+      description = "Nix-serve user";
+      uid = config.ids.uids.nix-serve;
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/nix-store-gcs-proxy.nix b/nixpkgs/nixos/modules/services/networking/nix-store-gcs-proxy.nix
new file mode 100644
index 000000000000..3f2ce5bca4da
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/nix-store-gcs-proxy.nix
@@ -0,0 +1,75 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  opts = { name, config, ... }: {
+    options = {
+      enable = mkOption {
+        default = true;
+        type = types.bool;
+        example = true;
+        description = "Whether to enable proxy for this bucket";
+      };
+      bucketName = mkOption {
+        type = types.str;
+        default = name;
+        example = "my-bucket-name";
+        description = "Name of Google storage bucket";
+      };
+      address = mkOption {
+        type = types.str;
+        example = "localhost:3000";
+        description = "The address of the proxy.";
+      };
+    };
+  };
+  enabledProxies = lib.filterAttrs (n: v: v.enable) config.services.nix-store-gcs-proxy;
+  mapProxies = function: lib.mkMerge (lib.mapAttrsToList function enabledProxies);
+in
+{
+  options.services.nix-store-gcs-proxy = mkOption {
+    type = types.attrsOf (types.submodule opts);
+    default = {};
+    description = ''
+      An attribute set describing an HTTP to GCS proxy that allows us to use GCS
+      bucket via HTTP protocol.
+    '';
+  };
+
+  config.systemd.services = mapProxies (name: cfg: {
+    "nix-store-gcs-proxy-${name}" = {
+      description = "A HTTP nix store that proxies requests to Google Storage";
+      wantedBy = ["multi-user.target"];
+
+      serviceConfig = {
+        RestartSec = 5;
+        StartLimitInterval = 10;
+        ExecStart = ''
+          ${pkgs.nix-store-gcs-proxy}/bin/nix-store-gcs-proxy \
+            --bucket-name ${cfg.bucketName} \
+            --addr ${cfg.address}
+        '';
+
+        DynamicUser = true;
+
+        ProtectSystem = "strict";
+        ProtectHome = true;
+        PrivateTmp = true;
+        PrivateDevices = true;
+        PrivateMounts = true;
+        PrivateUsers = true;
+
+        ProtectKernelTunables = true;
+        ProtectKernelModules = true;
+        ProtectControlGroups = true;
+
+        NoNewPrivileges = true;
+        LockPersonality = true;
+        RestrictRealtime = true;
+      };
+    };
+  });
+
+  meta.maintainers = [ maintainers.mrkkrp ];
+}
diff --git a/nixpkgs/nixos/modules/services/networking/nixops-dns.nix b/nixpkgs/nixos/modules/services/networking/nixops-dns.nix
new file mode 100644
index 000000000000..2bb1263b7fa2
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/nixops-dns.nix
@@ -0,0 +1,79 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  pkg = pkgs.nixops-dns;
+  cfg = config.services.nixops-dns;
+in
+
+{
+  options = {
+    services.nixops-dns = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable the nixops-dns resolution
+          of NixOps virtual machines via dnsmasq and fake domain name.
+        '';
+      };
+
+      user = mkOption {
+        type = types.str;
+        description = ''
+          The user the nixops-dns daemon should run as.
+          This should be the user, which is also used for nixops and
+          have the .nixops directory in its home.
+        '';
+      };
+
+      domain = mkOption {
+        type = types.str;
+        description = ''
+          Fake domain name to resolve to NixOps virtual machines.
+
+          For example "ops" will resolve "vm.ops".
+        '';
+        example = "ops";
+        default = "ops";
+      };
+
+      dnsmasq = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Enable dnsmasq forwarding to nixops-dns. This allows to use
+          nixops-dns for `services.nixops-dns.domain` resolution
+          while forwarding the rest of the queries to original resolvers.
+        '';
+      };
+
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.nixops-dns = {
+      description = "nixops-dns: DNS server for resolving NixOps machines";
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        Type = "simple";
+        User = cfg.user;
+        ExecStart="${pkg}/bin/nixops-dns --domain=.${cfg.domain}";
+      };
+    };
+
+    services.dnsmasq = mkIf cfg.dnsmasq {
+      enable = true;
+      resolveLocalQueries = true;
+      servers = [
+        "/${cfg.domain}/127.0.0.1#5300"
+      ];
+      extraConfig = ''
+        bind-interfaces
+        listen-address=127.0.0.1
+      '';
+    };
+
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/nntp-proxy.nix b/nixpkgs/nixos/modules/services/networking/nntp-proxy.nix
new file mode 100644
index 000000000000..cc061bf6e3b9
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/nntp-proxy.nix
@@ -0,0 +1,234 @@
+{ 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.enum [ "error" "warning" "notice" "info" "debug" ];
+        default = "info";
+        example = "error";
+        description = ''
+          Verbosity level
+        '';
+      };
+
+      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.users.${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}/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/nixpkgs/nixos/modules/services/networking/nsd.nix b/nixpkgs/nixos/modules/services/networking/nsd.nix
new file mode 100644
index 000000000000..6e3eed0c5570
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/nsd.nix
@@ -0,0 +1,980 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.services.nsd;
+
+  username = "nsd";
+  stateDir = "/var/lib/nsd";
+  pidFile = stateDir + "/var/nsd.pid";
+
+  # build nsd with the options needed for the given config
+  nsdPkg = pkgs.nsd.override {
+    configFile = "${configFile}/nsd.conf";
+
+    bind8Stats = cfg.bind8Stats;
+    ipv6 = cfg.ipv6;
+    ratelimit = cfg.ratelimit.enable;
+    rootServer = cfg.rootServer;
+    zoneStats = length (collect (x: (x.zoneStats or null) != null) cfg.zones) > 0;
+  };
+
+  mkZoneFileName = name: if name == "." then "root" else name;
+
+  nsdEnv = pkgs.buildEnv {
+    name = "nsd-env";
+
+    paths = [ configFile ]
+      ++ mapAttrsToList (name: zone: writeZoneData name zone.data) zoneConfigs;
+
+    postBuild = ''
+      echo "checking zone files"
+      cd $out/zones
+
+      for zoneFile in *; do
+        echo "|- checking zone '$out/zones/$zoneFile'"
+        ${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 {
+    name = "nsd-zone-${mkZoneFileName name}";
+    inherit text;
+    destination = "/zones/${mkZoneFileName name}";
+  };
+
+
+  # options are ordered alphanumerically by the nixos option name
+  configFile = pkgs.writeTextDir "nsd.conf" ''
+    server:
+      chroot:   "${stateDir}"
+      username: ${username}
+
+      # The directory for zonefile: files. The daemon chdirs here.
+      zonesdir: "${stateDir}"
+
+      # the list of dynamically added zones.
+      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}
+
+      ip-freebind:         ${yesOrNo  cfg.ipFreebind}
+      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}
+      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}
+      ${maybeToString "statistics: " cfg.statistics}
+      tcp-count:           ${toString cfg.tcpCount}
+      tcp-query-count:     ${toString cfg.tcpQueryCount}
+      tcp-timeout:         ${toString cfg.tcpTimeout}
+      verbosity:           ${toString cfg.verbosity}
+      ${maybeString "version: " cfg.version}
+      xfrd-reload-timeout: ${toString cfg.xfrdReloadTimeout}
+      zonefiles-check:     ${yesOrNo  cfg.zonefilesCheck}
+
+      ${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.remoteControl.port}
+      server-key-file:   "${cfg.remoteControl.serverKeyFile}"
+      server-cert-file:  "${cfg.remoteControl.serverCertFile}"
+
+    ${concatStrings (mapAttrsToList zoneConfigFile zoneConfigs)}
+
+    ${cfg.extraConfig}
+  '';
+
+  yesOrNo = b: if b then "yes" else "no";
+  maybeString = prefix: x: if x == null then "" else ''${prefix} "${x}"'';
+  maybeToString = prefix: x: if x == null then "" else ''${prefix} ${toString x}'';
+  forEach = pre: l: concatMapStrings (x: pre + x + "\n") l;
+
+
+  keyConfigFile = concatStrings (mapAttrsToList (keyName: keyOptions: ''
+    key:
+      name:      "${keyName}"
+      algorithm: "${keyOptions.algorithm}"
+      include:   "${stateDir}/private/${keyName}"
+  '') cfg.keys);
+
+  copyKeys = concatStrings (mapAttrsToList (keyName: keyOptions: ''
+    secret=$(cat "${keyOptions.keyFile}")
+    dest="${stateDir}/private/${keyName}"
+    echo "  secret: \"$secret\"" > "$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/${mkZoneFileName name}"
+      ${maybeString "outgoing-interface: " zone.outgoingInterface}
+    ${forEach     "  rrl-whitelist: "      zone.rrlWhitelist}
+      ${maybeString "zonestats: "          zone.zoneStats}
+
+      ${maybeToString "max-refresh-time: " zone.maxRefreshSecs}
+      ${maybeToString "min-refresh-time: " zone.minRefreshSecs}
+      ${maybeToString "max-retry-time:   " zone.maxRetrySecs}
+      ${maybeToString "min-retry-time:   " zone.minRetrySecs}
+
+      allow-axfr-fallback: ${yesOrNo       zone.allowAXFRFallback}
+    ${forEach     "  allow-notify: "       zone.allowNotify}
+    ${forEach     "  request-xfr: "        zone.requestXFR}
+
+    ${forEach     "  notify: "             zone.notify}
+      notify-retry:                        ${toString zone.notifyRetry}
+    ${forEach     "  provide-xfr: "        zone.provideXFR}
+  '';
+
+  zoneConfigs = zoneConfigs' {} "" { children = cfg.zones; };
+
+  zoneConfigs' = parent: name: zone:
+    if !(zone ? children) || zone.children == null || zone.children == { }
+      # leaf -> actual zone
+      then listToAttrs [ (nameValuePair name (parent // zone)) ]
+
+      # fork -> pattern
+      else zipAttrsWith (name: head) (
+        mapAttrsToList (name: child: zoneConfigs' (parent // zone // { children = {}; }) name child)
+                       zone.children
+      );
+
+  # fighting infinite recursion
+  zoneOptions = zoneOptionsRaw // childConfig zoneOptions1 true;
+  zoneOptions1 = zoneOptionsRaw // childConfig zoneOptions2 false;
+  zoneOptions2 = zoneOptionsRaw // childConfig zoneOptions3 false;
+  zoneOptions3 = zoneOptionsRaw // childConfig zoneOptions4 false;
+  zoneOptions4 = zoneOptionsRaw // childConfig zoneOptions5 false;
+  zoneOptions5 = zoneOptionsRaw // childConfig zoneOptions6 false;
+  zoneOptions6 = zoneOptionsRaw // childConfig null         false;
+
+  childConfig = x: v: { options.children = { type = types.attrsOf x; visible = v; }; };
+
+  # options are ordered alphanumerically
+  zoneOptionsRaw = types.submodule {
+    options = {
+
+      allowAXFRFallback = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          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"
+                  ];
+        description = ''
+          Listed primary servers are allowed to notify this secondary server.
+          <screen><![CDATA[
+          Format: <ip> <key-name | NOKEY | BLOCKED>
+
+          <ip> either a plain IPv4/IPv6 address or range. Valid patters for ranges:
+          * 10.0.0.0/24            # via subnet size
+          * 10.0.0.0&255.255.255.0 # via subnet mask
+          * 10.0.0.1-10.0.0.254    # via range
+
+          A optional port number could be added with a '@':
+          * 2001:1234::1@1234
+
+          <key-name | NOKEY | BLOCKED>
+          * <key-name> will use the specified TSIG key
+          * NOKEY      no TSIG signature is required
+          * BLOCKED    notifies from non-listed or blocked IPs will be ignored
+          * ]]></screen>
+        '';
+      };
+
+      children = mkOption {
+        default = {};
+        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.
+        '';
+      };
+
+      data = mkOption {
+        type = types.lines;
+        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.
+        '';
+      };
+
+      dnssec = mkEnableOption "DNSSEC";
+
+      dnssecPolicy = {
+        algorithm = mkOption {
+          type = types.str;
+          default = "RSASHA256";
+          description = "Which algorithm to use for DNSSEC";
+        };
+        keyttl = mkOption {
+          type = types.str;
+          default = "1h";
+          description = "TTL for dnssec records";
+        };
+        coverage = mkOption {
+          type = types.str;
+          default = "1y";
+          description = ''
+            The length of time to ensure that keys will be correct; no action will be taken to create new keys to be activated after this time.
+          '';
+        };
+        zsk = mkOption {
+          type = keyPolicy;
+          default = { keySize = 2048;
+                      prePublish = "1w";
+                      postPublish = "1w";
+                      rollPeriod = "1mo";
+                    };
+          description = "Key policy for zone signing keys";
+        };
+        ksk = mkOption {
+          type = keyPolicy;
+          default = { keySize = 4096;
+                      prePublish = "1mo";
+                      postPublish = "1mo";
+                      rollPeriod = "0";
+                    };
+          description = "Key policy for key signing keys";
+        };
+      };
+
+      maxRefreshSecs = mkOption {
+        type = types.nullOr types.int;
+        default = null;
+        description = ''
+          Limit refresh time for secondary zones. This is the timer which
+          checks to see if the zone has to be refetched when it expires.
+          Normally the value from the SOA record is used, but this  option
+          restricts that value.
+        '';
+      };
+
+      minRefreshSecs = mkOption {
+        type = types.nullOr types.int;
+        default = null;
+        description = ''
+          Limit refresh time for secondary zones.
+        '';
+      };
+
+      maxRetrySecs = mkOption {
+        type = types.nullOr types.int;
+        default = null;
+        description = ''
+          Limit retry time for secondary zones. This is the timeout after
+          a failed fetch attempt for the zone. Normally the value from
+          the SOA record is used, but this option restricts that value.
+        '';
+      };
+
+      minRetrySecs = mkOption {
+        type = types.nullOr types.int;
+        default = null;
+        description = ''
+          Limit retry time for secondary zones.
+        '';
+      };
+
+
+      notify = mkOption {
+        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.
+          <screen><![CDATA[
+          Format: <ip> <key-name | NOKEY>
+
+          <ip> a plain IPv4/IPv6 address with on optional port number (ip@port)
+
+          <key-name | NOKEY>
+          * <key-name> sign notifies with the specified key
+          * NOKEY      don't sign notifies
+          ]]></screen>
+        '';
+      };
+
+      notifyRetry = mkOption {
+        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" ];
+        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
+        '';
+      };
+
+      requestXFR = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [];
+        description = ''
+          Format: <code>[AXFR|UDP] &lt;ip-address&gt; &lt;key-name | NOKEY&gt;</code>
+        '';
+      };
+
+      rrlWhitelist = mkOption {
+        type = with types; listOf (enum [ "nxdomain" "error" "referral" "any" "rrsig" "wildcard" "nodata" "dnskey" "positive" "all" ]);
+        default = [];
+        description = ''
+          Whitelists the given rrl-types.
+        '';
+      };
+
+      zoneStats = mkOption {
+        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
+          to the group specified by this given name. Use "%s" to use the zones
+          name as the group. The groups are output from nsd-control stats
+          and stats_noreset.
+        '';
+      };
+    };
+  };
+
+  keyPolicy = types.submodule {
+    options = {
+      keySize = mkOption {
+        type = types.int;
+        description = "Key size in bits";
+      };
+      prePublish = mkOption {
+        type = types.str;
+        description = "How long in advance to publish new keys";
+      };
+      postPublish = mkOption {
+        type = types.str;
+        description = "How long after deactivation to keep a key in the zone";
+      };
+      rollPeriod = mkOption {
+        type = types.str;
+        description = "How frequently to change keys";
+      };
+    };
+  };
+
+  dnssecZones = (filterAttrs (n: v: if v ? dnssec then v.dnssec else false) zoneConfigs);
+
+  dnssec = dnssecZones != {};
+
+  dnssecTools = pkgs.bind.override { enablePython = true; };
+
+  signZones = optionalString dnssec ''
+    mkdir -p ${stateDir}/dnssec
+    chown ${username}:${username} ${stateDir}/dnssec
+    chmod 0600 ${stateDir}/dnssec
+
+    ${concatStrings (mapAttrsToList signZone dnssecZones)}
+  '';
+  signZone = name: zone: ''
+    ${dnssecTools}/bin/dnssec-keymgr -g ${dnssecTools}/bin/dnssec-keygen -s ${dnssecTools}/bin/dnssec-settime -K ${stateDir}/dnssec -c ${policyFile name zone.dnssecPolicy} ${name}
+    ${dnssecTools}/bin/dnssec-signzone -S -K ${stateDir}/dnssec -o ${name} -O full -N date ${stateDir}/zones/${name}
+    ${nsdPkg}/sbin/nsd-checkzone ${name} ${stateDir}/zones/${name}.signed && mv -v ${stateDir}/zones/${name}.signed ${stateDir}/zones/${name}
+  '';
+  policyFile = name: policy: pkgs.writeText "${name}.policy" ''
+    zone ${name} {
+      algorithm ${policy.algorithm};
+      key-size zsk ${toString policy.zsk.keySize};
+      key-size ksk ${toString policy.ksk.keySize};
+      keyttl ${policy.keyttl};
+      pre-publish zsk ${policy.zsk.prePublish};
+      pre-publish ksk ${policy.ksk.prePublish};
+      post-publish zsk ${policy.zsk.postPublish};
+      post-publish ksk ${policy.ksk.postPublish};
+      roll-period zsk ${policy.zsk.rollPeriod};
+      roll-period ksk ${policy.ksk.rollPeriod};
+      coverage ${policy.coverage};
+    };
+  '';
+in
+{
+  # options are ordered alphanumerically
+  options.services.nsd = {
+
+    enable = mkEnableOption "NSD authoritative DNS server";
+
+    bind8Stats = mkEnableOption "BIND8 like statistics";
+
+    dnssecInterval = mkOption {
+      type = types.str;
+      default = "1h";
+      description = ''
+        How often to check whether dnssec key rollover is required
+      '';
+    };
+
+    extraConfig = mkOption {
+      type = types.lines;
+      default = "";
+      description = ''
+        Extra nsd config.
+      '';
+    };
+
+    hideVersion = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Whether NSD should answer VERSION.BIND and VERSION.SERVER CHAOS class queries.
+      '';
+    };
+
+    identity = mkOption {
+      type = types.str;
+      default = "unidentified server";
+      description = ''
+        Identify the server (CH TXT ID.SERVER entry).
+      '';
+    };
+
+    interfaces = mkOption {
+      type = types.listOf types.str;
+      default = [ "127.0.0.0" "::1" ];
+      description = ''
+        What addresses the server should listen to.
+      '';
+    };
+
+    ipFreebind = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether to bind to nonlocal addresses and interfaces that are down.
+        Similar to ip-transparent.
+      '';
+    };
+
+    ipTransparent = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Allow binding to non local addresses.
+      '';
+    };
+
+    ipv4 = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Whether to listen on IPv4 connections.
+      '';
+    };
+
+    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";
+
+      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.
+        '';
+      };
+
+      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.
+        '';
+      };
+
+      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.
+        '';
+      };
+
+      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.
+        '';
+      };
+
+      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.
+        '';
+      };
+
+    };
+
+
+    remoteControl = {
+
+      enable = mkEnableOption "remote control via nsd-control";
+
+      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.
+        '';
+      };
+
+      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.
+        '';
+      };
+
+      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).
+        '';
+      };
+
+      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.
+        '';
+      };
+
+      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.
+        '';
+      };
+
+    };
+
+    zones = mkOption {
+      type = types.attrsOf zoneOptions;
+      default = {};
+      example = literalExample ''
+        { "serverGroup1" = {
+            provideXFR = [ "10.1.2.3 NOKEY" ];
+            children = {
+              "example.com." = {
+                data = '''
+                  $ORIGIN example.com.
+                  $TTL    86400
+                  @ IN SOA a.ns.example.com. admin.example.com. (
+                  ...
+                ''';
+              };
+              "example.org." = {
+                data = '''
+                  $ORIGIN example.org.
+                  $TTL    86400
+                  @ IN SOA a.ns.example.com. admin.example.com. (
+                  ...
+                ''';
+              };
+            };
+          };
+
+          "example.net." = {
+            provideXFR = [ "10.3.2.1 NOKEY" ];
+            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.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    assertions = singleton {
+      assertion = zoneConfigs ? "." -> cfg.rootServer;
+      message = "You have a root zone configured. If this is really what you "
+              + "want, please enable 'services.nsd.rootServer'.";
+    };
+
+    environment.systemPackages = [ nsdPkg ];
+
+    users.groups.${username}.gid = config.ids.gids.nsd;
+
+    users.users.${username} = {
+      description = "NSD service user";
+      home = stateDir;
+      createHome  = true;
+      uid = config.ids.uids.nsd;
+      group = username;
+    };
+
+    systemd.services.nsd = {
+      description = "NSD authoritative only domain name service";
+
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        ExecStart = "${nsdPkg}/sbin/nsd -d -c ${nsdEnv}/nsd.conf";
+        StandardError = "null";
+        PIDFile = pidFile;
+        Restart = "always";
+        RestartSec = "4s";
+        StartLimitBurst = 4;
+        StartLimitInterval = "5min";
+      };
+
+      preStart = ''
+        rm -Rf "${stateDir}/private/"
+        rm -Rf "${stateDir}/tmp/"
+
+        mkdir -m 0700 -p "${stateDir}/private"
+        mkdir -m 0700 -p "${stateDir}/tmp"
+        mkdir -m 0700 -p "${stateDir}/var"
+
+        cat > "${stateDir}/don't touch anything in here" << EOF
+        Everything in this directory except NSD's state in var and dnssec
+        is automatically generated and will be purged and redeployed by
+        the nsd.service pre-start script.
+        EOF
+
+        chown ${username}:${username} -R "${stateDir}/private"
+        chown ${username}:${username} -R "${stateDir}/tmp"
+        chown ${username}:${username} -R "${stateDir}/var"
+
+        rm -rf "${stateDir}/zones"
+        cp -rL "${nsdEnv}/zones" "${stateDir}/zones"
+
+        ${copyKeys}
+      '';
+    };
+
+    systemd.timers.nsd-dnssec = mkIf dnssec {
+      description = "Automatic DNSSEC key rollover";
+
+      wantedBy = [ "nsd.service" ];
+
+      timerConfig = {
+        OnActiveSec = cfg.dnssecInterval;
+        OnUnitActiveSec = cfg.dnssecInterval;
+      };
+    };
+
+    systemd.services.nsd-dnssec = mkIf dnssec {
+      description = "DNSSEC key rollover";
+
+      wantedBy = [ "nsd.service" ];
+      before = [ "nsd.service" ];
+
+      script = signZones;
+
+      postStop = ''
+        /run/current-system/systemd/bin/systemctl kill -s SIGHUP nsd.service
+      '';
+    };
+
+  };
+
+  meta.maintainers = with lib.maintainers; [ hrdinka ];
+}
diff --git a/nixpkgs/nixos/modules/services/networking/ntopng.nix b/nixpkgs/nixos/modules/services/networking/ntopng.nix
new file mode 100644
index 000000000000..c15257117137
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/ntopng.nix
@@ -0,0 +1,116 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.ntopng;
+  redisCfg = config.services.redis;
+
+  configFile = if cfg.configText != "" then
+    pkgs.writeText "ntopng.conf" ''
+      ${cfg.configText}
+    ''
+    else
+    pkgs.writeText "ntopng.conf" ''
+      ${concatStringsSep " " (map (e: "--interface=" + e) cfg.interfaces)}
+      --http-port=${toString cfg.http-port}
+      --redis=localhost:${toString redisCfg.port}
+      ${cfg.extraConfig}
+    '';
+
+in
+
+{
+
+  options = {
+
+    services.ntopng = {
+
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Enable ntopng, a high-speed web-based traffic analysis and flow
+          collection tool.
+
+          With the default configuration, ntopng monitors all network
+          interfaces and displays its findings at http://localhost:${toString
+          cfg.http-port}. Default username and password is admin/admin.
+
+          See the ntopng(8) manual page and http://www.ntop.org/products/ntop/
+          for more info.
+
+          Note that enabling ntopng will also enable redis (key-value
+          database server) for persistent data storage.
+        '';
+      };
+
+      interfaces = mkOption {
+        default = [ "any" ];
+        example = [ "eth0" "wlan0" ];
+        type = types.listOf types.str;
+        description = ''
+          List of interfaces to monitor. Use "any" to monitor all interfaces.
+        '';
+      };
+
+      http-port = mkOption {
+        default = 3000;
+        type = types.int;
+        description = ''
+          Sets the HTTP port of the embedded web server.
+        '';
+      };
+
+      configText = mkOption {
+        default = "";
+        example = ''
+          --interface=any
+          --http-port=3000
+          --disable-login
+        '';
+        type = types.lines;
+        description = ''
+          Overridable configuration file contents to use for ntopng. By
+          default, use the contents automatically generated by NixOS.
+        '';
+      };
+
+      extraConfig = mkOption {
+        default = "";
+        type = types.lines;
+        description = ''
+          Configuration lines that will be appended to the generated ntopng
+          configuration file. Note that this mechanism does not work when the
+          manual <option>configText</option> option is used.
+        '';
+      };
+
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    # ntopng uses redis for data storage
+    services.redis.enable = true;
+
+    # nice to have manual page and ntopng command in PATH
+    environment.systemPackages = [ pkgs.ntopng ];
+
+    systemd.services.ntopng = {
+      description = "Ntopng Network Monitor";
+      requires = [ "redis.service" ];
+      after = [ "network.target" "redis.service" ];
+      wantedBy = [ "multi-user.target" ];
+      preStart = "mkdir -p /var/lib/ntopng/";
+      serviceConfig.ExecStart = "${pkgs.ntopng}/bin/ntopng ${configFile}";
+      unitConfig.Documentation = "man:ntopng(8)";
+    };
+
+    # ntopng drops priveleges to user "nobody" and that user is already defined
+    # in users-groups.nix.
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/ntp/chrony.nix b/nixpkgs/nixos/modules/services/networking/ntp/chrony.nix
new file mode 100644
index 000000000000..b7e4c89a155c
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/ntp/chrony.nix
@@ -0,0 +1,125 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.chrony;
+
+  stateDir = "/var/lib/chrony";
+  keyFile = "${stateDir}/chrony.keys";
+
+  configFile = pkgs.writeText "chrony.conf" ''
+    ${concatMapStringsSep "\n" (server: "server " + server + " iburst") cfg.servers}
+
+    ${optionalString
+      (cfg.initstepslew.enabled && (cfg.servers != []))
+      "initstepslew ${toString cfg.initstepslew.threshold} ${concatStringsSep " " cfg.servers}"
+    }
+
+    driftfile ${stateDir}/chrony.drift
+    keyfile ${keyFile}
+
+    ${optionalString (!config.time.hardwareClockInLocalTime) "rtconutc"}
+
+    ${cfg.extraConfig}
+  '';
+
+  chronyFlags = "-n -m -u chrony -f ${configFile} ${toString cfg.extraFlags}";
+in
+{
+  options = {
+    services.chrony = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to synchronise your machine's time using chrony.
+          Make sure you disable NTP if you enable this service.
+        '';
+      };
+
+      servers = mkOption {
+        default = config.networking.timeServers;
+        description = ''
+          The set of NTP servers from which to synchronise.
+        '';
+      };
+
+      initstepslew = mkOption {
+        default = {
+          enabled = true;
+          threshold = 1000; # by default, same threshold as 'ntpd -g' (1000s)
+        };
+        description = ''
+          Allow chronyd to make a rapid measurement of the system clock error at
+          boot time, and to correct the system clock by stepping before normal
+          operation begins.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Extra configuration directives that should be added to
+          <literal>chrony.conf</literal>
+        '';
+      };
+
+      extraFlags = mkOption {
+        default = [];
+        example = [ "-s" ];
+        type = types.listOf types.str;
+        description = "Extra flags passed to the chronyd command.";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    meta.maintainers = with lib.maintainers; [ thoughtpolice ];
+
+    environment.systemPackages = [ pkgs.chrony ];
+
+    users.groups.chrony.gid = config.ids.gids.chrony;
+
+    users.users.chrony =
+      { uid = config.ids.uids.chrony;
+        group = "chrony";
+        description = "chrony daemon user";
+        home = stateDir;
+      };
+
+    services.timesyncd.enable = mkForce false;
+
+    systemd.services.systemd-timedated.environment = { SYSTEMD_TIMEDATED_NTP_SERVICES = "chronyd.service"; };
+
+    systemd.tmpfiles.rules = [
+      "d ${stateDir} 0755 chrony chrony - -"
+      "f ${keyFile} 0640 chrony chrony -"
+    ];
+
+    systemd.services.chronyd =
+      { description = "chrony NTP daemon";
+
+        wantedBy = [ "multi-user.target" ];
+        wants    = [ "time-sync.target" ];
+        before   = [ "time-sync.target" ];
+        after    = [ "network.target" ];
+        conflicts = [ "ntpd.service" "systemd-timesyncd.service" ];
+
+        path = [ pkgs.chrony ];
+
+        unitConfig.ConditionCapability = "CAP_SYS_TIME";
+        serviceConfig =
+          { Type = "simple";
+            ExecStart = "${pkgs.chrony}/bin/chronyd ${chronyFlags}";
+
+            ProtectHome = "yes";
+            ProtectSystem = "full";
+            PrivateTmp = "yes";
+            StateDirectory = "chrony";
+          };
+
+      };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/ntp/ntpd.nix b/nixpkgs/nixos/modules/services/networking/ntp/ntpd.nix
new file mode 100644
index 000000000000..51398851adc6
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/ntp/ntpd.nix
@@ -0,0 +1,148 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  inherit (pkgs) ntp;
+
+  cfg = config.services.ntp;
+
+  stateDir = "/var/lib/ntp";
+
+  ntpUser = "ntp";
+
+  configFile = pkgs.writeText "ntp.conf" ''
+    driftfile ${stateDir}/ntp.drift
+
+    restrict default ${toString cfg.restrictDefault}
+    restrict -6 default ${toString cfg.restrictDefault}
+    restrict source ${toString cfg.restrictSource}
+
+    restrict 127.0.0.1
+    restrict -6 ::1
+
+    ${toString (map (server: "server " + server + " iburst\n") cfg.servers)}
+
+    ${cfg.extraConfig}
+  '';
+
+  ntpFlags = "-c ${configFile} -u ${ntpUser}:nogroup ${toString cfg.extraFlags}";
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.ntp = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to synchronise your machine's time using ntpd, as a peer in
+          the NTP network.
+          </para>
+          <para>
+          Disables <literal>systemd.timesyncd</literal> if enabled.
+        '';
+      };
+
+      restrictDefault = mkOption {
+        type = types.listOf types.str;
+        description = ''
+          The restriction flags to be set by default.
+          </para>
+          <para>
+          The default flags prevent external hosts from using ntpd as a DDoS
+          reflector, setting system time, and querying OS/ntpd version. As
+          recommended in section 6.5.1.1.3, answer "No" of
+          http://support.ntp.org/bin/view/Support/AccessRestrictions
+        '';
+        default = [ "limited" "kod" "nomodify" "notrap" "noquery" "nopeer" ];
+      };
+
+      restrictSource = mkOption {
+        type = types.listOf types.str;
+        description = ''
+          The restriction flags to be set on source.
+          </para>
+          <para>
+          The default flags allow peers to be added by ntpd from configured
+          pool(s), but not by other means.
+        '';
+        default = [ "limited" "kod" "nomodify" "notrap" "noquery" ];
+      };
+
+      servers = mkOption {
+        default = config.networking.timeServers;
+        description = ''
+          The set of NTP servers from which to synchronise.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        example = ''
+          fudge 127.127.1.0 stratum 10
+        '';
+        description = ''
+          Additional text appended to <filename>ntp.conf</filename>.
+        '';
+      };
+
+      extraFlags = mkOption {
+        type = types.listOf types.str;
+        description = "Extra flags passed to the ntpd command.";
+        example = literalExample ''[ "--interface=eth0" ]'';
+        default = [];
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.ntp.enable {
+    meta.maintainers = with lib.maintainers; [ thoughtpolice ];
+
+    # Make tools such as ntpq available in the system path.
+    environment.systemPackages = [ pkgs.ntp ];
+    services.timesyncd.enable = mkForce false;
+
+    systemd.services.systemd-timedated.environment = { SYSTEMD_TIMEDATED_NTP_SERVICES = "ntpd.service"; };
+
+    users.users.${ntpUser} =
+      { uid = config.ids.uids.ntp;
+        description = "NTP daemon user";
+        home = stateDir;
+      };
+
+    systemd.services.ntpd =
+      { description = "NTP Daemon";
+
+        wantedBy = [ "multi-user.target" ];
+        wants = [ "time-sync.target" ];
+        before = [ "time-sync.target" ];
+
+        preStart =
+          ''
+            mkdir -m 0755 -p ${stateDir}
+            chown ${ntpUser} ${stateDir}
+          '';
+
+        serviceConfig = {
+          ExecStart = "@${ntp}/bin/ntpd ntpd -g ${ntpFlags}";
+          Type = "forking";
+        };
+      };
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/ntp/openntpd.nix b/nixpkgs/nixos/modules/services/networking/ntp/openntpd.nix
new file mode 100644
index 000000000000..67a04d48d308
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/ntp/openntpd.nix
@@ -0,0 +1,82 @@
+{ pkgs, lib, config, options, ... }:
+
+with lib;
+
+let
+  cfg = config.services.openntpd;
+
+  package = pkgs.openntpd_nixos;
+
+  configFile = ''
+    ${concatStringsSep "\n" (map (s: "server ${s}") cfg.servers)}
+    ${cfg.extraConfig}
+  '';
+
+  pidFile = "/run/openntpd.pid";
+
+in
+{
+  ###### interface
+
+  options.services.openntpd = {
+    enable = mkEnableOption "OpenNTP time synchronization server";
+
+    servers = mkOption {
+      default = config.services.ntp.servers;
+      type = types.listOf types.str;
+      inherit (options.services.ntp.servers) description;
+    };
+
+    extraConfig = mkOption {
+      type = with types; lines;
+      default = "";
+      example = ''
+        listen on 127.0.0.1
+        listen on ::1
+      '';
+      description = ''
+        Additional text appended to <filename>openntpd.conf</filename>.
+      '';
+    };
+
+    extraOptions = mkOption {
+      type = with types; separatedString " ";
+      default = "";
+      example = "-s";
+      description = ''
+        Extra options used when launching openntpd.
+      '';
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    meta.maintainers = with lib.maintainers; [ thoughtpolice ];
+    services.timesyncd.enable = mkForce false;
+
+    # Add ntpctl to the environment for status checking
+    environment.systemPackages = [ package ];
+
+    environment.etc."ntpd.conf".text = configFile;
+
+    users.users.ntp = {
+      uid = config.ids.uids.ntp;
+      description = "OpenNTP daemon user";
+      home = "/var/empty";
+    };
+
+    systemd.services.openntpd = {
+      description = "OpenNTP Server";
+      wantedBy = [ "multi-user.target" ];
+      wants = [ "network-online.target" "time-sync.target" ];
+      before = [ "time-sync.target" ];
+      after = [ "dnsmasq.service" "bind.service" "network-online.target" ];
+      serviceConfig = {
+        ExecStart = "${package}/sbin/ntpd -p ${pidFile} ${cfg.extraOptions}";
+        Type = "forking";
+        PIDFile = pidFile;
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/nullidentdmod.nix b/nixpkgs/nixos/modules/services/networking/nullidentdmod.nix
new file mode 100644
index 000000000000..b0d338a27941
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/nullidentdmod.nix
@@ -0,0 +1,34 @@
+{ config, lib, pkgs, ... }: with lib; let
+  cfg = config.services.nullidentdmod;
+
+in {
+  options.services.nullidentdmod = with types; {
+    enable = mkEnableOption "the nullidentdmod identd daemon";
+
+    userid = mkOption {
+      type = nullOr str;
+      description = "User ID to return. Set to null to return a random string each time.";
+      default = null;
+      example = "alice";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.sockets.nullidentdmod = {
+      description = "Socket for identd (NullidentdMod)";
+      listenStreams = [ "113" ];
+      socketConfig.Accept = true;
+      wantedBy = [ "sockets.target" ];
+    };
+
+    systemd.services."nullidentdmod@" = {
+      description = "NullidentdMod service";
+      serviceConfig = {
+        DynamicUser = true;
+        ExecStart = "${pkgs.nullidentdmod}/bin/nullidentdmod${optionalString (cfg.userid != null) " ${cfg.userid}"}";
+        StandardInput = "socket";
+        StandardOutput = "socket";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/nylon.nix b/nixpkgs/nixos/modules/services/networking/nylon.nix
new file mode 100644
index 000000000000..7c171281a926
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/nylon.nix
@@ -0,0 +1,166 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.nylon;
+
+  homeDir = "/var/lib/nylon";
+
+  configFile = cfg: pkgs.writeText "nylon-${cfg.name}.conf" ''
+    [General]
+    No-Simultaneous-Conn=${toString cfg.nrConnections}
+    Log=${if cfg.logging then "1" else "0"}
+    Verbose=${if cfg.verbosity then "1" else "0"}
+
+    [Server]
+    Binding-Interface=${cfg.acceptInterface}
+    Connecting-Interface=${cfg.bindInterface}
+    Port=${toString cfg.port}
+    Allow-IP=${concatStringsSep " " cfg.allowedIPRanges}
+    Deny-IP=${concatStringsSep " " cfg.deniedIPRanges}
+  '';
+
+  nylonOpts = { name, ... }: {
+
+    options = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enables nylon as a running service upon activation.
+        '';
+      };
+
+      name = mkOption {
+        type = types.str;
+        default = "";
+        description = "The name of this nylon instance.";
+      };
+
+      nrConnections = mkOption {
+        type = types.int;
+        default = 10;
+        description = ''
+          The number of allowed simultaneous connections to the daemon, default 10.
+        '';
+      };
+
+      logging = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable logging, default is no logging.
+        '';
+      };
+
+      verbosity = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable verbose output, default is to not be verbose.
+        '';
+      };
+
+      acceptInterface = mkOption {
+        type = types.str;
+        default = "lo";
+        description = ''
+          Tell nylon which interface to listen for client requests on, default is "lo".
+        '';
+      };
+
+      bindInterface = mkOption {
+        type = types.str;
+        default = "enp3s0f0";
+        description = ''
+          Tell nylon which interface to use as an uplink, default is "enp3s0f0".
+        '';
+      };
+
+      port = mkOption {
+        type = types.int;
+        default = 1080;
+        description = ''
+          What port to listen for client requests, default is 1080.
+        '';
+      };
+
+      allowedIPRanges = mkOption {
+        type = with types; listOf str;
+        default = [ "192.168.0.0/16" "127.0.0.1/8" "172.16.0.1/12" "10.0.0.0/8" ];
+        description = ''
+           Allowed client IP ranges are evaluated first, defaults to ARIN IPv4 private ranges:
+             [ "192.168.0.0/16" "127.0.0.0/8" "172.16.0.0/12" "10.0.0.0/8" ]
+        '';
+      };
+
+      deniedIPRanges = mkOption {
+        type = with types; listOf str;
+        default = [ "0.0.0.0/0" ];
+        description = ''
+          Denied client IP ranges, these gets evaluated after the allowed IP ranges, defaults to all IPv4 addresses:
+            [ "0.0.0.0/0" ]
+          To block all other access than the allowed.
+        '';
+      };
+    };
+    config = { name = mkDefault name; };
+  };
+
+  mkNamedNylon = cfg: {
+    "nylon-${cfg.name}" = {
+      description = "Nylon, a lightweight SOCKS proxy server";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig =
+      {
+        User = "nylon";
+        Group = "nylon";
+        WorkingDirectory = homeDir;
+        ExecStart = "${pkgs.nylon}/bin/nylon -f -c ${configFile cfg}";
+      };
+    };
+  };
+
+  anyNylons = collect (p: p ? enable) cfg;
+  enabledNylons = filter (p: p.enable == true) anyNylons;
+  nylonUnits = map (nylon: mkNamedNylon nylon) enabledNylons;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.nylon = mkOption {
+      default = {};
+      description = "Collection of named nylon instances";
+      type = with types; loaOf (submodule nylonOpts);
+      internal = true;
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf (length(enabledNylons) > 0) {
+
+    users.users.nylon = {
+      group = "nylon";
+      description = "Nylon SOCKS Proxy";
+      home = homeDir;
+      createHome = true;
+      uid = config.ids.uids.nylon;
+    };
+
+    users.groups.nylon.gid = config.ids.gids.nylon;
+
+    systemd.services = fold (a: b: a // b) {} nylonUnits;
+
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/ocserv.nix b/nixpkgs/nixos/modules/services/networking/ocserv.nix
new file mode 100644
index 000000000000..dc26ffeafeef
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/ocserv.nix
@@ -0,0 +1,99 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.ocserv;
+
+in
+
+{
+  options.services.ocserv = {
+    enable = mkEnableOption "ocserv";
+
+    config = mkOption {
+      type = types.lines;
+
+      description = ''
+        Configuration content to start an OCServ server.
+
+        For a full configuration reference,please refer to the online documentation
+        (https://ocserv.gitlab.io/www/manual.html), the openconnect
+        recipes (https://github.com/openconnect/recipes) or `man ocserv`.
+      '';
+
+      example = ''
+        # configuration examples from $out/doc without explanatory comments.
+        # for a full reference please look at the installed man pages.
+        auth = "plain[passwd=./sample.passwd]"
+        tcp-port = 443
+        udp-port = 443
+        run-as-user = nobody
+        run-as-group = nogroup
+        socket-file = /run/ocserv-socket
+        server-cert = certs/server-cert.pem
+        server-key = certs/server-key.pem
+        keepalive = 32400
+        dpd = 90
+        mobile-dpd = 1800
+        switch-to-tcp-timeout = 25
+        try-mtu-discovery = false
+        cert-user-oid = 0.9.2342.19200300.100.1.1
+        tls-priorities = "NORMAL:%SERVER_PRECEDENCE:%COMPAT:-VERS-SSL3.0"
+        auth-timeout = 240
+        min-reauth-time = 300
+        max-ban-score = 80
+        ban-reset-time = 1200
+        cookie-timeout = 300
+        deny-roaming = false
+        rekey-time = 172800
+        rekey-method = ssl
+        use-occtl = true
+        pid-file = /run/ocserv.pid
+        device = vpns
+        predictable-ips = true
+        default-domain = example.com
+        ipv4-network = 192.168.1.0
+        ipv4-netmask = 255.255.255.0
+        dns = 192.168.1.2
+        ping-leases = false
+        route = 10.10.10.0/255.255.255.0
+        route = 192.168.0.0/255.255.0.0
+        no-route = 192.168.5.0/255.255.255.0
+        cisco-client-compat = true
+        dtls-legacy = true
+
+        [vhost:www.example.com]
+        auth = "certificate"
+        ca-cert = certs/ca.pem
+        server-cert = certs/server-cert-secp521r1.pem
+        server-key = cersts/certs/server-key-secp521r1.pem
+        ipv4-network = 192.168.2.0
+        ipv4-netmask = 255.255.255.0
+        cert-user-oid = 0.9.2342.19200300.100.1.1
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.ocserv ];
+    environment.etc."ocserv/ocserv.conf".text = cfg.config;
+
+    security.pam.services.ocserv = {};
+
+    systemd.services.ocserv = {
+      description = "OpenConnect SSL VPN server";
+      documentation = [ "man:ocserv(8)" ];
+      after = [ "dbus.service" "network-online.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        PrivateTmp = true;
+        PIDFile = "/run/ocserv.pid";
+        ExecStart = "${pkgs.ocserv}/bin/ocserv --foreground --pid-file /run/ocesrv.pid --config /etc/ocserv/ocserv.conf";
+        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/ofono.nix b/nixpkgs/nixos/modules/services/networking/ofono.nix
new file mode 100644
index 000000000000..40ef9433de0f
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/ofono.nix
@@ -0,0 +1,44 @@
+# Ofono daemon.
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.ofono;
+
+  plugin_path =
+    lib.concatMapStringsSep ":"
+      (plugin: "${plugin}/lib/ofono/plugins")
+      cfg.plugins
+    ;
+
+in
+
+{
+  ###### interface
+  options = {
+    services.ofono = {
+      enable = mkEnableOption "Ofono";
+
+      plugins = mkOption {
+        type = types.listOf types.package;
+        default = [];
+        example = literalExample "[ pkgs.modem-manager-gui ]";
+        description = ''
+          The list of plugins to install.
+        '';
+      };
+    };
+  };
+
+  ###### implementation
+  config = mkIf cfg.enable {
+    services.dbus.packages = [ pkgs.ofono ];
+
+    systemd.packages = [ pkgs.ofono ];
+
+    systemd.services.ofono.environment.OFONO_PLUGIN_PATH = mkIf (cfg.plugins != []) plugin_path;
+
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/oidentd.nix b/nixpkgs/nixos/modules/services/networking/oidentd.nix
new file mode 100644
index 000000000000..feb84806ba99
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/oidentd.nix
@@ -0,0 +1,44 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.oidentd.enable = mkOption {
+      default = false;
+      type = types.bool;
+      description = ''
+        Whether to enable ‘oidentd’, an implementation of the Ident
+        protocol (RFC 1413).  It allows remote systems to identify the
+        name of the user associated with a TCP connection.
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.oidentd.enable {
+    systemd.services.oidentd = {
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig.Type = "forking";
+      script = "${pkgs.oidentd}/sbin/oidentd -u oidentd -g nogroup";
+    };
+
+    users.users.oidentd = {
+      description = "Ident Protocol daemon user";
+      group = "oidentd";
+      uid = config.ids.uids.oidentd;
+    };
+
+    users.groups.oidentd.gid = config.ids.gids.oidentd;
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/openfire.nix b/nixpkgs/nixos/modules/services/networking/openfire.nix
new file mode 100644
index 000000000000..fe0499d52323
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/openfire.nix
@@ -0,0 +1,56 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  ###### interface
+
+  options = {
+
+    services.openfire = {
+
+      enable = mkEnableOption "OpenFire XMPP server";
+
+      usePostgreSQL = mkOption {
+        type = types.bool;
+        default = true;
+        description = "
+          Whether you use PostgreSQL service for your storage back-end.
+        ";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.openfire.enable {
+
+    assertions = singleton
+      { assertion = !(config.services.openfire.usePostgreSQL -> config.services.postgresql.enable);
+        message = "OpenFire configured to use PostgreSQL but services.postgresql.enable is not enabled.";
+      };
+
+    systemd.services.openfire = {
+      description = "OpenFire XMPP server";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "networking.target" ] ++
+        optional config.services.openfire.usePostgreSQL "postgresql.service";
+      path = with pkgs; [ jre openfire coreutils which gnugrep gawk gnused ];
+      script = ''
+        export HOME=/tmp
+        mkdir /var/log/openfire || true
+        mkdir /etc/openfire || true
+        for i in ${pkgs.openfire}/conf.inst/*; do
+            if ! test -f /etc/openfire/$(basename $i); then
+                cp $i /etc/openfire/
+            fi
+        done
+        openfire start
+      ''; # */
+    };
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/openvpn.nix b/nixpkgs/nixos/modules/services/networking/openvpn.nix
new file mode 100644
index 000000000000..dcd7e9e5fa4c
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/openvpn.nix
@@ -0,0 +1,219 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.openvpn;
+
+  inherit (pkgs) openvpn;
+
+  makeOpenVPNJob = cfg: name:
+    let
+
+      path = (getAttr "openvpn-${name}" config.systemd.services).path;
+
+      upScript = ''
+        #! /bin/sh
+        export PATH=${path}
+
+        # For convenience in client scripts, extract the remote domain
+        # name and name server.
+        for var in ''${!foreign_option_*}; do
+          x=(''${!var})
+          if [ "''${x[0]}" = dhcp-option ]; then
+            if [ "''${x[1]}" = DOMAIN ]; then domain="''${x[2]}"
+            elif [ "''${x[1]}" = DNS ]; then nameserver="''${x[2]}"
+            fi
+          fi
+        done
+
+        ${cfg.up}
+        ${optionalString cfg.updateResolvConf
+           "${pkgs.update-resolv-conf}/libexec/openvpn/update-resolv-conf"}
+      '';
+
+      downScript = ''
+        #! /bin/sh
+        export PATH=${path}
+        ${optionalString cfg.updateResolvConf
+           "${pkgs.update-resolv-conf}/libexec/openvpn/update-resolv-conf"}
+        ${cfg.down}
+      '';
+
+      configFile = pkgs.writeText "openvpn-config-${name}"
+        ''
+          errors-to-stderr
+          ${optionalString (cfg.up != "" || cfg.down != "" || cfg.updateResolvConf) "script-security 2"}
+          ${cfg.config}
+          ${optionalString (cfg.up != "" || cfg.updateResolvConf)
+              "up ${pkgs.writeScript "openvpn-${name}-up" upScript}"}
+          ${optionalString (cfg.down != "" || cfg.updateResolvConf)
+              "down ${pkgs.writeScript "openvpn-${name}-down" downScript}"}
+          ${optionalString (cfg.authUserPass != null)
+              "auth-user-pass ${pkgs.writeText "openvpn-credentials-${name}" ''
+                ${cfg.authUserPass.username}
+                ${cfg.authUserPass.password}
+              ''}"}
+        '';
+
+    in {
+      description = "OpenVPN instance ‘${name}’";
+
+      wantedBy = optional cfg.autoStart "multi-user.target";
+      after = [ "network.target" ];
+
+      path = [ pkgs.iptables pkgs.iproute pkgs.nettools ];
+
+      serviceConfig.ExecStart = "@${openvpn}/sbin/openvpn openvpn --suppress-timestamps --config ${configFile}";
+      serviceConfig.Restart = "always";
+      serviceConfig.Type = "notify";
+    };
+
+in
+
+{
+  imports = [
+    (mkRemovedOptionModule [ "services" "openvpn" "enable" ] "")
+  ];
+
+  ###### interface
+
+  options = {
+
+    services.openvpn.servers = mkOption {
+      default = {};
+
+      example = literalExample ''
+        {
+          server = {
+            config = '''
+              # Simplest server configuration: https://community.openvpn.net/openvpn/wiki/StaticKeyMiniHowto
+              # server :
+              dev tun
+              ifconfig 10.8.0.1 10.8.0.2
+              secret /root/static.key
+            ''';
+            up = "ip route add ...";
+            down = "ip route del ...";
+          };
+
+          client = {
+            config = '''
+              client
+              remote vpn.example.org
+              dev tun
+              proto tcp-client
+              port 8080
+              ca /root/.vpn/ca.crt
+              cert /root/.vpn/alice.crt
+              key /root/.vpn/alice.key
+            ''';
+            up = "echo nameserver $nameserver | ''${pkgs.openresolv}/sbin/resolvconf -m 0 -a $dev";
+            down = "''${pkgs.openresolv}/sbin/resolvconf -d $dev";
+          };
+        }
+      '';
+
+      description = ''
+        Each attribute of this option defines a systemd service that
+        runs an OpenVPN instance.  These can be OpenVPN servers or
+        clients.  The name of each systemd service is
+        <literal>openvpn-<replaceable>name</replaceable>.service</literal>,
+        where <replaceable>name</replaceable> is the corresponding
+        attribute name.
+      '';
+
+      type = with types; attrsOf (submodule {
+
+        options = {
+
+          config = mkOption {
+            type = types.lines;
+            description = ''
+              Configuration of this OpenVPN instance.  See
+              <citerefentry><refentrytitle>openvpn</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+              for details.
+
+              To import an external config file, use the following definition:
+              <literal>config = "config /path/to/config.ovpn"</literal>
+            '';
+          };
+
+          up = mkOption {
+            default = "";
+            type = types.lines;
+            description = ''
+              Shell commands executed when the instance is starting.
+            '';
+          };
+
+          down = mkOption {
+            default = "";
+            type = types.lines;
+            description = ''
+              Shell commands executed when the instance is shutting down.
+            '';
+          };
+
+          autoStart = mkOption {
+            default = true;
+            type = types.bool;
+            description = "Whether this OpenVPN instance should be started automatically.";
+          };
+
+          updateResolvConf = mkOption {
+            default = false;
+            type = types.bool;
+            description = ''
+              Use the script from the update-resolv-conf package to automatically
+              update resolv.conf with the DNS information provided by openvpn. The
+              script will be run after the "up" commands and before the "down" commands.
+            '';
+          };
+
+          authUserPass = mkOption {
+            default = null;
+            description = ''
+              This option can be used to store the username / password credentials
+              with the "auth-user-pass" authentication method.
+
+              WARNING: Using this option will put the credentials WORLD-READABLE in the Nix store!
+            '';
+            type = types.nullOr (types.submodule {
+
+              options = {
+                username = mkOption {
+                  description = "The username to store inside the credentials file.";
+                  type = types.str;
+                };
+
+                password = mkOption {
+                  description = "The password to store inside the credentials file.";
+                  type = types.str;
+                };
+              };
+            });
+          };
+        };
+
+      });
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf (cfg.servers != {}) {
+
+    systemd.services = listToAttrs (mapAttrsFlatten (name: value: nameValuePair "openvpn-${name}" (makeOpenVPNJob value name)) cfg.servers);
+
+    environment.systemPackages = [ openvpn ];
+
+    boot.kernelModules = [ "tun" ];
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/ostinato.nix b/nixpkgs/nixos/modules/services/networking/ostinato.nix
new file mode 100644
index 000000000000..5e8cce5b89aa
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/ostinato.nix
@@ -0,0 +1,104 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  pkg = pkgs.ostinato;
+  cfg = config.services.ostinato;
+  configFile = pkgs.writeText "drone.ini" ''
+    [General]
+    RateAccuracy=${cfg.rateAccuracy}
+
+    [RpcServer]
+    Address=${cfg.rpcServer.address}
+
+    [PortList]
+    Include=${concatStringsSep "," cfg.portList.include}
+    Exclude=${concatStringsSep "," cfg.portList.exclude}
+  '';
+
+in
+{
+
+  ###### interface
+
+  options = {
+
+    services.ostinato = {
+
+      enable = mkEnableOption "Ostinato agent-controller (Drone)";
+
+      port = mkOption {
+        type = types.int;
+        default = 7878;
+        description = ''
+          Port to listen on.
+        '';
+      };
+
+      rateAccuracy = mkOption {
+        type = types.enum [ "High" "Low" ];
+        default = "High";
+        description = ''
+          To ensure that the actual transmit rate is as close as possible to
+          the configured transmit rate, Drone runs a busy-wait loop.
+          While this provides the maximum accuracy possible, the CPU
+          utilization is 100% while the transmit is on. You can however,
+          sacrifice the accuracy to reduce the CPU load.
+        '';
+      };
+
+      rpcServer = {
+        address = mkOption {
+          type = types.str;
+          default = "0.0.0.0";
+          description = ''
+            By default, the Drone RPC server will listen on all interfaces and
+            local IPv4 adresses for incoming connections from clients.  Specify
+            a single IPv4 or IPv6 address if you want to restrict that.
+            To listen on any IPv6 address, use ::
+          '';
+        };
+      };
+
+      portList = {
+        include = mkOption {
+          type = types.listOf types.str;
+          default = [];
+          example = ''[ "eth*" "lo*" ]'';
+          description = ''
+            For a port to pass the filter and appear on the port list managed
+            by drone, it be allowed by this include list.
+          '';
+        };
+        exclude = mkOption {
+          type = types.listOf types.str;
+          default = [];
+          example = ''[ "usbmon*" "eth0" ]'';
+          description = ''
+            A list of ports does not appear on the port list managed by drone.
+          '';
+        };
+      };
+
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ pkg ];
+
+    systemd.services.drone = {
+      description = "Ostinato agent-controller";
+      wantedBy = [ "multi-user.target" ];
+      script = ''
+        ${pkg}/bin/drone ${toString cfg.port} ${configFile}
+      '';
+    };
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/owamp.nix b/nixpkgs/nixos/modules/services/networking/owamp.nix
new file mode 100644
index 000000000000..637ed618b893
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/owamp.nix
@@ -0,0 +1,45 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.owamp;
+in
+{
+
+  ###### interface
+
+  options = {
+    services.owamp.enable = mkEnableOption ''Enable OWAMP server'';
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    users.users.owamp = {
+      group = "owamp";
+      description = "Owamp daemon";
+      isSystemUser = true;
+    };
+
+    users.groups.owamp = { };
+
+    systemd.services.owamp = {
+      description = "Owamp server";
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        ExecStart="${pkgs.owamp}/bin/owampd -R /run/owamp -d /run/owamp -v -Z ";
+        PrivateTmp = true;
+        Restart = "always";
+        Type="simple";
+        User = "owamp";
+        Group = "owamp";
+        RuntimeDirectory = "owamp";
+        StateDirectory = "owamp";
+        AmbientCapabilities = "cap_net_bind_service";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/pdns-recursor.nix b/nixpkgs/nixos/modules/services/networking/pdns-recursor.nix
new file mode 100644
index 000000000000..6ff181377fcc
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/pdns-recursor.nix
@@ -0,0 +1,224 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  dataDir  = "/var/lib/pdns-recursor";
+  username = "pdns-recursor";
+
+  cfg = config.services.pdns-recursor;
+
+  oneOrMore  = type: with types; either type (listOf type);
+  valueType  = with types; oneOf [ int str bool path ];
+  configType = with types; attrsOf (nullOr (oneOrMore valueType));
+
+  toBool    = val: if val then "yes" else "no";
+  serialize = val: with types;
+         if str.check       val then val
+    else if int.check       val then toString val
+    else if path.check      val then toString val
+    else if bool.check      val then toBool val
+    else if builtins.isList val then (concatMapStringsSep "," serialize val)
+    else "";
+
+  configFile = pkgs.writeText "recursor.conf"
+    (concatStringsSep "\n"
+      (flip mapAttrsToList cfg.settings
+        (name: val: "${name}=${serialize val}")));
+
+  mkDefaultAttrs = mapAttrs (n: v: mkDefault v);
+
+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;
+      default = {};
+      description = ''
+        DNS zones to be forwarded to other authoritative servers.
+      '';
+    };
+
+    forwardZonesRecurse = mkOption {
+      type = types.attrs;
+      example = { eth = "127.0.0.1:5353"; };
+      default = {};
+      description = ''
+        DNS zones to be forwarded to other recursive 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.
+      '';
+    };
+
+    settings = mkOption {
+      type = configType;
+      default = { };
+      example = literalExample ''
+        {
+          loglevel = 8;
+          log-common-errors = true;
+        }
+      '';
+      description = ''
+        PowerDNS Recursor settings. Use this option to configure Recursor
+        settings not exposed in a NixOS option or to bypass one.
+        See the full documentation at
+        <link xlink:href="https://doc.powerdns.com/recursor/settings.html"/>
+        for the available options.
+      '';
+    };
+
+    luaConfig = mkOption {
+      type = types.lines;
+      default = "";
+      description = ''
+        The content Lua configuration file for PowerDNS Recursor. See
+        <link xlink:href="https://doc.powerdns.com/recursor/lua-config/index.html"/>.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    services.pdns-recursor.settings = mkDefaultAttrs {
+      local-address = cfg.dns.address;
+      local-port    = cfg.dns.port;
+      allow-from    = cfg.dns.allowFrom;
+
+      webserver-address    = cfg.api.address;
+      webserver-port       = cfg.api.port;
+      webserver-allow-from = cfg.api.allowFrom;
+
+      forward-zones         = mapAttrsToList (zone: uri: "${zone}.=${uri}") cfg.forwardZones;
+      forward-zones-recurse = mapAttrsToList (zone: uri: "${zone}.=${uri}") cfg.forwardZonesRecurse;
+      export-etc-hosts = cfg.exportHosts;
+      dnssec           = cfg.dnssecValidation;
+      serve-rfc1918    = cfg.serveRFC1918;
+      lua-config-file  = pkgs.writeText "recursor.lua" cfg.luaConfig;
+
+      log-timestamp  = false;
+      disable-syslog = true;
+    };
+
+    users.users.${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}
+        '';
+      };
+
+      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
+      '';
+    };
+  };
+
+  imports = [
+   (mkRemovedOptionModule [ "services" "pdns-recursor" "extraConfig" ]
+     "To change extra Recursor settings use services.pdns-recursor.settings instead.")
+  ];
+
+  meta.maintainers = with lib.maintainers; [ rnhmjoj ];
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/pdnsd.nix b/nixpkgs/nixos/modules/services/networking/pdnsd.nix
new file mode 100644
index 000000000000..24b5bbc5104e
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/pdnsd.nix
@@ -0,0 +1,91 @@
+{ 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.users.${pdnsdUser} = {
+      uid = config.ids.uids.pdnsd;
+      group = pdnsdGroup;
+      description = "pdnsd user";
+    };
+
+    users.groups.${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/nixpkgs/nixos/modules/services/networking/pixiecore.nix b/nixpkgs/nixos/modules/services/networking/pixiecore.nix
new file mode 100644
index 000000000000..85aa40784af8
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/pixiecore.nix
@@ -0,0 +1,134 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.pixiecore;
+in
+{
+  meta.maintainers = with maintainers; [ bbigras danderson ];
+
+  options = {
+    services.pixiecore = {
+      enable = mkEnableOption "Pixiecore";
+
+      openFirewall = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Open ports (67, 69 UDP and 4011, 'port', 'statusPort' TCP) in the firewall for Pixiecore.
+        '';
+      };
+
+      mode = mkOption {
+        description = "Which mode to use";
+        default = "boot";
+        type = types.enum [ "api" "boot" ];
+      };
+
+      debug = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Log more things that aren't directly related to booting a recognized client";
+      };
+
+      dhcpNoBind = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Handle DHCP traffic without binding to the DHCP server port";
+      };
+
+      kernel = mkOption {
+        type = types.str or types.path;
+        default = "";
+        description = "Kernel path. Ignored unless mode is set to 'boot'";
+      };
+
+      initrd = mkOption {
+        type = types.str or types.path;
+        default = "";
+        description = "Initrd path. Ignored unless mode is set to 'boot'";
+      };
+
+      cmdLine = mkOption {
+        type = types.str;
+        default = "";
+        description = "Kernel commandline arguments. Ignored unless mode is set to 'boot'";
+      };
+
+      listen = mkOption {
+        type = types.str;
+        default = "0.0.0.0";
+        description = "IPv4 address to listen on";
+      };
+
+      port = mkOption {
+        type = types.port;
+        default = 80;
+        description = "Port to listen on for HTTP";
+      };
+
+      statusPort = mkOption {
+        type = types.port;
+        default = 80;
+        description = "HTTP port for status information (can be the same as --port)";
+      };
+
+      apiServer = mkOption {
+        type = types.str;
+        example = "localhost:8080";
+        description = "host:port to connect to the API. Ignored unless mode is set to 'api'";
+      };
+
+      extraArguments = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = "Additional command line arguments to pass to Pixiecore";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.groups.pixiecore = {};
+    users.users.pixiecore = {
+      description = "Pixiecore daemon user";
+      group = "pixiecore";
+    };
+
+    networking.firewall = mkIf cfg.openFirewall {
+      allowedTCPPorts = [ 4011 cfg.port cfg.statusPort ];
+      allowedUDPPorts = [ 67 69 ];
+    };
+
+    systemd.services.pixiecore = {
+      description = "Pixiecore server";
+      after = [ "network.target"];
+      wants = [ "network.target"];
+      wantedBy = [ "multi-user.target"];
+      serviceConfig = {
+        User = "pixiecore";
+        Restart = "always";
+        AmbientCapabilities = [ "cap_net_bind_service" ] ++ optional cfg.dhcpNoBind "cap_net_raw";
+        ExecStart =
+          let
+            argString =
+              if cfg.mode == "boot"
+              then [ "boot" cfg.kernel ]
+                   ++ optional (cfg.initrd != "") cfg.initrd
+                   ++ optionals (cfg.cmdLine != "") [ "--cmdline" cfg.cmdLine ]
+              else [ "api" cfg.apiServer ];
+          in
+            ''
+              ${pkgs.pixiecore}/bin/pixiecore \
+                ${lib.escapeShellArgs argString} \
+                ${optionalString cfg.debug "--debug"} \
+                ${optionalString cfg.dhcpNoBind "--dhcp-no-bind"} \
+                --listen-addr ${lib.escapeShellArg cfg.listen} \
+                --port ${toString cfg.port} \
+                --status-port ${toString cfg.statusPort} \
+                ${escapeShellArgs cfg.extraArguments}
+              '';
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/polipo.nix b/nixpkgs/nixos/modules/services/networking/polipo.nix
new file mode 100644
index 000000000000..1ff9388346b6
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/polipo.nix
@@ -0,0 +1,112 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.polipo;
+
+  polipoConfig = pkgs.writeText "polipo.conf" ''
+    proxyAddress = ${cfg.proxyAddress}
+    proxyPort = ${toString cfg.proxyPort}
+    allowedClients = ${concatStringsSep ", " cfg.allowedClients}
+    ${optionalString (cfg.parentProxy != "") "parentProxy = ${cfg.parentProxy}" }
+    ${optionalString (cfg.socksParentProxy != "") "socksParentProxy = ${cfg.socksParentProxy}" }
+    ${config.services.polipo.extraConfig}
+  '';
+
+in
+
+{
+
+  options = {
+
+    services.polipo = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to run the polipo caching web proxy.";
+      };
+
+      proxyAddress = mkOption {
+        type = types.str;
+        default = "127.0.0.1";
+        description = "IP address on which Polipo will listen.";
+      };
+
+      proxyPort = mkOption {
+        type = types.int;
+        default = 8123;
+        description = "TCP port on which Polipo will listen.";
+      };
+
+      allowedClients = mkOption {
+        type = types.listOf types.str;
+        default = [ "127.0.0.1" "::1" ];
+        example = [ "127.0.0.1" "::1" "134.157.168.0/24" "2001:660:116::/48" ];
+        description = ''
+          List of IP addresses or network addresses that may connect to Polipo.
+        '';
+      };
+
+      parentProxy = mkOption {
+        type = types.str;
+        default = "";
+        example = "localhost:8124";
+        description = ''
+          Hostname and port number of an HTTP parent proxy;
+          it should have the form ‘host:port’.
+        '';
+      };
+
+      socksParentProxy = mkOption {
+        type = types.str;
+        default = "";
+        example = "localhost:9050";
+        description = ''
+          Hostname and port number of an SOCKS parent proxy;
+          it should have the form ‘host:port’.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Polio configuration. Contents will be added
+          verbatim to the configuration file.
+        '';
+      };
+
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    users.users.polipo =
+      { uid = config.ids.uids.polipo;
+        description = "Polipo caching proxy user";
+        home = "/var/cache/polipo";
+        createHome = true;
+      };
+
+    users.groups.polipo =
+      { gid = config.ids.gids.polipo;
+        members = [ "polipo" ];
+      };
+
+    systemd.services.polipo = {
+      description = "caching web proxy";
+      after = [ "network.target" "nss-lookup.target" ];
+      wantedBy = [ "multi-user.target"];
+      serviceConfig = {
+        ExecStart  = "${pkgs.polipo}/bin/polipo -c ${polipoConfig}";
+        User = "polipo";
+      };
+    };
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/powerdns.nix b/nixpkgs/nixos/modules/services/networking/powerdns.nix
new file mode 100644
index 000000000000..ba05e15389f6
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/powerdns.nix
@@ -0,0 +1,49 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.powerdns;
+  configDir = pkgs.writeTextDir "pdns.conf" "${cfg.extraConfig}";
+in {
+  options = {
+    services.powerdns = {
+      enable = mkEnableOption "Powerdns domain name server";
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "launch=bind";
+        description = ''
+          Extra lines to be added verbatim to pdns.conf.
+          Powerdns will chroot to /var/lib/powerdns.
+          So any file, powerdns is supposed to be read,
+          should be in /var/lib/powerdns and needs to specified
+          relative to the chroot.
+        '';
+      };
+    };
+  };
+
+  config = mkIf config.services.powerdns.enable {
+    systemd.services.pdns = {
+      unitConfig.Documentation = "man:pdns_server(1) man:pdns_control(1)";
+      description = "Powerdns name server";
+      wantedBy = [ "multi-user.target" ];
+      after = ["network.target" "mysql.service" "postgresql.service" "openldap.service"];
+
+      serviceConfig = {
+        Restart="on-failure";
+        RestartSec="1";
+        StartLimitInterval="0";
+        PrivateDevices=true;
+        CapabilityBoundingSet="CAP_CHOWN CAP_NET_BIND_SERVICE CAP_SETGID CAP_SETUID CAP_SYS_CHROOT";
+        NoNewPrivileges=true;
+        ExecStartPre = "${pkgs.coreutils}/bin/mkdir -p /var/lib/powerdns";
+        ExecStart = "${pkgs.powerdns}/bin/pdns_server --setuid=nobody --setgid=nogroup --chroot=/var/lib/powerdns --socket-dir=/ --daemon=no --guardian=no --disable-syslog --write-pid=no --config-dir=${configDir}";
+        ProtectSystem="full";
+        ProtectHome=true;
+        RestrictAddressFamilies="AF_UNIX AF_INET AF_INET6";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/pppd.nix b/nixpkgs/nixos/modules/services/networking/pppd.nix
new file mode 100644
index 000000000000..c1cbdb461765
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/pppd.nix
@@ -0,0 +1,136 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.pppd;
+in
+{
+  meta = {
+    maintainers = with maintainers; [ danderson ];
+  };
+
+  options = {
+    services.pppd = {
+      enable = mkEnableOption "pppd";
+
+      package = mkOption {
+        default = pkgs.ppp;
+        defaultText = "pkgs.ppp";
+        type = types.package;
+        description = "pppd package to use.";
+      };
+
+      peers = mkOption {
+        default = {};
+        description = "pppd peers.";
+        type = types.attrsOf (types.submodule (
+          { name, ... }:
+          {
+            options = {
+              name = mkOption {
+                type = types.str;
+                default = name;
+                example = "dialup";
+                description = "Name of the PPP peer.";
+              };
+
+              enable = mkOption {
+                type = types.bool;
+                default = true;
+                example = false;
+                description = "Whether to enable this PPP peer.";
+              };
+
+              autostart = mkOption {
+                type = types.bool;
+                default = true;
+                example = false;
+                description = "Whether the PPP session is automatically started at boot time.";
+              };
+
+              config = mkOption {
+                type = types.lines;
+                default = "";
+                description = "pppd configuration for this peer, see the pppd(8) man page.";
+              };
+            };
+          }));
+      };
+    };
+  };
+
+  config = let
+    enabledConfigs = filter (f: f.enable) (attrValues cfg.peers);
+
+    mkEtc = peerCfg: {
+      name = "ppp/peers/${peerCfg.name}";
+      value.text = peerCfg.config;
+    };
+
+    mkSystemd = peerCfg: {
+      name = "pppd-${peerCfg.name}";
+      value = {
+        restartTriggers = [ config.environment.etc."ppp/peers/${peerCfg.name}".source ];
+        before = [ "network.target" ];
+        wants = [ "network.target" ];
+        after = [ "network-pre.target" ];
+        environment = {
+          # pppd likes to write directly into /var/run. This is rude
+          # on a modern system, so we use libredirect to transparently
+          # move those files into /run/pppd.
+          LD_PRELOAD = "${pkgs.libredirect}/lib/libredirect.so";
+          NIX_REDIRECTS = "/var/run=/run/pppd";
+        };
+        serviceConfig = {
+          ExecStart = "${getBin cfg.package}/sbin/pppd call ${peerCfg.name} nodetach nolog";
+          Restart = "always";
+          RestartSec = 5;
+
+          AmbientCapabilities = "CAP_SYS_TTY_CONFIG CAP_NET_ADMIN CAP_NET_RAW CAP_SYS_ADMIN";
+          CapabilityBoundingSet = "CAP_SYS_TTY_CONFIG CAP_NET_ADMIN CAP_NET_RAW CAP_SYS_ADMIN";
+          KeyringMode = "private";
+          LockPersonality = true;
+          MemoryDenyWriteExecute = true;
+          NoNewPrivileges = true;
+          PrivateMounts = true;
+          PrivateTmp = true;
+          ProtectControlGroups = true;
+          ProtectHome = true;
+          ProtectHostname = true;
+          ProtectKernelModules = true;
+          # pppd can be configured to tweak kernel settings.
+          ProtectKernelTunables = false;
+          ProtectSystem = "strict";
+          RemoveIPC = true;
+          RestrictAddressFamilies = "AF_PACKET AF_UNIX AF_PPPOX AF_ATMPVC AF_ATMSVC AF_INET AF_INET6 AF_IPX";
+          RestrictNamespaces = true;
+          RestrictRealtime = true;
+          RestrictSUIDSGID = true;
+          SecureBits = "no-setuid-fixup-locked noroot-locked";
+          SystemCallFilter = "@system-service";
+          SystemCallArchitectures = "native";
+
+          # All pppd instances on a system must share a runtime
+          # directory in order for PPP multilink to work correctly. So
+          # we give all instances the same /run/pppd directory to store
+          # things in.
+          #
+          # For the same reason, we can't set PrivateUsers=true, because
+          # all instances need to run as the same user to access the
+          # multilink database.
+          RuntimeDirectory = "pppd";
+          RuntimeDirectoryPreserve = true;
+        };
+        wantedBy = mkIf peerCfg.autostart [ "multi-user.target" ];
+      };
+    };
+
+    etcFiles = listToAttrs (map mkEtc enabledConfigs);
+    systemdConfigs = listToAttrs (map mkSystemd enabledConfigs);
+
+  in mkIf cfg.enable {
+    environment.etc = etcFiles;
+    systemd.services = systemdConfigs;
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/pptpd.nix b/nixpkgs/nixos/modules/services/networking/pptpd.nix
new file mode 100644
index 000000000000..3e7753b9dd35
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/pptpd.nix
@@ -0,0 +1,124 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+{
+  options = {
+    services.pptpd = {
+      enable = mkEnableOption "pptpd, the Point-to-Point Tunneling Protocol daemon";
+
+      serverIp = mkOption {
+        type        = types.str;
+        description = "The server-side IP address.";
+        default     = "10.124.124.1";
+      };
+
+      clientIpRange = mkOption {
+        type        = types.str;
+        description = "The range from which client IPs are drawn.";
+        default     = "10.124.124.2-11";
+      };
+
+      maxClients = mkOption {
+        type        = types.int;
+        description = "The maximum number of simultaneous connections.";
+        default     = 10;
+      };
+
+      extraPptpdOptions = mkOption {
+        type        = types.lines;
+        description = "Adds extra lines to the pptpd configuration file.";
+        default     = "";
+      };
+
+      extraPppdOptions = mkOption {
+        type        = types.lines;
+        description = "Adds extra lines to the pppd options file.";
+        default     = "";
+        example     = ''
+          ms-dns 8.8.8.8
+          ms-dns 8.8.4.4
+        '';
+      };
+    };
+  };
+
+  config = mkIf config.services.pptpd.enable {
+    systemd.services.pptpd = let
+      cfg = config.services.pptpd;
+
+      pptpd-conf = pkgs.writeText "pptpd.conf" ''
+        # Inspired from pptpd-1.4.0/samples/pptpd.conf
+        ppp ${ppp-pptpd-wrapped}/bin/pppd
+        option ${pppd-options}
+        pidfile /run/pptpd.pid
+        localip ${cfg.serverIp}
+        remoteip ${cfg.clientIpRange}
+        connections ${toString cfg.maxClients} # (Will get harmless warning if inconsistent with IP range)
+
+        # Extra
+        ${cfg.extraPptpdOptions}
+      '';
+
+      pppd-options = pkgs.writeText "ppp-options-pptpd.conf" ''
+        # From: cat pptpd-1.4.0/samples/options.pptpd | grep -v ^# | grep -v ^$
+        name pptpd
+        refuse-pap
+        refuse-chap
+        refuse-mschap
+        require-mschap-v2
+        require-mppe-128
+        proxyarp
+        lock
+        nobsdcomp
+        novj
+        novjccomp
+        nologfd
+
+        # Extra:
+        ${cfg.extraPppdOptions}
+      '';
+
+      ppp-pptpd-wrapped = pkgs.stdenv.mkDerivation {
+        name         = "ppp-pptpd-wrapped";
+        phases       = [ "installPhase" ];
+        buildInputs  = with pkgs; [ makeWrapper ];
+        installPhase = ''
+          mkdir -p $out/bin
+          makeWrapper ${pkgs.ppp}/bin/pppd $out/bin/pppd \
+            --set LD_PRELOAD    "${pkgs.libredirect}/lib/libredirect.so" \
+            --set NIX_REDIRECTS "/etc/ppp=/etc/ppp-pptpd"
+        '';
+      };
+    in {
+      description = "pptpd server";
+
+      requires = [ "network-online.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      preStart = ''
+        mkdir -p -m 700 /etc/ppp-pptpd
+
+        secrets="/etc/ppp-pptpd/chap-secrets"
+
+        [ -f "$secrets" ] || cat > "$secrets" << EOF
+        # From: pptpd-1.4.0/samples/chap-secrets
+        # Secrets for authentication using CHAP
+        # client	server	secret		IP addresses
+        #username	pptpd	password	*
+        EOF
+
+        chown root.root "$secrets"
+        chmod 600 "$secrets"
+      '';
+
+      serviceConfig = {
+        ExecStart = "${pkgs.pptpd}/bin/pptpd --conf ${pptpd-conf}";
+        KillMode  = "process";
+        Restart   = "on-success";
+        Type      = "forking";
+        PIDFile   = "/run/pptpd.pid";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/prayer.nix b/nixpkgs/nixos/modules/services/networking/prayer.nix
new file mode 100644
index 000000000000..f04dac01d9b8
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/prayer.nix
@@ -0,0 +1,89 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  inherit (pkgs) prayer;
+
+  cfg = config.services.prayer;
+
+  stateDir = "/var/lib/prayer";
+
+  prayerUser = "prayer";
+  prayerGroup = "prayer";
+
+  prayerExtraCfg = pkgs.writeText "extraprayer.cf" ''
+    prefix = "${prayer}"
+    var_prefix = "${stateDir}"
+    prayer_user = "${prayerUser}"
+    prayer_group = "${prayerGroup}"
+    sendmail_path = "/run/wrappers/bin/sendmail"
+
+    use_http_port ${cfg.port}
+
+    ${cfg.extraConfig}
+  '';
+
+  prayerCfg = pkgs.runCommand "prayer.cf" { preferLocalBuild = true; } ''
+    # We have to remove the http_port 80, or it will start a server there
+    cat ${prayer}/etc/prayer.cf | grep -v http_port > $out
+    cat ${prayerExtraCfg} >> $out
+  '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.prayer = {
+
+      enable = mkEnableOption "the prayer webmail http server";
+
+      port = mkOption {
+        default = "2080";
+        description = ''
+          Port the prayer http server is listening to.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "" ;
+        description = ''
+          Extra configuration. Contents will be added verbatim to the configuration file.
+        '';
+      };
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.prayer.enable {
+    environment.systemPackages = [ prayer ];
+
+    users.users.${prayerUser} =
+      { uid = config.ids.uids.prayer;
+        description = "Prayer daemon user";
+        home = stateDir;
+      };
+
+    users.groups.${prayerGroup} =
+      { gid = config.ids.gids.prayer; };
+
+    systemd.services.prayer = {
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig.Type = "forking";
+      preStart = ''
+        mkdir -m 0755 -p ${stateDir}
+        chown ${prayerUser}.${prayerGroup} ${stateDir}
+      '';
+      script = "${prayer}/sbin/prayer --config-file=${prayerCfg}";
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/privoxy.nix b/nixpkgs/nixos/modules/services/networking/privoxy.nix
new file mode 100644
index 000000000000..1f41c720adf5
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/privoxy.nix
@@ -0,0 +1,114 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  inherit (pkgs) privoxy;
+
+  cfg = config.services.privoxy;
+
+  confFile = pkgs.writeText "privoxy.conf" ''
+    user-manual ${privoxy}/share/doc/privoxy/user-manual
+    confdir ${privoxy}/etc/
+    listen-address  ${cfg.listenAddress}
+    enable-edit-actions ${if (cfg.enableEditActions == true) then "1" else "0"}
+    ${concatMapStrings (f: "actionsfile ${f}\n") cfg.actionsFiles}
+    ${concatMapStrings (f: "filterfile ${f}\n") cfg.filterFiles}
+    ${cfg.extraConfig}
+  '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.privoxy = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable the Privoxy non-caching filtering proxy.
+        '';
+      };
+
+      listenAddress = mkOption {
+        type = types.str;
+        default = "127.0.0.1:8118";
+        description = ''
+          Address the proxy server is listening to.
+        '';
+      };
+
+      actionsFiles = mkOption {
+        type = types.listOf types.str;
+        example = [ "match-all.action" "default.action" "/etc/privoxy/user.action" ];
+        default = [ "match-all.action" "default.action" ];
+        description = ''
+          List of paths to Privoxy action files.
+          These paths may either be absolute or relative to the privoxy configuration directory.
+        '';
+      };
+
+      filterFiles = mkOption {
+        type = types.listOf types.str;
+        example = [ "default.filter" "/etc/privoxy/user.filter" ];
+        default = [ "default.filter" ];
+        description = ''
+          List of paths to Privoxy filter files.
+          These paths may either be absolute or relative to the privoxy configuration directory.
+        '';
+      };
+
+      enableEditActions = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether or not the web-based actions file editor may be used.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "" ;
+        description = ''
+          Extra configuration. Contents will be added verbatim to the configuration file.
+        '';
+      };
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    users.users.privoxy = {
+      isSystemUser = true;
+      home = "/var/empty";
+      group = "privoxy";
+    };
+
+    users.groups.privoxy = {};
+
+    systemd.services.privoxy = {
+      description = "Filtering web proxy";
+      after = [ "network.target" "nss-lookup.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig.ExecStart = "${privoxy}/bin/privoxy --no-daemon --user privoxy ${confFile}";
+
+      serviceConfig.PrivateDevices = true;
+      serviceConfig.PrivateTmp = true;
+      serviceConfig.ProtectHome = true;
+      serviceConfig.ProtectSystem = "full";
+    };
+
+  };
+
+  meta.maintainers = with lib.maintainers; [ rnhmjoj ];
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/prosody.nix b/nixpkgs/nixos/modules/services/networking/prosody.nix
new file mode 100644
index 000000000000..cdd341c9fb62
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/prosody.nix
@@ -0,0 +1,882 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.services.prosody;
+
+  sslOpts = { ... }: {
+
+    options = {
+
+      key = mkOption {
+        type = types.path;
+        description = "Path to the key file.";
+      };
+
+      # TODO: rename to certificate to match the prosody config
+      cert = mkOption {
+        type = types.path;
+        description = "Path to the certificate file.";
+      };
+
+      extraOptions = mkOption {
+        type = types.attrs;
+        default = {};
+        description = "Extra SSL configuration options.";
+      };
+
+    };
+  };
+
+  discoOpts = {
+    options = {
+      url = mkOption {
+        type = types.str;
+        description = "URL of the endpoint you want to make discoverable";
+      };
+      description = mkOption {
+        type = types.str;
+        description = "A short description of the endpoint you want to advertise";
+      };
+    };
+  };
+
+  moduleOpts = {
+    # Required for compliance with https://compliance.conversations.im/about/
+    roster = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Allow users to have a roster";
+    };
+
+    saslauth = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Authentication for clients and servers. Recommended if you want to log in.";
+    };
+
+    tls = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Add support for secure TLS on c2s/s2s connections";
+    };
+
+    dialback = mkOption {
+      type = types.bool;
+      default = true;
+      description = "s2s dialback support";
+    };
+
+    disco = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Service discovery";
+    };
+
+    # Not essential, but recommended
+    carbons = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Keep multiple clients in sync";
+    };
+
+    csi = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Implements the CSI protocol that allows clients to report their active/inactive state to the server";
+    };
+
+    cloud_notify = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Push notifications to inform users of new messages or other pertinent information even when they have no XMPP clients online";
+    };
+
+    pep = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Enables users to publish their mood, activity, playing music and more";
+    };
+
+    private = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Private XML storage (for room bookmarks, etc.)";
+    };
+
+    blocklist = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Allow users to block communications with other users";
+    };
+
+    vcard = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Allow users to set vCards";
+    };
+
+    vcard_legacy = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Converts users profiles and Avatars between old and new formats";
+    };
+
+    bookmarks = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Allows interop between older clients that use XEP-0048: Bookmarks in its 1.0 version and recent clients which use it in PEP";
+    };
+
+    # Nice to have
+    version = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Replies to server version requests";
+    };
+
+    uptime = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Report how long server has been running";
+    };
+
+    time = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Let others know the time here on this server";
+    };
+
+    ping = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Replies to XMPP pings with pongs";
+    };
+
+    register = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Allow users to register on this server using a client and change passwords";
+    };
+
+    mam = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Store messages in an archive and allow users to access it";
+    };
+
+    smacks = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Allow a client to resume a disconnected session, and prevent message loss";
+    };
+
+    # Admin interfaces
+    admin_adhoc = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Allows administration via an XMPP client that supports ad-hoc commands";
+    };
+
+    http_files = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Serve static files from a directory over HTTP";
+    };
+
+    proxy65 = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Enables a file transfer proxy service which clients behind NAT can use";
+    };
+
+    admin_telnet = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Opens telnet console interface on localhost port 5582";
+    };
+
+    # HTTP modules
+    bosh = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Enable BOSH clients, aka 'Jabber over HTTP'";
+    };
+
+    websocket = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Enable WebSocket support";
+    };
+
+    # Other specific functionality
+    limits = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Enable bandwidth limiting for XMPP connections";
+    };
+
+    groups = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Shared roster support";
+    };
+
+    server_contact_info = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Publish contact information for this service";
+    };
+
+    announce = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Send announcement to all online users";
+    };
+
+    welcome = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Welcome users who register accounts";
+    };
+
+    watchregistrations = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Alert admins of registrations";
+    };
+
+    motd = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Send a message to users when they log in";
+    };
+
+    legacyauth = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Legacy authentication. Only used by some old clients and bots";
+    };
+  };
+
+  toLua = x:
+    if builtins.isString x then ''"${x}"''
+    else if builtins.isBool x then (if x == true then "true" else "false")
+    else if builtins.isInt x then toString x
+    else if builtins.isList x then ''{ ${lib.concatStringsSep ", " (map (n: toLua n) x) } }''
+    else throw "Invalid Lua value";
+
+  createSSLOptsStr = o: ''
+    ssl = {
+      cafile = "/etc/ssl/certs/ca-bundle.crt";
+      key = "${o.key}";
+      certificate = "${o.cert}";
+      ${concatStringsSep "\n" (mapAttrsToList (name: value: "${name} = ${toLua value};") o.extraOptions)}
+    };
+  '';
+
+  mucOpts = { ... }: {
+    options = {
+      domain = mkOption {
+        type = types.str;
+        description = "Domain name of the MUC";
+      };
+      name = mkOption {
+        type = types.str;
+        description = "The name to return in service discovery responses for the MUC service itself";
+        default = "Prosody Chatrooms";
+      };
+      restrictRoomCreation = mkOption {
+        type = types.enum [ true false "admin" "local" ];
+        default = false;
+        description = "Restrict room creation to server admins";
+      };
+      maxHistoryMessages = mkOption {
+        type = types.int;
+        default = 20;
+        description = "Specifies a limit on what each room can be configured to keep";
+      };
+      roomLocking = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Enables room locking, which means that a room must be
+          configured before it can be used. Locked rooms are invisible
+          and cannot be entered by anyone but the creator
+        '';
+      };
+      roomLockTimeout = mkOption {
+        type = types.int;
+        default = 300;
+        description = ''
+          Timout after which the room is destroyed or unlocked if not
+          configured, in seconds
+       '';
+      };
+      tombstones = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          When a room is destroyed, it leaves behind a tombstone which
+          prevents the room being entered or recreated. It also allows
+          anyone who was not in the room at the time it was destroyed
+          to learn about it, and to update their bookmarks. Tombstones
+          prevents the case where someone could recreate a previously
+          semi-anonymous room in order to learn the real JIDs of those
+          who often join there.
+        '';
+      };
+      tombstoneExpiry = mkOption {
+        type = types.int;
+        default = 2678400;
+        description = ''
+          This settings controls how long a tombstone is considered
+          valid. It defaults to 31 days. After this time, the room in
+          question can be created again.
+        '';
+      };
+
+      vcard_muc = mkOption {
+        type = types.bool;
+        default = true;
+      description = "Adds the ability to set vCard for Multi User Chat rooms";
+      };
+
+      # Extra parameters. Defaulting to prosody default values.
+      # Adding them explicitly to make them visible from the options
+      # documentation.
+      #
+      # See https://prosody.im/doc/modules/mod_muc for more details.
+      roomDefaultPublic = mkOption {
+        type = types.bool;
+        default = true;
+        description = "If set, the MUC rooms will be public by default.";
+      };
+      roomDefaultMembersOnly = mkOption {
+        type = types.bool;
+        default = false;
+        description = "If set, the MUC rooms will only be accessible to the members by default.";
+      };
+      roomDefaultModerated = mkOption {
+        type = types.bool;
+        default = false;
+        description = "If set, the MUC rooms will be moderated by default.";
+      };
+      roomDefaultPublicJids = mkOption {
+        type = types.bool;
+        default = false;
+        description = "If set, the MUC rooms will display the public JIDs by default.";
+      };
+      roomDefaultChangeSubject = mkOption {
+        type = types.bool;
+        default = false;
+        description = "If set, the rooms will display the public JIDs by default.";
+      };
+      roomDefaultHistoryLength = mkOption {
+        type = types.int;
+        default = 20;
+        description = "Number of history message sent to participants by default.";
+      };
+      roomDefaultLanguage = mkOption {
+        type = types.str;
+        default = "en";
+        description = "Default room language.";
+      };
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = "Additional MUC specific configuration";
+      };
+    };
+  };
+
+  uploadHttpOpts = { ... }: {
+    options = {
+      domain = mkOption {
+        type = types.nullOr types.str;
+        description = "Domain name for the http-upload service";
+      };
+      uploadFileSizeLimit = mkOption {
+        type = types.str;
+        default = "50 * 1024 * 1024";
+        description = "Maximum file size, in bytes. Defaults to 50MB.";
+      };
+      uploadExpireAfter = mkOption {
+        type = types.str;
+        default = "60 * 60 * 24 * 7";
+        description = "Max age of a file before it gets deleted, in seconds.";
+      };
+      userQuota = mkOption {
+        type = types.nullOr types.int;
+        default = null;
+        example = 1234;
+        description = ''
+          Maximum size of all uploaded files per user, in bytes. There
+          will be no quota if this option is set to null.
+        '';
+      };
+      httpUploadPath = mkOption {
+        type = types.str;
+        description = ''
+          Directory where the uploaded files will be stored. By
+          default, uploaded files are put in a sub-directory of the
+          default Prosody storage path (usually /var/lib/prosody).
+        '';
+        default = "/var/lib/prosody";
+      };
+    };
+  };
+
+  vHostOpts = { ... }: {
+
+    options = {
+
+      # TODO: require attribute
+      domain = mkOption {
+        type = types.str;
+        description = "Domain name";
+      };
+
+      enabled = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to enable the virtual host";
+      };
+
+      ssl = mkOption {
+        type = types.nullOr (types.submodule sslOpts);
+        default = null;
+        description = "Paths to SSL files";
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = "Additional virtual host specific configuration";
+      };
+
+    };
+
+  };
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.prosody = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to enable the prosody server";
+      };
+
+      xmppComplianceSuite = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          The XEP-0423 defines a set of recommended XEPs to implement
+          for a server. It's generally a good idea to implement this
+          set of extensions if you want to provide your users with a
+          good XMPP experience.
+
+          This NixOS module aims to provide a "advanced server"
+          experience as per defined in the XEP-0423[1] specification.
+
+          Setting this option to true will prevent you from building a
+          NixOS configuration which won't comply with this standard.
+          You can explicitely decide to ignore this standard if you
+          know what you are doing by setting this option to false.
+
+          [1] https://xmpp.org/extensions/xep-0423.html
+        '';
+      };
+
+      package = mkOption {
+        type = types.package;
+        description = "Prosody package to use";
+        default = pkgs.prosody;
+        defaultText = "pkgs.prosody";
+        example = literalExample ''
+          pkgs.prosody.override {
+            withExtraLibs = [ pkgs.luaPackages.lpty ];
+            withCommunityModules = [ "auth_external" ];
+          };
+        '';
+      };
+
+      dataDir = mkOption {
+        type = types.path;
+        description = "Directory where Prosody stores its data";
+        default = "/var/lib/prosody";
+      };
+
+      disco_items = mkOption {
+        type = types.listOf (types.submodule discoOpts);
+        default = [];
+        description = "List of discoverable items you want to advertise.";
+      };
+
+      user = mkOption {
+        type = types.str;
+        default = "prosody";
+        description = "User account under which prosody runs.";
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "prosody";
+        description = "Group account under which prosody runs.";
+      };
+
+      allowRegistration = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Allow account creation";
+      };
+
+      # HTTP server-related options
+      httpPorts = mkOption {
+        type = types.listOf types.int;
+        description = "Listening HTTP ports list for this service.";
+        default = [ 5280 ];
+      };
+
+      httpInterfaces = mkOption {
+        type = types.listOf types.str;
+        default = [ "*" "::" ];
+        description = "Interfaces on which the HTTP server will listen on.";
+      };
+
+      httpsPorts = mkOption {
+        type = types.listOf types.int;
+        description = "Listening HTTPS ports list for this service.";
+        default = [ 5281 ];
+      };
+
+      httpsInterfaces = mkOption {
+        type = types.listOf types.str;
+        default = [ "*" "::" ];
+        description = "Interfaces on which the HTTPS server will listen on.";
+      };
+
+      c2sRequireEncryption = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Force clients to use encrypted connections? This option will
+          prevent clients from authenticating unless they are using encryption.
+        '';
+      };
+
+      s2sRequireEncryption = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Force servers to use encrypted connections? This option will
+          prevent servers from authenticating unless they are using encryption.
+          Note that this is different from authentication.
+        '';
+      };
+
+      s2sSecureAuth = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Force certificate authentication for server-to-server connections?
+          This provides ideal security, but requires servers you communicate
+          with to support encryption AND present valid, trusted certificates.
+          For more information see https://prosody.im/doc/s2s#security
+        '';
+      };
+
+      s2sInsecureDomains = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "insecure.example.com" ];
+        description = ''
+          Some servers have invalid or self-signed certificates. You can list
+          remote domains here that will not be required to authenticate using
+          certificates. They will be authenticated using DNS instead, even
+          when s2s_secure_auth is enabled.
+        '';
+      };
+
+      s2sSecureDomains = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "jabber.org" ];
+        description = ''
+          Even if you leave s2s_secure_auth disabled, you can still require valid
+          certificates for some domains by specifying a list here.
+        '';
+      };
+
+
+      modules = moduleOpts;
+
+      extraModules = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = "Enable custom modules";
+      };
+
+      extraPluginPaths = mkOption {
+        type = types.listOf types.path;
+        default = [];
+        description = "Addtional path in which to look find plugins/modules";
+      };
+
+      uploadHttp = mkOption {
+        description = ''
+          Configures the Prosody builtin HTTP server to handle user uploads.
+        '';
+        type = types.nullOr (types.submodule uploadHttpOpts);
+        default = null;
+        example = {
+          domain = "uploads.my-xmpp-example-host.org";
+        };
+      };
+
+      muc = mkOption {
+        type = types.listOf (types.submodule mucOpts);
+        default = [ ];
+        example = [ {
+          domain = "conference.my-xmpp-example-host.org";
+        } ];
+        description = "Multi User Chat (MUC) configuration";
+      };
+
+      virtualHosts = mkOption {
+
+        description = "Define the virtual hosts";
+
+        type = with types; loaOf (submodule vHostOpts);
+
+        example = {
+          myhost = {
+            domain = "my-xmpp-example-host.org";
+            enabled = true;
+          };
+        };
+
+        default = {
+          localhost = {
+            domain = "localhost";
+            enabled = true;
+          };
+        };
+
+      };
+
+      ssl = mkOption {
+        type = types.nullOr (types.submodule sslOpts);
+        default = null;
+        description = "Paths to SSL files";
+      };
+
+      admins = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "admin1@example.com" "admin2@example.com" ];
+        description = "List of administrators of the current host";
+      };
+
+      authentication = mkOption {
+        type = types.enum [ "internal_plain" "internal_hashed" "cyrus" "anonymous" ];
+        default = "internal_hashed";
+        example = "internal_plain";
+        description = "Authentication mechanism used for logins.";
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = "Additional prosody configuration";
+      };
+
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    assertions = let
+      genericErrMsg = ''
+
+          Having a server not XEP-0423-compliant might make your XMPP
+          experience terrible. See the NixOS manual for further
+          informations.
+
+          If you know what you're doing, you can disable this warning by
+          setting config.services.prosody.xmppComplianceSuite to false.
+      '';
+      errors = [
+        { assertion = (builtins.length cfg.muc > 0) || !cfg.xmppComplianceSuite;
+          message = ''
+            You need to setup at least a MUC domain to comply with
+            XEP-0423.
+          '' + genericErrMsg;}
+        { assertion = cfg.uploadHttp != null || !cfg.xmppComplianceSuite;
+          message = ''
+            You need to setup the uploadHttp module through
+            config.services.prosody.uploadHttp to comply with
+            XEP-0423.
+          '' + genericErrMsg;}
+      ];
+    in errors;
+
+    environment.systemPackages = [ cfg.package ];
+
+    environment.etc."prosody/prosody.cfg.lua".text =
+      let
+        httpDiscoItems = if (cfg.uploadHttp != null)
+            then [{ url = cfg.uploadHttp.domain; description = "HTTP upload endpoint";}]
+            else [];
+        mucDiscoItems = builtins.foldl'
+            (acc: muc: [{ url = muc.domain; description = "${muc.domain} MUC endpoint";}] ++ acc)
+            []
+            cfg.muc;
+        discoItems = cfg.disco_items ++ httpDiscoItems ++ mucDiscoItems;
+      in ''
+
+      pidfile = "/run/prosody/prosody.pid"
+
+      log = "*syslog"
+
+      data_path = "${cfg.dataDir}"
+      plugin_paths = {
+        ${lib.concatStringsSep ", " (map (n: "\"${n}\"") cfg.extraPluginPaths) }
+      }
+
+      ${ optionalString  (cfg.ssl != null) (createSSLOptsStr cfg.ssl) }
+
+      admins = ${toLua cfg.admins}
+
+      -- we already build with libevent, so we can just enable it for a more performant server
+      use_libevent = true
+
+      modules_enabled = {
+
+        ${ lib.concatStringsSep "\n  " (lib.mapAttrsToList
+          (name: val: optionalString val "${toLua name};")
+        cfg.modules) }
+        ${ lib.concatStringsSep "\n" (map (x: "${toLua x};") cfg.package.communityModules)}
+        ${ lib.concatStringsSep "\n" (map (x: "${toLua x};") cfg.extraModules)}
+      };
+
+      disco_items = {
+      ${ lib.concatStringsSep "\n" (builtins.map (x: ''{ "${x.url}", "${x.description}"};'') discoItems)} 
+      };
+
+      allow_registration = ${toLua cfg.allowRegistration}
+
+      c2s_require_encryption = ${toLua cfg.c2sRequireEncryption}
+
+      s2s_require_encryption = ${toLua cfg.s2sRequireEncryption}
+
+      s2s_secure_auth = ${toLua cfg.s2sSecureAuth}
+
+      s2s_insecure_domains = ${toLua cfg.s2sInsecureDomains}
+
+      s2s_secure_domains = ${toLua cfg.s2sSecureDomains}
+
+      authentication = ${toLua cfg.authentication}
+
+      http_interfaces = ${toLua cfg.httpInterfaces}
+
+      https_interfaces = ${toLua cfg.httpsInterfaces}
+
+      http_ports = ${toLua cfg.httpPorts}
+
+      https_ports = ${toLua cfg.httpsPorts}
+
+      ${ cfg.extraConfig }
+
+      ${lib.concatMapStrings (muc: ''
+        Component ${toLua muc.domain} "muc"
+            modules_enabled = { "muc_mam"; ${optionalString muc.vcard_muc ''"vcard_muc";'' } }
+            name = ${toLua muc.name}
+            restrict_room_creation = ${toLua muc.restrictRoomCreation}
+            max_history_messages = ${toLua muc.maxHistoryMessages}
+            muc_room_locking = ${toLua muc.roomLocking}
+            muc_room_lock_timeout = ${toLua muc.roomLockTimeout}
+            muc_tombstones = ${toLua muc.tombstones}
+            muc_tombstone_expiry = ${toLua muc.tombstoneExpiry}
+            muc_room_default_public = ${toLua muc.roomDefaultPublic}
+            muc_room_default_members_only = ${toLua muc.roomDefaultMembersOnly}
+            muc_room_default_moderated = ${toLua muc.roomDefaultModerated}
+            muc_room_default_public_jids = ${toLua muc.roomDefaultPublicJids}
+            muc_room_default_change_subject = ${toLua muc.roomDefaultChangeSubject}
+            muc_room_default_history_length = ${toLua muc.roomDefaultHistoryLength}
+            muc_room_default_language = ${toLua muc.roomDefaultLanguage}
+            ${ muc.extraConfig }
+        '') cfg.muc}
+
+      ${ lib.optionalString (cfg.uploadHttp != null) ''
+        Component ${toLua cfg.uploadHttp.domain} "http_upload"
+            http_upload_file_size_limit = ${cfg.uploadHttp.uploadFileSizeLimit}
+            http_upload_expire_after = ${cfg.uploadHttp.uploadExpireAfter}
+            ${lib.optionalString (cfg.uploadHttp.userQuota != null) "http_upload_quota = ${toLua cfg.uploadHttp.userQuota}"}
+            http_upload_path = ${toLua cfg.uploadHttp.httpUploadPath}
+      ''}
+
+      ${ lib.concatStringsSep "\n" (lib.mapAttrsToList (n: v: ''
+        VirtualHost "${v.domain}"
+          enabled = ${boolToString v.enabled};
+          ${ optionalString (v.ssl != null) (createSSLOptsStr v.ssl) }
+          ${ v.extraConfig }
+        '') cfg.virtualHosts) }
+    '';
+
+    users.users.prosody = mkIf (cfg.user == "prosody") {
+      uid = config.ids.uids.prosody;
+      description = "Prosody user";
+      createHome = true;
+      inherit (cfg) group;
+      home = "${cfg.dataDir}";
+    };
+
+    users.groups.prosody = mkIf (cfg.group == "prosody") {
+      gid = config.ids.gids.prosody;
+    };
+
+    systemd.services.prosody = {
+      description = "Prosody XMPP server";
+      after = [ "network-online.target" ];
+      wants = [ "network-online.target" ];
+      wantedBy = [ "multi-user.target" ];
+      restartTriggers = [ config.environment.etc."prosody/prosody.cfg.lua".source ];
+      serviceConfig = {
+        User = cfg.user;
+        Group = cfg.group;
+        Type = "forking";
+        RuntimeDirectory = [ "prosody" ];
+        PIDFile = "/run/prosody/prosody.pid";
+        ExecStart = "${cfg.package}/bin/prosodyctl start";
+        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+
+        MemoryDenyWriteExecute = true;
+        PrivateDevices = true;
+        PrivateMounts = true;
+        PrivateTmp = true;
+        ProtectControlGroups = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+      };
+    };
+
+  };
+  meta.doc = ./prosody.xml;
+}
diff --git a/nixpkgs/nixos/modules/services/networking/prosody.xml b/nixpkgs/nixos/modules/services/networking/prosody.xml
new file mode 100644
index 000000000000..7859cb1578b7
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/prosody.xml
@@ -0,0 +1,88 @@
+<chapter xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         version="5.0"
+         xml:id="module-services-prosody">
+ <title>Prosody</title>
+ <para>
+  <link xlink:href="https://prosody.im/">Prosody</link> is an open-source, modern XMPP server.
+ </para>
+ <section xml:id="module-services-prosody-basic-usage">
+  <title>Basic usage</title>
+
+  <para>
+    A common struggle for most XMPP newcomers is to find the right set
+    of XMPP Extensions (XEPs) to setup. Forget to activate a few of
+    those and your XMPP experience might turn into a nightmare!
+  </para>
+
+  <para>
+    The XMPP community tackles this problem by creating a meta-XEP
+    listing a decent set of XEPs you should implement. This meta-XEP
+    is issued every year, the 2020 edition being
+    <link xlink:href="https://xmpp.org/extensions/xep-0423.html">XEP-0423</link>.
+  </para>
+  <para>
+    The NixOS Prosody module will implement most of these recommendend XEPs out of
+    the box. That being said, two components still require some
+    manual configuration: the
+    <link xlink:href="https://xmpp.org/extensions/xep-0045.html">Multi User Chat (MUC)</link>
+    and the <link xlink:href="https://xmpp.org/extensions/xep-0363.html">HTTP File Upload</link> ones.
+    You'll need to create a DNS subdomain for each of those. The current convention is to name your
+    MUC endpoint <literal>conference.example.org</literal> and your HTTP upload domain <literal>upload.example.org</literal>.
+  </para>
+  <para>
+    A good configuration to start with, including a
+    <link xlink:href="https://xmpp.org/extensions/xep-0045.html">Multi User Chat (MUC)</link>
+    endpoint as well as a <link xlink:href="https://xmpp.org/extensions/xep-0363.html">HTTP File Upload</link>
+    endpoint will look like this:
+    <programlisting>
+services.prosody = {
+  <link linkend="opt-services.prosody.enable">enable</link> = true;
+  <link linkend="opt-services.prosody.admins">admins</link> = [ "root@example.org" ];
+  <link linkend="opt-services.prosody.ssl.cert">ssl.cert</link> = "/var/lib/acme/example.org/fullchain.pem";
+  <link linkend="opt-services.prosody.ssl.key">ssl.key</link> = "/var/lib/acme/example.org/key.pem";
+  <link linkend="opt-services.prosody.virtualHosts">virtualHosts</link>."example.org" = {
+      <link linkend="opt-services.prosody.virtualHosts._name__.enabled">enabled</link> = true;
+      <link linkend="opt-services.prosody.virtualHosts._name__.domain">domain</link> = "example.org";
+      <link linkend="opt-services.prosody.virtualHosts._name__.ssl.cert">ssl.cert</link> = "/var/lib/acme/example.org/fullchain.pem";
+      <link linkend="opt-services.prosody.virtualHosts._name__.ssl.key">ssl.key</link> = "/var/lib/acme/example.org/key.pem";
+  };
+  <link linkend="opt-services.prosody.muc">muc</link> = [ {
+      <link linkend="opt-services.prosody.muc">domain</link> = "conference.example.org";
+  } ];
+  <link linkend="opt-services.prosody.uploadHttp">uploadHttp</link> = {
+      <link linkend="opt-services.prosody.uploadHttp.domain">domain</link> = "upload.example.org";
+  };
+};</programlisting>
+  </para>
+ </section>
+ <section xml:id="module-services-prosody-letsencrypt">
+  <title>Let's Encrypt Configuration</title>
+ <para>
+   As you can see in the code snippet from the
+   <link linkend="module-services-prosody-basic-usage">previous section</link>,
+   you'll need a single TLS certificate covering your main endpoint,
+   the MUC one as well as the HTTP Upload one. We can generate such a
+   certificate by leveraging the ACME
+   <link linkend="opt-security.acme.certs._name_.extraDomains">extraDomains</link> module option.
+ </para>
+ <para>
+   Provided the setup detailed in the previous section, you'll need the following acme configuration to generate
+   a TLS certificate for the three endponits:
+    <programlisting>
+security.acme = {
+  <link linkend="opt-security.acme.email">email</link> = "root@example.org";
+  <link linkend="opt-security.acme.acceptTerms">acceptTerms</link> = true;
+  <link linkend="opt-security.acme.certs">certs</link> = {
+    "example.org" = {
+      <link linkend="opt-security.acme.certs._name_.webroot">webroot</link> = "/var/www/example.org";
+      <link linkend="opt-security.acme.certs._name_.email">email</link> = "root@example.org";
+      <link linkend="opt-security.acme.certs._name_.extraDomains">extraDomains."conference.example.org"</link> = null;
+      <link linkend="opt-security.acme.certs._name_.extraDomains">extraDomains."upload.example.org"</link> = null;
+    };
+  };
+};</programlisting>
+ </para>
+</section>
+</chapter>
diff --git a/nixpkgs/nixos/modules/services/networking/quagga.nix b/nixpkgs/nixos/modules/services/networking/quagga.nix
new file mode 100644
index 000000000000..5acdd5af8f8f
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/quagga.nix
@@ -0,0 +1,185 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.quagga;
+
+  services = [ "babel" "bgp" "isis" "ospf6" "ospf" "pim" "rip" "ripng" ];
+  allServices = services ++ [ "zebra" ];
+
+  isEnabled = service: cfg.${service}.enable;
+
+  daemonName = service: if service == "zebra" then service else "${service}d";
+
+  configFile = service:
+    let
+      scfg = cfg.${service};
+    in
+      if scfg.configFile != null then scfg.configFile
+      else pkgs.writeText "${daemonName service}.conf"
+        ''
+          ! Quagga ${daemonName service} configuration
+          !
+          hostname ${config.networking.hostName}
+          log syslog
+          service password-encryption
+          !
+          ${scfg.config}
+          !
+          end
+        '';
+
+  serviceOptions = service:
+    {
+      enable = mkEnableOption "the Quagga ${toUpper service} routing protocol";
+
+      configFile = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        example = "/etc/quagga/${daemonName service}.conf";
+        description = ''
+          Configuration file to use for Quagga ${daemonName service}.
+          By default the NixOS generated files are used.
+        '';
+      };
+
+      config = mkOption {
+        type = types.lines;
+        default = "";
+        example =
+          let
+            examples = {
+              rip = ''
+                router rip
+                  network 10.0.0.0/8
+              '';
+
+              ospf = ''
+                router ospf
+                  network 10.0.0.0/8 area 0
+              '';
+
+              bgp = ''
+                router bgp 65001
+                  neighbor 10.0.0.1 remote-as 65001
+              '';
+            };
+          in
+            examples.${service} or "";
+        description = ''
+          ${daemonName service} configuration statements.
+        '';
+      };
+
+      vtyListenAddress = mkOption {
+        type = types.str;
+        default = "127.0.0.1";
+        description = ''
+          Address to bind to for the VTY interface.
+        '';
+      };
+
+      vtyListenPort = mkOption {
+        type = types.nullOr types.int;
+        default = null;
+        description = ''
+          TCP Port to bind to for the VTY interface.
+        '';
+      };
+    };
+
+in
+
+{
+
+  ###### interface
+  imports = [
+    {
+      options.services.quagga = {
+        zebra = (serviceOptions "zebra") // {
+          enable = mkOption {
+            type = types.bool;
+            default = any isEnabled services;
+            description = ''
+              Whether to enable the Zebra routing manager.
+
+              The Zebra routing manager is automatically enabled
+              if any routing protocols are configured.
+            '';
+          };
+        };
+      };
+    }
+    { options.services.quagga = (genAttrs services serviceOptions); }
+  ];
+
+  ###### implementation
+
+  config = mkIf (any isEnabled allServices) {
+
+    environment.systemPackages = [
+      pkgs.quagga               # for the vtysh tool
+    ];
+
+    users.users.quagga = {
+      description = "Quagga daemon user";
+      isSystemUser = true;
+      group = "quagga";
+    };
+
+    users.groups = {
+      quagga = {};
+      # Members of the quaggavty group can use vtysh to inspect the Quagga daemons
+      quaggavty = { members = [ "quagga" ]; };
+    };
+
+    systemd.services =
+      let
+        quaggaService = service:
+          let
+            scfg = cfg.${service};
+            daemon = daemonName service;
+          in
+            nameValuePair daemon ({
+              wantedBy = [ "multi-user.target" ];
+              restartTriggers = [ (configFile service) ];
+
+              serviceConfig = {
+                Type = "forking";
+                PIDFile = "/run/quagga/${daemon}.pid";
+                ExecStart = "@${pkgs.quagga}/libexec/quagga/${daemon} ${daemon} -d -f ${configFile service}"
+                  + optionalString (scfg.vtyListenAddress != "") " -A ${scfg.vtyListenAddress}"
+                  + optionalString (scfg.vtyListenPort != null) " -P ${toString scfg.vtyListenPort}";
+                ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+                Restart = "on-abort";
+              };
+            } // (
+              if service == "zebra" then
+                {
+                  description = "Quagga Zebra routing manager";
+                  unitConfig.Documentation = "man:zebra(8)";
+                  after = [ "network.target" ];
+                  preStart = ''
+                    install -m 0755 -o quagga -g quagga -d /run/quagga
+
+                    ${pkgs.iproute}/bin/ip route flush proto zebra
+                  '';
+                }
+              else
+                {
+                  description = "Quagga ${toUpper service} routing daemon";
+                  unitConfig.Documentation = "man:${daemon}(8) man:zebra(8)";
+                  bindsTo = [ "zebra.service" ];
+                  after = [ "network.target" "zebra.service" ];
+                }
+            ));
+       in
+         listToAttrs (map quaggaService (filter isEnabled allServices));
+
+  };
+
+  meta.maintainers = with lib.maintainers; [ tavyc ];
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/quassel.nix b/nixpkgs/nixos/modules/services/networking/quassel.nix
new file mode 100644
index 000000000000..da723ec86adf
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/quassel.nix
@@ -0,0 +1,132 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.quassel;
+  quassel = cfg.package;
+  user = if cfg.user != null then cfg.user else "quassel";
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.quassel = {
+
+      enable = mkEnableOption "the Quassel IRC client daemon";
+
+      certificateFile = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          Path to the certificate used for SSL connections with clients.
+        '';
+      };
+
+      requireSSL = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Require SSL for connections from clients.
+        '';
+      };
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.quasselDaemon;
+        defaultText = "pkgs.quasselDaemon";
+        description = ''
+          The package of the quassel daemon.
+        '';
+        example = literalExample "pkgs.quasselDaemon";
+      };
+
+      interfaces = mkOption {
+        default = [ "127.0.0.1" ];
+        description = ''
+          The interfaces the Quassel daemon will be listening to.  If `[ 127.0.0.1 ]',
+          only clients on the local host can connect to it; if `[ 0.0.0.0 ]', clients
+          can access it from any network interface.
+        '';
+      };
+
+      portNumber = mkOption {
+        default = 4242;
+        description = ''
+          The port number the Quassel daemon will be listening to.
+        '';
+      };
+
+      dataDir = mkOption {
+        default = ''/home/${user}/.config/quassel-irc.org'';
+        description = ''
+          The directory holding configuration files, the SQlite database and the SSL Cert.
+        '';
+      };
+
+      user = mkOption {
+        default = null;
+        description = ''
+          The existing user the Quassel daemon should run as. If left empty, a default "quassel" user will be created.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    assertions = [
+      { assertion = cfg.requireSSL -> cfg.certificateFile != null;
+        message = "Quassel needs a certificate file in order to require SSL";
+      }];
+
+    users.users = optionalAttrs (cfg.user == null) {
+      quassel = {
+        name = "quassel";
+        description = "Quassel IRC client daemon";
+        group = "quassel";
+        uid = config.ids.uids.quassel;
+      };
+    };
+
+    users.groups = optionalAttrs (cfg.user == null) {
+      quassel = {
+        name = "quassel";
+        gid = config.ids.gids.quassel;
+      };
+    };
+
+    systemd.tmpfiles.rules = [
+      "d '${cfg.dataDir}' - ${user} - - -"
+    ];
+
+    systemd.services.quassel =
+      { description = "Quassel IRC client daemon";
+
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ] ++ optional config.services.postgresql.enable "postgresql.service"
+                                     ++ optional config.services.mysql.enable "mysql.service";
+
+        serviceConfig =
+        {
+          ExecStart = concatStringsSep " " ([
+            "${quassel}/bin/quasselcore"
+            "--listen=${concatStringsSep "," cfg.interfaces}"
+            "--port=${toString cfg.portNumber}"
+            "--configdir=${cfg.dataDir}"
+          ] ++ optional cfg.requireSSL "--require-ssl"
+            ++ optional (cfg.certificateFile != null) "--ssl-cert=${cfg.certificateFile}");
+          User = user;
+        };
+      };
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/quicktun.nix b/nixpkgs/nixos/modules/services/networking/quicktun.nix
new file mode 100644
index 000000000000..fb783c836464
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/quicktun.nix
@@ -0,0 +1,118 @@
+{ config, pkgs, lib, ... }:
+
+let
+
+  cfg = config.services.quicktun;
+
+in
+
+with lib;
+
+{
+  options = {
+
+    services.quicktun = mkOption {
+      default = { };
+      description = "QuickTun tunnels";
+      type = types.attrsOf (types.submodule {
+        options = {
+          tunMode = mkOption {
+            type = types.int;
+            default = 0;
+            example = 1;
+            description = "";
+          };
+
+          remoteAddress = mkOption {
+            type = types.str;
+            example = "tunnel.example.com";
+            description = "";
+          };
+
+          localAddress = mkOption {
+            type = types.str;
+            example = "0.0.0.0";
+            description = "";
+          };
+
+          localPort = mkOption {
+            type = types.int;
+            default = 2998;
+            description = "";
+          };
+
+          remotePort = mkOption {
+            type = types.int;
+            default = 2998;
+            description = "";
+          };
+
+          remoteFloat = mkOption {
+            type = types.int;
+            default = 0;
+            description = "";
+          };
+
+          protocol = mkOption {
+            type = types.str;
+            default = "nacltai";
+            description = "";
+          };
+
+          privateKey = mkOption {
+            type = types.str;
+            description = "";
+          };
+
+          publicKey = mkOption {
+            type = types.str;
+            description = "";
+          };
+
+          timeWindow = mkOption {
+            type = types.int;
+            default = 5;
+            description = "";
+          };
+
+          upScript = mkOption {
+            type = types.lines;
+            default = "";
+            description = "";
+          };
+        };
+      });
+    };
+
+  };
+
+  config = mkIf (cfg != []) {
+    systemd.services = fold (a: b: a // b) {} (
+      mapAttrsToList (name: qtcfg: {
+        "quicktun-${name}" = {
+          wantedBy = [ "multi-user.target" ];
+          after = [ "network.target" ];
+          environment = {
+            INTERFACE = name;
+            TUN_MODE = toString qtcfg.tunMode;
+            REMOTE_ADDRESS = qtcfg.remoteAddress;
+            LOCAL_ADDRESS = qtcfg.localAddress;
+            LOCAL_PORT = toString qtcfg.localPort;
+            REMOTE_PORT = toString qtcfg.remotePort;
+            REMOTE_FLOAT = toString qtcfg.remoteFloat;
+            PRIVATE_KEY = qtcfg.privateKey;
+            PUBLIC_KEY = qtcfg.publicKey;
+            TIME_WINDOW = toString qtcfg.timeWindow;
+            TUN_UP_SCRIPT = pkgs.writeScript "quicktun-${name}-up.sh" qtcfg.upScript;
+            SUID = "nobody";
+          };
+          serviceConfig = {
+            Type = "simple";
+            ExecStart = "${pkgs.quicktun}/bin/quicktun.${qtcfg.protocol}";
+          };
+        };
+      }) cfg
+    );
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/quorum.nix b/nixpkgs/nixos/modules/services/networking/quorum.nix
new file mode 100644
index 000000000000..2f612c9db686
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/quorum.nix
@@ -0,0 +1,229 @@
+{ config, pkgs, lib, ... }:
+let
+
+  inherit (lib) mkEnableOption mkIf mkOption literalExample types optionalString;
+
+  cfg = config.services.quorum;
+  dataDir = "/var/lib/quorum";
+  genesisFile = pkgs.writeText "genesis.json" (builtins.toJSON cfg.genesis);
+  staticNodesFile = pkgs.writeText "static-nodes.json" (builtins.toJSON cfg.staticNodes);
+
+in {
+  options = {
+
+    services.quorum = {
+      enable = mkEnableOption "Quorum blockchain daemon";
+
+      user = mkOption {
+        type = types.str;
+        default = "quorum";
+        description = "The user as which to run quorum.";
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = cfg.user;
+        description = "The group as which to run quorum.";
+      };
+
+      port = mkOption {
+        type = types.port;
+        default = 21000;
+        description = "Override the default port on which to listen for connections.";
+      };
+
+      nodekeyFile = mkOption {
+        type = types.path;
+        default = "${dataDir}/nodekey";
+        description = "Path to the nodekey.";
+      };
+
+      staticNodes = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "enode://dd333ec28f0a8910c92eb4d336461eea1c20803eed9cf2c056557f986e720f8e693605bba2f4e8f289b1162e5ac7c80c914c7178130711e393ca76abc1d92f57@0.0.0.0:30303?discport=0" ];
+        description = "List of validator nodes.";
+      };
+
+      privateconfig = mkOption {
+        type = types.str;
+        default = "ignore";
+        description = "Configuration of privacy transaction manager.";
+      };
+
+      syncmode = mkOption {
+        type = types.enum [ "fast" "full" "light" ];
+        default = "full";
+        description = "Blockchain sync mode.";
+      };
+
+      blockperiod = mkOption {
+        type = types.int;
+        default = 5;
+        description = "Default minimum difference between two consecutive block's timestamps in seconds.";
+      };
+
+      permissioned = mkOption {
+        type = types.bool;
+        default = true;
+        description = "Allow only a defined list of nodes to connect.";
+      };
+
+      rpc = {
+        enable = mkOption {
+          type = types.bool;
+          default = true;
+          description = "Enable RPC interface.";
+        };
+
+        address = mkOption {
+          type = types.str;
+          default = "0.0.0.0";
+          description = "Listening address for RPC connections.";
+        };
+
+        port = mkOption {
+          type = types.port;
+          default = 22004;
+          description = "Override the default port on which to listen for RPC connections.";
+        };
+
+        api = mkOption {
+          type = types.str;
+          default = "admin,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum,istanbul";
+          description = "API's offered over the HTTP-RPC interface.";
+        };
+      };
+
+     ws = {
+        enable = mkOption {
+          type = types.bool;
+          default = true;
+          description = "Enable WS-RPC interface.";
+        };
+
+        address = mkOption {
+          type = types.str;
+          default = "0.0.0.0";
+          description = "Listening address for WS-RPC connections.";
+        };
+
+        port = mkOption {
+          type = types.port;
+          default = 8546;
+          description = "Override the default port on which to listen for WS-RPC connections.";
+        };
+
+        api = mkOption {
+          type = types.str;
+          default = "admin,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum,istanbul";
+          description = "API's offered over the WS-RPC interface.";
+        };
+
+       origins = mkOption {
+          type = types.str;
+          default = "*";
+          description = "Origins from which to accept websockets requests";
+       };
+     };
+
+      genesis = mkOption {
+        type = types.nullOr types.attrs;
+        default = null;
+        example = literalExample '' {
+          alloc = {
+            a47385db68718bdcbddc2d2bb7c54018066ec111 = {
+              balance = "1000000000000000000000000000";
+            };
+          };
+          coinbase = "0x0000000000000000000000000000000000000000";
+          config = {
+            byzantiumBlock = 4;
+            chainId = 494702925;
+            eip150Block = 2;
+            eip155Block = 3;
+            eip158Block = 3;
+            homesteadBlock = 1;
+            isQuorum = true;
+            istanbul = {
+              epoch = 30000;
+              policy = 0;
+            };
+          };
+          difficulty = "0x1";
+          extraData = "0x0000000000000000000000000000000000000000000000000000000000000000f85ad59438f0508111273d8e482f49410ca4078afc86a961b8410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0";
+          gasLimit = "0x2FEFD800";
+          mixHash = "0x63746963616c2062797a616e74696e65201111756c7420746f6c6572616e6365";
+          nonce = "0x0";
+          parentHash = "0x0000000000000000000000000000000000000000000000000000000000000000";
+          timestamp = "0x00";
+          }'';
+        description = "Blockchain genesis settings.";
+      };
+     };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.quorum ];
+    systemd.tmpfiles.rules = [
+      "d '${dataDir}' 0770 '${cfg.user}' '${cfg.group}' - -"
+    ];
+    systemd.services.quorum = {
+      description = "Quorum daemon";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      environment = {
+        PRIVATE_CONFIG = "${cfg.privateconfig}";
+      };
+      preStart = ''
+        if [ ! -d ${dataDir}/geth ]; then
+          if [ ! -d ${dataDir}/keystore ]; then
+            echo ERROR: You need to create a wallet before initializing your genesis file, run:
+            echo   # su -s /bin/sh - quorum
+            echo   $ geth --datadir ${dataDir} account new
+            echo and configure your genesis file accordingly.
+            exit 1;
+          fi
+          ln -s ${staticNodesFile} ${dataDir}/static-nodes.json
+          ${pkgs.quorum}/bin/geth --datadir ${dataDir} init ${genesisFile}
+        fi
+      '';
+      serviceConfig = {
+        User = cfg.user;
+        Group = cfg.group;
+        ExecStart = ''${pkgs.quorum}/bin/geth \
+            --nodiscover \
+            --verbosity 5 \
+            --nodekey ${cfg.nodekeyFile} \
+            --istanbul.blockperiod ${toString cfg.blockperiod} \
+            --syncmode ${cfg.syncmode} \
+            ${optionalString (cfg.permissioned)
+            "--permissioned"} \
+            --mine --minerthreads 1 \
+            ${optionalString (cfg.rpc.enable)
+            "--rpc --rpcaddr ${cfg.rpc.address} --rpcport ${toString cfg.rpc.port} --rpcapi ${cfg.rpc.api}"} \
+            ${optionalString (cfg.ws.enable)
+            "--ws --wsaddr ${cfg.ws.address} --wsport ${toString cfg.ws.port} --wsapi ${cfg.ws.api} --wsorigins ${cfg.ws.origins}"} \
+            --emitcheckpoints \
+            --datadir ${dataDir} \
+            --port ${toString cfg.port}'';
+        Restart = "on-failure";
+
+        # Hardening measures
+        PrivateTmp = "true";
+        ProtectSystem = "full";
+        NoNewPrivileges = "true";
+        PrivateDevices = "true";
+        MemoryDenyWriteExecute = "true";
+      };
+    };
+    users.users.${cfg.user} = {
+      name = cfg.user;
+      group = cfg.group;
+      description = "Quorum daemon user";
+      home = dataDir;
+      isSystemUser = true;
+    };
+    users.groups.${cfg.group} = {};
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/racoon.nix b/nixpkgs/nixos/modules/services/networking/racoon.nix
new file mode 100644
index 000000000000..328f4cb1497f
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/racoon.nix
@@ -0,0 +1,45 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.racoon;
+in {
+  options.services.racoon = {
+    enable = mkEnableOption "racoon";
+
+    config = mkOption {
+      description = "Contents of racoon configuration file.";
+      default = "";
+      type = types.str;
+    };
+
+    configPath = mkOption {
+      description = "Location of racoon config if config is not provided.";
+      default = "/etc/racoon/racoon.conf";
+      type = types.path;
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.racoon = {
+      description = "Racoon Daemon";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+      serviceConfig = {
+        ExecStart = "${pkgs.ipsecTools}/bin/racoon -f ${
+          if (cfg.config != "") then pkgs.writeText "racoon.conf" cfg.config
+          else cfg.configPath
+        }";
+        ExecReload = "${pkgs.ipsecTools}/bin/racoonctl reload-config";
+        PIDFile = "/run/racoon.pid";
+        Type = "forking";
+        Restart = "always";
+      };
+      preStart = ''
+        rm /run/racoon.pid || true
+        mkdir -p /var/racoon
+      '';
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/radicale.nix b/nixpkgs/nixos/modules/services/networking/radicale.nix
new file mode 100644
index 000000000000..30bf22586f86
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/radicale.nix
@@ -0,0 +1,89 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.radicale;
+
+  confFile = pkgs.writeText "radicale.conf" cfg.config;
+
+  # This enables us to default to version 2 while still not breaking configurations of people with version 1
+  defaultPackage = if versionAtLeast config.system.stateVersion "17.09" then {
+    pkg = pkgs.radicale2;
+    text = "pkgs.radicale2";
+  } else {
+    pkg = pkgs.radicale1;
+    text = "pkgs.radicale1";
+  };
+in
+
+{
+
+  options = {
+    services.radicale.enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+          Enable Radicale CalDAV and CardDAV server.
+      '';
+    };
+
+    services.radicale.package = mkOption {
+      type = types.package;
+      default = defaultPackage.pkg;
+      defaultText = defaultPackage.text;
+      description = ''
+        Radicale package to use. This defaults to version 1.x if
+        <literal>system.stateVersion &lt; 17.09</literal> and version 2.x
+        otherwise.
+      '';
+    };
+
+    services.radicale.config = mkOption {
+      type = types.str;
+      default = "";
+      description = ''
+        Radicale configuration, this will set the service
+        configuration file.
+      '';
+    };
+
+    services.radicale.extraArgs = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      description = "Extra arguments passed to the Radicale daemon.";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ cfg.package ];
+
+    users.users.radicale =
+      { uid = config.ids.uids.radicale;
+        description = "radicale user";
+        home = "/var/lib/radicale";
+        createHome = true;
+      };
+
+    users.groups.radicale =
+      { gid = config.ids.gids.radicale; };
+
+    systemd.services.radicale = {
+      description = "A Simple Calendar and Contact Server";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        ExecStart = concatStringsSep " " ([
+          "${cfg.package}/bin/radicale" "-C" confFile
+        ] ++ (
+          map escapeShellArg cfg.extraArgs
+        ));
+        User = "radicale";
+        Group = "radicale";
+      };
+    };
+  };
+
+  meta.maintainers = with lib.maintainers; [ aneeshusa infinisil ];
+}
diff --git a/nixpkgs/nixos/modules/services/networking/radvd.nix b/nixpkgs/nixos/modules/services/networking/radvd.nix
new file mode 100644
index 000000000000..f4b00c9b356e
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/radvd.nix
@@ -0,0 +1,73 @@
+# Module for the IPv6 Router Advertisement Daemon.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.radvd;
+
+  confFile = pkgs.writeText "radvd.conf" cfg.config;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.radvd.enable = mkOption {
+      type = types.bool;
+      default = false;
+      description =
+        ''
+          Whether to enable the Router Advertisement Daemon
+          (<command>radvd</command>), which provides link-local
+          advertisements of IPv6 router addresses and prefixes using
+          the Neighbor Discovery Protocol (NDP).  This enables
+          stateless address autoconfiguration in IPv6 clients on the
+          network.
+        '';
+    };
+
+    services.radvd.config = mkOption {
+      example =
+        ''
+          interface eth0 {
+            AdvSendAdvert on;
+            prefix 2001:db8:1234:5678::/64 { };
+          };
+        '';
+      description =
+        ''
+          The contents of the radvd configuration file.
+        '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    users.users.radvd =
+      { uid = config.ids.uids.radvd;
+        description = "Router Advertisement Daemon User";
+      };
+
+    systemd.services.radvd =
+      { description = "IPv6 Router Advertisement Daemon";
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ];
+        serviceConfig =
+          { ExecStart = "@${pkgs.radvd}/bin/radvd radvd -n -u radvd -C ${confFile}";
+            Restart = "always";
+          };
+      };
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/rdnssd.nix b/nixpkgs/nixos/modules/services/networking/rdnssd.nix
new file mode 100644
index 000000000000..469504c43172
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/rdnssd.nix
@@ -0,0 +1,80 @@
+# Module for rdnssd, a daemon that configures DNS servers in
+# /etc/resolv/conf from IPv6 RDNSS advertisements.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  mergeHook = pkgs.writeScript "rdnssd-merge-hook" ''
+    #! ${pkgs.runtimeShell} -e
+    ${pkgs.openresolv}/bin/resolvconf -u
+  '';
+in
+{
+
+  ###### interface
+
+  options = {
+
+    services.rdnssd.enable = mkOption {
+      type = types.bool;
+      default = false;
+      #default = config.networking.enableIPv6;
+      description =
+        ''
+          Whether to enable the RDNSS daemon
+          (<command>rdnssd</command>), which configures DNS servers in
+          <filename>/etc/resolv.conf</filename> from RDNSS
+          advertisements sent by IPv6 routers.
+        '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.rdnssd.enable {
+
+    assertions = [{
+      assertion = config.networking.resolvconf.enable;
+      message = "rdnssd needs resolvconf to work (probably something sets up a static resolv.conf)";
+    }];
+
+    systemd.services.rdnssd = {
+      description = "RDNSS daemon";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      preStart = ''
+        # Create the proper run directory
+        mkdir -p /run/rdnssd
+        touch /run/rdnssd/resolv.conf
+        chown -R rdnssd /run/rdnssd
+
+        # Link the resolvconf interfaces to rdnssd
+        rm -f /run/resolvconf/interfaces/rdnssd
+        ln -s /run/rdnssd/resolv.conf /run/resolvconf/interfaces/rdnssd
+        ${mergeHook}
+      '';
+
+      postStop = ''
+        rm -f /run/resolvconf/interfaces/rdnssd
+        ${mergeHook}
+      '';
+
+      serviceConfig = {
+        ExecStart = "@${pkgs.ndisc6}/bin/rdnssd rdnssd -p /run/rdnssd/rdnssd.pid -r /run/rdnssd/resolv.conf -u rdnssd -H ${mergeHook}";
+        Type = "forking";
+        PIDFile = "/run/rdnssd/rdnssd.pid";
+      };
+    };
+
+    users.users.rdnssd = {
+      description = "RDNSSD Daemon User";
+      uid = config.ids.uids.rdnssd;
+    };
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/redsocks.nix b/nixpkgs/nixos/modules/services/networking/redsocks.nix
new file mode 100644
index 000000000000..8481f9debf39
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/redsocks.nix
@@ -0,0 +1,272 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.services.redsocks;
+in
+{
+  ##### interface
+  options = {
+    services.redsocks = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to enable redsocks.";
+      };
+
+      log_debug = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Log connection progress.";
+      };
+
+      log_info = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Log start and end of client sessions.";
+      };
+
+      log = mkOption {
+        type = types.str;
+        default = "stderr";
+        description =
+          ''
+            Where to send logs.
+
+            Possible values are:
+              - stderr
+              - file:/path/to/file
+              - syslog:FACILITY where FACILITY is any of "daemon", "local0",
+              etc.
+          '';
+      };
+
+      chroot = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description =
+          ''
+            Chroot under which to run redsocks. Log file is opened before
+            chroot, but if logging to syslog /etc/localtime may be required.
+          '';
+      };
+
+      redsocks = mkOption {
+        description =
+          ''
+            Local port to proxy associations to be performed.
+
+            The example shows how to configure a proxy to handle port 80 as HTTP
+            relay, and all other ports as HTTP connect.
+          '';
+        example = [
+          { port = 23456; proxy = "1.2.3.4:8080"; type = "http-relay";
+            redirectCondition = "--dport 80";
+            doNotRedirect = [ "-d 1.2.0.0/16" ];
+          }
+          { port = 23457; proxy = "1.2.3.4:8080"; type = "http-connect";
+            redirectCondition = true;
+            doNotRedirect = [ "-d 1.2.0.0/16" ];
+          }
+        ];
+        type = types.listOf (types.submodule { options = {
+          ip = mkOption {
+            type = types.str;
+            default = "127.0.0.1";
+            description =
+              ''
+                IP on which redsocks should listen. Defaults to 127.0.0.1 for
+                security reasons.
+              '';
+          };
+
+          port = mkOption {
+            type = types.int;
+            default = 12345;
+            description = "Port on which redsocks should listen.";
+          };
+
+          proxy = mkOption {
+            type = types.str;
+            description =
+              ''
+                Proxy through which redsocks should forward incoming traffic.
+                Example: "example.org:8080"
+              '';
+          };
+
+          type = mkOption {
+            type = types.enum [ "socks4" "socks5" "http-connect" "http-relay" ];
+            description = "Type of proxy.";
+          };
+
+          login = mkOption {
+            type = with types; nullOr str;
+            default = null;
+            description = "Login to send to proxy.";
+          };
+
+          password = mkOption {
+            type = with types; nullOr str;
+            default = null;
+            description =
+              ''
+                Password to send to proxy. WARNING, this will end up
+                world-readable in the store! Awaiting
+                https://github.com/NixOS/nix/issues/8 to be able to fix.
+              '';
+          };
+
+          disclose_src = mkOption {
+            type = types.enum [ "false" "X-Forwarded-For" "Forwarded_ip"
+                                "Forwarded_ipport" ];
+            default = "false";
+            description =
+              ''
+                Way to disclose client IP to the proxy.
+                  - "false": do not disclose
+                http-connect supports the following ways:
+                  - "X-Forwarded-For": add header "X-Forwarded-For: IP"
+                  - "Forwarded_ip": add header "Forwarded: for=IP" (see RFC7239)
+                  - "Forwarded_ipport": add header 'Forwarded: for="IP:port"'
+              '';
+          };
+
+          redirectInternetOnly = mkOption {
+            type = types.bool;
+            default = true;
+            description = "Exclude all non-globally-routable IPs from redsocks";
+          };
+
+          doNotRedirect = mkOption {
+            type = with types; listOf str;
+            default = [];
+            description =
+              ''
+                Iptables filters that if matched will get the packet off of
+                redsocks.
+              '';
+            example = [ "-d 1.2.3.4" ];
+          };
+
+          redirectCondition = mkOption {
+            type = with types; either bool str;
+            default = false;
+            description =
+              ''
+                Conditions to make outbound packets go through this redsocks
+                instance.
+
+                If set to false, no packet will be forwarded. If set to true,
+                all packets will be forwarded (except packets excluded by
+                redirectInternetOnly).
+
+                If set to a string, this is an iptables filter that will be
+                matched against packets before getting them into redsocks. For
+                example, setting it to "--dport 80" will only send
+                packets to port 80 to redsocks. Note "-p tcp" is always
+                implicitly added, as udp can only be proxied through redudp or
+                the like.
+              '';
+          };
+        };});
+      };
+
+      # TODO: Add support for redudp and dnstc
+    };
+  };
+
+  ##### implementation
+  config = let
+    redsocks_blocks = concatMapStrings (block:
+      let proxy = splitString ":" block.proxy; in
+      ''
+        redsocks {
+          local_ip = ${block.ip};
+          local_port = ${toString block.port};
+
+          ip = ${elemAt proxy 0};
+          port = ${elemAt proxy 1};
+          type = ${block.type};
+
+          ${optionalString (block.login != null) "login = \"${block.login}\";"}
+          ${optionalString (block.password != null) "password = \"${block.password}\";"}
+
+          disclose_src = ${block.disclose_src};
+        }
+      '') cfg.redsocks;
+    configfile = pkgs.writeText "redsocks.conf"
+      ''
+        base {
+          log_debug = ${if cfg.log_debug then "on" else "off" };
+          log_info = ${if cfg.log_info then "on" else "off" };
+          log = ${cfg.log};
+
+          daemon = off;
+          redirector = iptables;
+
+          user = redsocks;
+          group = redsocks;
+          ${optionalString (cfg.chroot != null) "chroot = ${cfg.chroot};"}
+        }
+
+        ${redsocks_blocks}
+      '';
+    internetOnly = [ # TODO: add ipv6-equivalent
+      "-d 0.0.0.0/8"
+      "-d 10.0.0.0/8"
+      "-d 127.0.0.0/8"
+      "-d 169.254.0.0/16"
+      "-d 172.16.0.0/12"
+      "-d 192.168.0.0/16"
+      "-d 224.168.0.0/4"
+      "-d 240.168.0.0/4"
+    ];
+    redCond = block:
+      optionalString (isString block.redirectCondition) block.redirectCondition;
+    iptables = concatImapStrings (idx: block:
+      let chain = "REDSOCKS${toString idx}"; doNotRedirect =
+        concatMapStringsSep "\n"
+          (f: "ip46tables -t nat -A ${chain} ${f} -j RETURN 2>/dev/null || true")
+          (block.doNotRedirect ++ (optionals block.redirectInternetOnly internetOnly));
+      in
+      optionalString (block.redirectCondition != false)
+        ''
+          ip46tables -t nat -F ${chain} 2>/dev/null || true
+          ip46tables -t nat -N ${chain} 2>/dev/null || true
+          ${doNotRedirect}
+          ip46tables -t nat -A ${chain} -p tcp -j REDIRECT --to-ports ${toString block.port}
+
+          # TODO: show errors, when it will be easily possible by a switch to
+          # iptables-restore
+          ip46tables -t nat -A OUTPUT -p tcp ${redCond block} -j ${chain} 2>/dev/null || true
+        ''
+    ) cfg.redsocks;
+  in
+    mkIf cfg.enable {
+      users.groups.redsocks = {};
+      users.users.redsocks = {
+        description = "Redsocks daemon";
+        group = "redsocks";
+        isSystemUser = true;
+      };
+
+      systemd.services.redsocks = {
+        description = "Redsocks";
+        after = [ "network.target" ];
+        wantedBy = [ "multi-user.target" ];
+        script = "${pkgs.redsocks}/bin/redsocks -c ${configfile}";
+      };
+
+      networking.firewall.extraCommands = iptables;
+
+      networking.firewall.extraStopCommands =
+        concatImapStringsSep "\n" (idx: block:
+          let chain = "REDSOCKS${toString idx}"; in
+          optionalString (block.redirectCondition != false)
+            "ip46tables -t nat -D OUTPUT -p tcp ${redCond block} -j ${chain} 2>/dev/null || true"
+        ) cfg.redsocks;
+    };
+
+  meta.maintainers = with lib.maintainers; [ ekleog ];
+}
diff --git a/nixpkgs/nixos/modules/services/networking/resilio.nix b/nixpkgs/nixos/modules/services/networking/resilio.nix
new file mode 100644
index 000000000000..e74e03fc0b07
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/resilio.nix
@@ -0,0 +1,263 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.resilio;
+
+  resilioSync = pkgs.resilio-sync;
+
+  sharedFoldersRecord = map (entry: {
+    secret = entry.secret;
+    dir = entry.directory;
+
+    use_relay_server = entry.useRelayServer;
+    use_tracker = entry.useTracker;
+    use_dht = entry.useDHT;
+
+    search_lan = entry.searchLAN;
+    use_sync_trash = entry.useSyncTrash;
+    known_hosts = entry.knownHosts;
+  }) cfg.sharedFolders;
+
+  configFile = pkgs.writeText "config.json" (builtins.toJSON ({
+    device_name = cfg.deviceName;
+    storage_path = cfg.storagePath;
+    listening_port = cfg.listeningPort;
+    use_gui = false;
+    check_for_updates = cfg.checkForUpdates;
+    use_upnp = cfg.useUpnp;
+    download_limit = cfg.downloadLimit;
+    upload_limit = cfg.uploadLimit;
+    lan_encrypt_data = cfg.encryptLAN;
+  } // optionalAttrs cfg.enableWebUI {
+    webui = { listen = "${cfg.httpListenAddr}:${toString cfg.httpListenPort}"; } //
+      (optionalAttrs (cfg.httpLogin != "") { login = cfg.httpLogin; }) //
+      (optionalAttrs (cfg.httpPass != "") { password = cfg.httpPass; }) //
+      (optionalAttrs (cfg.apiKey != "") { api_key = cfg.apiKey; }) //
+      (optionalAttrs (cfg.directoryRoot != "") { directory_root = cfg.directoryRoot; });
+  } // optionalAttrs (sharedFoldersRecord != []) {
+    shared_folders = sharedFoldersRecord;
+  }));
+
+in
+{
+  options = {
+    services.resilio = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          If enabled, start the Resilio Sync daemon. Once enabled, you can
+          interact with the service through the Web UI, or configure it in your
+          NixOS configuration.
+        '';
+      };
+
+      deviceName = mkOption {
+        type = types.str;
+        example = "Voltron";
+        default = config.networking.hostName;
+        description = ''
+          Name of the Resilio Sync device.
+        '';
+      };
+
+      listeningPort = mkOption {
+        type = types.int;
+        default = 0;
+        example = 44444;
+        description = ''
+          Listening port. Defaults to 0 which randomizes the port.
+        '';
+      };
+
+      checkForUpdates = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Determines whether to check for updates and alert the user
+          about them in the UI.
+        '';
+      };
+
+      useUpnp = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Use Universal Plug-n-Play (UPnP)
+        '';
+      };
+
+      downloadLimit = mkOption {
+        type = types.int;
+        default = 0;
+        example = 1024;
+        description = ''
+          Download speed limit. 0 is unlimited (default).
+        '';
+      };
+
+      uploadLimit = mkOption {
+        type = types.int;
+        default = 0;
+        example = 1024;
+        description = ''
+          Upload speed limit. 0 is unlimited (default).
+        '';
+      };
+
+      httpListenAddr = mkOption {
+        type = types.str;
+        default = "0.0.0.0";
+        example = "1.2.3.4";
+        description = ''
+          HTTP address to bind to.
+        '';
+      };
+
+      httpListenPort = mkOption {
+        type = types.int;
+        default = 9000;
+        description = ''
+          HTTP port to bind on.
+        '';
+      };
+
+      httpLogin = mkOption {
+        type = types.str;
+        example = "allyourbase";
+        default = "";
+        description = ''
+          HTTP web login username.
+        '';
+      };
+
+      httpPass = mkOption {
+        type = types.str;
+        example = "arebelongtous";
+        default = "";
+        description = ''
+          HTTP web login password.
+        '';
+      };
+
+      encryptLAN = mkOption {
+        type = types.bool;
+        default = true;
+        description = "Encrypt LAN data.";
+      };
+
+      enableWebUI = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable Web UI for administration. Bound to the specified
+          <literal>httpListenAddress</literal> and
+          <literal>httpListenPort</literal>.
+          '';
+      };
+
+      storagePath = mkOption {
+        type = types.path;
+        default = "/var/lib/resilio-sync/";
+        description = ''
+          Where BitTorrent Sync will store it's database files (containing
+          things like username info and licenses). Generally, you should not
+          need to ever change this.
+        '';
+      };
+
+      apiKey = mkOption {
+        type = types.str;
+        default = "";
+        description = "API key, which enables the developer API.";
+      };
+
+      directoryRoot = mkOption {
+        type = types.str;
+        default = "";
+        example = "/media";
+        description = "Default directory to add folders in the web UI.";
+      };
+
+      sharedFolders = mkOption {
+        default = [];
+        example =
+          [ { secret         = "AHMYFPCQAHBM7LQPFXQ7WV6Y42IGUXJ5Y";
+              directory      = "/home/user/sync_test";
+              useRelayServer = true;
+              useTracker     = true;
+              useDHT         = false;
+              searchLAN      = true;
+              useSyncTrash   = true;
+              knownHosts     = [
+                "192.168.1.2:4444"
+                "192.168.1.3:4444"
+              ];
+            }
+          ];
+        description = ''
+          Shared folder list. If enabled, web UI must be
+          disabled. Secrets can be generated using <literal>rslsync
+          --generate-secret</literal>. Note that this secret will be
+          put inside the Nix store, so it is realistically not very
+          secret.
+
+          If you would like to be able to modify the contents of this
+          directories, it is recommended that you make your user a
+          member of the <literal>resilio</literal> group.
+
+          Directories in this list should be in the
+          <literal>resilio</literal> group, and that group must have
+          write access to the directory. It is also recommended that
+          <literal>chmod g+s</literal> is applied to the directory
+          so that any sub directories created will also belong to
+          the <literal>resilio</literal> group. Also,
+          <literal>setfacl -d -m group:resilio:rwx</literal> and
+          <literal>setfacl -m group:resilio:rwx</literal> should also
+          be applied so that the sub directories are writable by
+          the group.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    assertions =
+      [ { assertion = cfg.deviceName != "";
+          message   = "Device name cannot be empty.";
+        }
+        { assertion = cfg.enableWebUI -> cfg.sharedFolders == [];
+          message   = "If using shared folders, the web UI cannot be enabled.";
+        }
+        { assertion = cfg.apiKey != "" -> cfg.enableWebUI;
+          message   = "If you're using an API key, you must enable the web server.";
+        }
+      ];
+
+    users.users.rslsync = {
+      description     = "Resilio Sync Service user";
+      home            = cfg.storagePath;
+      createHome      = true;
+      uid             = config.ids.uids.rslsync;
+      group           = "rslsync";
+    };
+
+    users.groups.rslsync = {};
+
+    systemd.services.resilio = with pkgs; {
+      description = "Resilio Sync Service";
+      wantedBy    = [ "multi-user.target" ];
+      after       = [ "network.target" ];
+      serviceConfig = {
+        Restart   = "on-abort";
+        UMask     = "0002";
+        User      = "rslsync";
+        ExecStart = ''
+          ${resilioSync}/bin/rslsync --nodaemon --config ${configFile}
+        '';
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/rpcbind.nix b/nixpkgs/nixos/modules/services/networking/rpcbind.nix
new file mode 100644
index 000000000000..0a5df6987092
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/rpcbind.nix
@@ -0,0 +1,46 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.rpcbind = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable `rpcbind', an ONC RPC directory service
+          notably used by NFS and NIS, and which can be queried
+          using the rpcinfo(1) command. `rpcbind` is a replacement for
+          `portmap`.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.rpcbind.enable {
+    environment.systemPackages = [ pkgs.rpcbind ];
+
+    systemd.packages = [ pkgs.rpcbind ];
+
+    systemd.services.rpcbind = {
+      wantedBy = [ "multi-user.target" ];
+    };
+
+    users.users.rpc = {
+      group = "nogroup";
+      uid = config.ids.uids.rpc;
+    };
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/rxe.nix b/nixpkgs/nixos/modules/services/networking/rxe.nix
new file mode 100644
index 000000000000..c7d174a00de2
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/rxe.nix
@@ -0,0 +1,52 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.networking.rxe;
+
+in {
+  ###### interface
+
+  options = {
+    networking.rxe = {
+      enable = mkEnableOption "RDMA over converged ethernet";
+      interfaces = mkOption {
+        type = types.listOf types.str;
+        default = [ ];
+        example = [ "eth0" ];
+        description = ''
+          Enable RDMA on the listed interfaces. The corresponding virtual
+          RDMA interfaces will be named rxe_&lt;interface&gt;.
+          UDP port 4791 must be open on the respective ethernet interfaces.
+        '';
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    systemd.services.rxe = {
+      description = "RoCE interfaces";
+
+      wantedBy = [ "multi-user.target" ];
+      after = [ "systemd-modules-load.service" "network-online.target" ];
+      wants = [ "network-pre.target" ];
+
+      serviceConfig = {
+        Type = "oneshot";
+        RemainAfterExit = true;
+        ExecStart = map ( x:
+          "${pkgs.iproute}/bin/rdma link add rxe_${x} type rxe netdev ${x}"
+          ) cfg.interfaces;
+
+        ExecStop = map ( x:
+          "${pkgs.iproute}/bin/rdma link delete rxe_${x}"
+          ) cfg.interfaces;
+      };
+    };
+  };
+}
+
diff --git a/nixpkgs/nixos/modules/services/networking/sabnzbd.nix b/nixpkgs/nixos/modules/services/networking/sabnzbd.nix
new file mode 100644
index 000000000000..ff5aef7d1cb4
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/sabnzbd.nix
@@ -0,0 +1,67 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.sabnzbd;
+  inherit (pkgs) sabnzbd;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+    services.sabnzbd = {
+      enable = mkEnableOption "the sabnzbd server";
+
+      configFile = mkOption {
+        default = "/var/lib/sabnzbd/sabnzbd.ini";
+        description = "Path to config file.";
+      };
+
+      user = mkOption {
+        default = "sabnzbd";
+        description = "User to run the service as";
+      };
+
+      group = mkOption {
+        default = "sabnzbd";
+        description = "Group to run the service as";
+      };
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    users.users.sabnzbd = {
+          uid = config.ids.uids.sabnzbd;
+          group = "sabnzbd";
+          description = "sabnzbd user";
+          home = "/var/lib/sabnzbd/";
+          createHome = true;
+    };
+
+    users.groups.sabnzbd = {
+      gid = config.ids.gids.sabnzbd;
+    };
+
+    systemd.services.sabnzbd = {
+        description = "sabnzbd server";
+        wantedBy    = [ "multi-user.target" ];
+        after = [ "network.target" ];
+        serviceConfig = {
+          Type = "forking";
+          GuessMainPID = "no";
+          User = "${cfg.user}";
+          Group = "${cfg.group}";
+          ExecStart = "${sabnzbd}/bin/sabnzbd -d -f ${cfg.configFile}";
+        };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/searx.nix b/nixpkgs/nixos/modules/services/networking/searx.nix
new file mode 100644
index 000000000000..60fb3d5d6d44
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/searx.nix
@@ -0,0 +1,80 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.searx;
+
+  configFile = cfg.configFile;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.searx = {
+
+      enable = mkEnableOption
+        "the searx server. See https://github.com/asciimoo/searx";
+
+      configFile = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        description = "
+          The path of the Searx server configuration file. If no file
+          is specified, a default file is used (default config file has
+          debug mode enabled).
+        ";
+      };
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.searx;
+        defaultText = "pkgs.searx";
+        description = "searx package to use.";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.searx.enable {
+
+    users.users.searx =
+      { uid = config.ids.uids.searx;
+        description = "Searx user";
+        createHome = true;
+        home = "/var/lib/searx";
+      };
+
+    users.groups.searx =
+      { gid = config.ids.gids.searx;
+      };
+
+    systemd.services.searx =
+      {
+        description = "Searx server, the meta search engine.";
+        after = [ "network.target" ];
+        wantedBy = [ "multi-user.target" ];
+        serviceConfig = {
+          User = "searx";
+          ExecStart = "${cfg.package}/bin/searx-run";
+        };
+      } // (optionalAttrs (configFile != null) {
+        environment.SEARX_SETTINGS_PATH = configFile;
+      });
+
+    environment.systemPackages = [ cfg.package ];
+
+  };
+
+  meta.maintainers = with lib.maintainers; [ rnhmjoj ];
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/seeks.nix b/nixpkgs/nixos/modules/services/networking/seeks.nix
new file mode 100644
index 000000000000..40729225b6d0
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/seeks.nix
@@ -0,0 +1,75 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.seeks;
+
+  confDir = cfg.confDir;
+
+  seeks = pkgs.seeks.override { seeks_confDir = confDir; };
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.seeks = {
+
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = "
+          Whether to enable the Seeks server.
+        ";
+      };
+
+      confDir = mkOption {
+        default = "";
+        type = types.str;
+        description = "
+          The Seeks server configuration. If it is not specified,
+          a default configuration is used.
+        ";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.seeks.enable {
+
+    users.users.seeks =
+      { uid = config.ids.uids.seeks;
+        description = "Seeks user";
+        createHome = true;
+        home = "/var/lib/seeks";
+      };
+
+    users.groups.seeks =
+      { gid = config.ids.gids.seeks;
+      };
+
+    systemd.services.seeks =
+      {
+        description = "Seeks server, the p2p search engine.";
+        after = [ "network.target" ];
+        wantedBy = [ "multi-user.target" ];
+        serviceConfig = {
+          User = "seeks";
+          ExecStart = "${seeks}/bin/seeks";
+        };
+      };
+
+    environment.systemPackages = [ seeks ];
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/shadowsocks.nix b/nixpkgs/nixos/modules/services/networking/shadowsocks.nix
new file mode 100644
index 000000000000..af12db590f00
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/shadowsocks.nix
@@ -0,0 +1,112 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.shadowsocks;
+
+  opts = {
+    server = cfg.localAddress;
+    server_port = cfg.port;
+    method = cfg.encryptionMethod;
+    mode = cfg.mode;
+    user = "nobody";
+    fast_open = true;
+  } // optionalAttrs (cfg.password != null) { password = cfg.password; };
+
+  configFile = pkgs.writeText "shadowsocks.json" (builtins.toJSON opts);
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.shadowsocks = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to run shadowsocks-libev shadowsocks server.
+        '';
+      };
+
+      localAddress = mkOption {
+        type = types.coercedTo types.str singleton (types.listOf types.str);
+        default = [ "[::0]" "0.0.0.0" ];
+        description = ''
+          Local addresses to which the server binds.
+        '';
+      };
+
+      port = mkOption {
+        type = types.int;
+        default = 8388;
+        description = ''
+          Port which the server uses.
+        '';
+      };
+
+      password = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          Password for connecting clients.
+        '';
+      };
+
+      passwordFile = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        description = ''
+          Password file with a password for connecting clients.
+        '';
+      };
+
+      mode = mkOption {
+        type = types.enum [ "tcp_only" "tcp_and_udp" "udp_only" ];
+        default = "tcp_and_udp";
+        description = ''
+          Relay protocols.
+        '';
+      };
+
+      encryptionMethod = mkOption {
+        type = types.str;
+        default = "chacha20-ietf-poly1305";
+        description = ''
+          Encryption method. See <link xlink:href="https://github.com/shadowsocks/shadowsocks-org/wiki/AEAD-Ciphers"/>.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    assertions = singleton
+      { assertion = cfg.password == null || cfg.passwordFile == null;
+        message = "Cannot use both password and passwordFile for shadowsocks-libev";
+      };
+
+    systemd.services.shadowsocks-libev = {
+      description = "shadowsocks-libev Daemon";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      path = [ pkgs.shadowsocks-libev ] ++ optional (cfg.passwordFile != null) pkgs.jq;
+      serviceConfig.PrivateTmp = true;
+      script = ''
+        ${optionalString (cfg.passwordFile != null) ''
+          cat ${configFile} | jq --arg password "$(cat "${cfg.passwordFile}")" '. + { password: $password }' > /tmp/shadowsocks.json
+        ''}
+        exec ss-server -c ${if cfg.passwordFile != null then "/tmp/shadowsocks.json" else configFile}
+      '';
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/shairport-sync.nix b/nixpkgs/nixos/modules/services/networking/shairport-sync.nix
new file mode 100644
index 000000000000..b4b86a2d55be
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/shairport-sync.nix
@@ -0,0 +1,83 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.shairport-sync;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.shairport-sync = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable the shairport-sync daemon.
+
+          Running with a local system-wide or remote pulseaudio server
+          is recommended.
+        '';
+      };
+
+      arguments = mkOption {
+        default = "-v -o pa";
+        description = ''
+          Arguments to pass to the daemon. Defaults to a local pulseaudio
+          server.
+        '';
+      };
+
+      user = mkOption {
+        default = "shairport";
+        description = ''
+          User account name under which to run shairport-sync. The account
+          will be created.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.shairport-sync.enable {
+
+    services.avahi.enable = true;
+    services.avahi.publish.enable = true;
+    services.avahi.publish.userServices = true;
+
+    users.users.${cfg.user} =
+      { description = "Shairport user";
+        isSystemUser = true;
+        createHome = true;
+        home = "/var/lib/shairport-sync";
+        extraGroups = [ "audio" ] ++ optional config.hardware.pulseaudio.enable "pulse";
+      };
+
+    systemd.services.shairport-sync =
+      {
+        description = "shairport-sync";
+        after = [ "network.target" "avahi-daemon.service" ];
+        wantedBy = [ "multi-user.target" ];
+        serviceConfig = {
+          User = cfg.user;
+          ExecStart = "${pkgs.shairport-sync}/bin/shairport-sync ${cfg.arguments}";
+          RuntimeDirectory = "shairport-sync";
+        };
+      };
+
+    environment.systemPackages = [ pkgs.shairport-sync ];
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/shorewall.nix b/nixpkgs/nixos/modules/services/networking/shorewall.nix
new file mode 100644
index 000000000000..16383be2530f
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/shorewall.nix
@@ -0,0 +1,70 @@
+{ config, lib, pkgs, ... }:
+let
+  types = lib.types;
+  cfg = config.services.shorewall;
+in {
+  options = {
+    services.shorewall = {
+      enable = lib.mkOption {
+        type        = types.bool;
+        default     = false;
+        description = ''
+          Whether to enable Shorewall IPv4 Firewall.
+          <warning>
+            <para>
+            Enabling this service WILL disable the existing NixOS
+            firewall! Default firewall rules provided by packages are not
+            considered at the moment.
+            </para>
+          </warning>
+        '';
+      };
+      package = lib.mkOption {
+        type        = types.package;
+        default     = pkgs.shorewall;
+        defaultText = "pkgs.shorewall";
+        description = "The shorewall package to use.";
+      };
+      configs = lib.mkOption {
+        type        = types.attrsOf types.lines;
+        default     = {};
+        description = ''
+          This option defines the Shorewall configs.
+          The attribute name defines the name of the config,
+          and the attribute value defines the content of the config.
+        '';
+        apply = lib.mapAttrs (name: text: pkgs.writeText "${name}" text);
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    systemd.services.firewall.enable = false;
+    systemd.services.shorewall = {
+      description     = "Shorewall IPv4 Firewall";
+      after           = [ "ipset.target" ];
+      before          = [ "network-pre.target" ];
+      wants           = [ "network-pre.target" ];
+      wantedBy        = [ "multi-user.target" ];
+      reloadIfChanged = true;
+      restartTriggers = lib.attrValues cfg.configs;
+      serviceConfig = {
+        Type            = "oneshot";
+        RemainAfterExit = "yes";
+        ExecStart       = "${cfg.package}/bin/shorewall start";
+        ExecReload      = "${cfg.package}/bin/shorewall reload";
+        ExecStop        = "${cfg.package}/bin/shorewall stop";
+      };
+      preStart = ''
+        install -D -d -m 750 /var/lib/shorewall
+        install -D -d -m 755 /var/lock/subsys
+        touch                /var/log/shorewall.log
+        chown 750            /var/log/shorewall.log
+      '';
+    };
+    environment = {
+      etc = lib.mapAttrs' (name: conf: lib.nameValuePair "shorewall/${name}" {source=conf;}) cfg.configs;
+      systemPackages = [ cfg.package ];
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/shorewall6.nix b/nixpkgs/nixos/modules/services/networking/shorewall6.nix
new file mode 100644
index 000000000000..e081aedc6c34
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/shorewall6.nix
@@ -0,0 +1,70 @@
+{ config, lib, pkgs, ... }:
+let
+  types = lib.types;
+  cfg = config.services.shorewall6;
+in {
+  options = {
+    services.shorewall6 = {
+      enable = lib.mkOption {
+        type        = types.bool;
+        default     = false;
+        description = ''
+          Whether to enable Shorewall IPv6 Firewall.
+          <warning>
+            <para>
+            Enabling this service WILL disable the existing NixOS
+            firewall! Default firewall rules provided by packages are not
+            considered at the moment.
+            </para>
+          </warning>
+        '';
+      };
+      package = lib.mkOption {
+        type        = types.package;
+        default     = pkgs.shorewall;
+        defaultText = "pkgs.shorewall";
+        description = "The shorewall package to use.";
+      };
+      configs = lib.mkOption {
+        type        = types.attrsOf types.lines;
+        default     = {};
+        description = ''
+          This option defines the Shorewall configs.
+          The attribute name defines the name of the config,
+          and the attribute value defines the content of the config.
+        '';
+        apply = lib.mapAttrs (name: text: pkgs.writeText "${name}" text);
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    systemd.services.firewall.enable = false;
+    systemd.services.shorewall6 = {
+      description     = "Shorewall IPv6 Firewall";
+      after           = [ "ipset.target" ];
+      before          = [ "network-pre.target" ];
+      wants           = [ "network-pre.target" ];
+      wantedBy        = [ "multi-user.target" ];
+      reloadIfChanged = true;
+      restartTriggers = lib.attrValues cfg.configs;
+      serviceConfig = {
+        Type            = "oneshot";
+        RemainAfterExit = "yes";
+        ExecStart       = "${cfg.package}/bin/shorewall6 start";
+        ExecReload      = "${cfg.package}/bin/shorewall6 reload";
+        ExecStop        = "${cfg.package}/bin/shorewall6 stop";
+      };
+      preStart = ''
+        install -D -d -m 750 /var/lib/shorewall6
+        install -D -d -m 755 /var/lock/subsys
+        touch                /var/log/shorewall6.log
+        chown 750            /var/log/shorewall6.log
+      '';
+    };
+    environment = {
+      etc = lib.mapAttrs' (name: conf: lib.nameValuePair "shorewall6/${name}" {source=conf;}) cfg.configs;
+      systemPackages = [ cfg.package ];
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/shout.nix b/nixpkgs/nixos/modules/services/networking/shout.nix
new file mode 100644
index 000000000000..a808a7f39d05
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/shout.nix
@@ -0,0 +1,113 @@
+{ pkgs, lib, config, ... }:
+
+with lib;
+
+let
+  cfg = config.services.shout;
+  shoutHome = "/var/lib/shout";
+
+  defaultConfig = pkgs.runCommand "config.js" { preferLocalBuild = true; } ''
+    EDITOR=true ${pkgs.shout}/bin/shout config --home $PWD
+    mv config.js $out
+  '';
+
+  finalConfigFile = if (cfg.configFile != null) then cfg.configFile else ''
+    var _ = require('${pkgs.shout}/lib/node_modules/shout/node_modules/lodash')
+
+    module.exports = _.merge(
+      {},
+      require('${defaultConfig}'),
+      ${builtins.toJSON cfg.config}
+    )
+  '';
+
+in {
+  options.services.shout = {
+    enable = mkEnableOption "Shout web IRC client";
+
+    private = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Make your shout instance private. You will need to configure user
+        accounts by adding entries in <filename>${shoutHome}/users</filename>.
+      '';
+    };
+
+    listenAddress = mkOption {
+      type = types.str;
+      default = "0.0.0.0";
+      description = "IP interface to listen on for http connections.";
+    };
+
+    port = mkOption {
+      type = types.int;
+      default = 9000;
+      description = "TCP port to listen on for http connections.";
+    };
+
+    configFile = mkOption {
+      type = types.nullOr types.lines;
+      default = null;
+      description = ''
+        Contents of Shout's <filename>config.js</filename> file.
+
+        Used for backward compatibility, recommended way is now to use
+        the <literal>config</literal> option.
+
+        Documentation: http://shout-irc.com/docs/server/configuration.html
+      '';
+    };
+
+    config = mkOption {
+      default = {};
+      type = types.attrs;
+      example = {
+        displayNetwork = false;
+        defaults = {
+          name = "Your Network";
+          host = "localhost";
+          port = 6697;
+        };
+      };
+      description = ''
+        Shout <filename>config.js</filename> contents as attribute set (will be
+        converted to JSON to generate the configuration file).
+
+        The options defined here will be merged to the default configuration file.
+
+        Documentation: http://shout-irc.com/docs/server/configuration.html
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.users.shout = {
+      uid = config.ids.uids.shout;
+      description = "Shout daemon user";
+      home = shoutHome;
+      createHome = true;
+    };
+
+    systemd.services.shout = {
+      description = "Shout web IRC client";
+      wantedBy = [ "multi-user.target" ];
+      wants = [ "network-online.target" ];
+      after = [ "network-online.target" ];
+      preStart = "ln -sf ${pkgs.writeText "config.js" finalConfigFile} ${shoutHome}/config.js";
+      script = concatStringsSep " " [
+        "${pkgs.shout}/bin/shout"
+        (if cfg.private then "--private" else "--public")
+        "--port" (toString cfg.port)
+        "--host" (toString cfg.listenAddress)
+        "--home" shoutHome
+      ];
+      serviceConfig = {
+        User = "shout";
+        ProtectHome = "true";
+        ProtectSystem = "full";
+        PrivateTmp = "true";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/skydns.nix b/nixpkgs/nixos/modules/services/networking/skydns.nix
new file mode 100644
index 000000000000..e79d6de92644
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/skydns.nix
@@ -0,0 +1,92 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.services.skydns;
+
+in {
+  options.services.skydns = {
+    enable = mkEnableOption "skydns service";
+
+    etcd = {
+      machines = mkOption {
+        default = [ "http://127.0.0.1:2379" ];
+        type = types.listOf types.str;
+        description = "Skydns list of etcd endpoints to connect to.";
+      };
+
+      tlsKey = mkOption {
+        default = null;
+        type = types.nullOr types.path;
+        description = "Skydns path of TLS client certificate - private key.";
+      };
+
+      tlsPem = mkOption {
+        default = null;
+        type = types.nullOr types.path;
+        description = "Skydns path of TLS client certificate - public key.";
+      };
+
+      caCert = mkOption {
+        default = null;
+        type = types.nullOr types.path;
+        description = "Skydns path of TLS certificate authority public key.";
+      };
+    };
+
+    address = mkOption {
+      default = "0.0.0.0:53";
+      type = types.str;
+      description = "Skydns address to bind to.";
+    };
+
+    domain = mkOption {
+      default = "skydns.local.";
+      type = types.str;
+      description = "Skydns default domain if not specified by etcd config.";
+    };
+
+    nameservers = mkOption {
+      default = map (n: n + ":53") config.networking.nameservers;
+      type = types.listOf types.str;
+      description = "Skydns list of nameservers to forward DNS requests to when not authoritative for a domain.";
+      example = ["8.8.8.8:53" "8.8.4.4:53"];
+    };
+
+    package = mkOption {
+      default = pkgs.skydns;
+      defaultText = "pkgs.skydns";
+      type = types.package;
+      description = "Skydns package to use.";
+    };
+
+    extraConfig = mkOption {
+      default = {};
+      type = types.attrsOf types.str;
+      description = "Skydns attribute set of extra config options passed as environemnt variables.";
+    };
+  };
+
+  config = mkIf (cfg.enable) {
+    systemd.services.skydns = {
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" "etcd.service" ];
+      description = "Skydns Service";
+      environment = {
+        ETCD_MACHINES = concatStringsSep "," cfg.etcd.machines;
+        ETCD_TLSKEY = cfg.etcd.tlsKey;
+        ETCD_TLSPEM = cfg.etcd.tlsPem;
+        ETCD_CACERT = cfg.etcd.caCert;
+        SKYDNS_ADDR = cfg.address;
+        SKYDNS_DOMAIN = cfg.domain;
+        SKYDNS_NAMESERVERS = concatStringsSep "," cfg.nameservers;
+      };
+      serviceConfig = {
+        ExecStart = "${cfg.package}/bin/skydns";
+      };
+    };
+
+    environment.systemPackages = [ cfg.package ];
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/smartdns.nix b/nixpkgs/nixos/modules/services/networking/smartdns.nix
new file mode 100644
index 000000000000..f1888af70416
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/smartdns.nix
@@ -0,0 +1,61 @@
+{ lib, pkgs, config, ... }:
+
+with lib;
+
+let
+  inherit (lib.types) attrsOf coercedTo listOf oneOf str int bool;
+  cfg = config.services.smartdns;
+
+  confFile = pkgs.writeText "smartdns.conf" (with generators;
+    toKeyValue {
+      mkKeyValue = mkKeyValueDefault {
+        mkValueString = v:
+          if isBool v then
+            if v then "yes" else "no"
+          else
+            mkValueStringDefault { } v;
+      } " ";
+      listsAsDuplicateKeys =
+        true; # Allowing duplications because we need to deal with multiple entries with the same key.
+    } cfg.settings);
+in {
+  options.services.smartdns = {
+    enable = mkEnableOption "SmartDNS DNS server";
+
+    bindPort = mkOption {
+      type = types.port;
+      default = 53;
+      description = "DNS listening port number.";
+    };
+
+    settings = mkOption {
+      type =
+      let atom = oneOf [ str int bool ];
+      in attrsOf (coercedTo atom toList (listOf atom));
+      example = literalExample ''
+        {
+          bind = ":5353 -no-rule -group example";
+          cache-size = 4096;
+          server-tls = [ "8.8.8.8:853" "1.1.1.1:853" ];
+          server-https = "https://cloudflare-dns.com/dns-query -exclude-default-group";
+          prefetch-domain = true;
+          speed-check-mode = "ping,tcp:80";
+        };
+      '';
+      description = ''
+        A set that will be generated into configuration file, see the <link xlink:href="https://github.com/pymumu/smartdns/blob/master/ReadMe_en.md#configuration-parameter">SmartDNS README</link> for details of configuration parameters.
+        You could override the options here like <option>services.smartdns.bindPort</option> by writing <literal>settings.bind = ":5353 -no-rule -group example";</literal>.
+      '';
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    services.smartdns.settings.bind = mkDefault ":${toString cfg.bindPort}";
+
+    systemd.packages = [ pkgs.smartdns ];
+    systemd.services.smartdns.wantedBy = [ "multi-user.target" ];
+    environment.etc."smartdns/smartdns.conf".source = confFile;
+    environment.etc."default/smartdns".source =
+      "${pkgs.smartdns}/etc/default/smartdns";
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/smokeping.nix b/nixpkgs/nixos/modules/services/networking/smokeping.nix
new file mode 100644
index 000000000000..37ee2a803890
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/smokeping.nix
@@ -0,0 +1,320 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+
+  cfg = config.services.smokeping;
+  smokepingHome = "/var/lib/smokeping";
+  smokepingPidDir = "/run";
+  configFile =
+    if cfg.config == null
+      then
+        ''
+          *** General ***
+          cgiurl   = ${cfg.cgiUrl}
+          contact = ${cfg.ownerEmail}
+          datadir  = ${smokepingHome}/data
+          imgcache = ${smokepingHome}/cache
+          imgurl   = ${cfg.imgUrl}
+          linkstyle = ${cfg.linkStyle}
+          ${lib.optionalString (cfg.mailHost != "") "mailhost = ${cfg.mailHost}"}
+          owner = ${cfg.owner}
+          pagedir = ${smokepingHome}/cache
+          piddir  = ${smokepingPidDir}
+          ${lib.optionalString (cfg.sendmail != null) "sendmail = ${cfg.sendmail}"}
+          smokemail = ${cfg.smokeMailTemplate}
+          *** Presentation ***
+          template = ${cfg.presentationTemplate}
+          ${cfg.presentationConfig}
+          *** Alerts ***
+          ${cfg.alertConfig}
+          *** Database ***
+          ${cfg.databaseConfig}
+          *** Probes ***
+          ${cfg.probeConfig}
+          *** Targets ***
+          ${cfg.targetConfig}
+          ${cfg.extraConfig}
+        ''
+      else
+        cfg.config;
+
+  configPath = pkgs.writeText "smokeping.conf" configFile;
+  cgiHome = pkgs.writeScript "smokeping.fcgi" ''
+    #!${pkgs.bash}/bin/bash
+    ${cfg.package}/bin/smokeping_cgi ${configPath}
+  '';
+in
+
+{
+  options = {
+    services.smokeping = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Enable the smokeping service";
+      };
+      alertConfig = mkOption {
+        type = types.lines;
+        default = ''
+          to = root@localhost
+          from = smokeping@localhost
+        '';
+        example = literalExample ''
+          to = alertee@address.somewhere
+          from = smokealert@company.xy
+
+          +someloss
+          type = loss
+          # in percent
+          pattern = >0%,*12*,>0%,*12*,>0%
+          comment = loss 3 times  in a row;
+        '';
+        description = "Configuration for alerts.";
+      };
+      cgiUrl = mkOption {
+        type = types.str;
+        default = "http://${cfg.hostName}:${toString cfg.port}/smokeping.cgi";
+        defaultText = "http://\${hostName}:\${toString port}/smokeping.cgi";
+        example = "https://somewhere.example.com/smokeping.cgi";
+        description = "URL to the smokeping cgi.";
+      };
+      config = mkOption {
+        type = types.nullOr types.lines;
+        default = null;
+        description = "Full smokeping config supplied by the user. Overrides " +
+          "and replaces any other configuration supplied.";
+      };
+      databaseConfig = mkOption {
+        type = types.lines;
+        default = ''
+          step     = 300
+          pings    = 20
+          # consfn mrhb steps total
+          AVERAGE  0.5   1  1008
+          AVERAGE  0.5  12  4320
+              MIN  0.5  12  4320
+              MAX  0.5  12  4320
+          AVERAGE  0.5 144   720
+              MAX  0.5 144   720
+              MIN  0.5 144   720
+
+        '';
+        example = literalExample ''
+          # near constant pings.
+          step     = 30
+          pings    = 20
+          # consfn mrhb steps total
+          AVERAGE  0.5   1  10080
+          AVERAGE  0.5  12  43200
+              MIN  0.5  12  43200
+              MAX  0.5  12  43200
+          AVERAGE  0.5 144   7200
+              MAX  0.5 144   7200
+              MIN  0.5 144   7200
+        '';
+        description = ''Configure the ping frequency and retention of the rrd files.
+          Once set, changing the interval will require deletion or migration of all
+          the collected data.'';
+      };
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = "Any additional customization not already included.";
+      };
+      hostName = mkOption {
+        type = types.str;
+        default = config.networking.hostName;
+        example = "somewhere.example.com";
+        description = "DNS name for the urls generated in the cgi.";
+      };
+      imgUrl = mkOption {
+        type = types.str;
+        default = "http://${cfg.hostName}:${toString cfg.port}/cache";
+        defaultText = "http://\${hostName}:\${toString port}/cache";
+        example = "https://somewhere.example.com/cache";
+        description = "Base url for images generated in the cgi.";
+      };
+      linkStyle = mkOption {
+        type = types.enum ["original" "absolute" "relative"];
+        default = "relative";
+        example = "absolute";
+        description = "DNS name for the urls generated in the cgi.";
+      };
+      mailHost = mkOption {
+        type = types.str;
+        default = "";
+        example = "localhost";
+        description = "Use this SMTP server to send alerts";
+      };
+      owner = mkOption {
+        type = types.str;
+        default = "nobody";
+        example = "Joe Admin";
+        description = "Real name of the owner of the instance";
+      };
+      ownerEmail = mkOption {
+        type = types.str;
+        default = "no-reply@${cfg.hostName}";
+        example = "no-reply@yourdomain.com";
+        description = "Email contact for owner";
+      };
+      package = mkOption {
+        type = types.package;
+        default = pkgs.smokeping;
+        defaultText = "pkgs.smokeping";
+        description = "Specify a custom smokeping package";
+      };
+      port = mkOption {
+        type = types.int;
+        default = 8081;
+        example = 8081;
+        description = "TCP port to use for the web server.";
+      };
+      presentationConfig = mkOption {
+        type = types.lines;
+        default = ''
+          + charts
+          menu = Charts
+          title = The most interesting destinations
+          ++ stddev
+          sorter = StdDev(entries=>4)
+          title = Top Standard Deviation
+          menu = Std Deviation
+          format = Standard Deviation %f
+          ++ max
+          sorter = Max(entries=>5)
+          title = Top Max Roundtrip Time
+          menu = by Max
+          format = Max Roundtrip Time %f seconds
+          ++ loss
+          sorter = Loss(entries=>5)
+          title = Top Packet Loss
+          menu = Loss
+          format = Packets Lost %f
+          ++ median
+          sorter = Median(entries=>5)
+          title = Top Median Roundtrip Time
+          menu = by Median
+          format = Median RTT %f seconds
+          + overview
+          width = 600
+          height = 50
+          range = 10h
+          + detail
+          width = 600
+          height = 200
+          unison_tolerance = 2
+          "Last 3 Hours"    3h
+          "Last 30 Hours"   30h
+          "Last 10 Days"    10d
+          "Last 360 Days"   360d
+        '';
+        description = "presentation graph style";
+      };
+      presentationTemplate = mkOption {
+        type = types.str;
+        default = "${pkgs.smokeping}/etc/basepage.html.dist";
+        description = "Default page layout for the web UI.";
+      };
+      probeConfig = mkOption {
+        type = types.lines;
+        default = ''
+          + FPing
+          binary = ${config.security.wrapperDir}/fping
+        '';
+        description = "Probe configuration";
+      };
+      sendmail = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        example = "/run/wrappers/bin/sendmail";
+        description = "Use this sendmail compatible script to deliver alerts";
+      };
+      smokeMailTemplate = mkOption {
+        type = types.str;
+        default = "${cfg.package}/etc/smokemail.dist";
+        description = "Specify the smokemail template for alerts.";
+      };
+      targetConfig = mkOption {
+        type = types.lines;
+        default = ''
+					probe = FPing
+					menu = Top
+					title = Network Latency Grapher
+					remark = Welcome to the SmokePing website of xxx Company. \
+									 Here you will learn all about the latency of our network.
+					+ Local
+					menu = Local
+					title = Local Network
+					++ LocalMachine
+					menu = Local Machine
+					title = This host
+					host = localhost
+        '';
+        description = "Target configuration";
+      };
+      user = mkOption {
+        type = types.str;
+        default = "smokeping";
+        description = "User that runs smokeping and (optionally) thttpd";
+      };
+      webService = mkOption {
+        type = types.bool;
+        default = true;
+        description = "Enable a smokeping web interface";
+      };
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+    assertions = [
+      {
+        assertion = !(cfg.sendmail != null && cfg.mailHost != "");
+        message = "services.smokeping: sendmail and Mailhost cannot both be enabled.";
+      }
+    ];
+    security.wrappers = {
+      fping.source = "${pkgs.fping}/bin/fping";
+      fping6.source = "${pkgs.fping}/bin/fping6";
+    };
+    environment.systemPackages = [ pkgs.fping ];
+    users.users.${cfg.user} = {
+      isNormalUser = false;
+      isSystemUser = true;
+      uid = config.ids.uids.smokeping;
+      description = "smokeping daemon user";
+      home = smokepingHome;
+      createHome = true;
+    };
+    systemd.services.smokeping = {
+      wantedBy = [ "multi-user.target"];
+      serviceConfig = {
+        User = cfg.user;
+        Restart = "on-failure";
+      };
+      preStart = ''
+        mkdir -m 0755 -p ${smokepingHome}/cache ${smokepingHome}/data
+        rm -f ${smokepingHome}/cropper
+        ln -s ${cfg.package}/htdocs/cropper ${smokepingHome}/cropper
+        rm -f ${smokepingHome}/smokeping.fcgi
+        ln -s ${cgiHome} ${smokepingHome}/smokeping.fcgi
+        ${cfg.package}/bin/smokeping --check --config=${configPath}
+        ${cfg.package}/bin/smokeping --static --config=${configPath}
+      '';
+      script = ''${cfg.package}/bin/smokeping --config=${configPath} --nodaemon'';
+    };
+    systemd.services.thttpd = mkIf cfg.webService {
+      wantedBy = [ "multi-user.target"];
+      requires = [ "smokeping.service"];
+      partOf = [ "smokeping.service"];
+      path = with pkgs; [ bash rrdtool smokeping thttpd ];
+      script = ''thttpd -u ${cfg.user} -c "**.fcgi" -d ${smokepingHome} -p ${builtins.toString cfg.port} -D -nos'';
+      serviceConfig.Restart = "always";
+    };
+  };
+
+  meta.maintainers = with lib.maintainers; [ erictapen ];
+}
+
diff --git a/nixpkgs/nixos/modules/services/networking/sniproxy.nix b/nixpkgs/nixos/modules/services/networking/sniproxy.nix
new file mode 100644
index 000000000000..0345c12d3afe
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/sniproxy.nix
@@ -0,0 +1,99 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.sniproxy;
+
+  configFile = pkgs.writeText "sniproxy.conf" ''
+    user ${cfg.user}
+    pidfile /run/sniproxy.pid
+    ${cfg.config}
+  '';
+
+in
+{
+  options = {
+    services.sniproxy = {
+      enable = mkEnableOption "sniproxy server";
+
+      user = mkOption {
+        type = types.str;
+        default = "sniproxy";
+        description = "User account under which sniproxy runs.";
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "sniproxy";
+        description = "Group under which sniproxy runs.";
+      };
+
+      config = mkOption {
+        type = types.lines;
+        default = "";
+        description = "sniproxy.conf configuration excluding the daemon username and pid file.";
+        example = literalExample ''
+          error_log {
+            filename /var/log/sniproxy/error.log
+          }
+          access_log {
+            filename /var/log/sniproxy/access.log
+          }
+          listen 443 {
+            proto tls
+          }
+          table {
+            example.com 192.0.2.10
+            example.net 192.0.2.20
+        }
+        '';
+      };
+
+      logDir = mkOption {
+        type = types.str;
+        default = "/var/log/sniproxy/";
+        description = "Location of the log directory for sniproxy.";
+      };
+
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.sniproxy = {
+      description = "sniproxy server";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      preStart = ''
+        test -d ${cfg.logDir} || {
+          echo "Creating initial log directory for sniproxy in ${cfg.logDir}"
+          mkdir -p ${cfg.logDir}
+          chmod 640 ${cfg.logDir}
+          }
+        chown -R ${cfg.user}:${cfg.group} ${cfg.logDir}
+      '';
+
+      serviceConfig = {
+        Type = "forking";
+        ExecStart = "${pkgs.sniproxy}/bin/sniproxy -c ${configFile}";
+        Restart = "always";
+      };
+    };
+
+    users.users = mkIf (cfg.user == "sniproxy") {
+      sniproxy = {
+        group = cfg.group;
+        uid = config.ids.uids.sniproxy;
+      };
+    };
+
+    users.groups = mkIf (cfg.group == "sniproxy") {
+      sniproxy = {
+        gid = config.ids.gids.sniproxy;
+      };
+    };
+
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/softether.nix b/nixpkgs/nixos/modules/services/networking/softether.nix
new file mode 100644
index 000000000000..2dc73d81b258
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/softether.nix
@@ -0,0 +1,163 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.softether;
+
+  package = cfg.package.override { dataDir = cfg.dataDir; };
+
+in
+{
+
+  ###### interface
+
+  options = {
+
+    services.softether = {
+
+      enable = mkEnableOption "SoftEther VPN services";
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.softether;
+        defaultText = "pkgs.softether";
+        description = ''
+          softether derivation to use.
+        '';
+      };
+
+      vpnserver.enable = mkEnableOption "SoftEther VPN Server";
+
+      vpnbridge.enable = mkEnableOption "SoftEther VPN Bridge";
+
+      vpnclient = {
+        enable = mkEnableOption "SoftEther VPN Client";
+        up = mkOption {
+          type = types.lines;
+          default = "";
+          description = ''
+            Shell commands executed when the Virtual Network Adapter(s) is/are starting.
+          '';
+        };
+        down = mkOption {
+          type = types.lines;
+          default = "";
+          description = ''
+            Shell commands executed when the Virtual Network Adapter(s) is/are shutting down.
+          '';
+        };
+      };
+
+      dataDir = mkOption {
+        type = types.path;
+        default = "/var/lib/softether";
+        description = ''
+          Data directory for SoftEther VPN.
+        '';
+      };
+
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable (
+
+    mkMerge [{
+      environment.systemPackages = [ package ];
+
+      systemd.services.softether-init = {
+        description = "SoftEther VPN services initial task";
+        wantedBy = [ "network.target" ];
+        serviceConfig = {
+          Type = "oneshot";
+          RemainAfterExit = false;
+        };
+        script = ''
+            for d in vpnserver vpnbridge vpnclient vpncmd; do
+                if ! test -e ${cfg.dataDir}/$d; then
+                    ${pkgs.coreutils}/bin/mkdir -m0700 -p ${cfg.dataDir}/$d
+                    install -m0600 ${package}${cfg.dataDir}/$d/hamcore.se2 ${cfg.dataDir}/$d/hamcore.se2
+                fi
+            done
+            rm -rf ${cfg.dataDir}/vpncmd/vpncmd
+            ln -s ${package}${cfg.dataDir}/vpncmd/vpncmd ${cfg.dataDir}/vpncmd/vpncmd
+        '';
+      };
+    }
+
+    (mkIf (cfg.vpnserver.enable) {
+      systemd.services.vpnserver = {
+        description = "SoftEther VPN Server";
+        after = [ "softether-init.service" ];
+        requires = [ "softether-init.service" ];
+        wantedBy = [ "network.target" ];
+        serviceConfig = {
+          Type = "forking";
+          ExecStart = "${package}/bin/vpnserver start";
+          ExecStop = "${package}/bin/vpnserver stop";
+        };
+        preStart = ''
+            rm -rf ${cfg.dataDir}/vpnserver/vpnserver
+            ln -s ${package}${cfg.dataDir}/vpnserver/vpnserver ${cfg.dataDir}/vpnserver/vpnserver
+        '';
+        postStop = ''
+            rm -rf ${cfg.dataDir}/vpnserver/vpnserver
+        '';
+      };
+    })
+
+    (mkIf (cfg.vpnbridge.enable) {
+      systemd.services.vpnbridge = {
+        description = "SoftEther VPN Bridge";
+        after = [ "softether-init.service" ];
+        requires = [ "softether-init.service" ];
+        wantedBy = [ "network.target" ];
+        serviceConfig = {
+          Type = "forking";
+          ExecStart = "${package}/bin/vpnbridge start";
+          ExecStop = "${package}/bin/vpnbridge stop";
+        };
+        preStart = ''
+            rm -rf ${cfg.dataDir}/vpnbridge/vpnbridge
+            ln -s ${package}${cfg.dataDir}/vpnbridge/vpnbridge ${cfg.dataDir}/vpnbridge/vpnbridge
+        '';
+        postStop = ''
+            rm -rf ${cfg.dataDir}/vpnbridge/vpnbridge
+        '';
+      };
+    })
+
+    (mkIf (cfg.vpnclient.enable) {
+      systemd.services.vpnclient = {
+        description = "SoftEther VPN Client";
+        after = [ "softether-init.service" ];
+        requires = [ "softether-init.service" ];
+        wantedBy = [ "network.target" ];
+        serviceConfig = {
+          Type = "forking";
+          ExecStart = "${package}/bin/vpnclient start";
+          ExecStop = "${package}/bin/vpnclient stop";
+        };
+        preStart = ''
+            rm -rf ${cfg.dataDir}/vpnclient/vpnclient
+            ln -s ${package}${cfg.dataDir}/vpnclient/vpnclient ${cfg.dataDir}/vpnclient/vpnclient
+        '';
+        postStart = ''
+            sleep 1
+            ${cfg.vpnclient.up}
+        '';
+        postStop = ''
+            rm -rf ${cfg.dataDir}/vpnclient/vpnclient
+            sleep 1
+            ${cfg.vpnclient.down}
+        '';
+      };
+      boot.kernelModules = [ "tun" ];
+    })
+
+  ]);
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/spacecookie.nix b/nixpkgs/nixos/modules/services/networking/spacecookie.nix
new file mode 100644
index 000000000000..c4d06df6ad4a
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/spacecookie.nix
@@ -0,0 +1,83 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.spacecookie;
+  configFile = pkgs.writeText "spacecookie.json" (lib.generators.toJSON {} {
+    inherit (cfg) hostname port root;
+  });
+in {
+
+  options = {
+
+    services.spacecookie = {
+
+      enable = mkEnableOption "spacecookie";
+
+      hostname = mkOption {
+        type = types.str;
+        default = "localhost";
+        description = "The hostname the service is reachable via. Clients will use this hostname for further requests after loading the initial gopher menu.";
+      };
+
+      port = mkOption {
+        type = types.port;
+        default = 70;
+        description = "Port the gopher service should be exposed on.";
+      };
+
+      root = mkOption {
+        type = types.path;
+        default = "/srv/gopher";
+        description = "The root directory spacecookie serves via gopher.";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    systemd.sockets.spacecookie = {
+      description = "Socket for the Spacecookie Gopher Server";
+      wantedBy = [ "sockets.target" ];
+      listenStreams = [ "[::]:${toString cfg.port}" ];
+      socketConfig = {
+        BindIPv6Only = "both";
+      };
+    };
+
+    systemd.services.spacecookie = {
+      description = "Spacecookie Gopher Server";
+      wantedBy = [ "multi-user.target" ];
+      requires = [ "spacecookie.socket" ];
+
+      serviceConfig = {
+        Type = "notify";
+        ExecStart = "${pkgs.haskellPackages.spacecookie}/bin/spacecookie ${configFile}";
+        FileDescriptorStoreMax = 1;
+
+        DynamicUser = true;
+
+        ProtectSystem = "strict";
+        ProtectHome = true;
+        PrivateTmp = true;
+        PrivateDevices = true;
+        PrivateMounts = true;
+        PrivateUsers = true;
+
+        ProtectKernelTunables = true;
+        ProtectKernelModules = true;
+        ProtectControlGroups = true;
+
+        CapabilityBoundingSet = "";
+        NoNewPrivileges = true;
+        LockPersonality = true;
+        RestrictRealtime = true;
+
+        # AF_UNIX for communication with systemd
+        # AF_INET replaced by BindIPv6Only=both
+        RestrictAddressFamilies = "AF_UNIX AF_INET6";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/spiped.nix b/nixpkgs/nixos/modules/services/networking/spiped.nix
new file mode 100644
index 000000000000..e60d9abf42a6
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/spiped.nix
@@ -0,0 +1,220 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.spiped;
+in
+{
+  options = {
+    services.spiped = {
+      enable = mkOption {
+        type        = types.bool;
+        default     = false;
+        description = "Enable the spiped service module.";
+      };
+
+      config = mkOption {
+        type = types.attrsOf (types.submodule (
+          {
+            options = {
+              encrypt = mkOption {
+                type    = types.bool;
+                default = false;
+                description = ''
+                  Take unencrypted connections from the
+                  <literal>source</literal> socket and send encrypted
+                  connections to the <literal>target</literal> socket.
+                '';
+              };
+
+              decrypt = mkOption {
+                type    = types.bool;
+                default = false;
+                description = ''
+                  Take encrypted connections from the
+                  <literal>source</literal> socket and send unencrypted
+                  connections to the <literal>target</literal> socket.
+                '';
+              };
+
+              source = mkOption {
+                type    = types.str;
+                description = ''
+                  Address on which spiped should listen for incoming
+                  connections.  Must be in one of the following formats:
+                  <literal>/absolute/path/to/unix/socket</literal>,
+                  <literal>host.name:port</literal>,
+                  <literal>[ip.v4.ad.dr]:port</literal> or
+                  <literal>[ipv6::addr]:port</literal> - note that
+                  hostnames are resolved when spiped is launched and are
+                  not re-resolved later; thus if DNS entries change
+                  spiped will continue to connect to the expired
+                  address.
+                '';
+              };
+
+              target = mkOption {
+                type    = types.str;
+                description = "Address to which spiped should connect.";
+              };
+
+              keyfile = mkOption {
+                type    = types.path;
+                description = ''
+                  Name of a file containing the spiped key. As the
+                  daemon runs as the <literal>spiped</literal> user, the
+                  key file must be somewhere owned by that user. By
+                  default, we recommend putting the keys for any spipe
+                  services in <literal>/var/lib/spiped</literal>.
+                '';
+              };
+
+              timeout = mkOption {
+                type = types.int;
+                default = 5;
+                description = ''
+                  Timeout, in seconds, after which an attempt to connect to
+                  the target or a protocol handshake will be aborted (and the
+                  connection dropped) if not completed
+                '';
+              };
+
+              maxConns = mkOption {
+                type = types.int;
+                default = 100;
+                description = ''
+                  Limit on the number of simultaneous connections allowed.
+                '';
+              };
+
+              waitForDNS = mkOption {
+                type = types.bool;
+                default = false;
+                description = ''
+                  Wait for DNS. Normally when <literal>spiped</literal> is
+                  launched it resolves addresses and binds to its source
+                  socket before the parent process returns; with this option
+                  it will daemonize first and retry failed DNS lookups until
+                  they succeed. This allows <literal>spiped</literal> to
+                  launch even if DNS isn't set up yet, but at the expense of
+                  losing the guarantee that once <literal>spiped</literal> has
+                  finished launching it will be ready to create pipes.
+                '';
+              };
+
+              disableKeepalives = mkOption {
+                type = types.bool;
+                default = false;
+                description = "Disable transport layer keep-alives.";
+              };
+
+              weakHandshake = mkOption {
+                type = types.bool;
+                default = false;
+                description = ''
+                  Use fast/weak handshaking: This reduces the CPU time spent
+                  in the initial connection setup, at the expense of losing
+                  perfect forward secrecy.
+                '';
+              };
+
+              resolveRefresh = mkOption {
+                type = types.int;
+                default = 60;
+                description = ''
+                  Resolution refresh time for the target socket, in seconds.
+                '';
+              };
+
+              disableReresolution = mkOption {
+                type = types.bool;
+                default = false;
+                description = "Disable target address re-resolution.";
+              };
+            };
+          }
+        ));
+
+        default = {};
+
+        example = literalExample ''
+          {
+            pipe1 =
+              { keyfile = "/var/lib/spiped/pipe1.key";
+                encrypt = true;
+                source  = "localhost:6000";
+                target  = "endpoint.example.com:7000";
+              };
+            pipe2 =
+              { keyfile = "/var/lib/spiped/pipe2.key";
+                decrypt = true;
+                source  = "0.0.0.0:7000";
+                target  = "localhost:3000";
+              };
+          }
+        '';
+
+        description = ''
+          Configuration for a secure pipe daemon. The daemon can be
+          started, stopped, or examined using
+          <literal>systemctl</literal>, under the name
+          <literal>spiped@foo</literal>.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    assertions = mapAttrsToList (name: c: {
+      assertion = (c.encrypt -> !c.decrypt) || (c.decrypt -> c.encrypt);
+      message   = "A pipe must either encrypt or decrypt";
+    }) cfg.config;
+
+    users.groups.spiped.gid = config.ids.gids.spiped;
+    users.users.spiped = {
+      description = "Secure Pipe Service user";
+      group       = "spiped";
+      uid         = config.ids.uids.spiped;
+    };
+
+    systemd.services."spiped@" = {
+      description = "Secure pipe '%i'";
+      after       = [ "network.target" ];
+
+      serviceConfig = {
+        Restart   = "always";
+        User      = "spiped";
+        PermissionsStartOnly = true;
+      };
+
+      preStart  = ''
+        cd /var/lib/spiped
+        chmod -R 0660 *
+        chown -R spiped:spiped *
+      '';
+      scriptArgs = "%i";
+      script = "exec ${pkgs.spiped}/bin/spiped -F `cat /etc/spiped/$1.spec`";
+    };
+
+    system.activationScripts.spiped = optionalString (cfg.config != {})
+      "mkdir -p /var/lib/spiped";
+
+    # Setup spiped config files
+    environment.etc = mapAttrs' (name: cfg: nameValuePair "spiped/${name}.spec"
+      { text = concatStringsSep " "
+          [ (if cfg.encrypt then "-e" else "-d")        # Mode
+            "-s ${cfg.source}"                          # Source
+            "-t ${cfg.target}"                          # Target
+            "-k ${cfg.keyfile}"                         # Keyfile
+            "-n ${toString cfg.maxConns}"               # Max number of conns
+            "-o ${toString cfg.timeout}"                # Timeout
+            (optionalString cfg.waitForDNS "-D")        # Wait for DNS
+            (optionalString cfg.weakHandshake "-f")     # No PFS
+            (optionalString cfg.disableKeepalives "-j") # Keepalives
+            (if cfg.disableReresolution then "-R"
+              else "-r ${toString cfg.resolveRefresh}")
+          ];
+      }) cfg.config;
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/squid.nix b/nixpkgs/nixos/modules/services/networking/squid.nix
new file mode 100644
index 000000000000..9d063b92aa1e
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/squid.nix
@@ -0,0 +1,168 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.squid;
+
+
+  squidConfig = pkgs.writeText "squid.conf"
+    (if cfg.configText != null then cfg.configText else
+    ''
+    #
+    # Recommended minimum configuration (3.5):
+    #
+
+    # Example rule allowing access from your local networks.
+    # Adapt to list your (internal) IP networks from where browsing
+    # should be allowed
+    acl localnet src 10.0.0.0/8     # RFC 1918 possible internal network
+    acl localnet src 172.16.0.0/12  # RFC 1918 possible internal network
+    acl localnet src 192.168.0.0/16 # RFC 1918 possible internal network
+    acl localnet src 169.254.0.0/16 # RFC 3927 link-local (directly plugged) machines
+    acl localnet src fc00::/7       # RFC 4193 local private network range
+    acl localnet src fe80::/10      # RFC 4291 link-local (directly plugged) machines
+
+    acl SSL_ports port 443          # https
+    acl Safe_ports port 80          # http
+    acl Safe_ports port 21          # ftp
+    acl Safe_ports port 443         # https
+    acl Safe_ports port 70          # gopher
+    acl Safe_ports port 210         # wais
+    acl Safe_ports port 1025-65535  # unregistered ports
+    acl Safe_ports port 280         # http-mgmt
+    acl Safe_ports port 488         # gss-http
+    acl Safe_ports port 591         # filemaker
+    acl Safe_ports port 777         # multiling http
+    acl CONNECT method CONNECT
+
+    #
+    # Recommended minimum Access Permission configuration:
+    #
+    # Deny requests to certain unsafe ports
+    http_access deny !Safe_ports
+
+    # Deny CONNECT to other than secure SSL ports
+    http_access deny CONNECT !SSL_ports
+
+    # Only allow cachemgr access from localhost
+    http_access allow localhost manager
+    http_access deny manager
+
+    # We strongly recommend the following be uncommented to protect innocent
+    # web applications running on the proxy server who think the only
+    # one who can access services on "localhost" is a local user
+    http_access deny to_localhost
+
+    # Application logs to syslog, access and store logs have specific files
+    cache_log       syslog
+    access_log      stdio:/var/log/squid/access.log
+    cache_store_log stdio:/var/log/squid/store.log
+
+    # Required by systemd service
+    pid_filename    /run/squid.pid
+
+    # Run as user and group squid
+    cache_effective_user squid squid
+
+    #
+    # INSERT YOUR OWN RULE(S) HERE TO ALLOW ACCESS FROM YOUR CLIENTS
+    #
+    ${cfg.extraConfig}
+
+    # Example rule allowing access from your local networks.
+    # Adapt localnet in the ACL section to list your (internal) IP networks
+    # from where browsing should be allowed
+    http_access allow localnet
+    http_access allow localhost
+
+    # And finally deny all other access to this proxy
+    http_access deny all
+
+    # Squid normally listens to port 3128
+    http_port ${toString cfg.proxyPort}
+
+    # Leave coredumps in the first cache dir
+    coredump_dir /var/cache/squid
+
+    #
+    # Add any of your own refresh_pattern entries above these.
+    #
+    refresh_pattern ^ftp:           1440    20%     10080
+    refresh_pattern ^gopher:        1440    0%      1440
+    refresh_pattern -i (/cgi-bin/|\?) 0     0%      0
+    refresh_pattern .               0       20%     4320
+  '');
+
+in
+
+{
+
+  options = {
+
+    services.squid = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to run squid web proxy.";
+      };
+
+      proxyPort = mkOption {
+        type = types.int;
+        default = 3128;
+        description = "TCP port on which squid will listen.";
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Squid configuration. Contents will be added
+          verbatim to the configuration file.
+        '';
+      };
+
+      configText = mkOption {
+        type = types.nullOr types.lines;
+        default = null;
+        description = ''
+          Verbatim contents of squid.conf. If null (default), use the
+          autogenerated file from NixOS instead.
+        '';
+      };
+
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    users.users.squid = {
+      isSystemUser = true;
+      group = "squid";
+      home = "/var/cache/squid";
+      createHome = true;
+    };
+
+    users.groups.squid = {};
+
+    systemd.services.squid = {
+      description = "Squid caching web proxy";
+      after = [ "network.target" "nss-lookup.target" ];
+      wantedBy = [ "multi-user.target"];
+      preStart = ''
+        mkdir -p "/var/log/squid"
+        chown squid:squid "/var/log/squid"
+      '';
+      serviceConfig = {
+        Type="forking";
+        PIDFile="/run/squid.pid";
+        ExecStart  = "${pkgs.squid}/bin/squid -YCs -f ${squidConfig}";
+      };
+    };
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/ssh/lshd.nix b/nixpkgs/nixos/modules/services/networking/ssh/lshd.nix
new file mode 100644
index 000000000000..41d0584080e4
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/ssh/lshd.nix
@@ -0,0 +1,183 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  inherit (pkgs) lsh;
+
+  cfg = config.services.lshd;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.lshd = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable the GNU lshd SSH2 daemon, which allows
+          secure remote login.
+        '';
+      };
+
+      portNumber = mkOption {
+        default = 22;
+        description = ''
+          The port on which to listen for connections.
+        '';
+      };
+
+      interfaces = mkOption {
+        default = [];
+        description = ''
+          List of network interfaces where listening for connections.
+          When providing the empty list, `[]', lshd listens on all
+          network interfaces.
+        '';
+        example = [ "localhost" "1.2.3.4:443" ];
+      };
+
+      hostKey = mkOption {
+        default = "/etc/lsh/host-key";
+        description = ''
+          Path to the server's private key.  Note that this key must
+          have been created, e.g., using "lsh-keygen --server |
+          lsh-writekey --server", so that you can run lshd.
+        '';
+      };
+
+      syslog = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''Whether to enable syslog output.'';
+      };
+
+      passwordAuthentication = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''Whether to enable password authentication.'';
+      };
+
+      publicKeyAuthentication = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''Whether to enable public key authentication.'';
+      };
+
+      rootLogin = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''Whether to enable remote root login.'';
+      };
+
+      loginShell = mkOption {
+        default = null;
+        description = ''
+          If non-null, override the default login shell with the
+          specified value.
+        '';
+        example = "/nix/store/xyz-bash-10.0/bin/bash10";
+      };
+
+      srpKeyExchange = mkOption {
+        default = false;
+        description = ''
+          Whether to enable SRP key exchange and user authentication.
+        '';
+      };
+
+      tcpForwarding = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''Whether to enable TCP/IP forwarding.'';
+      };
+
+      x11Forwarding = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''Whether to enable X11 forwarding.'';
+      };
+
+      subsystems = mkOption {
+        description = ''
+          List of subsystem-path pairs, where the head of the pair
+          denotes the subsystem name, and the tail denotes the path to
+          an executable implementing it.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    services.lshd.subsystems = [ ["sftp" "${pkgs.lsh}/sbin/sftp-server"] ];
+
+    systemd.services.lshd = {
+      description = "GNU lshd SSH2 daemon";
+
+      after = [ "network.target" ];
+
+      wantedBy = [ "multi-user.target" ];
+
+      environment = {
+        LD_LIBRARY_PATH = config.system.nssModules.path;
+      };
+
+      preStart = ''
+        test -d /etc/lsh || mkdir -m 0755 -p /etc/lsh
+        test -d /var/spool/lsh || mkdir -m 0755 -p /var/spool/lsh
+
+        if ! test -f /var/spool/lsh/yarrow-seed-file
+        then
+            # XXX: It would be nice to provide feedback to the
+            # user when this fails, so that they can retry it
+            # manually.
+            ${lsh}/bin/lsh-make-seed --sloppy \
+               -o /var/spool/lsh/yarrow-seed-file
+        fi
+
+        if ! test -f "${cfg.hostKey}"
+        then
+            ${lsh}/bin/lsh-keygen --server | \
+            ${lsh}/bin/lsh-writekey --server -o "${cfg.hostKey}"
+        fi
+      '';
+
+      script = with cfg; ''
+        ${lsh}/sbin/lshd --daemonic \
+          --password-helper="${lsh}/sbin/lsh-pam-checkpw" \
+          -p ${toString portNumber} \
+          ${if interfaces == [] then ""
+            else (concatStrings (map (i: "--interface=\"${i}\"")
+                                     interfaces))} \
+          -h "${hostKey}" \
+          ${if !syslog then "--no-syslog" else ""} \
+          ${if passwordAuthentication then "--password" else "--no-password" } \
+          ${if publicKeyAuthentication then "--publickey" else "--no-publickey" } \
+          ${if rootLogin then "--root-login" else "--no-root-login" } \
+          ${if loginShell != null then "--login-shell=\"${loginShell}\"" else "" } \
+          ${if srpKeyExchange then "--srp-keyexchange" else "--no-srp-keyexchange" } \
+          ${if !tcpForwarding then "--no-tcpip-forward" else "--tcpip-forward"} \
+          ${if x11Forwarding then "--x11-forward" else "--no-x11-forward" } \
+          --subsystems=${concatStringsSep ","
+                                          (map (pair: (head pair) + "=" +
+                                                      (head (tail pair)))
+                                               subsystems)}
+      '';
+    };
+
+    security.pam.services.lshd = {};
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix b/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix
new file mode 100644
index 000000000000..a294bbfba0aa
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix
@@ -0,0 +1,557 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  # The splicing information needed for nativeBuildInputs isn't available
+  # on the derivations likely to be used as `cfgc.package`.
+  # This middle-ground solution ensures *an* sshd can do their basic validation
+  # on the configuration.
+  validationPackage = if pkgs.stdenv.buildPlatform == pkgs.stdenv.hostPlatform
+    then cfgc.package
+    else pkgs.buildPackages.openssh;
+
+  sshconf = pkgs.runCommand "sshd.conf-validated" { nativeBuildInputs = [ validationPackage ]; } ''
+    cat >$out <<EOL
+    ${cfg.extraConfig}
+    EOL
+
+    ssh-keygen -q -f mock-hostkey -N ""
+    sshd -t -f $out -h mock-hostkey
+  '';
+
+  cfg  = config.services.openssh;
+  cfgc = config.programs.ssh;
+
+  nssModulesPath = config.system.nssModules.path;
+
+  userOptions = {
+
+    options.openssh.authorizedKeys = {
+      keys = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          A list of verbatim OpenSSH public keys that should be added to the
+          user's authorized keys. The keys are added to a file that the SSH
+          daemon reads in addition to the the user's authorized_keys file.
+          You can combine the <literal>keys</literal> and
+          <literal>keyFiles</literal> options.
+          Warning: If you are using <literal>NixOps</literal> then don't use this
+          option since it will replace the key required for deployment via ssh.
+        '';
+      };
+
+      keyFiles = mkOption {
+        type = types.listOf types.path;
+        default = [];
+        description = ''
+          A list of files each containing one OpenSSH public key that should be
+          added to the user's authorized keys. The contents of the files are
+          read at build time and added to a file that the SSH daemon reads in
+          addition to the the user's authorized_keys file. You can combine the
+          <literal>keyFiles</literal> and <literal>keys</literal> options.
+        '';
+      };
+    };
+
+  };
+
+  authKeysFiles = let
+    mkAuthKeyFile = u: nameValuePair "ssh/authorized_keys.d/${u.name}" {
+      mode = "0444";
+      source = pkgs.writeText "${u.name}-authorized_keys" ''
+        ${concatStringsSep "\n" u.openssh.authorizedKeys.keys}
+        ${concatMapStrings (f: readFile f + "\n") u.openssh.authorizedKeys.keyFiles}
+      '';
+    };
+    usersWithKeys = attrValues (flip filterAttrs config.users.users (n: u:
+      length u.openssh.authorizedKeys.keys != 0 || length u.openssh.authorizedKeys.keyFiles != 0
+    ));
+  in listToAttrs (map mkAuthKeyFile usersWithKeys);
+
+in
+
+{
+  imports = [
+    (mkAliasOptionModule [ "services" "sshd" "enable" ] [ "services" "openssh" "enable" ])
+    (mkAliasOptionModule [ "services" "openssh" "knownHosts" ] [ "programs" "ssh" "knownHosts" ])
+  ];
+
+  ###### interface
+
+  options = {
+
+    services.openssh = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable the OpenSSH secure shell daemon, which
+          allows secure remote logins.
+        '';
+      };
+
+      startWhenNeeded = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          If set, <command>sshd</command> is socket-activated; that
+          is, instead of having it permanently running as a daemon,
+          systemd will start an instance for each incoming connection.
+        '';
+      };
+
+      forwardX11 = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to allow X11 connections to be forwarded.
+        '';
+      };
+
+      allowSFTP = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to enable the SFTP subsystem in the SSH daemon.  This
+          enables the use of commands such as <command>sftp</command> and
+          <command>sshfs</command>.
+        '';
+      };
+
+      sftpFlags = mkOption {
+        type = with types; listOf str;
+        default = [];
+        example = [ "-f AUTHPRIV" "-l INFO" ];
+        description = ''
+          Commandline flags to add to sftp-server.
+        '';
+      };
+
+      permitRootLogin = mkOption {
+        default = "prohibit-password";
+        type = types.enum ["yes" "without-password" "prohibit-password" "forced-commands-only" "no"];
+        description = ''
+          Whether the root user can login using ssh.
+        '';
+      };
+
+      gatewayPorts = mkOption {
+        type = types.str;
+        default = "no";
+        description = ''
+          Specifies whether remote hosts are allowed to connect to
+          ports forwarded for the client.  See
+          <citerefentry><refentrytitle>sshd_config</refentrytitle>
+          <manvolnum>5</manvolnum></citerefentry>.
+        '';
+      };
+
+      ports = mkOption {
+        type = types.listOf types.port;
+        default = [22];
+        description = ''
+          Specifies on which ports the SSH daemon listens.
+        '';
+      };
+
+      openFirewall = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to automatically open the specified ports in the firewall.
+        '';
+      };
+
+      listenAddresses = mkOption {
+        type = with types; listOf (submodule {
+          options = {
+            addr = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              description = ''
+                Host, IPv4 or IPv6 address to listen to.
+              '';
+            };
+            port = mkOption {
+              type = types.nullOr types.int;
+              default = null;
+              description = ''
+                Port to listen to.
+              '';
+            };
+          };
+        });
+        default = [];
+        example = [ { addr = "192.168.3.1"; port = 22; } { addr = "0.0.0.0"; port = 64022; } ];
+        description = ''
+          List of addresses and ports to listen on (ListenAddress directive
+          in config). If port is not specified for address sshd will listen
+          on all ports specified by <literal>ports</literal> option.
+          NOTE: this will override default listening on all local addresses and port 22.
+          NOTE: setting this option won't automatically enable given ports
+          in firewall configuration.
+        '';
+      };
+
+      passwordAuthentication = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Specifies whether password authentication is allowed.
+        '';
+      };
+
+      challengeResponseAuthentication = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Specifies whether challenge/response authentication is allowed.
+        '';
+      };
+
+      hostKeys = mkOption {
+        type = types.listOf types.attrs;
+        default =
+          [ { type = "rsa"; bits = 4096; path = "/etc/ssh/ssh_host_rsa_key"; }
+            { type = "ed25519"; path = "/etc/ssh/ssh_host_ed25519_key"; }
+          ];
+        example =
+          [ { type = "rsa"; bits = 4096; path = "/etc/ssh/ssh_host_rsa_key"; rounds = 100; openSSHFormat = true; }
+            { type = "ed25519"; path = "/etc/ssh/ssh_host_ed25519_key"; rounds = 100; comment = "key comment"; }
+          ];
+        description = ''
+          NixOS can automatically generate SSH host keys.  This option
+          specifies the path, type and size of each key.  See
+          <citerefentry><refentrytitle>ssh-keygen</refentrytitle>
+          <manvolnum>1</manvolnum></citerefentry> for supported types
+          and sizes.
+        '';
+      };
+
+      authorizedKeysFiles = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = "Files from which authorized keys are read.";
+      };
+
+      authorizedKeysCommand = mkOption {
+        type = types.str;
+        default = "none";
+        description = ''
+          Specifies a program to be used to look up the user's public
+          keys. The program must be owned by root, not writable by group
+          or others and specified by an absolute path.
+        '';
+      };
+
+      authorizedKeysCommandUser = mkOption {
+        type = types.str;
+        default = "nobody";
+        description = ''
+          Specifies the user under whose account the AuthorizedKeysCommand
+          is run. It is recommended to use a dedicated user that has no
+          other role on the host than running authorized keys commands.
+        '';
+      };
+
+      kexAlgorithms = mkOption {
+        type = types.listOf types.str;
+        default = [
+          "curve25519-sha256@libssh.org"
+          "diffie-hellman-group-exchange-sha256"
+        ];
+        description = ''
+          Allowed key exchange algorithms
+          </para>
+          <para>
+          Defaults to recommended settings from both
+          <link xlink:href="https://stribika.github.io/2015/01/04/secure-secure-shell.html" />
+          and
+          <link xlink:href="https://wiki.mozilla.org/Security/Guidelines/OpenSSH#Modern_.28OpenSSH_6.7.2B.29" />
+        '';
+      };
+
+      ciphers = mkOption {
+        type = types.listOf types.str;
+        default = [
+          "chacha20-poly1305@openssh.com"
+          "aes256-gcm@openssh.com"
+          "aes128-gcm@openssh.com"
+          "aes256-ctr"
+          "aes192-ctr"
+          "aes128-ctr"
+        ];
+        description = ''
+          Allowed ciphers
+          </para>
+          <para>
+          Defaults to recommended settings from both
+          <link xlink:href="https://stribika.github.io/2015/01/04/secure-secure-shell.html" />
+          and
+          <link xlink:href="https://wiki.mozilla.org/Security/Guidelines/OpenSSH#Modern_.28OpenSSH_6.7.2B.29" />
+        '';
+      };
+
+      macs = mkOption {
+        type = types.listOf types.str;
+        default = [
+          "hmac-sha2-512-etm@openssh.com"
+          "hmac-sha2-256-etm@openssh.com"
+          "umac-128-etm@openssh.com"
+          "hmac-sha2-512"
+          "hmac-sha2-256"
+          "umac-128@openssh.com"
+        ];
+        description = ''
+          Allowed MACs
+          </para>
+          <para>
+          Defaults to recommended settings from both
+          <link xlink:href="https://stribika.github.io/2015/01/04/secure-secure-shell.html" />
+          and
+          <link xlink:href="https://wiki.mozilla.org/Security/Guidelines/OpenSSH#Modern_.28OpenSSH_6.7.2B.29" />
+        '';
+      };
+
+      logLevel = mkOption {
+        type = types.enum [ "QUIET" "FATAL" "ERROR" "INFO" "VERBOSE" "DEBUG" "DEBUG1" "DEBUG2" "DEBUG3" ];
+        default = "VERBOSE";
+        description = ''
+          Gives the verbosity level that is used when logging messages from sshd(8). The possible values are:
+          QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, and DEBUG3. The default is VERBOSE. DEBUG and DEBUG1
+          are equivalent. DEBUG2 and DEBUG3 each specify higher levels of debugging output. Logging with a DEBUG level
+          violates the privacy of users and is not recommended.
+
+          LogLevel VERBOSE logs user's key fingerprint on login.
+          Needed to have a clear audit track of which key was used to log in.
+        '';
+      };
+
+      useDns = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Specifies whether sshd(8) should look up the remote host name, and to check that the resolved host name for
+          the remote IP address maps back to the very same IP address.
+          If this option is set to no (the default) then only addresses and not host names may be used in
+          ~/.ssh/authorized_keys from and sshd_config Match Host directives.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = "Verbatim contents of <filename>sshd_config</filename>.";
+      };
+
+      moduliFile = mkOption {
+        example = "/etc/my-local-ssh-moduli;";
+        type = types.path;
+        description = ''
+          Path to <literal>moduli</literal> file to install in
+          <literal>/etc/ssh/moduli</literal>. If this option is unset, then
+          the <literal>moduli</literal> file shipped with OpenSSH will be used.
+        '';
+      };
+
+      strictModes = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether sshd should check file modes and ownership of directories
+        '';
+      };
+    };
+
+    users.users = mkOption {
+      type = with types; loaOf (submodule userOptions);
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    users.users = {
+      sshd = {
+        isSystemUser = true;
+        description = "SSH privilege separation user";
+      };
+    } // (optionalAttrs (cfg.authorizedKeysCommand != null) {
+      ${cfg.authorizedKeysCommandUser} = {};
+    });
+
+    services.openssh.moduliFile = mkDefault "${cfgc.package}/etc/ssh/moduli";
+
+    environment.etc = authKeysFiles //
+      { "ssh/moduli".source = cfg.moduliFile;
+        "ssh/sshd_config".source = sshconf;
+      };
+
+    systemd =
+      let
+        service =
+          { description = "SSH Daemon";
+            wantedBy = optional (!cfg.startWhenNeeded) "multi-user.target";
+            after = [ "network.target" ];
+            stopIfChanged = false;
+            path = [ cfgc.package pkgs.gawk ];
+            environment.LD_LIBRARY_PATH = nssModulesPath;
+
+            restartTriggers = optionals (!cfg.startWhenNeeded) [
+              config.environment.etc."ssh/sshd_config".source
+            ];
+
+            preStart =
+              ''
+                # Make sure we don't write to stdout, since in case of
+                # socket activation, it goes to the remote side (#19589).
+                exec >&2
+
+                mkdir -m 0755 -p /etc/ssh
+
+                ${flip concatMapStrings cfg.hostKeys (k: ''
+                  if ! [ -f "${k.path}" ]; then
+                      ssh-keygen \
+                        -t "${k.type}" \
+                        ${if k ? bits then "-b ${toString k.bits}" else ""} \
+                        ${if k ? rounds then "-a ${toString k.rounds}" else ""} \
+                        ${if k ? comment then "-C '${k.comment}'" else ""} \
+                        ${if k ? openSSHFormat && k.openSSHFormat then "-o" else ""} \
+                        -f "${k.path}" \
+                        -N ""
+                  fi
+                '')}
+              '';
+
+            serviceConfig =
+              { ExecStart =
+                  (optionalString cfg.startWhenNeeded "-") +
+                  "${cfgc.package}/bin/sshd " + (optionalString cfg.startWhenNeeded "-i ") +
+                  "-f /etc/ssh/sshd_config";
+                KillMode = "process";
+              } // (if cfg.startWhenNeeded then {
+                StandardInput = "socket";
+                StandardError = "journal";
+              } else {
+                Restart = "always";
+                Type = "simple";
+              });
+
+          };
+      in
+
+      if cfg.startWhenNeeded then {
+
+        sockets.sshd =
+          { description = "SSH Socket";
+            wantedBy = [ "sockets.target" ];
+            socketConfig.ListenStream = if cfg.listenAddresses != [] then
+              map (l: "${l.addr}:${toString (if l.port != null then l.port else 22)}") cfg.listenAddresses
+            else
+              cfg.ports;
+            socketConfig.Accept = true;
+          };
+
+        services."sshd@" = service;
+
+      } else {
+
+        services.sshd = service;
+
+      };
+
+    networking.firewall.allowedTCPPorts = if cfg.openFirewall then cfg.ports else [];
+
+    security.pam.services.sshd =
+      { startSession = true;
+        showMotd = true;
+        unixAuth = cfg.passwordAuthentication;
+      };
+
+    # These values are merged with the ones defined externally, see:
+    # https://github.com/NixOS/nixpkgs/pull/10155
+    # https://github.com/NixOS/nixpkgs/pull/41745
+    services.openssh.authorizedKeysFiles =
+      [ ".ssh/authorized_keys" ".ssh/authorized_keys2" "/etc/ssh/authorized_keys.d/%u" ];
+
+    services.openssh.extraConfig = mkOrder 0
+      ''
+        UsePAM yes
+
+        AddressFamily ${if config.networking.enableIPv6 then "any" else "inet"}
+        ${concatMapStrings (port: ''
+          Port ${toString port}
+        '') cfg.ports}
+
+        ${concatMapStrings ({ port, addr, ... }: ''
+          ListenAddress ${addr}${if port != null then ":" + toString port else ""}
+        '') cfg.listenAddresses}
+
+        ${optionalString cfgc.setXAuthLocation ''
+            XAuthLocation ${pkgs.xorg.xauth}/bin/xauth
+        ''}
+
+        ${if cfg.forwardX11 then ''
+          X11Forwarding yes
+        '' else ''
+          X11Forwarding no
+        ''}
+
+        ${optionalString cfg.allowSFTP ''
+          Subsystem sftp ${cfgc.package}/libexec/sftp-server ${concatStringsSep " " cfg.sftpFlags}
+        ''}
+
+        PermitRootLogin ${cfg.permitRootLogin}
+        GatewayPorts ${cfg.gatewayPorts}
+        PasswordAuthentication ${if cfg.passwordAuthentication then "yes" else "no"}
+        ChallengeResponseAuthentication ${if cfg.challengeResponseAuthentication then "yes" else "no"}
+
+        PrintMotd no # handled by pam_motd
+
+        ${optionalString (cfg.authorizedKeysCommand != null) ''
+          AuthorizedKeysCommand ${cfg.authorizedKeysCommand}
+          AuthorizedKeysCommandUser ${cfg.authorizedKeysCommandUser}
+        ''}
+        AuthorizedKeysFile ${toString cfg.authorizedKeysFiles}
+        ${optionalString (cfg.authorizedKeysCommand != "none") ''
+          AuthorizedKeysCommand ${cfg.authorizedKeysCommand}
+          AuthorizedKeysCommandUser ${cfg.authorizedKeysCommandUser}
+        ''}
+
+        ${flip concatMapStrings cfg.hostKeys (k: ''
+          HostKey ${k.path}
+        '')}
+
+        KexAlgorithms ${concatStringsSep "," cfg.kexAlgorithms}
+        Ciphers ${concatStringsSep "," cfg.ciphers}
+        MACs ${concatStringsSep "," cfg.macs}
+
+        LogLevel ${cfg.logLevel}
+
+        ${if cfg.useDns then ''
+          UseDNS yes
+        '' else ''
+          UseDNS no
+        ''}
+
+        StrictModes ${if cfg.strictModes then "yes" else "no"}
+
+      '';
+
+    assertions = [{ assertion = if cfg.forwardX11 then cfgc.setXAuthLocation else true;
+                    message = "cannot enable X11 forwarding without setting xauth location";}]
+      ++ forEach cfg.listenAddresses ({ addr, ... }: {
+        assertion = addr != null;
+        message = "addr must be specified in each listenAddresses entry";
+      });
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/sslh.nix b/nixpkgs/nixos/modules/services/networking/sslh.nix
new file mode 100644
index 000000000000..c4fa370a5fef
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/sslh.nix
@@ -0,0 +1,160 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.sslh;
+  user = "sslh";
+  configFile = pkgs.writeText "sslh.conf" ''
+    verbose: ${boolToString cfg.verbose};
+    foreground: true;
+    inetd: false;
+    numeric: false;
+    transparent: ${boolToString cfg.transparent};
+    timeout: "${toString cfg.timeout}";
+
+    listen:
+    (
+      { host: "${cfg.listenAddress}"; port: "${toString cfg.port}"; }
+    );
+
+    ${cfg.appendConfig}
+  '';
+  defaultAppendConfig = ''
+    protocols:
+    (
+      { name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; probe: "builtin"; },
+      { name: "openvpn"; host: "localhost"; port: "1194"; probe: "builtin"; },
+      { name: "xmpp"; host: "localhost"; port: "5222"; probe: "builtin"; },
+      { name: "http"; host: "localhost"; port: "80"; probe: "builtin"; },
+      { name: "ssl"; host: "localhost"; port: "443"; probe: "builtin"; },
+      { name: "anyprot"; host: "localhost"; port: "443"; probe: "builtin"; }
+    );
+  '';
+in
+{
+  options = {
+    services.sslh = {
+      enable = mkEnableOption "sslh";
+
+      verbose = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Verbose logs.";
+      };
+
+      timeout = mkOption {
+        type = types.int;
+        default = 2;
+        description = "Timeout in seconds.";
+      };
+
+      transparent = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Will the services behind sslh (Apache, sshd and so on) see the external IP and ports as if the external world connected directly to them";
+      };
+
+      listenAddress = mkOption {
+        type = types.str;
+        default = "0.0.0.0";
+        description = "Listening address or hostname.";
+      };
+
+      port = mkOption {
+        type = types.int;
+        default = 443;
+        description = "Listening port.";
+      };
+
+      appendConfig = mkOption {
+        type = types.str;
+        default = defaultAppendConfig;
+        description = "Verbatim configuration file.";
+      };
+    };
+  };
+
+  config = mkMerge [
+    (mkIf cfg.enable {
+      systemd.services.sslh = {
+        description = "Applicative Protocol Multiplexer (e.g. share SSH and HTTPS on the same port)";
+        after = [ "network.target" ];
+        wantedBy = [ "multi-user.target" ];
+
+        serviceConfig = {
+          DynamicUser          = true;
+          User                 = "sslh";
+          PermissionsStartOnly = true;
+          Restart              = "always";
+          RestartSec           = "1s";
+          ExecStart            = "${pkgs.sslh}/bin/sslh -F${configFile}";
+          KillMode             = "process";
+          AmbientCapabilities  = "CAP_NET_BIND_SERVICE CAP_NET_ADMIN CAP_SETGID CAP_SETUID";
+          PrivateTmp           = true;
+          PrivateDevices       = true;
+          ProtectSystem        = "full";
+          ProtectHome          = true;
+        };
+      };
+    })
+
+    # code from https://github.com/yrutschle/sslh#transparent-proxy-support
+    # the only difference is using iptables mark 0x2 instead of 0x1 to avoid conflicts with nixos/nat module
+    (mkIf (cfg.enable && cfg.transparent) {
+      # Set route_localnet = 1 on all interfaces so that ssl can use "localhost" as destination
+      boot.kernel.sysctl."net.ipv4.conf.default.route_localnet" = 1;
+      boot.kernel.sysctl."net.ipv4.conf.all.route_localnet"     = 1;
+
+      systemd.services.sslh = let
+        iptablesCommands = [
+          # DROP martian packets as they would have been if route_localnet was zero
+          # Note: packets not leaving the server aren't affected by this, thus sslh will still work
+          { table = "raw";    command = "PREROUTING  ! -i lo -d 127.0.0.0/8 -j DROP"; }
+          { table = "mangle"; command = "POSTROUTING ! -o lo -s 127.0.0.0/8 -j DROP"; }
+          # Mark all connections made by ssl for special treatment (here sslh is run as user ${user})
+          { table = "nat";    command = "OUTPUT -m owner --uid-owner ${user} -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x02/0x0f"; }
+          # Outgoing packets that should go to sslh instead have to be rerouted, so mark them accordingly (copying over the connection mark)
+          { table = "mangle"; command = "OUTPUT ! -o lo -p tcp -m connmark --mark 0x02/0x0f -j CONNMARK --restore-mark --mask 0x0f"; }
+        ];
+        ip6tablesCommands = [
+          { table = "raw";    command = "PREROUTING  ! -i lo -d ::1/128     -j DROP"; }
+          { table = "mangle"; command = "POSTROUTING ! -o lo -s ::1/128     -j DROP"; }
+          { table = "nat";    command = "OUTPUT -m owner --uid-owner ${user} -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x02/0x0f"; }
+          { table = "mangle"; command = "OUTPUT ! -o lo -p tcp -m connmark --mark 0x02/0x0f -j CONNMARK --restore-mark --mask 0x0f"; }
+        ];
+      in {
+        path = [ pkgs.iptables pkgs.iproute pkgs.procps ];
+
+        preStart = ''
+          # Cleanup old iptables entries which might be still there
+          ${concatMapStringsSep "\n" ({table, command}: "while iptables -w -t ${table} -D ${command} 2>/dev/null; do echo; done") iptablesCommands}
+          ${concatMapStringsSep "\n" ({table, command}:       "iptables -w -t ${table} -A ${command}"                           ) iptablesCommands}
+
+          # Configure routing for those marked packets
+          ip rule  add fwmark 0x2 lookup 100
+          ip route add local 0.0.0.0/0 dev lo table 100
+
+        '' + optionalString config.networking.enableIPv6 ''
+          ${concatMapStringsSep "\n" ({table, command}: "while ip6tables -w -t ${table} -D ${command} 2>/dev/null; do echo; done") ip6tablesCommands}
+          ${concatMapStringsSep "\n" ({table, command}:       "ip6tables -w -t ${table} -A ${command}"                           ) ip6tablesCommands}
+
+          ip -6 rule  add fwmark 0x2 lookup 100
+          ip -6 route add local ::/0 dev lo table 100
+        '';
+
+        postStop = ''
+          ${concatMapStringsSep "\n" ({table, command}: "iptables -w -t ${table} -D ${command}") iptablesCommands}
+
+          ip rule  del fwmark 0x2 lookup 100
+          ip route del local 0.0.0.0/0 dev lo table 100
+        '' + optionalString config.networking.enableIPv6 ''
+          ${concatMapStringsSep "\n" ({table, command}: "ip6tables -w -t ${table} -D ${command}") ip6tablesCommands}
+
+          ip -6 rule  del fwmark 0x2 lookup 100
+          ip -6 route del local ::/0 dev lo table 100
+        '';
+      };
+    })
+  ];
+}
diff --git a/nixpkgs/nixos/modules/services/networking/strongswan-swanctl/module.nix b/nixpkgs/nixos/modules/services/networking/strongswan-swanctl/module.nix
new file mode 100644
index 000000000000..0fec3ef00ad9
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/strongswan-swanctl/module.nix
@@ -0,0 +1,84 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+with (import ./param-lib.nix lib);
+
+let
+  cfg = config.services.strongswan-swanctl;
+  swanctlParams = import ./swanctl-params.nix lib;
+in  {
+  options.services.strongswan-swanctl = {
+    enable = mkEnableOption "strongswan-swanctl service";
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.strongswan;
+      defaultText = "pkgs.strongswan";
+      description = ''
+        The strongswan derivation to use.
+      '';
+    };
+
+    strongswan.extraConfig = mkOption {
+      type = types.str;
+      default = "";
+      description = ''
+        Contents of the <literal>strongswan.conf</literal> file.
+      '';
+    };
+
+    swanctl = paramsToOptions swanctlParams;
+  };
+
+  config = mkIf cfg.enable {
+
+    assertions = [
+      { assertion = !config.services.strongswan.enable;
+        message = "cannot enable both services.strongswan and services.strongswan-swanctl. Choose either one.";
+      }
+    ];
+
+    environment.etc."swanctl/swanctl.conf".text =
+      paramsToConf cfg.swanctl swanctlParams;
+
+    # The swanctl command complains when the following directories don't exist:
+    # See: https://wiki.strongswan.org/projects/strongswan/wiki/Swanctldirectory
+    system.activationScripts.strongswan-swanctl-etc = stringAfter ["etc"] ''
+      mkdir -p '/etc/swanctl/x509'     # Trusted X.509 end entity certificates
+      mkdir -p '/etc/swanctl/x509ca'   # Trusted X.509 Certificate Authority certificates
+      mkdir -p '/etc/swanctl/x509ocsp'
+      mkdir -p '/etc/swanctl/x509aa'   # Trusted X.509 Attribute Authority certificates
+      mkdir -p '/etc/swanctl/x509ac'   # Attribute Certificates
+      mkdir -p '/etc/swanctl/x509crl'  # Certificate Revocation Lists
+      mkdir -p '/etc/swanctl/pubkey'   # Raw public keys
+      mkdir -p '/etc/swanctl/private'  # Private keys in any format
+      mkdir -p '/etc/swanctl/rsa'      # PKCS#1 encoded RSA private keys
+      mkdir -p '/etc/swanctl/ecdsa'    # Plain ECDSA private keys
+      mkdir -p '/etc/swanctl/bliss'
+      mkdir -p '/etc/swanctl/pkcs8'    # PKCS#8 encoded private keys of any type
+      mkdir -p '/etc/swanctl/pkcs12'   # PKCS#12 containers
+    '';
+
+    systemd.services.strongswan-swanctl = {
+      description = "strongSwan IPsec IKEv1/IKEv2 daemon using swanctl";
+      wantedBy = [ "multi-user.target" ];
+      after    = [ "network-online.target" ];
+      path     = with pkgs; [ kmod iproute iptables utillinux ];
+      environment = {
+        STRONGSWAN_CONF = pkgs.writeTextFile {
+          name = "strongswan.conf";
+          text = cfg.strongswan.extraConfig;
+        };
+        SWANCTL_DIR = "/etc/swanctl";
+      };
+      restartTriggers = [ config.environment.etc."swanctl/swanctl.conf".source ];
+      serviceConfig = {
+        ExecStart     = "${cfg.package}/sbin/charon-systemd";
+        Type          = "notify";
+        ExecStartPost = "${cfg.package}/sbin/swanctl --load-all --noprompt";
+        ExecReload    = "${cfg.package}/sbin/swanctl --reload";
+        Restart       = "on-abnormal";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/strongswan-swanctl/param-constructors.nix b/nixpkgs/nixos/modules/services/networking/strongswan-swanctl/param-constructors.nix
new file mode 100644
index 000000000000..dfdfc50d8ae2
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/strongswan-swanctl/param-constructors.nix
@@ -0,0 +1,162 @@
+# In the following context a parameter is an attribute set that
+# contains a NixOS option and a render function. It also contains the
+# attribute: '_type = "param"' so we can distinguish it from other
+# sets.
+#
+# The render function is used to convert the value of the option to a
+# snippet of strongswan.conf. Most parameters simply render their
+# value to a string. For example, take the following parameter:
+#
+#   threads = mkIntParam 10 "Threads to use for request handling.";
+#
+# When a users defines the corresponding option as for example:
+#
+#   services.strongswan-swanctl.strongswan.threads = 32;
+#
+# It will get rendered to the following snippet in strongswan.conf:
+#
+#   threads = 32
+#
+# Some parameters however need to be able to change the attribute
+# name. For example, take the following parameter:
+#
+#   id = mkPrefixedAttrsOfParam (mkOptionalStrParam "") "...";
+#
+# A user can define the corresponding option as for example:
+#
+#   id = {
+#     "foo" = "bar";
+#     "baz" = "qux";
+#   };
+#
+# This will get rendered to the following snippet:
+#
+#   foo-id = bar
+#   baz-id = qux
+#
+# For this reason the render function is not simply a function from
+# value -> string but a function from a value to an attribute set:
+# { "${name}" = string }. This allows parameters to change the attribute
+# name like in the previous example.
+
+lib :
+
+with lib;
+with (import ./param-lib.nix lib);
+
+rec {
+  mkParamOfType = type : strongswanDefault : description : {
+    _type = "param";
+    option = mkOption {
+      type = types.nullOr type;
+      default = null;
+      description = documentDefault description strongswanDefault;
+    };
+    render = single toString;
+  };
+
+  documentDefault = description : strongswanDefault :
+    if strongswanDefault == null
+    then description
+    else description + ''
+      </para><para>
+      StrongSwan default: <literal><![CDATA[${builtins.toJSON strongswanDefault}]]></literal>
+    '';
+
+  single = f: name: value: { ${name} = f value; };
+
+  mkStrParam         = mkParamOfType types.str;
+  mkOptionalStrParam = mkStrParam null;
+
+  mkEnumParam = values : mkParamOfType (types.enum values);
+
+  mkIntParam         = mkParamOfType types.int;
+  mkOptionalIntParam = mkIntParam null;
+
+  # We should have floats in Nix...
+  mkFloatParam = mkStrParam;
+
+  # TODO: Check for hex format:
+  mkHexParam         = mkStrParam;
+  mkOptionalHexParam = mkOptionalStrParam;
+
+  # TODO: Check for duration format:
+  mkDurationParam         = mkStrParam;
+  mkOptionalDurationParam = mkOptionalStrParam;
+
+  mkYesNoParam = strongswanDefault : description : {
+    _type = "param";
+    option = mkOption {
+      type = types.nullOr types.bool;
+      default = null;
+      description = documentDefault description strongswanDefault;
+    };
+    render = single (b: if b then "yes" else "no");
+  };
+  yes = true;
+  no  = false;
+
+  mkSpaceSepListParam = mkSepListParam " ";
+  mkCommaSepListParam = mkSepListParam ",";
+
+  mkSepListParam = sep : strongswanDefault : description : {
+    _type = "param";
+    option = mkOption {
+      type = types.nullOr (types.listOf types.str);
+      default = null;
+      description = documentDefault description strongswanDefault;
+    };
+    render = single (value: concatStringsSep sep value);
+  };
+
+  mkAttrsOfParams = params :
+    mkAttrsOf params (types.submodule {options = paramsToOptions params;});
+
+  mkAttrsOfParam = param :
+    mkAttrsOf param param.option.type;
+
+  mkAttrsOf = param : option : description : {
+    _type = "param";
+    option = mkOption {
+      type = types.attrsOf option;
+      default = {};
+      inherit description;
+    };
+    render = single (attrs:
+      (paramsToRenderedStrings attrs
+        (mapAttrs (_n: _v: param) attrs)));
+  };
+
+  mkPrefixedAttrsOfParams = params :
+    mkPrefixedAttrsOf params (types.submodule {options = paramsToOptions params;});
+
+  mkPrefixedAttrsOfParam = param :
+    mkPrefixedAttrsOf param param.option.type;
+
+  mkPrefixedAttrsOf = p : option : description : {
+    _type = "param";
+    option = mkOption {
+      type = types.attrsOf option;
+      default = {};
+      inherit description;
+    };
+    render = prefix: attrs:
+      let prefixedAttrs = mapAttrs' (name: nameValuePair "${prefix}-${name}") attrs;
+      in paramsToRenderedStrings prefixedAttrs
+           (mapAttrs (_n: _v: p) prefixedAttrs);
+  };
+
+  mkPostfixedAttrsOfParams = params : description : {
+    _type = "param";
+    option = mkOption {
+      type = types.attrsOf (types.submodule {options = paramsToOptions params;});
+      default = {};
+      inherit description;
+    };
+    render = postfix: attrs:
+      let postfixedAttrs = mapAttrs' (name: nameValuePair "${name}-${postfix}") attrs;
+      in paramsToRenderedStrings postfixedAttrs
+           (mapAttrs (_n: _v: params) postfixedAttrs);
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/strongswan-swanctl/param-lib.nix b/nixpkgs/nixos/modules/services/networking/strongswan-swanctl/param-lib.nix
new file mode 100644
index 000000000000..2bbb39a76049
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/strongswan-swanctl/param-lib.nix
@@ -0,0 +1,82 @@
+lib :
+
+with lib;
+
+rec {
+  paramsToConf = cfg : ps : mkConf 0 (paramsToRenderedStrings cfg ps);
+
+  # mkConf takes an indentation level (which usually starts at 0) and a nested
+  # attribute set of strings and will render that set to a strongswan.conf style
+  # configuration format. For example:
+  #
+  #   mkConf 0 {a = "1"; b = { c = { "foo" = "2"; "bar" = "3"; }; d = "4";};}   =>   ''
+  #   a = 1
+  #   b {
+  #     c {
+  #       foo = 2
+  #       bar = 3
+  #     }
+  #     d = 4
+  #   }''
+  mkConf = indent : ps :
+    concatMapStringsSep "\n"
+      (name:
+        let value = ps.${name};
+            indentation = replicate indent " ";
+        in
+        indentation + (
+          if isAttrs value
+          then "${name} {\n" +
+                 mkConf (indent + 2) value + "\n" +
+               indentation + "}"
+          else "${name} = ${value}"
+        )
+      )
+      (attrNames ps);
+
+  replicate = n : c : concatStrings (builtins.genList (_x : c) n);
+
+  # `paramsToRenderedStrings cfg ps` converts the NixOS configuration `cfg`
+  # (typically the "config" argument of a NixOS module) and the set of
+  # parameters `ps` (an attribute set where the values are constructed using the
+  # parameter constructors in ./param-constructors.nix) to a nested attribute
+  # set of strings (rendered parameters).
+  paramsToRenderedStrings = cfg : ps :
+    filterEmptySets (
+      (mapParamsRecursive (path: name: param:
+        let value = attrByPath path null cfg;
+        in optionalAttrs (value != null) (param.render name value)
+      ) ps));
+
+  filterEmptySets = set : filterAttrs (n: v: (v != null)) (mapAttrs (name: value:
+    if isAttrs value
+    then let value' = filterEmptySets value;
+         in if value' == {}
+            then null
+            else value'
+    else value
+  ) set);
+
+  # Recursively map over every parameter in the given attribute set.
+  mapParamsRecursive = mapAttrsRecursiveCond' (as: (!(as ? _type && as._type == "param")));
+
+  mapAttrsRecursiveCond' = cond: f: set:
+    let
+      recurse = path: set:
+        let
+          g =
+            name: value:
+            if isAttrs value && cond value
+              then { ${name} = recurse (path ++ [name]) value; }
+              else f (path ++ [name]) name value;
+        in mapAttrs'' g set;
+    in recurse [] set;
+
+  mapAttrs'' = f: set:
+    foldl' (a: b: a // b) {} (map (attr: f attr set.${attr}) (attrNames set));
+
+  # Extract the options from the given set of parameters.
+  paramsToOptions = ps :
+    mapParamsRecursive (_path: name: param: { ${name} = param.option; }) ps;
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/strongswan-swanctl/swanctl-params.nix b/nixpkgs/nixos/modules/services/networking/strongswan-swanctl/swanctl-params.nix
new file mode 100644
index 000000000000..808cb863a9cf
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/strongswan-swanctl/swanctl-params.nix
@@ -0,0 +1,1300 @@
+# See: https://wiki.strongswan.org/projects/strongswan/wiki/Swanctlconf
+#
+# When strongSwan is upgraded please update the parameters in this file. You can
+# see which parameters should be deleted, changed or added by diffing
+# swanctl.opt:
+#
+#   git clone https://github.com/strongswan/strongswan.git
+#   cd strongswan
+#   git diff 5.7.2..5.8.0 src/swanctl/swanctl.opt
+
+lib: with (import ./param-constructors.nix lib);
+
+let
+  certParams = {
+    file = mkOptionalStrParam ''
+      Absolute path to the certificate to load. Passed as-is to the daemon, so
+      it must be readable by it.
+      </para><para>
+      Configure either this or <option>handle</option>, but not both, in one section.
+    '';
+
+    handle = mkOptionalHexParam ''
+      Hex-encoded CKA_ID or handle of the certificate on a token or TPM,
+      respectively.
+      </para><para>
+      Configure either this or <option>file</option>, but not both, in one section.
+    '';
+
+    slot = mkOptionalIntParam ''
+      Optional slot number of the token that stores the certificate.
+    '';
+
+    module = mkOptionalStrParam ''
+      Optional PKCS#11 module name.
+    '';
+  };
+in {
+  authorities = mkAttrsOfParams ({
+
+    cacert = mkOptionalStrParam ''
+      The certificates may use a relative path from the swanctl
+      <literal>x509ca</literal> directory or an absolute path.
+      </para><para>
+      Configure one of <option>cacert</option>,
+      <option>file</option>, or
+      <option>handle</option> per section.
+    '';
+
+    cert_uri_base = mkOptionalStrParam ''
+      Defines the base URI for the Hash and URL feature supported by
+      IKEv2. Instead of exchanging complete certificates, IKEv2 allows one to
+      send an URI that resolves to the DER encoded certificate. The certificate
+      URIs are built by appending the SHA1 hash of the DER encoded certificates
+      to this base URI.
+    '';
+
+    crl_uris = mkCommaSepListParam [] ''
+      List of CRL distribution points (ldap, http, or file URI).
+    '';
+
+    ocsp_uris = mkCommaSepListParam [] ''
+      List of OCSP URIs.
+    '';
+
+  } // certParams) ''
+    Section defining complementary attributes of certification authorities, each
+    in its own subsection with an arbitrary yet unique name
+  '';
+
+  connections = mkAttrsOfParams {
+
+    version = mkIntParam 0 ''
+      IKE major version to use for connection.
+      <itemizedlist>
+      <listitem><para>1 uses IKEv1 aka ISAKMP,</para></listitem>
+      <listitem><para>2 uses IKEv2.</para></listitem>
+      <listitem><para>A connection using the default of 0 accepts both IKEv1 and IKEv2 as
+      responder, and initiates the connection actively with IKEv2.</para></listitem>
+      </itemizedlist>
+    '';
+
+    local_addrs	= mkCommaSepListParam [] ''
+      Local address(es) to use for IKE communication. Takes
+      single IPv4/IPv6 addresses, DNS names, CIDR subnets or IP address ranges.
+      </para><para>
+      As initiator, the first non-range/non-subnet is used to initiate the
+      connection from. As responder, the local destination address must match at
+      least to one of the specified addresses, subnets or ranges.
+      </para><para>
+      If FQDNs are assigned they are resolved every time a configuration lookup
+      is done. If DNS resolution times out, the lookup is delayed for that time.
+    '';
+
+    remote_addrs = mkCommaSepListParam [] ''
+      Remote address(es) to use for IKE communication. Takes
+      single IPv4/IPv6 addresses, DNS names, CIDR subnets or IP address ranges.
+      </para><para>
+      As initiator, the first non-range/non-subnet is used to initiate the
+      connection to. As responder, the initiator source address must match at
+      least to one of the specified addresses, subnets or ranges.
+      </para><para>
+      If FQDNs are assigned they are resolved every time a configuration lookup
+      is done. If DNS resolution times out, the lookup is delayed for that time.
+      To initiate a connection, at least one specific address or DNS name must
+      be specified.
+    '';
+
+    local_port = mkIntParam 500 ''
+      Local UDP port for IKE communication. By default the port of the socket
+      backend is used, which is usually <literal>500</literal>. If port
+      <literal>500</literal> is used, automatic IKE port floating to port
+      <literal>4500</literal> is used to work around NAT issues.
+      </para><para>
+      Using a non-default local IKE port requires support from the socket
+      backend in use (socket-dynamic).
+    '';
+
+    remote_port = mkIntParam 500 ''
+      Remote UDP port for IKE communication. If the default of port
+      <literal>500</literal> is used, automatic IKE port floating to port
+      <literal>4500</literal> is used to work around NAT issues.
+    '';
+
+    proposals = mkCommaSepListParam ["default"] ''
+      A proposal is a set of algorithms. For non-AEAD algorithms, this includes
+      for IKE an encryption algorithm, an integrity algorithm, a pseudo random
+      function and a Diffie-Hellman group. For AEAD algorithms, instead of
+      encryption and integrity algorithms, a combined algorithm is used.
+      </para><para>
+      In IKEv2, multiple algorithms of the same kind can be specified in a
+      single proposal, from which one gets selected. In IKEv1, only one
+      algorithm per kind is allowed per proposal, more algorithms get implicitly
+      stripped. Use multiple proposals to offer different algorithms
+      combinations in IKEv1.
+      </para><para>
+      Algorithm keywords get separated using dashes. Multiple proposals may be
+      specified in a list. The special value <literal>default</literal> forms a
+      default proposal of supported algorithms considered safe, and is usually a
+      good choice for interoperability.
+    '';
+
+    vips = mkCommaSepListParam [] ''
+      List of virtual IPs to request in IKEv2 configuration payloads or IKEv1
+      Mode Config. The wildcard addresses <literal>0.0.0.0</literal> and
+      <literal>::</literal> request an arbitrary address, specific addresses may
+      be defined. The responder may return a different address, though, or none
+      at all.
+    '';
+
+    aggressive = mkYesNoParam no ''
+      Enables Aggressive Mode instead of Main Mode with Identity
+      Protection. Aggressive Mode is considered less secure, because the ID and
+      HASH payloads are exchanged unprotected. This allows a passive attacker to
+      snoop peer identities, and even worse, start dictionary attacks on the
+      Preshared Key.
+    '';
+
+    pull = mkYesNoParam yes ''
+      If the default of yes is used, Mode Config works in pull mode, where the
+      initiator actively requests a virtual IP. With no, push mode is used,
+      where the responder pushes down a virtual IP to the initiating peer.
+      </para><para>
+      Push mode is currently supported for IKEv1, but not in IKEv2. It is used
+      by a few implementations only, pull mode is recommended.
+    '';
+
+    dscp = mkStrParam "000000" ''
+      Differentiated Services Field Codepoint to set on outgoing IKE packets for
+      this connection. The value is a six digit binary encoded string specifying
+      the Codepoint to set, as defined in RFC 2474.
+    '';
+
+    encap = mkYesNoParam no ''
+      To enforce UDP encapsulation of ESP packets, the IKE daemon can fake the
+      NAT detection payloads. This makes the peer believe that NAT takes place
+      on the path, forcing it to encapsulate ESP packets in UDP.
+      </para><para>
+      Usually this is not required, but it can help to work around connectivity
+      issues with too restrictive intermediary firewalls.
+    '';
+
+    mobike = mkYesNoParam yes ''
+      Enables MOBIKE on IKEv2 connections. MOBIKE is enabled by default on IKEv2
+      connections, and allows mobility of clients and multi-homing on servers by
+      migrating active IPsec tunnels.
+      </para><para>
+      Usually keeping MOBIKE enabled is unproblematic, as it is not used if the
+      peer does not indicate support for it. However, due to the design of
+      MOBIKE, IKEv2 always floats to port 4500 starting from the second
+      exchange. Some implementations don't like this behavior, hence it can be
+      disabled.
+    '';
+
+    dpd_delay = mkDurationParam "0s" ''
+      Interval to check the liveness of a peer actively using IKEv2
+      INFORMATIONAL exchanges or IKEv1 R_U_THERE messages. Active DPD checking
+      is only enforced if no IKE or ESP/AH packet has been received for the
+      configured DPD delay.
+    '';
+
+    dpd_timeout = mkDurationParam "0s" ''
+      Charon by default uses the normal retransmission mechanism and timeouts to
+      check the liveness of a peer, as all messages are used for liveness
+      checking. For compatibility reasons, with IKEv1 a custom interval may be
+      specified; this option has no effect on connections using IKEv2.
+    '';
+
+    fragmentation = mkEnumParam ["yes" "accept" "force" "no"] "yes" ''
+      Use IKE fragmentation (proprietary IKEv1 extension or RFC 7383 IKEv2
+      fragmentation). Acceptable values are <literal>yes</literal> (the default
+      since 5.5.1), <literal>accept</literal> (since versions:5.5.3),
+      <literal>force</literal> and <literal>no</literal>.
+      <itemizedlist>
+      <listitem><para>If set to <literal>yes</literal>, and the peer
+      supports it, oversized IKE messages will be sent in fragments.</para></listitem>
+      <listitem><para>If set to
+      <literal>accept</literal>, support for fragmentation is announced to the peer but the daemon
+      does not send its own messages in fragments.</para></listitem>
+      <listitem><para>If set to <literal>force</literal> (only
+      supported for IKEv1) the initial IKE message will already be fragmented if
+      required.</para></listitem>
+      <listitem><para>Finally, setting the option to <literal>no</literal> will disable announcing
+      support for this feature.</para></listitem>
+      </itemizedlist>
+      </para><para>
+      Note that fragmented IKE messages sent by a peer are always processed
+      irrespective of the value of this option (even when set to no).
+    '';
+
+    childless = mkEnumParam [ "allow" "force" "never" ] "allow" ''
+      Use childless IKE_SA initiation (RFC 6023) for IKEv2.  Acceptable values
+      are <literal>allow</literal> (the default), <literal>force</literal> and
+      <literal>never</literal>. If set to <literal>allow</literal>, responders
+      will accept childless IKE_SAs (as indicated via notify in the IKE_SA_INIT
+      response) while initiators continue to create regular IKE_SAs with the
+      first CHILD_SA created during IKE_AUTH, unless the IKE_SA is initiated
+      explicitly without any children (which will fail if the responder does not
+      support or has disabled this extension).  If set to
+      <literal>force</literal>, only childless initiation is accepted and the
+      first CHILD_SA is created with a separate CREATE_CHILD_SA exchange
+      (e.g. to use an independent DH exchange for all CHILD_SAs). Finally,
+      setting the option to <literal>never</literal> disables support for
+      childless IKE_SAs as responder.
+    '';
+
+    send_certreq = mkYesNoParam yes ''
+      Send certificate request payloads to offer trusted root CA certificates to
+      the peer. Certificate requests help the peer to choose an appropriate
+      certificate/private key for authentication and are enabled by default.
+      Disabling certificate requests can be useful if too many trusted root CA
+      certificates are installed, as each certificate request increases the size
+      of the initial IKE packets.
+   '';
+
+    send_cert = mkEnumParam ["always" "never" "ifasked" ] "ifasked" ''
+      Send certificate payloads when using certificate authentication.
+      <itemizedlist>
+      <listitem><para>With the default of <literal>ifasked</literal> the daemon sends
+      certificate payloads only if certificate requests have been received.</para></listitem>
+      <listitem><para><literal>never</literal> disables sending of certificate payloads
+      altogether,</para></listitem>
+      <listitem><para><literal>always</literal> causes certificate payloads to be sent
+      unconditionally whenever certificate authentication is used.</para></listitem>
+      </itemizedlist>
+    '';
+
+    ppk_id = mkOptionalStrParam ''
+       String identifying the Postquantum Preshared Key (PPK) to be used.
+    '';
+
+    ppk_required = mkYesNoParam no ''
+       Whether a Postquantum Preshared Key (PPK) is required for this connection.
+    '';
+
+    keyingtries = mkIntParam 1 ''
+      Number of retransmission sequences to perform during initial
+      connect. Instead of giving up initiation after the first retransmission
+      sequence with the default value of <literal>1</literal>, additional
+      sequences may be started according to the configured value. A value of
+      <literal>0</literal> initiates a new sequence until the connection
+      establishes or fails with a permanent error.
+    '';
+
+    unique = mkEnumParam ["no" "never" "keep" "replace"] "no" ''
+      Connection uniqueness policy to enforce. To avoid multiple connections
+      from the same user, a uniqueness policy can be enforced.
+      </para><para>
+      <itemizedlist>
+      <listitem><para>
+      The value <literal>never</literal> does never enforce such a policy, even
+      if a peer included INITIAL_CONTACT notification messages,
+      </para></listitem>
+      <listitem><para>
+      whereas <literal>no</literal> replaces existing connections for the same
+      identity if a new one has the INITIAL_CONTACT notify.
+      </para></listitem>
+      <listitem><para>
+      <literal>keep</literal> rejects new connection attempts if the same user
+      already has an active connection,
+      </para></listitem>
+      <listitem><para>
+      <literal>replace</literal> deletes any existing connection if a new one
+      for the same user gets established.
+      </para></listitem>
+      </itemizedlist>
+      To compare connections for uniqueness, the remote IKE identity is used. If
+      EAP or XAuth authentication is involved, the EAP-Identity or XAuth
+      username is used to enforce the uniqueness policy instead.
+      </para><para>
+      On initiators this setting specifies whether an INITIAL_CONTACT notify is
+      sent during IKE_AUTH if no existing connection is found with the remote
+      peer (determined by the identities of the first authentication
+      round). Unless set to <literal>never</literal> the client will send a notify.
+    '';
+
+    reauth_time	= mkDurationParam "0s" ''
+      Time to schedule IKE reauthentication. IKE reauthentication recreates the
+      IKE/ISAKMP SA from scratch and re-evaluates the credentials. In asymmetric
+      configurations (with EAP or configuration payloads) it might not be
+      possible to actively reauthenticate as responder. The IKEv2
+      reauthentication lifetime negotiation can instruct the client to perform
+      reauthentication.
+      </para><para>
+      Reauthentication is disabled by default. Enabling it usually may lead to
+      small connection interruptions, as strongSwan uses a break-before-make
+      policy with IKEv2 to avoid any conflicts with associated tunnel resources.
+    '';
+
+    rekey_time = mkDurationParam "4h" ''
+      IKE rekeying refreshes key material using a Diffie-Hellman exchange, but
+      does not re-check associated credentials. It is supported in IKEv2 only,
+      IKEv1 performs a reauthentication procedure instead.
+      </para><para>
+      With the default value IKE rekeying is scheduled every 4 hours, minus the
+      configured rand_time. If a reauth_time is configured, rekey_time defaults
+      to zero, disabling rekeying; explicitly set both to enforce rekeying and
+      reauthentication.
+    '';
+
+    over_time = mkOptionalDurationParam ''
+      Hard IKE_SA lifetime if rekey/reauth does not complete, as time. To avoid
+      having an IKE/ISAKMP kept alive if IKE reauthentication or rekeying fails
+      perpetually, a maximum hard lifetime may be specified. If the IKE_SA fails
+      to rekey or reauthenticate within the specified time, the IKE_SA gets
+      closed.
+      </para><para>
+      In contrast to CHILD_SA rekeying, over_time is relative in time to the
+      rekey_time and reauth_time values, as it applies to both.
+      </para><para>
+      The default is 10% of the longer of <option>rekey_time</option> and
+      <option>reauth_time</option>.
+    '';
+
+    rand_time = mkOptionalDurationParam ''
+      Time range from which to choose a random value to subtract from
+      rekey/reauth times. To avoid having both peers initiating the rekey/reauth
+      procedure simultaneously, a random time gets subtracted from the
+      rekey/reauth times.
+      </para><para>
+      The default is equal to the configured <option>over_time</option>.
+    '';
+
+    pools = mkCommaSepListParam [] ''
+      List of named IP pools to allocate virtual IP addresses
+      and other configuration attributes from. Each name references a pool by
+      name from either the pools section or an external pool.
+    '';
+
+    if_id_in = mkStrParam "0" ''
+      XFRM interface ID set on inbound policies/SA, can be overridden by child
+      config, see there for details.
+    '';
+
+    if_id_out = mkStrParam "0" ''
+      XFRM interface ID set on outbound policies/SA, can be overridden by child
+      config, see there for details.
+    '';
+
+    mediation = mkYesNoParam no ''
+      Whether this connection is a mediation connection, that is, whether this
+      connection is used to mediate other connections using the IKEv2 Mediation
+      Extension. Mediation connections create no CHILD_SA.
+    '';
+
+    mediated_by = mkOptionalStrParam ''
+      The name of the connection to mediate this connection through. If given,
+      the connection will be mediated through the named mediation
+      connection. The mediation connection must have mediation enabled.
+    '';
+
+    mediation_peer = mkOptionalStrParam ''
+      Identity under which the peer is registered at the mediation server, that
+      is, the IKE identity the other end of this connection uses as its local
+      identity on its connection to the mediation server. This is the identity
+      we request the mediation server to mediate us with. Only relevant on
+      connections that set mediated_by. If it is not given, the remote IKE
+      identity of the first authentication round of this connection will be
+      used.
+    '';
+
+    local = mkPrefixedAttrsOfParams {
+
+      round = mkIntParam 0 ''
+        Optional numeric identifier by which authentication rounds are
+        sorted. If not specified rounds are ordered by their position in the
+        config file/vici message.
+      '';
+
+      certs = mkCommaSepListParam [] ''
+        List of certificate candidates to use for
+        authentication. The certificates may use a relative path from the
+        swanctl <literal>x509</literal> directory or an absolute path.
+        </para><para>
+        The certificate used for authentication is selected based on the
+        received certificate request payloads. If no appropriate CA can be
+        located, the first certificate is used.
+      '';
+
+      cert = mkPostfixedAttrsOfParams certParams ''
+        Section for a certificate candidate to use for
+        authentication. Certificates in certs are transmitted as binary blobs,
+        these sections offer more flexibility.
+      '';
+
+      pubkeys = mkCommaSepListParam [] ''
+        List of raw public key candidates to use for
+        authentication. The public keys may use a relative path from the swanctl
+        <literal>pubkey</literal> directory or an absolute path.
+        </para><para>
+        Even though multiple local public keys could be defined in principle,
+        only the first public key in the list is used for authentication.
+      '';
+
+      auth = mkStrParam "pubkey" ''
+        Authentication to perform locally.
+        <itemizedlist>
+        <listitem><para>
+        The default <literal>pubkey</literal> uses public key authentication
+        using a private key associated to a usable certificate.
+        </para></listitem>
+        <listitem><para>
+        <literal>psk</literal> uses pre-shared key authentication.
+        </para></listitem>
+        <listitem><para>
+        The IKEv1 specific <literal>xauth</literal> is used for XAuth or Hybrid
+        authentication,
+        </para></listitem>
+        <listitem><para>
+        while the IKEv2 specific <literal>eap</literal> keyword defines EAP
+        authentication.
+        </para></listitem>
+        <listitem><para>
+        For <literal>xauth</literal>, a specific backend name may be appended,
+        separated by a dash. The appropriate <literal>xauth</literal> backend is
+        selected to perform the XAuth exchange. For traditional XAuth, the
+        <literal>xauth</literal> method is usually defined in the second
+        authentication round following an initial <literal>pubkey</literal> (or
+        <literal>psk</literal>) round. Using <literal>xauth</literal> in the
+        first round performs Hybrid Mode client authentication.
+        </para></listitem>
+        <listitem><para>
+        For <literal>eap</literal>, a specific EAP method name may be appended, separated by a
+        dash. An EAP module implementing the appropriate method is selected to
+        perform the EAP conversation.
+        </para></listitem>
+        <listitem><para>
+        Since 5.4.0, if both peers support RFC 7427 ("Signature Authentication
+        in IKEv2") specific hash algorithms to be used during IKEv2
+        authentication may be configured. To do so use <literal>ike:</literal>
+        followed by a trust chain signature scheme constraint (see description
+        of the <option>remote</option> section's <option>auth</option>
+        keyword). For example, with <literal>ike:pubkey-sha384-sha256</literal>
+        a public key signature scheme with either SHA-384 or SHA-256 would get
+        used for authentication, in that order and depending on the hash
+        algorithms supported by the peer. If no specific hash algorithms are
+        configured, the default is to prefer an algorithm that matches or
+        exceeds the strength of the signature key. If no constraints with
+        <literal>ike:</literal> prefix are configured any signature scheme
+        constraint (without <literal>ike:</literal> prefix) will also apply to
+        IKEv2 authentication, unless this is disabled in
+        <literal>strongswan.conf</literal>. To use RSASSA-PSS signatures use
+        <literal>rsa/pss</literal> instead of <literal>pubkey</literal> or
+        <literal>rsa</literal> as in e.g.
+        <literal>ike:rsa/pss-sha256</literal>. If <literal>pubkey</literal> or
+        <literal>rsa</literal> constraints are configured RSASSA-PSS signatures
+        will only be used if enabled in <literal>strongswan.conf</literal>(5).
+        </para></listitem>
+        </itemizedlist>
+      '';
+
+      id = mkOptionalStrParam ''
+        IKE identity to use for authentication round. When using certificate
+        authentication, the IKE identity must be contained in the certificate,
+        either as subject or as subjectAltName.
+      '';
+
+      eap_id = mkOptionalStrParam ''
+        Client EAP-Identity to use in EAP-Identity exchange and the EAP method.
+      '';
+
+      aaa_id = mkOptionalStrParam ''
+        Server side EAP-Identity to expect in the EAP method. Some EAP methods,
+        such as EAP-TLS, use an identity for the server to perform mutual
+        authentication. This identity may differ from the IKE identity,
+        especially when EAP authentication is delegated from the IKE responder
+        to an AAA backend.
+        </para><para>
+        For EAP-(T)TLS, this defines the identity for which the server must
+        provide a certificate in the TLS exchange.
+      '';
+
+      xauth_id = mkOptionalStrParam ''
+        Client XAuth username used in the XAuth exchange.
+      '';
+
+    } ''
+      Section for a local authentication round. A local authentication round
+      defines the rules how authentication is performed for the local
+      peer. Multiple rounds may be defined to use IKEv2 RFC 4739 Multiple
+      Authentication or IKEv1 XAuth.
+      </para><para>
+      Each round is defined in a section having <literal>local</literal> as
+      prefix, and an optional unique suffix. To define a single authentication
+      round, the suffix may be omitted.
+    '';
+
+    remote = mkPrefixedAttrsOfParams {
+
+      round = mkIntParam 0 ''
+        Optional numeric identifier by which authentication rounds are
+        sorted. If not specified rounds are ordered by their position in the
+        config file/vici message.
+      '';
+
+      id = mkStrParam "%any" ''
+        IKE identity to expect for authentication round. When using certificate
+        authentication, the IKE identity must be contained in the certificate,
+        either as subject or as subjectAltName.
+      '';
+
+      eap_id = mkOptionalStrParam ''
+        Identity to use as peer identity during EAP authentication. If set to
+        <literal>%any</literal> the EAP-Identity method will be used to ask the
+        client for an EAP identity.
+      '';
+
+      groups = mkCommaSepListParam [] ''
+        Authorization group memberships to require. The peer
+        must prove membership to at least one of the specified groups. Group
+        membership can be certified by different means, for example by
+        appropriate Attribute Certificates or by an AAA backend involved in the
+        authentication.
+      '';
+
+      cert_policy = mkCommaSepListParam [] ''
+        List of certificate policy OIDs the peer's certificate
+        must have. OIDs are specified using the numerical dotted representation.
+      '';
+
+      certs = mkCommaSepListParam [] ''
+        List of certificates to accept for authentication. The certificates may
+        use a relative path from the swanctl <literal>x509</literal> directory
+        or an absolute path.
+      '';
+
+      cert = mkPostfixedAttrsOfParams certParams ''
+        Section for a certificate candidate to use for
+        authentication. Certificates in certs are transmitted as binary blobs,
+        these sections offer more flexibility.
+      '';
+
+      cacerts = mkCommaSepListParam [] ''
+        List of CA certificates to accept for
+        authentication. The certificates may use a relative path from the
+        swanctl <literal>x509ca</literal> directory or an absolute path.
+      '';
+
+      cacert = mkPostfixedAttrsOfParams certParams ''
+        Section for a CA certificate to accept for authentication. Certificates
+        in cacerts are transmitted as binary blobs, these sections offer more
+        flexibility.
+      '';
+
+      pubkeys = mkCommaSepListParam [] ''
+        List of raw public keys to accept for
+        authentication. The public keys may use a relative path from the swanctl
+        <literal>pubkey</literal> directory or an absolute path.
+      '';
+
+      revocation = mkEnumParam ["strict" "ifuri" "relaxed"] "relaxed" ''
+        Certificate revocation policy for CRL or OCSP revocation.
+        <itemizedlist>
+        <listitem><para>
+        A <literal>strict</literal> revocation policy fails if no revocation information is
+        available, i.e. the certificate is not known to be unrevoked.
+        </para></listitem>
+        <listitem><para>
+        <literal>ifuri</literal> fails only if a CRL/OCSP URI is available, but certificate
+        revocation checking fails, i.e. there should be revocation information
+        available, but it could not be obtained.
+        </para></listitem>
+        <listitem><para>
+        The default revocation policy <literal>relaxed</literal> fails only if a certificate is
+        revoked, i.e. it is explicitly known that it is bad.
+        </para></listitem>
+        </itemizedlist>
+      '';
+
+      auth = mkStrParam "pubkey" ''
+        Authentication to expect from remote. See the <option>local</option>
+        section's <option>auth</option> keyword description about the details of
+        supported mechanisms.
+        </para><para>
+        Since 5.4.0, to require a trustchain public key strength for the remote
+        side, specify the key type followed by the minimum strength in bits (for
+        example <literal>ecdsa-384</literal> or
+        <literal>rsa-2048-ecdsa-256</literal>). To limit the acceptable set of
+        hashing algorithms for trustchain validation, append hash algorithms to
+        pubkey or a key strength definition (for example
+        <literal>pubkey-sha256-sha512</literal>,
+        <literal>rsa-2048-sha256-sha384-sha512</literal> or
+        <literal>rsa-2048-sha256-ecdsa-256-sha256-sha384</literal>).
+        Unless disabled in <literal>strongswan.conf</literal>, or explicit IKEv2
+        signature constraints are configured (refer to the description of the
+        <option>local</option> section's <option>auth</option> keyword for
+        details), such key types and hash algorithms are also applied as
+        constraints against IKEv2 signature authentication schemes used by the
+        remote side. To require RSASSA-PSS signatures use
+        <literal>rsa/pss</literal> instead of <literal>pubkey</literal> or
+        <literal>rsa</literal> as in e.g. <literal>rsa/pss-sha256</literal>. If
+        <literal>pubkey</literal> or <literal>rsa</literal> constraints are
+        configured RSASSA-PSS signatures will only be accepted if enabled in
+        <literal>strongswan.conf</literal>(5).
+        </para><para>
+        To specify trust chain constraints for EAP-(T)TLS, append a colon to the
+        EAP method, followed by the key type/size and hash algorithm as
+        discussed above (e.g. <literal>eap-tls:ecdsa-384-sha384</literal>).
+      '';
+
+    } ''
+      Section for a remote authentication round. A remote authentication round
+      defines the constraints how the peers must authenticate to use this
+      connection. Multiple rounds may be defined to use IKEv2 RFC 4739 Multiple
+      Authentication or IKEv1 XAuth.
+      </para><para>
+      Each round is defined in a section having <literal>remote</literal> as
+      prefix, and an optional unique suffix. To define a single authentication
+      round, the suffix may be omitted.
+    '';
+
+    children = mkAttrsOfParams {
+      ah_proposals = mkCommaSepListParam [] ''
+        AH proposals to offer for the CHILD_SA. A proposal is a set of
+        algorithms. For AH, this includes an integrity algorithm and an optional
+        Diffie-Hellman group. If a DH group is specified, CHILD_SA/Quick Mode
+        rekeying and initial negotiation uses a separate Diffie-Hellman exchange
+        using the specified group (refer to esp_proposals for details).
+        </para><para>
+        In IKEv2, multiple algorithms of the same kind can be specified in a
+        single proposal, from which one gets selected. In IKEv1, only one
+        algorithm per kind is allowed per proposal, more algorithms get
+        implicitly stripped. Use multiple proposals to offer different algorithms
+        combinations in IKEv1.
+        </para><para>
+        Algorithm keywords get separated using dashes. Multiple proposals may be
+        specified in a list. The special value <literal>default</literal> forms
+        a default proposal of supported algorithms considered safe, and is
+        usually a good choice for interoperability. By default no AH proposals
+        are included, instead ESP is proposed.
+     '';
+
+      esp_proposals = mkCommaSepListParam ["default"] ''
+        ESP proposals to offer for the CHILD_SA. A proposal is a set of
+        algorithms. For ESP non-AEAD proposals, this includes an integrity
+        algorithm, an encryption algorithm, an optional Diffie-Hellman group and
+        an optional Extended Sequence Number Mode indicator. For AEAD proposals,
+        a combined mode algorithm is used instead of the separate
+        encryption/integrity algorithms.
+        </para><para>
+        If a DH group is specified, CHILD_SA/Quick Mode rekeying and initial
+        negotiation use a separate Diffie-Hellman exchange using the specified
+        group. However, for IKEv2, the keys of the CHILD_SA created implicitly
+        with the IKE_SA will always be derived from the IKE_SA's key material. So
+        any DH group specified here will only apply when the CHILD_SA is later
+        rekeyed or is created with a separate CREATE_CHILD_SA exchange. A
+        proposal mismatch might, therefore, not immediately be noticed when the
+        SA is established, but may later cause rekeying to fail.
+        </para><para>
+        Extended Sequence Number support may be indicated with the
+        <literal>esn</literal> and <literal>noesn</literal> values, both may be
+        included to indicate support for both modes. If omitted,
+        <literal>noesn</literal> is assumed.
+        </para><para>
+        In IKEv2, multiple algorithms of the same kind can be specified in a
+        single proposal, from which one gets selected. In IKEv1, only one
+        algorithm per kind is allowed per proposal, more algorithms get
+        implicitly stripped. Use multiple proposals to offer different algorithms
+        combinations in IKEv1.
+        </para><para>
+        Algorithm keywords get separated using dashes. Multiple proposals may be
+        specified as a list. The special value <literal>default</literal> forms
+        a default proposal of supported algorithms considered safe, and is
+        usually a good choice for interoperability. If no algorithms are
+        specified for AH nor ESP, the default set of algorithms for ESP is
+        included.
+      '';
+
+      sha256_96 = mkYesNoParam no ''
+        HMAC-SHA-256 is used with 128-bit truncation with IPsec. For
+        compatibility with implementations that incorrectly use 96-bit truncation
+        this option may be enabled to configure the shorter truncation length in
+        the kernel. This is not negotiated, so this only works with peers that
+        use the incorrect truncation length (or have this option enabled).
+      '';
+
+      local_ts = mkCommaSepListParam ["dynamic"] ''
+        List of local traffic selectors to include in CHILD_SA. Each selector is
+        a CIDR subnet definition, followed by an optional proto/port
+        selector. The special value <literal>dynamic</literal> may be used
+        instead of a subnet definition, which gets replaced by the tunnel outer
+        address or the virtual IP, if negotiated. This is the default.
+        </para><para>
+        A protocol/port selector is surrounded by opening and closing square
+        brackets. Between these brackets, a numeric or getservent(3) protocol
+        name may be specified. After the optional protocol restriction, an
+        optional port restriction may be specified, separated by a slash. The
+        port restriction may be numeric, a getservent(3) service name, or the
+        special value <literal>opaque</literal> for RFC 4301 OPAQUE
+        selectors. Port ranges may be specified as well, none of the kernel
+        backends currently support port ranges, though.
+        </para><para>
+        When IKEv1 is used only the first selector is interpreted, except if the
+        Cisco Unity extension plugin is used. This is due to a limitation of the
+        IKEv1 protocol, which only allows a single pair of selectors per
+        CHILD_SA. So to tunnel traffic matched by several pairs of selectors when
+        using IKEv1 several children (CHILD_SAs) have to be defined that cover
+        the selectors.  The IKE daemon uses traffic selector narrowing for IKEv1,
+        the same way it is standardized and implemented for IKEv2. However, this
+        may lead to problems with other implementations. To avoid that, configure
+        identical selectors in such scenarios.
+      '';
+
+      remote_ts = mkCommaSepListParam ["dynamic"] ''
+        List of remote selectors to include in CHILD_SA. See
+        <option>local_ts</option> for a description of the selector syntax.
+      '';
+
+      rekey_time = mkDurationParam "1h" ''
+        Time to schedule CHILD_SA rekeying. CHILD_SA rekeying refreshes key
+        material, optionally using a Diffie-Hellman exchange if a group is
+        specified in the proposal.  To avoid rekey collisions initiated by both
+        ends simultaneously, a value in the range of <option>rand_time</option>
+        gets subtracted to form the effective soft lifetime.
+        </para><para>
+        By default CHILD_SA rekeying is scheduled every hour, minus
+        <option>rand_time</option>.
+      '';
+
+      life_time = mkOptionalDurationParam ''
+        Maximum lifetime before CHILD_SA gets closed. Usually this hard lifetime
+        is never reached, because the CHILD_SA gets rekeyed before. If that fails
+        for whatever reason, this limit closes the CHILD_SA.  The default is 10%
+        more than the <option>rekey_time</option>.
+      '';
+
+      rand_time = mkOptionalDurationParam ''
+        Time range from which to choose a random value to subtract from
+        <option>rekey_time</option>. The default is the difference between
+        <option>life_time</option> and <option>rekey_time</option>.
+      '';
+
+      rekey_bytes = mkIntParam 0 ''
+        Number of bytes processed before initiating CHILD_SA rekeying. CHILD_SA
+        rekeying refreshes key material, optionally using a Diffie-Hellman
+        exchange if a group is specified in the proposal.
+        </para><para>
+        To avoid rekey collisions initiated by both ends simultaneously, a value
+        in the range of <option>rand_bytes</option> gets subtracted to form the
+        effective soft volume limit.
+        </para><para>
+        Volume based CHILD_SA rekeying is disabled by default.
+      '';
+
+      life_bytes = mkOptionalIntParam ''
+        Maximum bytes processed before CHILD_SA gets closed. Usually this hard
+        volume limit is never reached, because the CHILD_SA gets rekeyed
+        before. If that fails for whatever reason, this limit closes the
+        CHILD_SA.  The default is 10% more than <option>rekey_bytes</option>.
+      '';
+
+      rand_bytes = mkOptionalIntParam ''
+        Byte range from which to choose a random value to subtract from
+        <option>rekey_bytes</option>. The default is the difference between
+        <option>life_bytes</option> and <option>rekey_bytes</option>.
+      '';
+
+      rekey_packets = mkIntParam 0 ''
+        Number of packets processed before initiating CHILD_SA rekeying. CHILD_SA
+        rekeying refreshes key material, optionally using a Diffie-Hellman
+        exchange if a group is specified in the proposal.
+        </para><para>
+        To avoid rekey collisions initiated by both ends simultaneously, a value
+        in the range of <option>rand_packets</option> gets subtracted to form
+        the effective soft packet count limit.
+        </para><para>
+        Packet count based CHILD_SA rekeying is disabled by default.
+      '';
+
+      life_packets = mkOptionalIntParam ''
+        Maximum number of packets processed before CHILD_SA gets closed. Usually
+        this hard packets limit is never reached, because the CHILD_SA gets
+        rekeyed before. If that fails for whatever reason, this limit closes the
+        CHILD_SA.
+        </para><para>
+        The default is 10% more than <option>rekey_bytes</option>.
+      '';
+
+      rand_packets = mkOptionalIntParam ''
+        Packet range from which to choose a random value to subtract from
+        <option>rekey_packets</option>. The default is the difference between
+        <option>life_packets</option> and <option>rekey_packets</option>.
+      '';
+
+      updown = mkOptionalStrParam ''
+        Updown script to invoke on CHILD_SA up and down events.
+      '';
+
+      hostaccess = mkYesNoParam no ''
+        Hostaccess variable to pass to <literal>updown</literal> script.
+      '';
+
+      mode = mkEnumParam [ "tunnel"
+                           "transport"
+                           "transport_proxy"
+                           "beet"
+                           "pass"
+                           "drop"
+                         ] "tunnel" ''
+        IPsec Mode to establish CHILD_SA with.
+        <itemizedlist>
+        <listitem><para>
+        <literal>tunnel</literal> negotiates the CHILD_SA in IPsec Tunnel Mode,
+        </para></listitem>
+        <listitem><para>
+        whereas <literal>transport</literal> uses IPsec Transport Mode.
+        </para></listitem>
+        <listitem><para>
+        <literal>transport_proxy</literal> signifying the special Mobile IPv6
+        Transport Proxy Mode.
+        </para></listitem>
+        <listitem><para>
+        <literal>beet</literal> is the Bound End to End Tunnel mixture mode,
+        working with fixed inner addresses without the need to include them in
+        each packet.
+        </para></listitem>
+        <listitem><para>
+        Both <literal>transport</literal> and <literal>beet</literal> modes are
+        subject to mode negotiation; <literal>tunnel</literal> mode is
+        negotiated if the preferred mode is not available.
+        </para></listitem>
+        <listitem><para>
+        <literal>pass</literal> and <literal>drop</literal> are used to install
+        shunt policies which explicitly bypass the defined traffic from IPsec
+        processing or drop it, respectively.
+        </para></listitem>
+        </itemizedlist>
+      '';
+
+      policies = mkYesNoParam yes ''
+        Whether to install IPsec policies or not. Disabling this can be useful in
+        some scenarios e.g. MIPv6, where policies are not managed by the IKE
+        daemon. Since 5.3.3.
+      '';
+
+      policies_fwd_out = mkYesNoParam no ''
+        Whether to install outbound FWD IPsec policies or not. Enabling this is
+        required in case there is a drop policy that would match and block
+        forwarded traffic for this CHILD_SA. Since 5.5.1.
+      '';
+
+      dpd_action = mkEnumParam ["clear" "trap" "restart"] "clear" ''
+        Action to perform for this CHILD_SA on DPD timeout. The default clear
+        closes the CHILD_SA and does not take further action. trap installs a
+        trap policy, which will catch matching traffic and tries to re-negotiate
+        the tunnel on-demand. restart immediately tries to re-negotiate the
+        CHILD_SA under a fresh IKE_SA.
+      '';
+
+      ipcomp = mkYesNoParam no ''
+        Enable IPComp compression before encryption. If enabled, IKE tries to
+        negotiate IPComp compression to compress ESP payload data prior to
+        encryption.
+      '';
+
+      inactivity = mkDurationParam "0s" ''
+        Timeout before closing CHILD_SA after inactivity. If no traffic has been
+        processed in either direction for the configured timeout, the CHILD_SA
+        gets closed due to inactivity. The default value of 0 disables inactivity
+        checks.
+      '';
+
+      reqid = mkIntParam 0 ''
+        Fixed reqid to use for this CHILD_SA. This might be helpful in some
+        scenarios, but works only if each CHILD_SA configuration is instantiated
+        not more than once. The default of 0 uses dynamic reqids, allocated
+        incrementally.
+      '';
+
+      priority = mkIntParam 0 ''
+        Optional fixed priority for IPsec policies. This could be useful to
+        install high-priority drop policies. The default of 0 uses dynamically
+        calculated priorities based on the size of the traffic selectors.
+      '';
+
+      interface = mkOptionalStrParam ''
+        Optional interface name to restrict outbound IPsec policies.
+      '';
+
+      mark_in = mkStrParam "0/0x00000000" ''
+        Netfilter mark and mask for input traffic. On Linux, Netfilter may
+        require marks on each packet to match an SA/policy having that option
+        set. This allows installing duplicate policies and enables Netfilter
+        rules to select specific SAs/policies for incoming traffic. Note that
+        inbound marks are only set on policies, by default, unless
+        <option>mark_in_sa</option> is enabled. The special value
+        <literal>%unique</literal> sets a unique mark on each CHILD_SA instance,
+        beyond that the value <literal>%unique-dir</literal> assigns a different
+        unique mark for each
+        </para><para>
+        An additional mask may be appended to the mark, separated by
+        <literal>/</literal>. The default mask if omitted is
+        <literal>0xffffffff</literal>.
+      '';
+
+      mark_in_sa = mkYesNoParam no ''
+        Whether to set <option>mark_in</option> on the inbound SA. By default,
+        the inbound mark is only set on the inbound policy. The tuple destination
+        address, protocol and SPI is unique and the mark is not required to find
+        the correct SA, allowing to mark traffic after decryption instead (where
+        more specific selectors may be used) to match different policies. Marking
+        packets before decryption is still possible, even if no mark is set on
+        the SA.
+      '';
+
+      mark_out = mkStrParam "0/0x00000000" ''
+        Netfilter mark and mask for output traffic. On Linux, Netfilter may
+        require marks on each packet to match a policy/SA having that option
+        set. This allows installing duplicate policies and enables Netfilter
+        rules to select specific policies/SAs for outgoing traffic. The special
+        value <literal>%unique</literal> sets a unique mark on each CHILD_SA
+        instance, beyond that the value <literal>%unique-dir</literal> assigns a
+        different unique mark for each CHILD_SA direction (in/out).
+        </para><para>
+        An additional mask may be appended to the mark, separated by
+        <literal>/</literal>. The default mask if omitted is
+        <literal>0xffffffff</literal>.
+      '';
+
+      set_mark_in = mkStrParam "0/0x00000000" ''
+        Netfilter mark applied to packets after the inbound IPsec SA processed
+        them. This way it's not necessary to mark packets via Netfilter before
+        decryption or right afterwards to match policies or process them
+        differently (e.g. via policy routing).
+
+        An additional mask may be appended to the mark, separated by
+        <literal>/</literal>. The default mask if omitted is 0xffffffff. The
+        special value <literal>%same</literal> uses the value (but not the mask)
+        from <option>mark_in</option> as mark value, which can be fixed,
+        <literal>%unique</literal> or <literal>%unique-dir</literal>.
+
+        Setting marks in XFRM input requires Linux 4.19 or higher.
+      '';
+
+      set_mark_out = mkStrParam "0/0x00000000" ''
+        Netfilter mark applied to packets after the outbound IPsec SA processed
+        them. This allows processing ESP packets differently than the original
+        traffic (e.g. via policy routing).
+
+        An additional mask may be appended to the mark, separated by
+        <literal>/</literal>. The default mask if omitted is 0xffffffff. The
+        special value <literal>%same</literal> uses the value (but not the mask)
+        from <option>mark_out</option> as mark value, which can be fixed,
+        <literal>%unique_</literal> or <literal>%unique-dir</literal>.
+
+        Setting marks in XFRM output is supported since Linux 4.14. Setting a
+        mask requires at least Linux 4.19.
+      '';
+
+      if_id_in = mkStrParam "0" ''
+        XFRM interface ID set on inbound policies/SA. This allows installing
+        duplicate policies/SAs and associates them with an interface with the
+        same ID. The special value <literal>%unique</literal> sets a unique
+        interface ID on each CHILD_SA instance, beyond that the value
+        <literal>%unique-dir</literal> assigns a different unique interface ID
+        for each CHILD_SA direction (in/out).
+      '';
+
+      if_id_out = mkStrParam "0" ''
+        XFRM interface ID set on outbound policies/SA. This allows installing
+        duplicate policies/SAs and associates them with an interface with the
+        same ID. The special value <literal>%unique</literal> sets a unique
+        interface ID on each CHILD_SA instance, beyond that the value
+        <literal>%unique-dir</literal> assigns a different unique interface ID
+        for each CHILD_SA direction (in/out).
+
+        The daemon will not install routes for CHILD_SAs that have this option set.
+     '';
+
+      tfc_padding = mkParamOfType (with lib.types; either int (enum ["mtu"])) 0 ''
+        Pads ESP packets with additional data to have a consistent ESP packet
+        size for improved Traffic Flow Confidentiality. The padding defines the
+        minimum size of all ESP packets sent.  The default value of
+        <literal>0</literal> disables TFC padding, the special value
+        <literal>mtu</literal> adds TFC padding to create a packet size equal to
+        the Path Maximum Transfer Unit.
+      '';
+
+      replay_window = mkIntParam 32 ''
+        IPsec replay window to configure for this CHILD_SA. Larger values than
+        the default of <literal>32</literal> are supported using the Netlink
+        backend only, a value of <literal>0</literal> disables IPsec replay
+        protection.
+      '';
+
+      hw_offload = mkEnumParam ["yes" "no" "auto"] "no" ''
+        Enable hardware offload for this CHILD_SA, if supported by the IPsec
+        implementation. The value <literal>yes</literal> enforces offloading
+        and the installation will fail if it's not supported by either kernel or
+        device. The value <literal>auto</literal> enables offloading, if it's
+        supported, but the installation does not fail otherwise.
+      '';
+
+      copy_df = mkYesNoParam yes ''
+        Whether to copy the DF bit to the outer IPv4 header in tunnel mode. This
+        effectively disables Path MTU discovery (PMTUD). Controlling this
+        behavior is not supported by all kernel interfaces.
+      '';
+
+      copy_ecn = mkYesNoParam yes ''
+        Whether to copy the ECN (Explicit Congestion Notification) header field
+        to/from the outer IP header in tunnel mode. Controlling this behavior is
+        not supported by all kernel interfaces.
+      '';
+
+      copy_dscp = mkEnumParam [ "out" "in" "yes" "no" ] "out" ''
+        Whether to copy the DSCP (Differentiated Services Field Codepoint)
+        header field to/from the outer IP header in tunnel mode. The value
+        <literal>out</literal> only copies the field from the inner to the outer
+        header, the value <literal>in</literal> does the opposite and only
+        copies the field from the outer to the inner header when decapsulating,
+        the value <literal>yes</literal> copies the field in both directions,
+        and the value <literal>no</literal> disables copying the field
+        altogether. Setting this to <literal>yes</literal> or
+        <literal>in</literal> could allow an attacker to adversely affect other
+        traffic at the receiver, which is why the default is
+        <literal>out</literal>. Controlling this behavior is not supported by
+        all kernel interfaces.
+      '';
+
+      start_action = mkEnumParam ["none" "trap" "start"] "none" ''
+        Action to perform after loading the configuration.
+        <itemizedlist>
+        <listitem><para>
+        The default of <literal>none</literal> loads the connection only, which
+        then can be manually initiated or used as a responder configuration.
+        </para></listitem>
+        <listitem><para>
+        The value <literal>trap</literal> installs a trap policy, which triggers
+        the tunnel as soon as matching traffic has been detected.
+        </para></listitem>
+        <listitem><para>
+        The value <literal>start</literal> initiates the connection actively.
+        </para></listitem>
+        </itemizedlist>
+        When unloading or replacing a CHILD_SA configuration having a
+        <option>start_action</option> different from <literal>none</literal>,
+        the inverse action is performed. Configurations with
+        <literal>start</literal> get closed, while such with
+        <literal>trap</literal> get uninstalled.
+      '';
+
+      close_action = mkEnumParam ["none" "trap" "start"] "none" ''
+        Action to perform after a CHILD_SA gets closed by the peer.
+        <itemizedlist>
+        <listitem><para>
+        The default of <literal>none</literal> does not take any action,
+        </para></listitem>
+        <listitem><para>
+        <literal>trap</literal> installs a trap policy for the CHILD_SA.
+        </para></listitem>
+        <listitem><para>
+        <literal>start</literal> tries to re-create the CHILD_SA.
+        </para></listitem>
+        </itemizedlist>
+        </para><para>
+        <option>close_action</option> does not provide any guarantee that the
+        CHILD_SA is kept alive. It acts on explicit close messages only, but not
+        on negotiation failures. Use trap policies to reliably re-create failed
+        CHILD_SAs.
+      '';
+
+    } ''
+      CHILD_SA configuration sub-section. Each connection definition may have
+      one or more sections in its <option>children</option> subsection. The
+      section name defines the name of the CHILD_SA configuration, which must be
+      unique within the connection (denoted &#60;child&#62; below).
+    '';
+  } ''
+    Section defining IKE connection configurations, each in its own subsection
+    with an arbitrary yet unique name
+  '';
+
+  secrets = let
+    mkEapXauthParams = mkPrefixedAttrsOfParams {
+      secret = mkOptionalStrParam ''
+        Value of the EAP/XAuth secret. It may either be an ASCII string, a hex
+        encoded string if it has a 0x prefix or a Base64 encoded string if it
+        has a 0s prefix in its value.
+      '';
+
+      id = mkPrefixedAttrsOfParam (mkOptionalStrParam "") ''
+        Identity the EAP/XAuth secret belongs to. Multiple unique identities may
+        be specified, each having an <literal>id</literal> prefix, if a secret
+        is shared between multiple users.
+      '';
+
+    } ''
+      EAP secret section for a specific secret. Each EAP secret is defined in a
+      unique section having the <literal>eap</literal> prefix. EAP secrets are
+      used for XAuth authentication as well.
+    '';
+
+  in {
+
+    eap   = mkEapXauthParams;
+    xauth = mkEapXauthParams;
+
+    ntlm = mkPrefixedAttrsOfParams {
+      secret = mkOptionalStrParam ''
+        Value of the NTLM secret, which is the NT Hash of the actual secret,
+        that is, MD4(UTF-16LE(secret)). The resulting 16-byte value may either
+        be given as a hex encoded string with a 0x prefix or as a Base64 encoded
+        string with a 0s prefix.
+      '';
+
+      id = mkPrefixedAttrsOfParam (mkOptionalStrParam "") ''
+        Identity the NTLM secret belongs to. Multiple unique identities may be
+        specified, each having an id prefix, if a secret is shared between
+        multiple users.
+      '';
+    } ''
+      NTLM secret section for a specific secret. Each NTLM secret is defined in
+      a unique section having the <literal>ntlm</literal> prefix. NTLM secrets
+      may only be used for EAP-MSCHAPv2 authentication.
+    '';
+
+    ike = mkPrefixedAttrsOfParams {
+      secret = mkOptionalStrParam ''
+        Value of the IKE preshared secret. It may either be an ASCII string, a
+        hex encoded string if it has a 0x prefix or a Base64 encoded string if
+        it has a 0s prefix in its value.
+      '';
+
+      id = mkPrefixedAttrsOfParam (mkOptionalStrParam "") ''
+        IKE identity the IKE preshared secret belongs to. Multiple unique
+        identities may be specified, each having an <literal>id</literal>
+        prefix, if a secret is shared between multiple peers.
+      '';
+    } ''
+      IKE preshared secret section for a specific secret. Each IKE PSK is
+      defined in a unique section having the <literal>ike</literal> prefix.
+    '';
+
+    ppk = mkPrefixedAttrsOfParams {
+      secret = mkOptionalStrParam ''
+	      Value of the PPK. It may either be an ASCII string, a hex encoded string
+	      if it has a <literal>0x</literal> prefix or a Base64 encoded string if
+	      it has a <literal>0s</literal> prefix in its value. Should have at least
+	      256 bits of entropy for 128-bit security.
+      '';
+
+      id = mkPrefixedAttrsOfParam (mkOptionalStrParam "") ''
+	      PPK identity the PPK belongs to. Multiple unique identities may be
+	      specified, each having an <literal>id</literal> prefix, if a secret is
+	      shared between multiple peers.
+      '';
+    } ''
+	    Postquantum Preshared Key (PPK) section for a specific secret. Each PPK is
+	    defined in a unique section having the <literal>ppk</literal> prefix.
+    '';
+
+    private = mkPrefixedAttrsOfParams {
+      file = mkOptionalStrParam ''
+        File name in the private folder for which this passphrase should be used.
+      '';
+
+      secret = mkOptionalStrParam ''
+        Value of decryption passphrase for private key.
+      '';
+    } ''
+      Private key decryption passphrase for a key in the
+      <literal>private</literal> folder.
+    '';
+
+    rsa = mkPrefixedAttrsOfParams {
+      file = mkOptionalStrParam ''
+        File name in the <literal>rsa</literal> folder for which this passphrase
+        should be used.
+      '';
+      secret = mkOptionalStrParam ''
+        Value of decryption passphrase for RSA key.
+      '';
+    } ''
+      Private key decryption passphrase for a key in the <literal>rsa</literal>
+      folder.
+    '';
+
+    ecdsa = mkPrefixedAttrsOfParams {
+      file = mkOptionalStrParam ''
+        File name in the <literal>ecdsa</literal> folder for which this
+        passphrase should be used.
+      '';
+      secret = mkOptionalStrParam ''
+        Value of decryption passphrase for ECDSA key.
+      '';
+    } ''
+      Private key decryption passphrase for a key in the
+      <literal>ecdsa</literal> folder.
+    '';
+
+    pkcs8 = mkPrefixedAttrsOfParams {
+      file = mkOptionalStrParam ''
+        File name in the <literal>pkcs8</literal> folder for which this
+        passphrase should be used.
+      '';
+      secret = mkOptionalStrParam ''
+        Value of decryption passphrase for PKCS#8 key.
+      '';
+    } ''
+      Private key decryption passphrase for a key in the
+      <literal>pkcs8</literal> folder.
+    '';
+
+    pkcs12 = mkPrefixedAttrsOfParams {
+      file = mkOptionalStrParam ''
+        File name in the <literal>pkcs12</literal> folder for which this
+        passphrase should be used.
+      '';
+      secret = mkOptionalStrParam ''
+        Value of decryption passphrase for PKCS#12 container.
+      '';
+    } ''
+      PKCS#12 decryption passphrase for a container in the
+      <literal>pkcs12</literal> folder.
+    '';
+
+    token = mkPrefixedAttrsOfParams {
+      handle = mkOptionalHexParam ''
+        Hex-encoded CKA_ID or handle of the private key on the token or TPM,
+        respectively.
+      '';
+
+      slot = mkOptionalIntParam ''
+        Optional slot number to access the token.
+      '';
+
+      module = mkOptionalStrParam ''
+        Optional PKCS#11 module name to access the token.
+      '';
+
+      pin = mkOptionalStrParam ''
+        Optional PIN required to access the key on the token. If none is
+        provided the user is prompted during an interactive
+        <literal>--load-creds</literal> call.
+      '';
+    } ''Definition for a private key that's stored on a token/smartcard/TPM.'';
+
+  };
+
+  pools = mkAttrsOfParams {
+    addrs = mkOptionalStrParam ''
+      Subnet or range defining addresses allocated in pool. Accepts a single
+      CIDR subnet defining the pool to allocate addresses from or an address
+      range (&#60;from&#62;-&#60;to&#62;). Pools must be unique and non-overlapping.
+    '';
+
+    dns           = mkCommaSepListParam [] "Address or CIDR subnets";
+    nbns          = mkCommaSepListParam [] "Address or CIDR subnets";
+    dhcp          = mkCommaSepListParam [] "Address or CIDR subnets";
+    netmask       = mkCommaSepListParam [] "Address or CIDR subnets";
+    server        = mkCommaSepListParam [] "Address or CIDR subnets";
+    subnet        = mkCommaSepListParam [] "Address or CIDR subnets";
+    split_include = mkCommaSepListParam [] "Address or CIDR subnets";
+    split_exclude = mkCommaSepListParam [] "Address or CIDR subnets";
+  } ''
+    Section defining named pools. Named pools may be referenced by connections
+    with the pools option to assign virtual IPs and other configuration
+    attributes. Each pool must have a unique name (denoted &#60;name&#62; below).
+  '';
+}
diff --git a/nixpkgs/nixos/modules/services/networking/strongswan.nix b/nixpkgs/nixos/modules/services/networking/strongswan.nix
new file mode 100644
index 000000000000..13a1a897c5ed
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/strongswan.nix
@@ -0,0 +1,170 @@
+{ config, lib, pkgs, ... }:
+
+let
+
+  inherit (builtins) toFile;
+  inherit (lib) concatMapStringsSep concatStringsSep mapAttrsToList
+                mkIf mkEnableOption mkOption types literalExample;
+
+  cfg = config.services.strongswan;
+
+  ipsecSecrets = secrets: toFile "ipsec.secrets" (
+    concatMapStringsSep "\n" (f: "include ${f}") secrets
+  );
+
+  ipsecConf = {setup, connections, ca}:
+    let
+      # https://wiki.strongswan.org/projects/strongswan/wiki/IpsecConf
+      makeSections = type: sections: concatStringsSep "\n\n" (
+        mapAttrsToList (sec: attrs:
+          "${type} ${sec}\n" +
+            (concatStringsSep "\n" ( mapAttrsToList (k: v: "  ${k}=${v}") attrs ))
+        ) sections
+      );
+      setupConf       = makeSections "config" { inherit setup; };
+      connectionsConf = makeSections "conn" connections;
+      caConf          = makeSections "ca" ca;
+
+    in
+    builtins.toFile "ipsec.conf" ''
+      ${setupConf}
+      ${connectionsConf}
+      ${caConf}
+    '';
+
+  strongswanConf = {setup, connections, ca, secretsFile, managePlugins, enabledPlugins}: toFile "strongswan.conf" ''
+    charon {
+      ${if managePlugins then "load_modular = no" else ""}
+      ${if managePlugins then ("load = " + (concatStringsSep " " enabledPlugins)) else ""}
+      plugins {
+        stroke {
+          secrets_file = ${secretsFile}
+        }
+      }
+    }
+
+    starter {
+      config_file = ${ipsecConf { inherit setup connections ca; }}
+    }
+  '';
+
+in
+{
+  options.services.strongswan = {
+    enable = mkEnableOption "strongSwan";
+
+    secrets = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      example = [ "/run/keys/ipsec-foo.secret" ];
+      description = ''
+        A list of paths to IPSec secret files. These
+        files will be included into the main ipsec.secrets file with
+        the <literal>include</literal> directive. It is safer if these
+        paths are absolute.
+      '';
+    };
+
+    setup = mkOption {
+      type = types.attrsOf types.str;
+      default = {};
+      example = { cachecrls = "yes"; strictcrlpolicy = "yes"; };
+      description = ''
+        A set of options for the ‘config setup’ section of the
+        <filename>ipsec.conf</filename> file. Defines general
+        configuration parameters.
+      '';
+    };
+
+    connections = mkOption {
+      type = types.attrsOf (types.attrsOf types.str);
+      default = {};
+      example = literalExample ''
+        {
+          "%default" = {
+            keyexchange = "ikev2";
+            keyingtries = "1";
+          };
+          roadwarrior = {
+            auto       = "add";
+            leftcert   = "/run/keys/moonCert.pem";
+            leftid     = "@moon.strongswan.org";
+            leftsubnet = "10.1.0.0/16";
+            right      = "%any";
+          };
+        }
+      '';
+      description = ''
+        A set of connections and their options for the ‘conn xxx’
+        sections of the <filename>ipsec.conf</filename> file.
+      '';
+    };
+
+    ca = mkOption {
+      type = types.attrsOf (types.attrsOf types.str);
+      default = {};
+      example = {
+        strongswan = {
+          auto   = "add";
+          cacert = "/run/keys/strongswanCert.pem";
+          crluri = "http://crl2.strongswan.org/strongswan.crl";
+        };
+      };
+      description = ''
+        A set of CAs (certification authorities) and their options for
+        the ‘ca xxx’ sections of the <filename>ipsec.conf</filename>
+        file.
+      '';
+    };
+
+    managePlugins = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        If set to true, this option will disable automatic plugin loading and
+        then tell strongSwan to enable the plugins specified in the
+        <option>enabledPlugins</option> option.
+      '';
+    };
+
+    enabledPlugins = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      description = ''
+        A list of additional plugins to enable if
+        <option>managePlugins</option> is true.
+      '';
+    };
+  };
+
+
+  config = with cfg;
+  let
+    secretsFile = ipsecSecrets cfg.secrets;
+  in
+  mkIf enable
+    {
+
+    # here we should use the default strongswan ipsec.secrets and
+    # append to it (default one is empty so not a pb for now)
+    environment.etc."ipsec.secrets".source = secretsFile;
+
+    systemd.services.strongswan = {
+      description = "strongSwan IPSec Service";
+      wantedBy = [ "multi-user.target" ];
+      path = with pkgs; [ kmod iproute iptables utillinux ]; # XXX Linux
+      after = [ "network-online.target" ];
+      environment = {
+        STRONGSWAN_CONF = strongswanConf { inherit setup connections ca secretsFile managePlugins enabledPlugins; };
+      };
+      serviceConfig = {
+        ExecStart  = "${pkgs.strongswan}/sbin/ipsec start --nofork";
+      };
+      preStart = ''
+        # with 'nopeerdns' setting, ppp writes into this folder
+        mkdir -m 700 -p /etc/ppp
+      '';
+    };
+  };
+}
+
diff --git a/nixpkgs/nixos/modules/services/networking/stubby.nix b/nixpkgs/nixos/modules/services/networking/stubby.nix
new file mode 100644
index 000000000000..c5e0f929a126
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/stubby.nix
@@ -0,0 +1,217 @@
+{ config, lib, pkgs, ...}:
+
+with lib;
+
+let
+  cfg = config.services.stubby;
+
+  fallbacks = concatMapStringsSep "\n  " (x: "- ${x}") cfg.fallbackProtocols;
+  listeners = concatMapStringsSep "\n  " (x: "- ${x}") cfg.listenAddresses;
+
+  # By default, the recursive resolvers maintained by the getdns
+  # project itself are enabled. More information about both getdns's servers,
+  # as well as third party options for upstream resolvers, can be found here:
+  # https://dnsprivacy.org/wiki/display/DP/DNS+Privacy+Test+Servers
+  #
+  # You can override these values by supplying a yaml-formatted array of your
+  # preferred upstream resolvers in the following format:
+  #
+  # 106 # - address_data: IPv4 or IPv6 address of the upstream
+  #   port: Port for UDP/TCP (default is 53)
+  #   tls_auth_name: Authentication domain name checked against the server
+  #                  certificate
+  #   tls_pubkey_pinset: An SPKI pinset verified against the keys in the server
+  #                      certificate
+  #     - digest: Only "sha256" is currently supported
+  #       value: Base64 encoded value of the sha256 fingerprint of the public
+  #              key
+  #   tls_port: Port for TLS (default is 853)
+
+  defaultUpstream = ''
+    - address_data: 145.100.185.15
+      tls_auth_name: "dnsovertls.sinodun.com"
+      tls_pubkey_pinset:
+        - digest: "sha256"
+          value: 62lKu9HsDVbyiPenApnc4sfmSYTHOVfFgL3pyB+cBL4=
+    - address_data: 145.100.185.16
+      tls_auth_name: "dnsovertls1.sinodun.com"
+      tls_pubkey_pinset:
+        - digest: "sha256"
+          value: cE2ecALeE5B+urJhDrJlVFmf38cJLAvqekONvjvpqUA=
+    - address_data: 185.49.141.37
+      tls_auth_name: "getdnsapi.net"
+      tls_pubkey_pinset:
+        - digest: "sha256"
+          value: foxZRnIh9gZpWnl+zEiKa0EJ2rdCGroMWm02gaxSc9Q=
+    - address_data: 2001:610:1:40ba:145:100:185:15
+      tls_auth_name: "dnsovertls.sinodun.com"
+      tls_pubkey_pinset:
+        - digest: "sha256"
+          value: 62lKu9HsDVbyiPenApnc4sfmSYTHOVfFgL3pyB+cBL4=
+    - address_data: 2001:610:1:40ba:145:100:185:16
+      tls_auth_name: "dnsovertls1.sinodun.com"
+      tls_pubkey_pinset:
+        - digest: "sha256"
+          value: cE2ecALeE5B+urJhDrJlVFmf38cJLAvqekONvjvpqUA=
+    - address_data: 2a04:b900:0:100::38
+      tls_auth_name: "getdnsapi.net"
+      tls_pubkey_pinset:
+        - digest: "sha256"
+          value: foxZRnIh9gZpWnl+zEiKa0EJ2rdCGroMWm02gaxSc9Q=
+  '';
+
+  # Resolution type is not changeable here because it is required per the
+  # stubby documentation:
+  #
+  # "resolution_type: Work in stub mode only (not recursive mode) - required for Stubby
+  # operation."
+  #
+  # https://dnsprivacy.org/wiki/display/DP/Configuring+Stubby
+
+  confFile = pkgs.writeText "stubby.yml" ''
+    resolution_type: GETDNS_RESOLUTION_STUB
+    dns_transport_list:
+      ${fallbacks}
+    appdata_dir: "/var/cache/stubby"
+    tls_authentication: ${cfg.authenticationMode}
+    tls_query_padding_blocksize: ${toString cfg.queryPaddingBlocksize}
+    edns_client_subnet_private: ${if cfg.subnetPrivate then "1" else "0"}
+    idle_timeout: ${toString cfg.idleTimeout}
+    listen_addresses:
+      ${listeners}
+    round_robin_upstreams: ${if cfg.roundRobinUpstreams then "1" else "0"}
+    ${cfg.extraConfig}
+    upstream_recursive_servers:
+    ${cfg.upstreamServers}
+  '';
+in
+
+{
+  options = {
+    services.stubby = {
+
+      enable = mkEnableOption "Stubby DNS resolver";
+
+      fallbackProtocols = mkOption {
+        default = [ "GETDNS_TRANSPORT_TLS" ];
+        type = with types; listOf (enum [
+          "GETDNS_TRANSPORT_TLS"
+          "GETDNS_TRANSPORT_TCP"
+          "GETDNS_TRANSPORT_UDP"
+        ]);
+        description = ''
+          Ordered list composed of one or more transport protocols.
+          Strict mode should only use <literal>GETDNS_TRANSPORT_TLS</literal>.
+          Other options are <literal>GETDNS_TRANSPORT_UDP</literal> and
+          <literal>GETDNS_TRANSPORT_TCP</literal>.
+        '';
+      };
+
+      authenticationMode = mkOption {
+        default = "GETDNS_AUTHENTICATION_REQUIRED";
+        type = types.enum [
+          "GETDNS_AUTHENTICATION_REQUIRED"
+          "GETDNS_AUTHENTICATION_NONE"
+        ];
+        description = ''
+          Selects the Strict or Opportunistic usage profile.
+          For strict, set to <literal>GETDNS_AUTHENTICATION_REQUIRED</literal>.
+          for opportunistic, use <literal>GETDNS_AUTHENTICATION_NONE</literal>.
+        '';
+      };
+
+      queryPaddingBlocksize = mkOption {
+        default = 128;
+        type = types.int;
+        description = ''
+          EDNS0 option to pad the size of the DNS query to the given blocksize.
+        '';
+      };
+
+      subnetPrivate = mkOption {
+        default = true;
+        type = types.bool;
+        description = ''
+          EDNS0 option for ECS client privacy. Default is
+          <literal>true</literal>. If set, this option prevents the client
+          subnet from being sent to authoritative nameservers.
+        '';
+      };
+
+      idleTimeout = mkOption {
+        default = 10000;
+        type = types.int;
+        description = "EDNS0 option for keepalive idle timeout expressed in
+        milliseconds.";
+      };
+
+      listenAddresses = mkOption {
+        default = [ "127.0.0.1" "0::1" ];
+        type = with types; listOf str;
+        description = ''
+          Sets the listen address for the stubby daemon.
+          Uses port 53 by default.
+          Ise IP@port to specify a different port.
+        '';
+      };
+
+      roundRobinUpstreams = mkOption {
+        default = true;
+        type = types.bool;
+        description = ''
+          Instructs stubby to distribute queries across all available name
+          servers. Default is <literal>true</literal>. Set to
+          <literal>false</literal> in order to use the first available.
+        '';
+      };
+
+      upstreamServers = mkOption {
+        default = defaultUpstream;
+        type = types.lines;
+        description = ''
+          Replace default upstreams. See <citerefentry><refentrytitle>stubby
+          </refentrytitle><manvolnum>1</manvolnum></citerefentry> for an
+          example of the entry formatting. In Strict mode, at least one of the
+          following settings must be supplied for each nameserver:
+          <literal>tls_auth_name</literal> or
+          <literal>tls_pubkey_pinset</literal>.
+        '';
+      };
+
+      debugLogging = mkOption {
+        default = false;
+        type = types.bool;
+        description = "Enable or disable debug level logging.";
+      };
+
+      extraConfig = mkOption {
+        default = "";
+        type = types.lines;
+        description = ''
+          Add additional configuration options. see <citerefentry>
+          <refentrytitle>stubby</refentrytitle><manvolnum>1</manvolnum>
+          </citerefentry>for more options.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.stubby ];
+    systemd.services.stubby = {
+      description = "Stubby local DNS resolver";
+      after = [ "network.target" ];
+      before = [ "nss-lookup.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        Type = "notify";
+        AmbientCapabilities = "CAP_NET_BIND_SERVICE";
+        CapabilityBoundingSet = "CAP_NET_BIND_SERVICE";
+        ExecStart = "${pkgs.stubby}/bin/stubby -C ${confFile} ${optionalString cfg.debugLogging "-l"}";
+        DynamicUser = true;
+        CacheDirectory = "stubby";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/stunnel.nix b/nixpkgs/nixos/modules/services/networking/stunnel.nix
new file mode 100644
index 000000000000..ab51bba2f6ac
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/stunnel.nix
@@ -0,0 +1,234 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.stunnel;
+  yesNo = val: if val then "yes" else "no";
+
+  verifyChainPathAssert = n: c: {
+    assertion = c.verifyHostname == null || (c.verifyChain || c.verifyPeer);
+    message =  "stunnel: \"${n}\" client configuration - hostname verification " +
+      "is not possible without either verifyChain or verifyPeer enabled";
+  };
+
+  serverConfig = {
+    options = {
+      accept = mkOption {
+        type = types.int;
+        description = "On which port stunnel should listen for incoming TLS connections.";
+      };
+
+      connect = mkOption {
+        type = types.int;
+        description = "To which port the decrypted connection should be forwarded.";
+      };
+
+      cert = mkOption {
+        type = types.path;
+        description = "File containing both the private and public keys.";
+      };
+    };
+  };
+
+  clientConfig = {
+    options = {
+      accept = mkOption {
+        type = types.str;
+        description = "IP:Port on which connections should be accepted.";
+      };
+
+      connect = mkOption {
+        type = types.str;
+        description = "IP:Port destination to connect to.";
+      };
+
+      verifyChain = mkOption {
+        type = types.bool;
+        default = true;
+        description = "Check if the provided certificate has a valid certificate chain (against CAPath).";
+      };
+
+      verifyPeer = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Check if the provided certificate is contained in CAPath.";
+      };
+
+      CAPath = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        description = "Path to a directory containing certificates to validate against.";
+      };
+
+      CAFile = mkOption {
+        type = types.nullOr types.path;
+        default = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
+        description = "Path to a file containing certificates to validate against.";
+      };
+
+      verifyHostname = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = "If set, stunnel checks if the provided certificate is valid for the given hostname.";
+      };
+    };
+  };
+
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.stunnel = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to enable the stunnel TLS tunneling service.";
+      };
+
+      user = mkOption {
+        type = with types; nullOr str;
+        default = "nobody";
+        description = "The user under which stunnel runs.";
+      };
+
+      group = mkOption {
+        type = with types; nullOr str;
+        default = "nogroup";
+        description = "The group under which stunnel runs.";
+      };
+
+      logLevel = mkOption {
+        type = types.enum [ "emerg" "alert" "crit" "err" "warning" "notice" "info" "debug" ];
+        default = "info";
+        description = "Verbosity of stunnel output.";
+      };
+
+      fipsMode = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Enable FIPS 140-2 mode required for compliance.";
+      };
+
+      enableInsecureSSLv3 = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Enable support for the insecure SSLv3 protocol.";
+      };
+
+
+      servers = mkOption {
+        description = "Define the server configuations.";
+        type = with types; attrsOf (submodule serverConfig);
+        example = {
+          fancyWebserver = {
+            enable = true;
+            accept = 443;
+            connect = 8080;
+            cert = "/path/to/pem/file";
+          };
+        };
+        default = { };
+      };
+
+      clients = mkOption {
+        description = "Define the client configurations.";
+        type = with types; attrsOf (submodule clientConfig);
+        example = {
+          foobar = {
+            accept = "0.0.0.0:8080";
+            connect = "nixos.org:443";
+            verifyChain = false;
+          };
+        };
+        default = { };
+      };
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    assertions = concatLists [
+      (singleton {
+        assertion = (length (attrValues cfg.servers) != 0) || ((length (attrValues cfg.clients)) != 0);
+        message = "stunnel: At least one server- or client-configuration has to be present.";
+      })
+
+      (mapAttrsToList verifyChainPathAssert cfg.clients)
+    ];
+
+    environment.systemPackages = [ pkgs.stunnel ];
+
+    environment.etc."stunnel.cfg".text = ''
+      ${ if cfg.user != null then "setuid = ${cfg.user}" else "" }
+      ${ if cfg.group != null then "setgid = ${cfg.group}" else "" }
+
+      debug = ${cfg.logLevel}
+
+      ${ optionalString cfg.fipsMode "fips = yes" }
+      ${ optionalString cfg.enableInsecureSSLv3 "options = -NO_SSLv3" }
+
+      ; ----- SERVER CONFIGURATIONS -----
+      ${ lib.concatStringsSep "\n"
+           (lib.mapAttrsToList
+             (n: v: ''
+               [${n}]
+               accept = ${toString v.accept}
+               connect = ${toString v.connect}
+               cert = ${v.cert}
+
+             '')
+           cfg.servers)
+      }
+
+      ; ----- CLIENT CONFIGURATIONS -----
+      ${ lib.concatStringsSep "\n"
+           (lib.mapAttrsToList
+             (n: v: ''
+               [${n}]
+               client = yes
+               accept = ${v.accept}
+               connect = ${v.connect}
+               verifyChain = ${yesNo v.verifyChain}
+               verifyPeer = ${yesNo v.verifyPeer}
+               ${optionalString (v.CAPath != null) "CApath = ${v.CAPath}"}
+               ${optionalString (v.CAFile != null) "CAFile = ${v.CAFile}"}
+               ${optionalString (v.verifyHostname != null) "checkHost = ${v.verifyHostname}"}
+               OCSPaia = yes
+
+             '')
+           cfg.clients)
+      }
+    '';
+
+    systemd.services.stunnel = {
+      description = "stunnel TLS tunneling service";
+      after = [ "network.target" ];
+      wants = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      restartTriggers = [ config.environment.etc."stunnel.cfg".source ];
+      serviceConfig = {
+        ExecStart = "${pkgs.stunnel}/bin/stunnel ${config.environment.etc."stunnel.cfg".source}";
+        Type = "forking";
+      };
+    };
+
+    meta.maintainers = with maintainers; [
+      # Server side
+      lschuermann
+      # Client side
+      das_j
+    ];
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/supplicant.nix b/nixpkgs/nixos/modules/services/networking/supplicant.nix
new file mode 100644
index 000000000000..b5b9989ce186
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/supplicant.nix
@@ -0,0 +1,249 @@
+{ config, lib, utils, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.networking.supplicant;
+
+  # We must escape interfaces due to the systemd interpretation
+  subsystemDevice = interface:
+    "sys-subsystem-net-devices-${utils.escapeSystemdPath interface}.device";
+
+  serviceName = iface: "supplicant-${if (iface=="WLAN") then "wlan@" else (
+                                     if (iface=="LAN") then "lan@" else (
+                                     if (iface=="DBUS") then "dbus"
+                                     else (replaceChars [" "] ["-"] iface)))}";
+
+  # TODO: Use proper privilege separation for wpa_supplicant
+  supplicantService = iface: suppl:
+    let
+      deps = (if (iface=="WLAN"||iface=="LAN") then ["sys-subsystem-net-devices-%i.device"] else (
+             if (iface=="DBUS") then ["dbus.service"]
+             else (map subsystemDevice (splitString " " iface))))
+             ++ optional (suppl.bridge!="") (subsystemDevice suppl.bridge);
+
+      ifaceArg = concatStringsSep " -N " (map (i: "-i${i}") (splitString " " iface));
+      driverArg = optionalString (suppl.driver != null) "-D${suppl.driver}";
+      bridgeArg = optionalString (suppl.bridge!="") "-b${suppl.bridge}";
+      confFileArg = optionalString (suppl.configFile.path!=null) "-c${suppl.configFile.path}";
+      extraConfFile = pkgs.writeText "supplicant-extra-conf-${replaceChars [" "] ["-"] iface}" ''
+        ${optionalString suppl.userControlled.enable "ctrl_interface=DIR=${suppl.userControlled.socketDir} GROUP=${suppl.userControlled.group}"}
+        ${optionalString suppl.configFile.writable "update_config=1"}
+        ${suppl.extraConf}
+      '';
+    in
+      { description = "Supplicant ${iface}${optionalString (iface=="WLAN"||iface=="LAN") " %I"}";
+        wantedBy = [ "multi-user.target" ] ++ deps;
+        wants = [ "network.target" ];
+        bindsTo = deps;
+        after = deps;
+        before = [ "network.target" ];
+
+        path = [ pkgs.coreutils ];
+
+        preStart = ''
+          ${optionalString (suppl.configFile.path!=null) ''
+            touch -a ${suppl.configFile.path}
+            chmod 600 ${suppl.configFile.path}
+          ''}
+          ${optionalString suppl.userControlled.enable ''
+            if ! test -e ${suppl.userControlled.socketDir}; then
+                mkdir -m 0770 -p ${suppl.userControlled.socketDir}
+                chgrp ${suppl.userControlled.group} ${suppl.userControlled.socketDir}
+            fi
+
+            if test "$(stat --printf '%G' ${suppl.userControlled.socketDir})" != "${suppl.userControlled.group}"; then
+                echo "ERROR: bad ownership on ${suppl.userControlled.socketDir}" >&2
+                exit 1
+            fi
+          ''}
+        '';
+
+        serviceConfig.ExecStart = "${pkgs.wpa_supplicant}/bin/wpa_supplicant -s ${driverArg} ${confFileArg} -I${extraConfFile} ${bridgeArg} ${suppl.extraCmdArgs} ${if (iface=="WLAN"||iface=="LAN") then "-i%I" else (if (iface=="DBUS") then "-u" else ifaceArg)}";
+
+      };
+
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    networking.supplicant = mkOption {
+      type = with types; attrsOf (submodule {
+        options = {
+  
+          configFile = {
+  
+            path = mkOption {
+              type = types.nullOr types.path;
+              default = null;
+              example = literalExample "/etc/wpa_supplicant.conf";
+              description = ''
+                External <literal>wpa_supplicant.conf</literal> configuration file.
+                The configuration options defined declaratively within <literal>networking.supplicant</literal> have
+                precedence over options defined in <literal>configFile</literal>.
+              '';
+            };
+  
+            writable = mkOption {
+              type = types.bool;
+              default = false;
+              description = ''
+                Whether the configuration file at <literal>configFile.path</literal> should be written to by
+                <literal>wpa_supplicant</literal>.
+              '';
+            };
+  
+          };
+  
+          extraConf = mkOption {
+            type = types.lines;
+            default = "";
+            example = ''
+              ap_scan=1
+              device_name=My-NixOS-Device
+              device_type=1-0050F204-1
+              driver_param=use_p2p_group_interface=1
+              disable_scan_offload=1
+              p2p_listen_reg_class=81
+              p2p_listen_channel=1
+              p2p_oper_reg_class=81
+              p2p_oper_channel=1
+              manufacturer=NixOS
+              model_name=NixOS_Unstable
+              model_number=2015
+            '';
+            description = ''
+              Configuration options for <literal>wpa_supplicant.conf</literal>.
+              Options defined here have precedence over options in <literal>configFile</literal>.
+              NOTE: Do not write sensitive data into <literal>extraConf</literal> as it will
+              be world-readable in the <literal>nix-store</literal>. For sensitive information
+              use the <literal>configFile</literal> instead.
+            '';
+          };
+  
+          extraCmdArgs = mkOption {
+            type = types.str;
+            default = "";
+            example = "-e/run/wpa_supplicant/entropy.bin";
+            description =
+              "Command line arguments to add when executing <literal>wpa_supplicant</literal>.";
+          };
+  
+          driver = mkOption {
+            type = types.nullOr types.str;
+            default = "nl80211,wext";
+            description = "Force a specific wpa_supplicant driver.";
+          };
+  
+          bridge = mkOption {
+            type = types.str;
+            default = "";
+            description = "Name of the bridge interface that wpa_supplicant should listen at.";
+          };
+  
+          userControlled = {
+  
+            enable = mkOption {
+              type = types.bool;
+              default = false;
+              description = ''
+                Allow normal users to control wpa_supplicant through wpa_gui or wpa_cli.
+                This is useful for laptop users that switch networks a lot and don't want
+                to depend on a large package such as NetworkManager just to pick nearby
+                access points.
+              '';
+            };
+  
+            socketDir = mkOption {
+              type = types.str;
+              default = "/run/wpa_supplicant";
+              description = "Directory of sockets for controlling wpa_supplicant.";
+            };
+  
+            group = mkOption {
+              type = types.str;
+              default = "wheel";
+              example = "network";
+              description = "Members of this group can control wpa_supplicant.";
+            };
+  
+          };
+        };
+      });
+
+      default = { };
+
+      example = literalExample ''
+        { "wlan0 wlan1" = {
+            configFile.path = "/etc/wpa_supplicant.conf";
+            userControlled.group = "network";
+            extraConf = '''
+              ap_scan=1
+              p2p_disabled=1
+            ''';
+            extraCmdArgs = "-u -W";
+            bridge = "br0";
+          };
+        }
+      '';
+
+      description = ''
+        Interfaces for which to start <command>wpa_supplicant</command>.
+        The supplicant is used to scan for and associate with wireless networks,
+        or to authenticate with 802.1x capable network switches.
+
+        The value of this option is an attribute set. Each attribute configures a
+        <command>wpa_supplicant</command> service, where the attribute name specifies
+        the name of the interface that <command>wpa_supplicant</command> operates on.
+        The attribute name can be a space separated list of interfaces.
+        The attribute names <literal>WLAN</literal>, <literal>LAN</literal> and <literal>DBUS</literal>
+        have a special meaning. <literal>WLAN</literal> and <literal>LAN</literal> are
+        configurations for universal <command>wpa_supplicant</command> service that is
+        started for each WLAN interface or for each LAN interface, respectively.
+        <literal>DBUS</literal> defines a device-unrelated <command>wpa_supplicant</command>
+        service that can be accessed through <literal>D-Bus</literal>.
+      '';
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf (cfg != {}) {
+
+    environment.systemPackages =  [ pkgs.wpa_supplicant ];
+
+    services.dbus.packages = [ pkgs.wpa_supplicant ];
+
+    systemd.services = mapAttrs' (n: v: nameValuePair (serviceName n) (supplicantService n v)) cfg;
+
+    services.udev.packages = [
+      (pkgs.writeTextFile {
+        name = "99-zzz-60-supplicant.rules";
+        destination = "/etc/udev/rules.d/99-zzz-60-supplicant.rules";
+        text = ''
+          ${flip (concatMapStringsSep "\n") (filter (n: n!="WLAN" && n!="LAN" && n!="DBUS") (attrNames cfg)) (iface:
+            flip (concatMapStringsSep "\n") (splitString " " iface) (i: ''
+              ACTION=="add", SUBSYSTEM=="net", ENV{INTERFACE}=="${i}", TAG+="systemd", ENV{SYSTEMD_WANTS}+="supplicant-${replaceChars [" "] ["-"] iface}.service", TAG+="SUPPLICANT_ASSIGNED"''))}
+
+          ${optionalString (hasAttr "WLAN" cfg) ''
+            ACTION=="add", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", TAG!="SUPPLICANT_ASSIGNED", TAG+="systemd", PROGRAM="${pkgs.systemd}/bin/systemd-escape -p %E{INTERFACE}", ENV{SYSTEMD_WANTS}+="supplicant-wlan@$result.service"
+          ''}
+          ${optionalString (hasAttr "LAN" cfg) ''
+            ACTION=="add", SUBSYSTEM=="net", ENV{DEVTYPE}=="lan", TAG!="SUPPLICANT_ASSIGNED", TAG+="systemd", PROGRAM="${pkgs.systemd}/bin/systemd-escape -p %E{INTERFACE}", ENV{SYSTEMD_WANTS}+="supplicant-lan@$result.service"
+          ''}
+        '';
+      })];
+
+  };
+
+}
+
diff --git a/nixpkgs/nixos/modules/services/networking/supybot.nix b/nixpkgs/nixos/modules/services/networking/supybot.nix
new file mode 100644
index 000000000000..dc9fb31ffd0b
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/supybot.nix
@@ -0,0 +1,161 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg  = config.services.supybot;
+  isStateDirHome = hasPrefix "/home/" cfg.stateDir;
+  isStateDirVar = cfg.stateDir == "/var/lib/supybot";
+  pyEnv = pkgs.python3.withPackages (p: [ p.limnoria ] ++ (cfg.extraPackages p));
+in
+{
+  options = {
+
+    services.supybot = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Enable Supybot, an IRC bot (also known as Limnoria).";
+      };
+
+      stateDir = mkOption {
+        type = types.path;
+        default = if versionAtLeast config.system.stateVersion "20.09"
+          then "/var/lib/supybot"
+          else "/home/supybot";
+        defaultText = "/var/lib/supybot";
+        description = "The root directory, logs and plugins are stored here";
+      };
+
+      configFile = mkOption {
+        type = types.path;
+        description = ''
+          Path to initial supybot config file. This can be generated by
+          running supybot-wizard.
+
+          Note: all paths should include the full path to the stateDir
+          directory (backup conf data logs logs/plugins plugins tmp web).
+        '';
+      };
+
+      plugins = mkOption {
+        type = types.attrsOf types.path;
+        default = {};
+        description = ''
+          Attribute set of additional plugins that will be symlinked to the
+          <filename>plugin</filename> subdirectory.
+
+          Please note that you still need to add the plugins to the config
+          file (or with <literal>!load</literal>) using their attribute name.
+        '';
+        example = literalExample ''
+          let
+            plugins = pkgs.fetchzip {
+              url = "https://github.com/ProgVal/Supybot-plugins/archive/57c2450c.zip";
+              sha256 = "077snf84ibnva3sbpzdfpfma6hcdw7dflwnhg6pw7mgnf0nd84qd";
+            };
+          in
+          {
+            Wikipedia = "''${plugins}/Wikipedia";
+            Decide = ./supy-decide;
+          }
+        '';
+      };
+
+      extraPackages = mkOption {
+        default = p: [];
+        description = ''
+          Extra Python packages available to supybot plugins. The
+          value must be a function which receives the attrset defined
+          in <varname>python3Packages</varname> as the sole argument.
+        '';
+        example = literalExample ''p: [ p.lxml p.requests ]'';
+      };
+
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ pkgs.python3Packages.limnoria ];
+
+    users.users.supybot = {
+      uid = config.ids.uids.supybot;
+      group = "supybot";
+      description = "Supybot IRC bot user";
+      home = cfg.stateDir;
+      isSystemUser = true;
+    };
+
+    users.groups.supybot = {
+      gid = config.ids.gids.supybot;
+    };
+
+    systemd.services.supybot = {
+      description = "Supybot, an IRC bot";
+      documentation = [ "https://limnoria.readthedocs.io/" ];
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      preStart = ''
+        # This needs to be created afresh every time
+        rm -f '${cfg.stateDir}/supybot.cfg.bak'
+      '';
+
+      serviceConfig = {
+        ExecStart = "${pyEnv}/bin/supybot ${cfg.stateDir}/supybot.cfg";
+        PIDFile = "/run/supybot.pid";
+        User = "supybot";
+        Group = "supybot";
+        UMask = "0007";
+        Restart = "on-abort";
+        StartLimitInterval = "5m";
+        StartLimitBurst = "1";
+
+        NoNewPrivileges = true;
+        PrivateDevices = true;
+        PrivateMounts = true;
+        PrivateTmp = true;
+        ProtectControlGroups = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
+        RestrictSUIDSGID = true;
+        SystemCallArchitectures = "native";
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        LockPersonality = true;
+        MemoryDenyWriteExecute = true;
+        RemoveIPC = true;
+        ProtectHostname = true;
+        CapabilityBoundingSet = "";
+        ProtectSystem = "full";
+      }
+      // optionalAttrs isStateDirVar {
+        StateDirectory = "supybot";
+        ProtectSystem = "strict";
+      }
+      // optionalAttrs (!isStateDirHome) {
+        ProtectHome = true;
+      };
+    };
+
+    systemd.tmpfiles.rules = [
+      "d '${cfg.stateDir}'              0700 supybot supybot - -"
+      "d '${cfg.stateDir}/backup'       0750 supybot supybot - -"
+      "d '${cfg.stateDir}/conf'         0750 supybot supybot - -"
+      "d '${cfg.stateDir}/data'         0750 supybot supybot - -"
+      "d '${cfg.stateDir}/plugins'      0750 supybot supybot - -"
+      "d '${cfg.stateDir}/logs'         0750 supybot supybot - -"
+      "d '${cfg.stateDir}/logs/plugins' 0750 supybot supybot - -"
+      "d '${cfg.stateDir}/tmp'          0750 supybot supybot - -"
+      "d '${cfg.stateDir}/web'          0750 supybot supybot - -"
+      "L '${cfg.stateDir}/supybot.cfg'  -    -       -       - ${cfg.configFile}"
+    ]
+    ++ (flip mapAttrsToList cfg.plugins (name: dest:
+      "L+ '${cfg.stateDir}/plugins/${name}' - - - - ${dest}"
+    ));
+
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/syncplay.nix b/nixpkgs/nixos/modules/services/networking/syncplay.nix
new file mode 100644
index 000000000000..e3147c10502c
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/syncplay.nix
@@ -0,0 +1,80 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.syncplay;
+
+  cmdArgs =
+    [ "--port" cfg.port ]
+    ++ optionals (cfg.salt != null) [ "--salt" cfg.salt ]
+    ++ optionals (cfg.certDir != null) [ "--tls" cfg.certDir ];
+
+in
+{
+  options = {
+    services.syncplay = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "If enabled, start the Syncplay server.";
+      };
+
+      port = mkOption {
+        type = types.int;
+        default = 8999;
+        description = ''
+          TCP port to bind to.
+        '';
+      };
+
+      salt = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          Salt to allow room operator passwords generated by this server
+          instance to still work when the server is restarted.
+        '';
+      };
+
+      certDir = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        description = ''
+          TLS certificates directory to use for encryption. See
+          <link xlink:href="https://github.com/Syncplay/syncplay/wiki/TLS-support"/>.
+        '';
+      };
+
+      user = mkOption {
+        type = types.str;
+        default = "nobody";
+        description = ''
+          User to use when running Syncplay.
+        '';
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "nogroup";
+        description = ''
+          Group to use when running Syncplay.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.syncplay = {
+      description = "Syncplay Service";
+      wantedBy    = [ "multi-user.target" ];
+      after       = [ "network-online.target "];
+
+      serviceConfig = {
+        ExecStart = "${pkgs.syncplay}/bin/syncplay-server ${escapeShellArgs cmdArgs}";
+        User = cfg.user;
+        Group = cfg.group;
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/syncthing-relay.nix b/nixpkgs/nixos/modules/services/networking/syncthing-relay.nix
new file mode 100644
index 000000000000..f5ca63e78930
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/syncthing-relay.nix
@@ -0,0 +1,121 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.syncthing.relay;
+
+  dataDirectory = "/var/lib/syncthing-relay";
+
+  relayOptions =
+    [
+      "--keys=${dataDirectory}"
+      "--listen=${cfg.listenAddress}:${toString cfg.port}"
+      "--status-srv=${cfg.statusListenAddress}:${toString cfg.statusPort}"
+      "--provided-by=${escapeShellArg cfg.providedBy}"
+    ]
+    ++ optional (cfg.pools != null) "--pools=${escapeShellArg (concatStringsSep "," cfg.pools)}"
+    ++ optional (cfg.globalRateBps != null) "--global-rate=${toString cfg.globalRateBps}"
+    ++ optional (cfg.perSessionRateBps != null) "--per-session-rate=${toString cfg.perSessionRateBps}"
+    ++ cfg.extraOptions;
+in {
+  ###### interface
+
+  options.services.syncthing.relay = {
+    enable = mkEnableOption "Syncthing relay service";
+
+    listenAddress = mkOption {
+      type = types.str;
+      default = "";
+      example = "1.2.3.4";
+      description = ''
+        Address to listen on for relay traffic.
+      '';
+    };
+
+    port = mkOption {
+      type = types.port;
+      default = 22067;
+      description = ''
+        Port to listen on for relay traffic. This port should be added to
+        <literal>networking.firewall.allowedTCPPorts</literal>.
+      '';
+    };
+
+    statusListenAddress = mkOption {
+      type = types.str;
+      default = "";
+      example = "1.2.3.4";
+      description = ''
+        Address to listen on for serving the relay status API.
+      '';
+    };
+
+    statusPort = mkOption {
+      type = types.port;
+      default = 22070;
+      description = ''
+        Port to listen on for serving the relay status API. This port should be
+        added to <literal>networking.firewall.allowedTCPPorts</literal>.
+      '';
+    };
+
+    pools = mkOption {
+      type = types.nullOr (types.listOf types.str);
+      default = null;
+      description = ''
+        Relay pools to join. If null, uses the default global pool.
+      '';
+    };
+
+    providedBy = mkOption {
+      type = types.str;
+      default = "";
+      description = ''
+        Human-readable description of the provider of the relay (you).
+      '';
+    };
+
+    globalRateBps = mkOption {
+      type = types.nullOr types.ints.positive;
+      default = null;
+      description = ''
+        Global bandwidth rate limit in bytes per second.
+      '';
+    };
+
+    perSessionRateBps = mkOption {
+      type = types.nullOr types.ints.positive;
+      default = null;
+      description = ''
+        Per session bandwidth rate limit in bytes per second.
+      '';
+    };
+
+    extraOptions = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      description = ''
+        Extra command line arguments to pass to strelaysrv.
+      '';
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    systemd.services.syncthing-relay = {
+      description = "Syncthing relay service";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+
+      serviceConfig = {
+        DynamicUser = true;
+        StateDirectory = baseNameOf dataDirectory;
+
+        Restart = "on-failure";
+        ExecStart = "${pkgs.syncthing-relay}/bin/strelaysrv ${concatStringsSep " " relayOptions}";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/syncthing.nix b/nixpkgs/nixos/modules/services/networking/syncthing.nix
new file mode 100644
index 000000000000..e717d78feed5
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/syncthing.nix
@@ -0,0 +1,528 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.syncthing;
+  defaultUser = "syncthing";
+
+  devices = mapAttrsToList (name: device: {
+    deviceID = device.id;
+    inherit (device) name addresses introducer;
+  }) cfg.declarative.devices;
+
+  folders = mapAttrsToList ( _: folder: {
+    inherit (folder) path id label type;
+    devices = map (device: { deviceId = cfg.declarative.devices.${device}.id; }) folder.devices;
+    rescanIntervalS = folder.rescanInterval;
+    fsWatcherEnabled = folder.watch;
+    fsWatcherDelayS = folder.watchDelay;
+    ignorePerms = folder.ignorePerms;
+    versioning = folder.versioning;
+  }) (filterAttrs (
+    _: folder:
+    folder.enable
+  ) cfg.declarative.folders);
+
+  # get the api key by parsing the config.xml
+  getApiKey = pkgs.writers.writeDash "getAPIKey" ''
+    ${pkgs.libxml2}/bin/xmllint \
+      --xpath 'string(configuration/gui/apikey)'\
+      ${cfg.configDir}/config.xml
+  '';
+
+  updateConfig = pkgs.writers.writeDash "merge-syncthing-config" ''
+    set -efu
+    # wait for syncthing port to open
+    until ${pkgs.curl}/bin/curl -Ss ${cfg.guiAddress} -o /dev/null; do
+      sleep 1
+    done
+
+    API_KEY=$(${getApiKey})
+    OLD_CFG=$(${pkgs.curl}/bin/curl -Ss \
+      -H "X-API-Key: $API_KEY" \
+      ${cfg.guiAddress}/rest/system/config)
+
+    # generate the new config by merging with the nixos config options
+    NEW_CFG=$(echo "$OLD_CFG" | ${pkgs.jq}/bin/jq -s '.[] as $in | $in * {
+      "devices": (${builtins.toJSON devices}${optionalString (! cfg.declarative.overrideDevices) " + $in.devices"}),
+      "folders": (${builtins.toJSON folders}${optionalString (! cfg.declarative.overrideFolders) " + $in.folders"})
+    }')
+
+    # POST the new config to syncthing
+    echo "$NEW_CFG" | ${pkgs.curl}/bin/curl -Ss \
+      -H "X-API-Key: $API_KEY" \
+      ${cfg.guiAddress}/rest/system/config -d @-
+
+    # restart syncthing after sending the new config
+    ${pkgs.curl}/bin/curl -Ss \
+      -H "X-API-Key: $API_KEY" \
+      -X POST \
+      ${cfg.guiAddress}/rest/system/restart
+  '';
+in {
+  ###### interface
+  options = {
+    services.syncthing = {
+
+      enable = mkEnableOption ''
+        Syncthing - the self-hosted open-source alternative
+        to Dropbox and Bittorrent Sync. Initial interface will be
+        available on http://127.0.0.1:8384/.
+      '';
+
+      declarative = {
+        cert = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          description = ''
+            Path to users cert.pem file, will be copied into the syncthing's
+            <literal>configDir</literal>
+          '';
+        };
+
+        key = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          description = ''
+            Path to users key.pem file, will be copied into the syncthing's
+            <literal>configDir</literal>
+          '';
+        };
+
+        overrideDevices = mkOption {
+          type = types.bool;
+          default = true;
+          description = ''
+            Whether to delete the devices which are not configured via the
+            <literal>declarative.devices</literal> option.
+            If set to false, devices added via the webinterface will
+            persist but will have to be deleted manually.
+          '';
+        };
+
+        devices = mkOption {
+          default = {};
+          description = ''
+            Peers/devices which syncthing should communicate with.
+          '';
+          example = {
+            bigbox = {
+              id = "7CFNTQM-IMTJBHJ-3UWRDIU-ZGQJFR6-VCXZ3NB-XUH3KZO-N52ITXR-LAIYUAU";
+              addresses = [ "tcp://192.168.0.10:51820" ];
+            };
+          };
+          type = types.attrsOf (types.submodule ({ name, ... }: {
+            options = {
+
+              name = mkOption {
+                type = types.str;
+                default = name;
+                description = ''
+                  Name of the device
+                '';
+              };
+
+              addresses = mkOption {
+                type = types.listOf types.str;
+                default = [];
+                description = ''
+                  The addresses used to connect to the device.
+                  If this is let empty, dynamic configuration is attempted
+                '';
+              };
+
+              id = mkOption {
+                type = types.str;
+                description = ''
+                  The id of the other peer, this is mandatory. It's documented at
+                  https://docs.syncthing.net/dev/device-ids.html
+                '';
+              };
+
+              introducer = mkOption {
+                type = types.bool;
+                default = false;
+                description = ''
+                  If the device should act as an introducer and be allowed
+                  to add folders on this computer.
+                '';
+              };
+
+            };
+          }));
+        };
+
+        overrideFolders = mkOption {
+          type = types.bool;
+          default = true;
+          description = ''
+            Whether to delete the folders which are not configured via the
+            <literal>declarative.folders</literal> option.
+            If set to false, folders added via the webinterface will persist
+            but will have to be deleted manually.
+          '';
+        };
+
+        folders = mkOption {
+          default = {};
+          description = ''
+            folders which should be shared by syncthing.
+          '';
+          example = literalExample ''
+            {
+              "/home/user/sync" = {
+                id = "syncme";
+                devices = [ "bigbox" ];
+              };
+            }
+          '';
+          type = types.attrsOf (types.submodule ({ name, ... }: {
+            options = {
+
+              enable = mkOption {
+                type = types.bool;
+                default = true;
+                description = ''
+                  share this folder.
+                  This option is useful when you want to define all folders
+                  in one place, but not every machine should share all folders.
+                '';
+              };
+
+              path = mkOption {
+                type = types.str;
+                default = name;
+                description = ''
+                  The path to the folder which should be shared.
+                '';
+              };
+
+              id = mkOption {
+                type = types.str;
+                default = name;
+                description = ''
+                  The id of the folder. Must be the same on all devices.
+                '';
+              };
+
+              label = mkOption {
+                type = types.str;
+                default = name;
+                description = ''
+                  The label of the folder.
+                '';
+              };
+
+              devices = mkOption {
+                type = types.listOf types.str;
+                default = [];
+                description = ''
+                  The devices this folder should be shared with. Must be defined
+                  in the <literal>declarative.devices</literal> attribute.
+                '';
+              };
+
+              versioning = mkOption {
+                default = null;
+                description = ''
+                  How to keep changed/deleted files with syncthing.
+                  There are 4 different types of versioning with different parameters.
+                  See https://docs.syncthing.net/users/versioning.html
+                '';
+                example = [
+                  {
+                    versioning = {
+                      type = "simple";
+                      params.keep = "10";
+                    };
+                  }
+                  {
+                    versioning = {
+                      type = "trashcan";
+                      params.cleanoutDays = "1000";
+                    };
+                  }
+                  {
+                    versioning = {
+                      type = "staggered";
+                      params = {
+                        cleanInterval = "3600";
+                        maxAge = "31536000";
+                        versionsPath = "/syncthing/backup";
+                      };
+                    };
+                  }
+                  {
+                    versioning = {
+                      type = "external";
+                      params.versionsPath = pkgs.writers.writeBash "backup" ''
+                        folderpath="$1"
+                        filepath="$2"
+                        rm -rf "$folderpath/$filepath"
+                      '';
+                    };
+                  }
+                ];
+                type = with types; nullOr (submodule {
+                  options = {
+                    type = mkOption {
+                      type = enum [ "external" "simple" "staggered" "trashcan" ];
+                      description = ''
+                        Type of versioning.
+                        See https://docs.syncthing.net/users/versioning.html
+                      '';
+                    };
+                    params = mkOption {
+                      type = attrsOf (either str path);
+                      description = ''
+                        Parameters for versioning. Structure depends on versioning.type.
+                        See https://docs.syncthing.net/users/versioning.html
+                      '';
+                    };
+                  };
+                });
+              };
+
+
+
+              rescanInterval = mkOption {
+                type = types.int;
+                default = 3600;
+                description = ''
+                  How often the folders should be rescaned for changes.
+                '';
+              };
+
+              type = mkOption {
+                type = types.enum [ "sendreceive" "sendonly" "receiveonly" ];
+                default = "sendreceive";
+                description = ''
+                  Whether to send only changes from this folder, only receive them
+                  or propagate both.
+                '';
+              };
+
+              watch = mkOption {
+                type = types.bool;
+                default = true;
+                description = ''
+                  Whether the folder should be watched for changes by inotify.
+                '';
+              };
+
+              watchDelay = mkOption {
+                type = types.int;
+                default = 10;
+                description = ''
+                  The delay after an inotify event is triggered.
+                '';
+              };
+
+              ignorePerms = mkOption {
+                type = types.bool;
+                default = true;
+                description = ''
+                  Whether to propagate permission changes.
+                '';
+              };
+
+            };
+          }));
+        };
+      };
+
+      guiAddress = mkOption {
+        type = types.str;
+        default = "127.0.0.1:8384";
+        description = ''
+          Address to serve the GUI.
+        '';
+      };
+
+      systemService = mkOption {
+        type = types.bool;
+        default = true;
+        description = "Auto launch Syncthing as a system service.";
+      };
+
+      user = mkOption {
+        type = types.str;
+        default = defaultUser;
+        description = ''
+          Syncthing will be run under this user (user will be created if it doesn't exist.
+          This can be your user name).
+        '';
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = defaultUser;
+        description = ''
+          Syncthing will be run under this group (group will not be created if it doesn't exist.
+          This can be your user name).
+        '';
+      };
+
+      all_proxy = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        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 {
+        type = types.path;
+        default = "/var/lib/syncthing";
+        description = ''
+          Path where synced directories will exist.
+        '';
+      };
+
+      configDir = mkOption {
+        type = types.path;
+        description = ''
+          Path where the settings and keys will exist.
+        '';
+        default =
+          let
+            nixos = config.system.stateVersion;
+            cond  = versionAtLeast nixos "19.03";
+          in cfg.dataDir + (optionalString cond "/.config/syncthing");
+      };
+
+      openDefaultPorts = mkOption {
+        type = types.bool;
+        default = false;
+        example = literalExample "true";
+        description = ''
+          Open the default ports in the firewall:
+            - TCP 22000 for transfers
+            - UDP 21027 for discovery
+          If multiple users are running syncthing on this machine, you will need to manually open a set of ports for each instance and leave this disabled.
+          Alternatively, if are running only a single instance on this machine using the default ports, enable this.
+        '';
+      };
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.syncthing;
+        defaultText = "pkgs.syncthing";
+        example = literalExample "pkgs.syncthing";
+        description = ''
+          Syncthing package to use.
+        '';
+      };
+    };
+  };
+
+  imports = [
+    (mkRemovedOptionModule ["services" "syncthing" "useInotify"] ''
+      This option was removed because syncthing now has the inotify functionality included under the name "fswatcher".
+      It can be enabled on a per-folder basis through the webinterface.
+    '')
+  ];
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    networking.firewall = mkIf cfg.openDefaultPorts {
+      allowedTCPPorts = [ 22000 ];
+      allowedUDPPorts = [ 21027 ];
+    };
+
+    systemd.packages = [ pkgs.syncthing ];
+
+    users.users = mkIf (cfg.systemService && cfg.user == defaultUser) {
+      ${defaultUser} =
+        { group = cfg.group;
+          home  = cfg.dataDir;
+          createHome = true;
+          uid = config.ids.uids.syncthing;
+          description = "Syncthing daemon user";
+        };
+    };
+
+    users.groups = mkIf (cfg.systemService && cfg.group == defaultUser) {
+      ${defaultUser}.gid =
+        config.ids.gids.syncthing;
+    };
+
+    systemd.services = {
+      syncthing = mkIf cfg.systemService {
+        description = "Syncthing service";
+        after = [ "network.target" ];
+        environment = {
+          STNORESTART = "yes";
+          STNOUPGRADE = "yes";
+          inherit (cfg) all_proxy;
+        } // config.networking.proxy.envVars;
+        wantedBy = [ "multi-user.target" ];
+        serviceConfig = {
+          Restart = "on-failure";
+          SuccessExitStatus = "2 3 4";
+          RestartForceExitStatus="3 4";
+          User = cfg.user;
+          Group = cfg.group;
+          ExecStartPre = mkIf (cfg.declarative.cert != null || cfg.declarative.key != null)
+            "+${pkgs.writers.writeBash "syncthing-copy-keys" ''
+              install -dm700 -o ${cfg.user} -g ${cfg.group} ${cfg.configDir}
+              ${optionalString (cfg.declarative.cert != null) ''
+                install -Dm400 -o ${cfg.user} -g ${cfg.group} ${toString cfg.declarative.cert} ${cfg.configDir}/cert.pem
+              ''}
+              ${optionalString (cfg.declarative.key != null) ''
+                install -Dm400 -o ${cfg.user} -g ${cfg.group} ${toString cfg.declarative.key} ${cfg.configDir}/key.pem
+              ''}
+            ''}"
+          ;
+          ExecStart = ''
+            ${cfg.package}/bin/syncthing \
+              -no-browser \
+              -gui-address=${cfg.guiAddress} \
+              -home=${cfg.configDir}
+          '';
+          MemoryDenyWriteExecute = true;
+          NoNewPrivileges = true;
+          PrivateDevices = true;
+          PrivateMounts = true;
+          PrivateTmp = true;
+          PrivateUsers = true;
+          ProtectControlGroups = true;
+          ProtectHostname = true;
+          ProtectKernelModules = true;
+          ProtectKernelTunables = true;
+          RestrictNamespaces = true;
+          RestrictRealtime = true;
+          RestrictSUIDSGID = true;
+          CapabilityBoundingSet = [
+            "~CAP_SYS_PTRACE" "~CAP_SYS_ADMIN"
+            "~CAP_SETGID" "~CAP_SETUID" "~CAP_SETPCAP"
+            "~CAP_SYS_TIME" "~CAP_KILL"
+          ];
+        };
+      };
+      syncthing-init = mkIf (
+        cfg.declarative.devices != {} || cfg.declarative.folders != {}
+      ) {
+        after = [ "syncthing.service" ];
+        wantedBy = [ "multi-user.target" ];
+
+        serviceConfig = {
+          User = cfg.user;
+          RemainAfterExit = true;
+          Type = "oneshot";
+          ExecStart = updateConfig;
+        };
+      };
+
+      syncthing-resume = {
+        wantedBy = [ "suspend.target" ];
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/tailscale.nix b/nixpkgs/nixos/modules/services/networking/tailscale.nix
new file mode 100644
index 000000000000..4d6aeb75ebd1
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/tailscale.nix
@@ -0,0 +1,49 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let cfg = config.services.tailscale;
+in {
+  meta.maintainers = with maintainers; [ danderson mbaillie ];
+
+  options.services.tailscale = {
+    enable = mkEnableOption "Tailscale client daemon";
+
+    port = mkOption {
+      type = types.port;
+      default = 41641;
+      description = "The port to listen on for tunnel traffic (0=autoselect).";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.tailscale = {
+      description = "Tailscale client daemon";
+
+      after = [ "network-pre.target" ];
+      wants = [ "network-pre.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      unitConfig = {
+        StartLimitIntervalSec = 0;
+        StartLimitBurst = 0;
+      };
+
+      serviceConfig = {
+        ExecStart =
+          "${pkgs.tailscale}/bin/tailscaled --port ${toString cfg.port}";
+
+        RuntimeDirectory = "tailscale";
+        RuntimeDirectoryMode = 755;
+
+        StateDirectory = "tailscale";
+        StateDirectoryMode = 750;
+
+        CacheDirectory = "tailscale";
+        CacheDirectoryMode = 750;
+
+        Restart = "on-failure";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/tcpcrypt.nix b/nixpkgs/nixos/modules/services/networking/tcpcrypt.nix
new file mode 100644
index 000000000000..5a91054e1668
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/tcpcrypt.nix
@@ -0,0 +1,80 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.networking.tcpcrypt;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    networking.tcpcrypt.enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether to enable opportunistic TCP encryption. If the other end
+        speaks Tcpcrypt, then your traffic will be encrypted; otherwise
+        it will be sent in clear text. Thus, Tcpcrypt alone provides no
+        guarantees -- it is best effort. If, however, a Tcpcrypt
+        connection is successful and any attackers that exist are
+        passive, then Tcpcrypt guarantees privacy.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    users.users.tcpcryptd = {
+      uid = config.ids.uids.tcpcryptd;
+      description = "tcpcrypt daemon user";
+    };
+
+    systemd.services.tcpcrypt = {
+      description = "tcpcrypt";
+
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+
+      path = [ pkgs.iptables pkgs.tcpcrypt pkgs.procps ];
+
+      preStart = ''
+        mkdir -p /run/tcpcryptd
+        chown tcpcryptd /run/tcpcryptd
+        sysctl -n net.ipv4.tcp_ecn > /run/tcpcryptd/pre-tcpcrypt-ecn-state
+        sysctl -w net.ipv4.tcp_ecn=0
+
+        iptables -t raw -N nixos-tcpcrypt
+        iptables -t raw -A nixos-tcpcrypt -p tcp -m mark --mark 0x0/0x10 -j NFQUEUE --queue-num 666
+        iptables -t raw -I PREROUTING -j nixos-tcpcrypt
+
+        iptables -t mangle -N nixos-tcpcrypt
+        iptables -t mangle -A nixos-tcpcrypt -p tcp -m mark --mark 0x0/0x10 -j NFQUEUE --queue-num 666
+        iptables -t mangle -I POSTROUTING -j nixos-tcpcrypt
+      '';
+
+      script = "tcpcryptd -x 0x10";
+
+      postStop = ''
+        if [ -f /run/tcpcryptd/pre-tcpcrypt-ecn-state ]; then
+          sysctl -w net.ipv4.tcp_ecn=$(cat /run/tcpcryptd/pre-tcpcrypt-ecn-state)
+        fi
+
+        iptables -t mangle -D POSTROUTING -j nixos-tcpcrypt || true
+        iptables -t raw -D PREROUTING -j nixos-tcpcrypt || true
+
+        iptables -t raw -F nixos-tcpcrypt || true
+        iptables -t raw -X nixos-tcpcrypt || true
+
+        iptables -t mangle -F nixos-tcpcrypt || true
+        iptables -t mangle -X nixos-tcpcrypt || true
+      '';
+    };
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/teamspeak3.nix b/nixpkgs/nixos/modules/services/networking/teamspeak3.nix
new file mode 100644
index 000000000000..fadb32dcd777
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/teamspeak3.nix
@@ -0,0 +1,142 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  ts3 = pkgs.teamspeak_server;
+  cfg = config.services.teamspeak3;
+  user = "teamspeak";
+  group = "teamspeak";
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.teamspeak3 = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to run the Teamspeak3 voice communication server daemon.
+        '';
+      };
+
+      dataDir = mkOption {
+        type = types.path;
+        default = "/var/lib/teamspeak3-server";
+        description = ''
+          Directory to store TS3 database and other state/data files.
+        '';
+      };
+
+      logPath = mkOption {
+        type = types.path;
+        default = "/var/log/teamspeak3-server/";
+        description = ''
+          Directory to store log files in.
+        '';
+      };
+
+      voiceIP = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "0.0.0.0";
+        description = ''
+          IP on which the server instance will listen for incoming voice connections. Defaults to any IP.
+        '';
+      };
+
+      defaultVoicePort = mkOption {
+        type = types.int;
+        default = 9987;
+        description = ''
+          Default UDP port for clients to connect to virtual servers - used for first virtual server, subsequent ones will open on incrementing port numbers by default.
+        '';
+      };
+
+      fileTransferIP = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "0.0.0.0";
+        description = ''
+          IP on which the server instance will listen for incoming file transfer connections. Defaults to any IP.
+        '';
+      };
+
+      fileTransferPort = mkOption {
+        type = types.int;
+        default = 30033;
+        description = ''
+          TCP port opened for file transfers.
+        '';
+      };
+
+      queryIP = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "0.0.0.0";
+        description = ''
+          IP on which the server instance will listen for incoming ServerQuery connections. Defaults to any IP.
+        '';
+      };
+
+      queryPort = mkOption {
+        type = types.int;
+        default = 10011;
+        description = ''
+          TCP port opened for ServerQuery connections.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    users.users.teamspeak = {
+      description = "Teamspeak3 voice communication server daemon";
+      group = group;
+      uid = config.ids.uids.teamspeak;
+      home = cfg.dataDir;
+      createHome = true;
+    };
+
+    users.groups.teamspeak = {
+      gid = config.ids.gids.teamspeak;
+    };
+
+    systemd.tmpfiles.rules = [
+      "d '${cfg.logPath}' - ${user} ${group} - -"
+    ];
+
+    systemd.services.teamspeak3-server = {
+      description = "Teamspeak3 voice communication server daemon";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        ExecStart = ''
+          ${ts3}/bin/ts3server \
+            dbsqlpath=${ts3}/lib/teamspeak/sql/ logpath=${cfg.logPath} \
+            ${optionalString (cfg.voiceIP != null) "voice_ip=${cfg.voiceIP}"} \
+            default_voice_port=${toString cfg.defaultVoicePort} \
+            ${optionalString (cfg.fileTransferIP != null) "filetransfer_ip=${cfg.fileTransferIP}"} \
+            filetransfer_port=${toString cfg.fileTransferPort} \
+            ${optionalString (cfg.queryIP != null) "query_ip=${cfg.queryIP}"} \
+            query_port=${toString cfg.queryPort} license_accepted=1
+        '';
+        WorkingDirectory = cfg.dataDir;
+        User = user;
+        Group = group;
+      };
+    };
+  };
+
+  meta.maintainers = with lib.maintainers; [ arobyn ];
+}
diff --git a/nixpkgs/nixos/modules/services/networking/tedicross.nix b/nixpkgs/nixos/modules/services/networking/tedicross.nix
new file mode 100644
index 000000000000..0716975f594a
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/tedicross.nix
@@ -0,0 +1,100 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  dataDir = "/var/lib/tedicross";
+  cfg = config.services.tedicross;
+  configJSON = pkgs.writeText "tedicross-settings.json" (builtins.toJSON cfg.config);
+  configYAML = pkgs.runCommand "tedicross-settings.yaml" { preferLocalBuild = true; } ''
+    ${pkgs.remarshal}/bin/json2yaml -i ${configJSON} -o $out
+  '';
+
+in {
+  options = {
+    services.tedicross = {
+      enable = mkEnableOption "the TediCross Telegram-Discord bridge service";
+
+      config = mkOption {
+        type = types.attrs;
+        # from https://github.com/TediCross/TediCross/blob/master/example.settings.yaml
+        example = literalExample ''
+          {
+            telegram = {
+              useFirstNameInsteadOfUsername = false;
+              colonAfterSenderName = false;
+              skipOldMessages = true;
+              sendEmojiWithStickers = true;
+            };
+            discord = {
+              useNickname = false;
+              skipOldMessages = true;
+              displayTelegramReplies = "embed";
+              replyLength = 100;
+            };
+            bridges = [
+              {
+                name = "Default bridge";
+                direction = "both";
+                telegram = {
+                  chatId = -123456789;
+                  relayJoinMessages = true;
+                  relayLeaveMessages = true;
+                  sendUsernames = true;
+                  ignoreCommands = true;
+                };
+                discord = {
+                  serverId = "DISCORD_SERVER_ID";
+                  channelId = "DISCORD_CHANNEL_ID";
+                  relayJoinMessages = true;
+                  relayLeaveMessages = true;
+                  sendUsernames = true;
+                  crossDeleteOnTelegram = true;
+                };
+              }
+            ];
+
+            debug = false;
+          }
+        '';
+        description = ''
+          <filename>settings.yaml</filename> configuration as a Nix attribute set.
+          Secret tokens should be specified using <option>environmentFile</option>
+          instead of this world-readable file.
+        '';
+      };
+
+      environmentFile = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        description = ''
+          File containing environment variables to be passed to the TediCross service,
+          in which secret tokens can be specified securely using the
+          <literal>TELEGRAM_BOT_TOKEN</literal> and <literal>DISCORD_BOT_TOKEN</literal>
+          keys.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    # from https://github.com/TediCross/TediCross/blob/master/guides/autostart/Linux.md
+    systemd.services.tedicross = {
+      description = "TediCross Telegram-Discord bridge service";
+      wantedBy = [ "multi-user.target" ];
+      wants = [ "network-online.target" ];
+      after = [ "network-online.target" ];
+      serviceConfig = {
+        Type = "simple";
+        ExecStart = "${pkgs.nodePackages.tedicross}/bin/tedicross --config='${configYAML}' --data-dir='${dataDir}'";
+        Restart = "always";
+        DynamicUser = true;
+        StateDirectory = baseNameOf dataDir;
+        EnvironmentFile = cfg.environmentFile;
+      };
+    };
+  };
+
+  meta.maintainers = with maintainers; [ pacien ];
+}
+
diff --git a/nixpkgs/nixos/modules/services/networking/tftpd.nix b/nixpkgs/nixos/modules/services/networking/tftpd.nix
new file mode 100644
index 000000000000..c9c0a2b321d5
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/tftpd.nix
@@ -0,0 +1,46 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.tftpd.enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether to enable tftpd, a Trivial File Transfer Protocol server.
+        The server will be run as an xinetd service.
+      '';
+    };
+
+    services.tftpd.path = mkOption {
+      type = types.path;
+      default = "/srv/tftp";
+      description = ''
+        Where the tftp server files are stored.
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.tftpd.enable {
+
+    services.xinetd.enable = true;
+
+    services.xinetd.services = singleton
+      { name = "tftp";
+        protocol = "udp";
+        server = "${pkgs.netkittftp}/sbin/in.tftpd";
+        serverArgs = "${config.services.tftpd.path}";
+      };
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/thelounge.nix b/nixpkgs/nixos/modules/services/networking/thelounge.nix
new file mode 100644
index 000000000000..a1b06703484b
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/thelounge.nix
@@ -0,0 +1,75 @@
+{ pkgs, lib, config, ... }:
+
+with lib;
+
+let
+  cfg = config.services.thelounge;
+  dataDir = "/var/lib/thelounge";
+  configJsData = "module.exports = " + builtins.toJSON (
+    { private = cfg.private; port = cfg.port; } // cfg.extraConfig
+  );
+in {
+  options.services.thelounge = {
+    enable = mkEnableOption "The Lounge web IRC client";
+
+    private = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Make your The Lounge instance private. You will need to configure user
+        accounts by using the (<command>thelounge</command>) command or by adding
+        entries in <filename>${dataDir}/users</filename>. You might need to restart
+        The Lounge after making changes to the state directory.
+      '';
+    };
+
+    port = mkOption {
+      type = types.port;
+      default = 9000;
+      description = "TCP port to listen on for http connections.";
+    };
+
+    extraConfig = mkOption {
+      default = {};
+      type = types.attrs;
+      example = literalExample ''{
+        reverseProxy = true;
+        defaults = {
+          name = "Your Network";
+          host = "localhost";
+          port = 6697;
+        };
+      }'';
+      description = ''
+        The Lounge's <filename>config.js</filename> contents as attribute set (will be
+        converted to JSON to generate the configuration file).
+
+        The options defined here will be merged to the default configuration file.
+        Note: In case of duplicate configuration, options from <option>extraConfig</option> have priority.
+
+        Documentation: <link xlink:href="https://thelounge.chat/docs/server/configuration" />
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.users.thelounge = {
+      description = "thelounge service user";
+      group = "thelounge";
+      isSystemUser = true;
+    };
+    users.groups.thelounge = {};
+    systemd.services.thelounge = {
+      description = "The Lounge web IRC client";
+      wantedBy = [ "multi-user.target" ];
+      preStart = "ln -sf ${pkgs.writeText "config.js" configJsData} ${dataDir}/config.js";
+      serviceConfig = {
+        User = "thelounge";
+        StateDirectory = baseNameOf dataDir;
+        ExecStart = "${pkgs.thelounge}/bin/thelounge start";
+      };
+    };
+
+    environment.systemPackages = [ pkgs.thelounge ];
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/tinc.nix b/nixpkgs/nixos/modules/services/networking/tinc.nix
new file mode 100644
index 000000000000..e98aafc20937
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/tinc.nix
@@ -0,0 +1,212 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.tinc;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.tinc = {
+
+      networks = mkOption {
+        default = { };
+        type = with types; attrsOf (submodule {
+          options = {
+
+            extraConfig = mkOption {
+              default = "";
+              type = types.lines;
+              description = ''
+                Extra lines to add to the tinc service configuration file.
+              '';
+            };
+
+            name = mkOption {
+              default = null;
+              type = types.nullOr types.str;
+              description = ''
+                The name of the node which is used as an identifier when communicating
+                with the remote nodes in the mesh. If null then the hostname of the system
+                is used to derive a name (note that tinc may replace non-alphanumeric characters in
+                hostnames by underscores).
+              '';
+            };
+
+            ed25519PrivateKeyFile = mkOption {
+              default = null;
+              type = types.nullOr types.path;
+              description = ''
+                Path of the private ed25519 keyfile.
+              '';
+            };
+
+            debugLevel = mkOption {
+              default = 0;
+              type = types.addCheck types.int (l: l >= 0 && l <= 5);
+              description = ''
+                The amount of debugging information to add to the log. 0 means little
+                logging while 5 is the most logging. <command>man tincd</command> for
+                more details.
+              '';
+            };
+
+            hosts = mkOption {
+              default = { };
+              type = types.attrsOf types.lines;
+              description = ''
+                The name of the host in the network as well as the configuration for that host.
+                This name should only contain alphanumerics and underscores.
+              '';
+            };
+
+            interfaceType = mkOption {
+              default = "tun";
+              type = types.enum [ "tun" "tap" ];
+              description = ''
+                The type of virtual interface used for the network connection
+              '';
+            };
+
+            listenAddress = mkOption {
+              default = null;
+              type = types.nullOr types.str;
+              description = ''
+                The ip address to listen on for incoming connections.
+              '';
+            };
+
+            bindToAddress = mkOption {
+              default = null;
+              type = types.nullOr types.str;
+              description = ''
+                The ip address to bind to (both listen on and send packets from).
+              '';
+            };
+
+            package = mkOption {
+              type = types.package;
+              default = pkgs.tinc_pre;
+              defaultText = "pkgs.tinc_pre";
+              description = ''
+                The package to use for the tinc daemon's binary.
+              '';
+            };
+
+            chroot = mkOption {
+              default = true;
+              type = types.bool;
+              description = ''
+                Change process root directory to the directory where the config file is located (/etc/tinc/netname/), for added security.
+                The chroot is performed after all the initialization is done, after writing pid files and opening network sockets.
+
+                Note that tinc can't run scripts anymore (such as tinc-down or host-up), unless it is setup to be runnable inside chroot environment.
+              '';
+            };
+          };
+        });
+
+        description = ''
+          Defines the tinc networks which will be started.
+          Each network invokes a different daemon.
+        '';
+      };
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf (cfg.networks != { }) {
+
+    environment.etc = fold (a: b: a // b) { }
+      (flip mapAttrsToList cfg.networks (network: data:
+        flip mapAttrs' data.hosts (host: text: nameValuePair
+          ("tinc/${network}/hosts/${host}")
+          ({ mode = "0644"; user = "tinc.${network}"; inherit text; })
+        ) // {
+          "tinc/${network}/tinc.conf" = {
+            mode = "0444";
+            text = ''
+              Name = ${if data.name == null then "$HOST" else data.name}
+              DeviceType = ${data.interfaceType}
+              ${optionalString (data.ed25519PrivateKeyFile != null) "Ed25519PrivateKeyFile = ${data.ed25519PrivateKeyFile}"}
+              ${optionalString (data.listenAddress != null) "ListenAddress = ${data.listenAddress}"}
+              ${optionalString (data.bindToAddress != null) "BindToAddress = ${data.bindToAddress}"}
+              Interface = tinc.${network}
+              ${data.extraConfig}
+            '';
+          };
+        }
+      ));
+
+    systemd.services = flip mapAttrs' cfg.networks (network: data: nameValuePair
+      ("tinc.${network}")
+      ({
+        description = "Tinc Daemon - ${network}";
+        wantedBy = [ "multi-user.target" ];
+        path = [ data.package ];
+        restartTriggers = [ config.environment.etc."tinc/${network}/tinc.conf".source ];
+        serviceConfig = {
+          Type = "simple";
+          Restart = "always";
+          RestartSec = "3";
+          ExecStart = "${data.package}/bin/tincd -D -U tinc.${network} -n ${network} ${optionalString (data.chroot) "-R"} --pidfile /run/tinc.${network}.pid -d ${toString data.debugLevel}";
+        };
+        preStart = ''
+          mkdir -p /etc/tinc/${network}/hosts
+          chown tinc.${network} /etc/tinc/${network}/hosts
+          mkdir -p /etc/tinc/${network}/invitations
+          chown tinc.${network} /etc/tinc/${network}/invitations
+
+          # Determine how we should generate our keys
+          if type tinc >/dev/null 2>&1; then
+            # Tinc 1.1+ uses the tinc helper application for key generation
+          ${if data.ed25519PrivateKeyFile != null then "  # Keyfile managed by nix" else ''
+            # Prefer ED25519 keys (only in 1.1+)
+            [ -f "/etc/tinc/${network}/ed25519_key.priv" ] || tinc -n ${network} generate-ed25519-keys
+          ''}
+            # Otherwise use RSA keys
+            [ -f "/etc/tinc/${network}/rsa_key.priv" ] || tinc -n ${network} generate-rsa-keys 4096
+          else
+            # Tinc 1.0 uses the tincd application
+            [ -f "/etc/tinc/${network}/rsa_key.priv" ] || tincd -n ${network} -K 4096
+          fi
+        '';
+      })
+    );
+
+    environment.systemPackages = let
+      cli-wrappers = pkgs.stdenv.mkDerivation {
+        name = "tinc-cli-wrappers";
+        buildInputs = [ pkgs.makeWrapper ];
+        buildCommand = ''
+          mkdir -p $out/bin
+          ${concatStringsSep "\n" (mapAttrsToList (network: data:
+            optionalString (versionAtLeast data.package.version "1.1pre") ''
+              makeWrapper ${data.package}/bin/tinc "$out/bin/tinc.${network}" \
+                --add-flags "--pidfile=/run/tinc.${network}.pid" \
+                --add-flags "--config=/etc/tinc/${network}"
+            '') cfg.networks)}
+        '';
+      };
+    in [ cli-wrappers ];
+
+    users.users = flip mapAttrs' cfg.networks (network: _:
+      nameValuePair ("tinc.${network}") ({
+        description = "Tinc daemon user for ${network}";
+        isSystemUser = true;
+      })
+    );
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/tinydns.nix b/nixpkgs/nixos/modules/services/networking/tinydns.nix
new file mode 100644
index 000000000000..79507b2ebcdd
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/tinydns.nix
@@ -0,0 +1,55 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  ###### interface
+
+  options = {
+    services.tinydns = {
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = "Whether to run the tinydns dns server";
+      };
+
+      data = mkOption {
+        type = types.lines;
+        default = "";
+        description = "The DNS data to serve, in the format described by tinydns-data(8)";
+      };
+
+      ip = mkOption {
+        default = "0.0.0.0";
+        type = types.str;
+        description = "IP address on which to listen for connections";
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf config.services.tinydns.enable {
+    environment.systemPackages = [ pkgs.djbdns ];
+
+    users.users.tinydns.isSystemUser = true;
+
+    systemd.services.tinydns = {
+      description = "djbdns tinydns server";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+      path = with pkgs; [ daemontools djbdns ];
+      preStart = ''
+        rm -rf /var/lib/tinydns
+        tinydns-conf tinydns tinydns /var/lib/tinydns ${config.services.tinydns.ip}
+        cd /var/lib/tinydns/root/
+        ln -sf ${pkgs.writeText "tinydns-data" config.services.tinydns.data} data
+        tinydns-data
+      '';
+      script = ''
+        cd /var/lib/tinydns
+        exec ./run
+      '';
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/tox-bootstrapd.nix b/nixpkgs/nixos/modules/services/networking/tox-bootstrapd.nix
new file mode 100644
index 000000000000..f88e34827d00
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/tox-bootstrapd.nix
@@ -0,0 +1,79 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  home = "/var/lib/tox-bootstrapd";
+  PIDFile = "${home}/pid";
+
+  pkg = pkgs.libtoxcore;
+  cfg = config.services.toxBootstrapd;
+  cfgFile = builtins.toFile "tox-bootstrapd.conf"
+    ''
+      port = ${toString cfg.port}
+      keys_file_path = "${home}/keys"
+      pid_file_path = "${PIDFile}"
+      ${cfg.extraConfig}
+    '';
+in
+{
+  options =
+    { services.toxBootstrapd =
+        { enable = mkOption {
+            type = types.bool;
+            default = false;
+            description =
+              ''
+                Whether to enable the Tox DHT bootstrap daemon.
+              '';
+          };
+
+          port = mkOption {
+            type = types.int;
+            default = 33445;
+            description = "Listening port (UDP).";
+          };
+
+          keysFile = mkOption {
+            type = types.str;
+            default = "${home}/keys";
+            description = "Node key file.";
+          };
+
+          extraConfig = mkOption {
+            type = types.lines;
+            default = "";
+            description =
+              ''
+                Configuration for bootstrap daemon.
+                See <link xlink:href="https://github.com/irungentoo/toxcore/blob/master/other/bootstrap_daemon/tox-bootstrapd.conf"/>
+                and <link xlink:href="http://wiki.tox.im/Nodes"/>.
+             '';
+          };
+      };
+
+    };
+
+  config = mkIf config.services.toxBootstrapd.enable {
+
+    users.users.tox-bootstrapd =
+      { uid = config.ids.uids.tox-bootstrapd;
+        description = "Tox bootstrap daemon user";
+        inherit home;
+        createHome = true;
+      };
+
+    systemd.services.tox-bootstrapd = {
+      description = "Tox DHT bootstrap daemon";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig =
+        { ExecStart = "${pkg}/bin/tox-bootstrapd --config=${cfgFile}";
+          Type = "forking";
+          inherit PIDFile;
+          User = "tox-bootstrapd";
+        };
+    };
+
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/tox-node.nix b/nixpkgs/nixos/modules/services/networking/tox-node.nix
new file mode 100644
index 000000000000..c24e7fd12850
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/tox-node.nix
@@ -0,0 +1,95 @@
+{ lib, pkgs, config, ... }:
+
+with lib;
+
+let
+  pkg = pkgs.tox-node;
+  cfg = config.services.tox-node;
+  homeDir = "/var/lib/tox-node";
+
+  configFile = let
+    # fetchurl should be switched to getting this file from tox-node.src once
+    # the dpkg directory is in a release
+    src = pkgs.fetchurl {
+      url = "https://raw.githubusercontent.com/tox-rs/tox-node/master/dpkg/config.yml";
+      sha256 = "1431wzpzm786mcvyzk1rp7ar418n45dr75hdggxvlm7pkpam31xa";
+    };
+    confJSON = pkgs.writeText "config.json" (
+      builtins.toJSON {
+        log-type = cfg.logType;
+        keys-file = cfg.keysFile;
+        udp-address = cfg.udpAddress;
+        tcp-addresses = cfg.tcpAddresses;
+        tcp-connections-limit = cfg.tcpConnectionLimit;
+        lan-discovery = cfg.lanDiscovery;
+        threads = cfg.threads;
+        motd = cfg.motd;
+      }
+    );
+  in with pkgs; runCommand "config.yml" {} ''
+    ${remarshal}/bin/remarshal -if yaml -of json ${src} -o src.json
+    ${jq}/bin/jq -s '(.[0] | with_entries( select(.key == "bootstrap-nodes"))) * .[1]' src.json ${confJSON} > $out
+  '';
+
+in {
+  options.services.tox-node = {
+    enable = mkEnableOption "Tox Node service";
+
+    logType = mkOption {
+      type = types.enum [ "Stderr" "Stdout" "Syslog" "None" ];
+      default = "Stderr";
+      description = "Logging implementation.";
+    };
+    keysFile = mkOption {
+      type = types.str;
+      default = "${homeDir}/keys";
+      description = "Path to the file where DHT keys are stored.";
+    };
+    udpAddress = mkOption {
+      type = types.str;
+      default = "0.0.0.0:33445";
+      description = "UDP address to run DHT node.";
+    };
+    tcpAddresses = mkOption {
+      type = types.listOf types.str;
+      default = [ "0.0.0.0:33445" ];
+      description = "TCP addresses to run TCP relay.";
+    };
+    tcpConnectionLimit = mkOption {
+      type = types.int;
+      default = 8192;
+      description = "Maximum number of active TCP connections relay can hold";
+    };
+    lanDiscovery = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Enable local network discovery.";
+    };
+    threads = mkOption {
+      type = types.int;
+      default = 1;
+      description = "Number of threads for execution";
+    };
+    motd = mkOption {
+      type = types.str;
+      default = "Hi from tox-rs! I'm up {{uptime}}. TCP: incoming {{tcp_packets_in}}, outgoing {{tcp_packets_out}}, UDP: incoming {{udp_packets_in}}, outgoing {{udp_packets_out}}";
+      description = "Message of the day";
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    systemd.services.tox-node = {
+      description = "Tox Node";
+
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        ExecStart = "${pkg}/bin/tox-node config ${configFile}";
+        StateDirectory = "tox-node";
+        DynamicUser = true;
+        Restart = "always";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/toxvpn.nix b/nixpkgs/nixos/modules/services/networking/toxvpn.nix
new file mode 100644
index 000000000000..9e97faeebc1e
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/toxvpn.nix
@@ -0,0 +1,68 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+{
+  options = {
+    services.toxvpn = {
+      enable = mkEnableOption "toxvpn running on startup";
+
+      localip = mkOption {
+        type        = types.str;
+        default     = "10.123.123.1";
+        description = "your ip on the vpn";
+      };
+
+      port = mkOption {
+        type        = types.int;
+        default     = 33445;
+        description = "udp port for toxcore, port-forward to help with connectivity if you run many nodes behind one NAT";
+      };
+
+      auto_add_peers = mkOption {
+        type        = types.listOf types.str;
+        default     = [];
+        example     = ''[ "toxid1" "toxid2" ]'';
+        description = "peers to automatically connect to on startup";
+      };
+    };
+  };
+
+  config = mkIf config.services.toxvpn.enable {
+    systemd.services.toxvpn = {
+      description = "toxvpn daemon";
+
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+
+      preStart = ''
+        mkdir -p /run/toxvpn || true
+        chown toxvpn /run/toxvpn
+      '';
+
+      path = [ pkgs.toxvpn ];
+
+      script = ''
+        exec toxvpn -i ${config.services.toxvpn.localip} -l /run/toxvpn/control -u toxvpn -p ${toString config.services.toxvpn.port} ${lib.concatMapStringsSep " " (x: "-a ${x}") config.services.toxvpn.auto_add_peers}
+      '';
+
+      serviceConfig = {
+        KillMode  = "process";
+        Restart   = "on-success";
+        Type      = "notify";
+      };
+
+      restartIfChanged = false; # Likely to be used for remote admin
+    };
+
+    environment.systemPackages = [ pkgs.toxvpn ];
+
+    users.users = {
+      toxvpn = {
+        uid        = config.ids.uids.toxvpn;
+        home       = "/var/lib/toxvpn";
+        createHome = true;
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/trickster.nix b/nixpkgs/nixos/modules/services/networking/trickster.nix
new file mode 100644
index 000000000000..8760dd5a9382
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/trickster.nix
@@ -0,0 +1,112 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.trickster;
+in
+{
+
+  options = {
+    services.trickster = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable Trickster.
+        '';
+      };
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.trickster;
+        defaultText = "pkgs.trickster";
+        description = ''
+          Package that should be used for trickster.
+        '';
+      };
+
+      configFile = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        description = ''
+          Path to configuration file.
+        '';
+      };
+
+      instance-id = mkOption {
+        type = types.nullOr types.int;
+        default = null;
+        description = ''
+          Instance ID for when running multiple processes (default null).
+        '';
+      };
+
+      log-level = mkOption {
+        type = types.str;
+        default = "info";
+        description = ''
+          Level of Logging to use (debug, info, warn, error) (default "info").
+        '';
+      };
+
+      metrics-port = mkOption {
+        type = types.port;
+        default = 8082;
+        description = ''
+          Port that the /metrics endpoint will listen on.
+        '';
+      };
+
+      origin = mkOption {
+        type = types.str;
+        default = "http://prometheus:9090";
+        description = ''
+          URL to the Prometheus Origin. Enter it like you would in grafana, e.g., http://prometheus:9090 (default http://prometheus:9090).
+        '';
+      };
+
+      profiler-port = mkOption {
+        type = types.nullOr types.port;
+        default = null;
+        description = ''
+          Port that the /debug/pprof endpoint will listen on.
+        '';
+      };
+
+      proxy-port = mkOption {
+        type = types.port;
+        default = 9090;
+        description = ''
+          Port that the Proxy server will listen on.
+        '';
+      };
+
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.trickster = {
+      description = "Dashboard Accelerator for Prometheus";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        DynamicUser = true;
+        ExecStart = ''
+          ${cfg.package}/bin/trickster \
+          -log-level ${cfg.log-level} \
+          -metrics-port ${toString cfg.metrics-port} \
+          -origin ${cfg.origin} \
+          -proxy-port ${toString cfg.proxy-port} \
+          ${optionalString (cfg.configFile != null) "-config ${cfg.configFile}"} \
+          ${optionalString (cfg.profiler-port != null) "-profiler-port ${cfg.profiler-port}"} \
+          ${optionalString (cfg.instance-id != null) "-instance-id ${cfg.instance-id}"}
+        '';
+        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+        Restart = "always";
+      };
+    };
+
+  };  
+}
+
diff --git a/nixpkgs/nixos/modules/services/networking/tvheadend.nix b/nixpkgs/nixos/modules/services/networking/tvheadend.nix
new file mode 100644
index 000000000000..ccf879996631
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/tvheadend.nix
@@ -0,0 +1,61 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let cfg     = config.services.tvheadend;
+    pidFile = "${config.users.users.tvheadend.home}/tvheadend.pid";
+in
+
+{
+  options = {
+    services.tvheadend = {
+      enable = mkEnableOption "Tvheadend";
+      httpPort = mkOption {
+        type        = types.int;
+        default     = 9981;
+        description = "Port to bind HTTP to.";
+      };
+
+      htspPort = mkOption {
+        type        = types.int;
+        default     = 9982;
+        description = "Port to bind HTSP to.";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.users.tvheadend = {
+      description = "Tvheadend Service user";
+      home        = "/var/lib/tvheadend";
+      createHome  = true;
+      uid         = config.ids.uids.tvheadend;
+    };
+
+    systemd.services.tvheadend = {
+      description = "Tvheadend TV streaming server";
+      wantedBy    = [ "multi-user.target" ];
+      after       = [ "network.target" ];
+
+      serviceConfig = {
+        Type         = "forking";
+        PIDFile      = pidFile;
+        Restart      = "always";
+        RestartSec   = 5;
+        User         = "tvheadend";
+        Group        = "video";
+        ExecStart    = ''
+                       ${pkgs.tvheadend}/bin/tvheadend \
+                       --http_port ${toString cfg.httpPort} \
+                       --htsp_port ${toString cfg.htspPort} \
+                       -f \
+                       -C \
+                       -p ${pidFile} \
+                       -u tvheadend \
+                       -g video
+                       '';
+        ExecStop     = "${pkgs.coreutils}/bin/rm ${pidFile}";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/unbound.nix b/nixpkgs/nixos/modules/services/networking/unbound.nix
new file mode 100644
index 000000000000..baed83591e1e
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/unbound.nix
@@ -0,0 +1,148 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.unbound;
+
+  stateDir = "/var/lib/unbound";
+
+  access = concatMapStringsSep "\n  " (x: "access-control: ${x} allow") cfg.allowedAccess;
+
+  interfaces = concatMapStringsSep "\n  " (x: "interface: ${x}") cfg.interfaces;
+
+  isLocalAddress = x: substring 0 3 x == "::1" || substring 0 9 x == "127.0.0.1";
+
+  forward =
+    optionalString (any isLocalAddress cfg.forwardAddresses) ''
+      do-not-query-localhost: no
+    '' +
+    optionalString (cfg.forwardAddresses != []) ''
+      forward-zone:
+        name: .
+    '' +
+    concatMapStringsSep "\n" (x: "    forward-addr: ${x}") cfg.forwardAddresses;
+
+  rootTrustAnchorFile = "${stateDir}/root.key";
+
+  trustAnchor = optionalString cfg.enableRootTrustAnchor
+    "auto-trust-anchor-file: ${rootTrustAnchorFile}";
+
+  confFile = pkgs.writeText "unbound.conf" ''
+    server:
+      directory: "${stateDir}"
+      username: unbound
+      chroot: "${stateDir}"
+      pidfile: ""
+      ${interfaces}
+      ${access}
+      ${trustAnchor}
+    ${cfg.extraConfig}
+    ${forward}
+  '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+    services.unbound = {
+
+      enable = mkEnableOption "Unbound domain name server";
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.unbound;
+        defaultText = "pkgs.unbound";
+        description = "The unbound package to use";
+      };
+
+      allowedAccess = mkOption {
+        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" ] ++ optional config.networking.enableIPv6 "::1";
+        type = types.listOf types.str;
+        description = "What addresses the server should listen on.";
+      };
+
+      forwardAddresses = mkOption {
+        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 = "";
+        type = types.lines;
+        description = ''
+          Extra unbound config. See
+          <citerefentry><refentrytitle>unbound.conf</refentrytitle><manvolnum>8
+          </manvolnum></citerefentry>.
+        '';
+      };
+
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ cfg.package ];
+
+    users.users.unbound = {
+      description = "unbound daemon user";
+      isSystemUser = true;
+    };
+
+    networking.resolvconf.useLocalResolver = mkDefault true;
+
+    systemd.services.unbound = {
+      description = "Unbound recursive Domain Name Server";
+      after = [ "network.target" ];
+      before = [ "nss-lookup.target" ];
+      wants = [ "nss-lookup.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      preStart = ''
+        mkdir -m 0755 -p ${stateDir}/dev/
+        cp ${confFile} ${stateDir}/unbound.conf
+        ${optionalString cfg.enableRootTrustAnchor ''
+          ${cfg.package}/bin/unbound-anchor -a ${rootTrustAnchorFile} || echo "Root anchor updated!"
+          chown unbound ${stateDir} ${rootTrustAnchorFile}
+        ''}
+        touch ${stateDir}/dev/random
+        ${pkgs.utillinux}/bin/mount --bind -n /dev/urandom ${stateDir}/dev/random
+      '';
+
+      serviceConfig = {
+        ExecStart = "${cfg.package}/bin/unbound -d -c ${stateDir}/unbound.conf";
+        ExecStopPost="${pkgs.utillinux}/bin/umount ${stateDir}/dev/random";
+
+        ProtectSystem = true;
+        ProtectHome = true;
+        PrivateDevices = true;
+        Restart = "always";
+        RestartSec = "5s";
+      };
+    };
+
+    # If networkmanager is enabled, ask it to interface with unbound.
+    networking.networkmanager.dns = "unbound";
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/unifi.nix b/nixpkgs/nixos/modules/services/networking/unifi.nix
new file mode 100644
index 000000000000..4bdfa8143dce
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/unifi.nix
@@ -0,0 +1,180 @@
+{ config, lib, pkgs, utils, ... }:
+with lib;
+let
+  cfg = config.services.unifi;
+  stateDir = "/var/lib/unifi";
+  cmd = ''
+    @${cfg.jrePackage}/bin/java java \
+        ${optionalString (cfg.initialJavaHeapSize != null) "-Xms${(toString cfg.initialJavaHeapSize)}m"} \
+        ${optionalString (cfg.maximumJavaHeapSize != null) "-Xmx${(toString cfg.maximumJavaHeapSize)}m"} \
+        -jar ${stateDir}/lib/ace.jar
+  '';
+  mountPoints = [
+    {
+      what = "${cfg.unifiPackage}/dl";
+      where = "${stateDir}/dl";
+    }
+    {
+      what = "${cfg.unifiPackage}/lib";
+      where = "${stateDir}/lib";
+    }
+    {
+      what = "${cfg.mongodbPackage}/bin";
+      where = "${stateDir}/bin";
+    }
+    {
+      what = "${cfg.dataDir}";
+      where = "${stateDir}/data";
+    }
+  ];
+  systemdMountPoints = map (m: "${utils.escapeSystemdPath m.where}.mount") mountPoints;
+in
+{
+
+  options = {
+
+    services.unifi.enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether or not to enable the unifi controller service.
+      '';
+    };
+
+    services.unifi.jrePackage = mkOption {
+      type = types.package;
+      default = pkgs.jre8;
+      defaultText = "pkgs.jre8";
+      description = ''
+        The JRE package to use. Check the release notes to ensure it is supported.
+      '';
+    };
+
+    services.unifi.unifiPackage = mkOption {
+      type = types.package;
+      default = pkgs.unifiLTS;
+      defaultText = "pkgs.unifiLTS";
+      description = ''
+        The unifi package to use.
+      '';
+    };
+
+    services.unifi.mongodbPackage = mkOption {
+      type = types.package;
+      default = pkgs.mongodb;
+      defaultText = "pkgs.mongodb";
+      description = ''
+        The mongodb package to use.
+      '';
+    };
+
+    services.unifi.dataDir = mkOption {
+      type = types.str;
+      default = "${stateDir}/data";
+      description = ''
+        Where to store the database and other data.
+
+        This directory will be bind-mounted to ${stateDir}/data as part of the service startup.
+      '';
+    };
+
+    services.unifi.openPorts = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Whether or not to open the minimum required ports on the firewall.
+
+        This is necessary to allow firmware upgrades and device discovery to
+        work. For remote login, you should additionally open (or forward) port
+        8443.
+      '';
+    };
+
+    services.unifi.initialJavaHeapSize = mkOption {
+      type = types.nullOr types.int;
+      default = null;
+      example = 1024;
+      description = ''
+        Set the initial heap size for the JVM in MB. If this option isn't set, the
+        JVM will decide this value at runtime.
+      '';
+    };
+
+    services.unifi.maximumJavaHeapSize = mkOption {
+      type = types.nullOr types.int;
+      default = null;
+      example = 4096;
+      description = ''
+        Set the maximimum heap size for the JVM in MB. If this option isn't set, the
+        JVM will decide this value at runtime.
+      '';
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    users.users.unifi = {
+      uid = config.ids.uids.unifi;
+      description = "UniFi controller daemon user";
+      home = "${stateDir}";
+    };
+
+    networking.firewall = mkIf cfg.openPorts {
+      # https://help.ubnt.com/hc/en-us/articles/218506997
+      allowedTCPPorts = [
+        8080  # Port for UAP to inform controller.
+        8880  # Port for HTTP portal redirect, if guest portal is enabled.
+        8843  # Port for HTTPS portal redirect, ditto.
+        6789  # Port for UniFi mobile speed test.
+      ];
+      allowedUDPPorts = [
+        3478  # UDP port used for STUN.
+        10001 # UDP port used for device discovery.
+      ];
+    };
+
+    # We must create the binary directories as bind mounts instead of symlinks
+    # This is because the controller resolves all symlinks to absolute paths
+    # to be used as the working directory.
+    systemd.mounts = map ({ what, where }: {
+        bindsTo = [ "unifi.service" ];
+        partOf = [ "unifi.service" ];
+        unitConfig.RequiresMountsFor = stateDir;
+        options = "bind";
+        what = what;
+        where = where;
+      }) mountPoints;
+
+    systemd.tmpfiles.rules = [
+      "d '${stateDir}' 0700 unifi - - -"
+      "d '${stateDir}/data' 0700 unifi - - -"
+      "d '${stateDir}/webapps' 0700 unifi - - -"
+      "L+ '${stateDir}/webapps/ROOT' - - - - ${cfg.unifiPackage}/webapps/ROOT"
+    ];
+
+    systemd.services.unifi = {
+      description = "UniFi controller daemon";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ] ++ systemdMountPoints;
+      partOf = systemdMountPoints;
+      bindsTo = systemdMountPoints;
+      unitConfig.RequiresMountsFor = stateDir;
+      # This a HACK to fix missing dependencies of dynamic libs extracted from jars
+      environment.LD_LIBRARY_PATH = with pkgs.stdenv; "${cc.cc.lib}/lib";
+
+      serviceConfig = {
+        Type = "simple";
+        ExecStart = "${(removeSuffix "\n" cmd)} start";
+        ExecStop = "${(removeSuffix "\n" cmd)} stop";
+        Restart = "on-failure";
+        User = "unifi";
+        UMask = "0077";
+        WorkingDirectory = "${stateDir}";
+      };
+    };
+
+  };
+
+  meta.maintainers = with lib.maintainers; [ erictapen ];
+}
diff --git a/nixpkgs/nixos/modules/services/networking/v2ray.nix b/nixpkgs/nixos/modules/services/networking/v2ray.nix
new file mode 100644
index 000000000000..6a924a16449a
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/v2ray.nix
@@ -0,0 +1,87 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  options = {
+
+    services.v2ray = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to run v2ray server.
+
+          Either <literal>configFile</literal> or <literal>config</literal> must be specified.
+        '';
+      };
+
+      configFile = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "/etc/v2ray/config.json";
+        description = ''
+          The absolute path to the configuration file.
+
+          Either <literal>configFile</literal> or <literal>config</literal> must be specified.
+
+          See <link xlink:href="https://v2ray.com/en/configuration/overview.html"/>.
+        '';
+      };
+
+      config = mkOption {
+        type = types.nullOr (types.attrsOf types.unspecified);
+        default = null;
+        example = {
+          inbounds = [{
+            port = 1080;
+            listen = "127.0.0.1";
+            protocol = "http";
+          }];
+          outbounds = [{
+            protocol = "freedom";
+          }];
+        };
+        description = ''
+          The configuration object.
+
+          Either `configFile` or `config` must be specified.
+
+          See <link xlink:href="https://v2ray.com/en/configuration/overview.html"/>.
+        '';
+      };
+    };
+
+  };
+
+  config = let
+    cfg = config.services.v2ray;
+    configFile = if cfg.configFile != null
+      then cfg.configFile
+      else pkgs.writeTextFile {
+        name = "v2ray.json";
+        text = builtins.toJSON cfg.config;
+        checkPhase = ''
+          ${pkgs.v2ray}/bin/v2ray -test -config $out
+        '';
+      };
+
+  in mkIf cfg.enable {
+    assertions = [
+      {
+        assertion = (cfg.configFile == null) != (cfg.config == null);
+        message = "Either but not both `configFile` and `config` should be specified for v2ray.";
+      }
+    ];
+
+    systemd.services.v2ray = {
+      description = "v2ray Daemon";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      path = [ pkgs.v2ray ];
+      script = ''
+        exec v2ray -config ${configFile}
+      '';
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/vsftpd.nix b/nixpkgs/nixos/modules/services/networking/vsftpd.nix
new file mode 100644
index 000000000000..c57994533c17
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/vsftpd.nix
@@ -0,0 +1,326 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  /* minimal secure setup:
+
+   enable = true;
+   forceLocalLoginsSSL = true;
+   forceLocalDataSSL = true;
+   userlistDeny = false;
+   localUsers = true;
+   userlist = ["non-root-user" "other-non-root-user"];
+   rsaCertFile = "/var/vsftpd/vsftpd.pem";
+
+  */
+
+  cfg = config.services.vsftpd;
+
+  inherit (pkgs) vsftpd;
+
+  yesNoOption = nixosName: vsftpdName: default: description: {
+    cfgText = "${vsftpdName}=${if getAttr nixosName cfg then "YES" else "NO"}";
+
+    nixosOption = {
+      type = types.bool;
+      name = nixosName;
+      value = mkOption {
+        inherit description default;
+        type = types.bool;
+      };
+    };
+  };
+
+  optionDescription = [
+    (yesNoOption "allowWriteableChroot" "allow_writeable_chroot" false ''
+      Allow the use of writeable root inside chroot().
+    '')
+    (yesNoOption "virtualUseLocalPrivs" "virtual_use_local_privs" false ''
+      If enabled, virtual users will use the same privileges as local
+      users. By default, virtual users will use the same privileges as
+      anonymous users, which tends to be more restrictive (especially
+      in terms of write access).
+    '')
+    (yesNoOption "anonymousUser" "anonymous_enable" false ''
+      Whether to enable the anonymous FTP user.
+    '')
+    (yesNoOption "anonymousUserNoPassword" "no_anon_password" false ''
+      Whether to disable the password for the anonymous FTP user.
+    '')
+    (yesNoOption "localUsers" "local_enable" false ''
+      Whether to enable FTP for local users.
+    '')
+    (yesNoOption "writeEnable" "write_enable" false ''
+      Whether any write activity is permitted to users.
+    '')
+    (yesNoOption "anonymousUploadEnable" "anon_upload_enable" false ''
+      Whether any uploads are permitted to anonymous users.
+    '')
+    (yesNoOption "anonymousMkdirEnable" "anon_mkdir_write_enable" false ''
+      Whether any uploads are permitted to anonymous users.
+    '')
+    (yesNoOption "chrootlocalUser" "chroot_local_user" false ''
+      Whether local users are confined to their home directory.
+    '')
+    (yesNoOption "userlistEnable" "userlist_enable" false ''
+      Whether users are included.
+    '')
+    (yesNoOption "userlistDeny" "userlist_deny" false ''
+      Specifies whether <option>userlistFile</option> is a list of user
+      names to allow or deny access.
+      The default <literal>false</literal> means whitelist/allow.
+    '')
+    (yesNoOption "forceLocalLoginsSSL" "force_local_logins_ssl" false ''
+      Only applies if <option>sslEnable</option> is true. Non anonymous (local) users
+      must use a secure SSL connection to send a password.
+    '')
+    (yesNoOption "forceLocalDataSSL" "force_local_data_ssl" false ''
+      Only applies if <option>sslEnable</option> is true. Non anonymous (local) users
+      must use a secure SSL connection for sending/receiving data on data connection.
+    '')
+    (yesNoOption "portPromiscuous" "port_promiscuous" false ''
+      Set to YES if you want to disable the PORT security check that ensures that
+      outgoing data connections can only connect to the client. Only enable if you
+      know what you are doing!
+    '')
+    (yesNoOption "ssl_tlsv1" "ssl_tlsv1" true  ''
+      Only applies if <option>ssl_enable</option> is activated. If
+      enabled, this option will permit TLS v1 protocol connections.
+      TLS v1 connections are preferred.
+    '')
+    (yesNoOption "ssl_sslv2" "ssl_sslv2" false ''
+      Only applies if <option>ssl_enable</option> is activated. If
+      enabled, this option will permit SSL v2 protocol connections.
+      TLS v1 connections are preferred.
+    '')
+    (yesNoOption "ssl_sslv3" "ssl_sslv3" false ''
+      Only applies if <option>ssl_enable</option> is activated. If
+      enabled, this option will permit SSL v3 protocol connections.
+      TLS v1 connections are preferred.
+    '')
+  ];
+
+  configFile = pkgs.writeText "vsftpd.conf"
+    ''
+      ${concatMapStrings (x: "${x.cfgText}\n") optionDescription}
+      ${optionalString (cfg.rsaCertFile != null) ''
+        ssl_enable=YES
+        rsa_cert_file=${cfg.rsaCertFile}
+      ''}
+      ${optionalString (cfg.rsaKeyFile != null) ''
+        rsa_private_key_file=${cfg.rsaKeyFile}
+      ''}
+      ${optionalString (cfg.userlistFile != null) ''
+        userlist_file=${cfg.userlistFile}
+      ''}
+      background=YES
+      listen=NO
+      listen_ipv6=YES
+      nopriv_user=vsftpd
+      secure_chroot_dir=/var/empty
+      ${optionalString (cfg.localRoot != null) ''
+        local_root=${cfg.localRoot}
+      ''}
+      syslog_enable=YES
+      ${optionalString (pkgs.stdenv.hostPlatform.system == "x86_64-linux") ''
+        seccomp_sandbox=NO
+      ''}
+      anon_umask=${cfg.anonymousUmask}
+      ${optionalString cfg.anonymousUser ''
+        anon_root=${cfg.anonymousUserHome}
+      ''}
+      ${optionalString cfg.enableVirtualUsers ''
+        guest_enable=YES
+        guest_username=vsftpd
+      ''}
+      pam_service_name=vsftpd
+      ${cfg.extraConfig}
+    '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.vsftpd = {
+
+      enable = mkEnableOption "vsftpd";
+
+      userlist = mkOption {
+        default = [];
+        description = "See <option>userlistFile</option>.";
+      };
+
+      userlistFile = mkOption {
+        type = types.path;
+        default = pkgs.writeText "userlist" (concatMapStrings (x: "${x}\n") cfg.userlist);
+        defaultText = "pkgs.writeText \"userlist\" (concatMapStrings (x: \"\${x}\n\") cfg.userlist)";
+        description = ''
+          Newline separated list of names to be allowed/denied if <option>userlistEnable</option>
+          is <literal>true</literal>. Meaning see <option>userlistDeny</option>.
+
+          The default is a file containing the users from <option>userlist</option>.
+
+          If explicitely set to null userlist_file will not be set in vsftpd's config file.
+        '';
+      };
+
+      enableVirtualUsers = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable the <literal>pam_userdb</literal>-based
+          virtual user system
+        '';
+      };
+
+      userDbPath = mkOption {
+        type = types.nullOr types.str;
+        example = "/etc/vsftpd/userDb";
+        default = null;
+        description = ''
+          Only applies if <option>enableVirtualUsers</option> is true.
+          Path pointing to the <literal>pam_userdb</literal> user
+          database used by vsftpd to authenticate the virtual users.
+
+          This user list should be stored in the Berkeley DB database
+          format.
+
+          To generate a new user database, create a text file, add
+          your users using the following format:
+          <programlisting>
+          user1
+          password1
+          user2
+          password2
+          </programlisting>
+
+          You can then install <literal>pkgs.db</literal> to generate
+          the Berkeley DB using
+          <programlisting>
+          db_load -T -t hash -f logins.txt userDb.db
+          </programlisting>
+
+          Caution: <literal>pam_userdb</literal> will automatically
+          append a <literal>.db</literal> suffix to the filename you
+          provide though this option. This option shouldn't include
+          this filetype suffix.
+        '';
+      };
+
+      localRoot = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "/var/www/$USER";
+        description = ''
+          This option represents a directory which vsftpd will try to
+          change into after a local (i.e. non- anonymous) login.
+
+          Failure is silently ignored.
+        '';
+      };
+
+      anonymousUserHome = mkOption {
+        type = types.path;
+        default = "/home/ftp/";
+        description = ''
+          Directory to consider the HOME of the anonymous user.
+        '';
+      };
+
+      rsaCertFile = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        description = "RSA certificate file.";
+      };
+
+      rsaKeyFile = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        description = "RSA private key file.";
+      };
+
+      anonymousUmask = mkOption {
+        type = types.str;
+        default = "077";
+        example = "002";
+        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));
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    assertions = [
+      { assertion =
+              (cfg.forceLocalLoginsSSL -> cfg.rsaCertFile != null)
+          &&  (cfg.forceLocalDataSSL -> cfg.rsaCertFile != null);
+        message = "vsftpd: If forceLocalLoginsSSL or forceLocalDataSSL is true then a rsaCertFile must be provided!";
+      }
+      {
+        assertion = (cfg.enableVirtualUsers -> cfg.userDbPath != null)
+                 && (cfg.enableVirtualUsers -> cfg.localUsers != null);
+        message = "vsftpd: If enableVirtualUsers is true, you need to setup both the userDbPath and localUsers options.";
+      }];
+
+    users.users = {
+      "vsftpd" = {
+        uid = config.ids.uids.vsftpd;
+        description = "VSFTPD user";
+        home = if cfg.localRoot != null
+               then cfg.localRoot # <= Necessary for virtual users.
+               else "/homeless-shelter";
+      };
+    } // optionalAttrs cfg.anonymousUser {
+      "ftp" = { name = "ftp";
+          uid = config.ids.uids.ftp;
+          group = "ftp";
+          description = "Anonymous FTP user";
+          home = cfg.anonymousUserHome;
+        };
+    };
+
+    users.groups.ftp.gid = config.ids.gids.ftp;
+
+    # If you really have to access root via FTP use mkOverride or userlistDeny
+    # = false and whitelist root
+    services.vsftpd.userlist = if cfg.userlistDeny then ["root"] else [];
+
+    systemd = {
+      tmpfiles.rules = optional cfg.anonymousUser
+        #Type Path                       Mode User   Gr    Age Arg
+        "d    '${builtins.toString cfg.anonymousUserHome}' 0555 'ftp'  'ftp' -   -";
+      services.vsftpd = {
+        description = "Vsftpd Server";
+
+        wantedBy = [ "multi-user.target" ];
+
+        serviceConfig.ExecStart = "@${vsftpd}/sbin/vsftpd vsftpd ${configFile}";
+        serviceConfig.Restart = "always";
+        serviceConfig.Type = "forking";
+      };
+    };
+
+    security.pam.services.vsftpd.text = mkIf (cfg.enableVirtualUsers && cfg.userDbPath != null)''
+      auth required pam_userdb.so db=${cfg.userDbPath}
+      account required pam_userdb.so db=${cfg.userDbPath}
+    '';
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/wakeonlan.nix b/nixpkgs/nixos/modules/services/networking/wakeonlan.nix
new file mode 100644
index 000000000000..ebfba263cd8f
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/wakeonlan.nix
@@ -0,0 +1,56 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  interfaces = config.services.wakeonlan.interfaces;
+
+  ethtool = "${pkgs.ethtool}/sbin/ethtool";
+
+  passwordParameter = password : if (password == "") then "" else
+    "sopass ${password}";
+
+  methodParameter = {method, password} :
+    if method == "magicpacket" then "wol g"
+    else if method == "password" then "wol s so ${passwordParameter password}"
+    else throw "Wake-On-Lan method not supported";
+
+  line = { interface, method ? "magicpacket", password ? "" }: ''
+    ${ethtool} -s ${interface} ${methodParameter {inherit method password;}}
+  '';
+
+  concatStrings = fold (x: y: x + y) "";
+  lines = concatStrings (map (l: line l) interfaces);
+
+in
+{
+
+  ###### interface
+
+  options = {
+
+    services.wakeonlan.interfaces = mkOption {
+      default = [ ];
+      example = [
+        {
+          interface = "eth0";
+          method = "password";
+          password = "00:11:22:33:44:55";
+        }
+      ];
+      description = ''
+        Interfaces where to enable Wake-On-LAN, and how. Two methods available:
+        "magicpacket" and "password". The password has the shape of six bytes
+        in hexadecimal separated by a colon each. For more information,
+        check the ethtool manual.
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config.powerManagement.powerDownCommands = lines;
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/websockify.nix b/nixpkgs/nixos/modules/services/networking/websockify.nix
new file mode 100644
index 000000000000..d9177df65bd6
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/websockify.nix
@@ -0,0 +1,54 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let cfg = config.services.networking.websockify; in {
+  options = {
+    services.networking.websockify = {
+      enable = mkOption {  
+        description = "Whether to enable websockify to forward websocket connections to TCP connections.";
+
+        default = false;   
+
+        type = types.bool; 
+      };
+
+      sslCert = mkOption {
+        description = "Path to the SSL certificate.";
+        type = types.path;
+      };
+
+      sslKey = mkOption {
+        description = "Path to the SSL key.";
+        default = cfg.sslCert;
+        defaultText = "config.services.networking.websockify.sslCert";
+        type = types.path;
+      };
+
+      portMap = mkOption {
+        description = "Ports to map by default.";
+        default = {};
+        type = types.attrsOf types.int;
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services."websockify@" = {
+      description = "Service to forward websocket connections to TCP connections (from port:to port %I)";
+      script = ''
+        IFS=':' read -a array <<< "$1"
+        ${pkgs.pythonPackages.websockify}/bin/websockify --ssl-only \
+          --cert=${cfg.sslCert} --key=${cfg.sslKey} 0.0.0.0:''${array[0]} 0.0.0.0:''${array[1]}
+      '';
+      scriptArgs = "%i";
+    };
+
+    systemd.targets.default-websockify = {
+      description = "Target to start all default websockify@ services";
+      unitConfig.X-StopOnReconfiguration = true;
+      wants = mapAttrsToList (name: value: "websockify@${name}:${toString value}.service") cfg.portMap;
+      wantedBy = [ "multi-user.target" ];
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/wg-quick.nix b/nixpkgs/nixos/modules/services/networking/wg-quick.nix
new file mode 100644
index 000000000000..ff1bdeed9f48
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/wg-quick.nix
@@ -0,0 +1,312 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.networking.wg-quick;
+
+  kernel = config.boot.kernelPackages;
+
+  # interface options
+
+  interfaceOpts = { ... }: {
+    options = {
+      address = mkOption {
+        example = [ "192.168.2.1/24" ];
+        default = [];
+        type = with types; listOf str;
+        description = "The IP addresses of the interface.";
+      };
+
+      dns = mkOption {
+        example = [ "192.168.2.2" ];
+        default = [];
+        type = with types; listOf str;
+        description = "The IP addresses of DNS servers to configure.";
+      };
+
+      privateKey = mkOption {
+        example = "yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk=";
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          Base64 private key generated by wg genkey.
+
+          Warning: Consider using privateKeyFile instead if you do not
+          want to store the key in the world-readable Nix store.
+        '';
+      };
+
+      privateKeyFile = mkOption {
+        example = "/private/wireguard_key";
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          Private key file as generated by wg genkey.
+        '';
+      };
+
+      listenPort = mkOption {
+        default = null;
+        type = with types; nullOr int;
+        example = 51820;
+        description = ''
+          16-bit port for listening. Optional; if not specified,
+          automatically generated based on interface name.
+        '';
+      };
+
+      preUp = mkOption {
+        example = literalExample ''
+          ${pkgs.iproute}/bin/ip netns add foo
+        '';
+        default = "";
+        type = with types; coercedTo (listOf str) (concatStringsSep "\n") lines;
+        description = ''
+          Commands called at the start of the interface setup.
+        '';
+      };
+
+      preDown = mkOption {
+        example = literalExample ''
+          ${pkgs.iproute}/bin/ip netns del foo
+        '';
+        default = "";
+        type = with types; coercedTo (listOf str) (concatStringsSep "\n") lines;
+        description = ''
+          Command called before the interface is taken down.
+        '';
+      };
+
+      postUp = mkOption {
+        example = literalExample ''
+          ${pkgs.iproute}/bin/ip netns add foo
+        '';
+        default = "";
+        type = with types; coercedTo (listOf str) (concatStringsSep "\n") lines;
+        description = ''
+          Commands called after the interface setup.
+        '';
+      };
+
+      postDown = mkOption {
+        example = literalExample ''
+          ${pkgs.iproute}/bin/ip netns del foo
+        '';
+        default = "";
+        type = with types; coercedTo (listOf str) (concatStringsSep "\n") lines;
+        description = ''
+          Command called after the interface is taken down.
+        '';
+      };
+
+      table = mkOption {
+        example = "main";
+        default = null;
+        type = with types; nullOr str;
+        description = ''
+          The kernel routing table to add this interface's
+          associated routes to. Setting this is useful for e.g. policy routing
+          ("ip rule") or virtual routing and forwarding ("ip vrf"). Both numeric
+          table IDs and table names (/etc/rt_tables) can be used. Defaults to
+          "main".
+        '';
+      };
+
+      mtu = mkOption {
+        example = 1248;
+        default = null;
+        type = with types; nullOr int;
+        description = ''
+          If not specified, the MTU is automatically determined
+          from the endpoint addresses or the system default route, which is usually
+          a sane choice. However, to manually specify an MTU to override this
+          automatic discovery, this value may be specified explicitly.
+        '';
+      };
+
+      peers = mkOption {
+        default = [];
+        description = "Peers linked to the interface.";
+        type = with types; listOf (submodule peerOpts);
+      };
+    };
+  };
+
+  # peer options
+
+  peerOpts = {
+    options = {
+      publicKey = mkOption {
+        example = "xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=";
+        type = types.str;
+        description = "The base64 public key the peer.";
+      };
+
+      presharedKey = mkOption {
+        default = null;
+        example = "rVXs/Ni9tu3oDBLS4hOyAUAa1qTWVA3loR8eL20os3I=";
+        type = with types; nullOr str;
+        description = ''
+          Base64 preshared key generated by wg genpsk. Optional,
+          and may be omitted. This option adds an additional layer of
+          symmetric-key cryptography to be mixed into the already existing
+          public-key cryptography, for post-quantum resistance.
+
+          Warning: Consider using presharedKeyFile instead if you do not
+          want to store the key in the world-readable Nix store.
+        '';
+      };
+
+      presharedKeyFile = mkOption {
+        default = null;
+        example = "/private/wireguard_psk";
+        type = with types; nullOr str;
+        description = ''
+          File pointing to preshared key as generated by wg pensk. Optional,
+          and may be omitted. This option adds an additional layer of
+          symmetric-key cryptography to be mixed into the already existing
+          public-key cryptography, for post-quantum resistance.
+        '';
+      };
+
+      allowedIPs = mkOption {
+        example = [ "10.192.122.3/32" "10.192.124.1/24" ];
+        type = with types; listOf str;
+        description = ''List of IP (v4 or v6) addresses with CIDR masks from
+        which this peer is allowed to send incoming traffic and to which
+        outgoing traffic for this peer is directed. The catch-all 0.0.0.0/0 may
+        be specified for matching all IPv4 addresses, and ::/0 may be specified
+        for matching all IPv6 addresses.'';
+      };
+
+      endpoint = mkOption {
+        default = null;
+        example = "demo.wireguard.io:12913";
+        type = with types; nullOr str;
+        description = ''Endpoint IP or hostname of the peer, followed by a colon,
+        and then a port number of the peer.'';
+      };
+
+      persistentKeepalive = mkOption {
+        default = null;
+        type = with types; nullOr int;
+        example = 25;
+        description = ''This is optional and is by default off, because most
+        users will not need it. It represents, in seconds, between 1 and 65535
+        inclusive, how often to send an authenticated empty packet to the peer,
+        for the purpose of keeping a stateful firewall or NAT mapping valid
+        persistently. For example, if the interface very rarely sends traffic,
+        but it might at anytime receive traffic from a peer, and it is behind
+        NAT, the interface might benefit from having a persistent keepalive
+        interval of 25 seconds; however, most users will not need this.'';
+      };
+    };
+  };
+
+  writeScriptFile = name: text: ((pkgs.writeShellScriptBin name text) + "/bin/${name}");
+
+  generateUnit = name: values:
+    assert assertMsg ((values.privateKey != null) != (values.privateKeyFile != null)) "Only one of privateKey or privateKeyFile may be set";
+    let
+      preUpFile = if values.preUp != "" then writeScriptFile "preUp.sh" values.preUp else null;
+      postUp =
+            optional (values.privateKeyFile != null) "wg set ${name} private-key <(cat ${values.privateKeyFile})" ++
+            (concatMap (peer: optional (peer.presharedKeyFile != null) "wg set ${name} peer ${peer.publicKey} preshared-key <(cat ${peer.presharedKeyFile})") values.peers) ++
+            optional (values.postUp != null) values.postUp;
+      postUpFile = if postUp != [] then writeScriptFile "postUp.sh" (concatMapStringsSep "\n" (line: line) postUp) else null;
+      preDownFile = if values.preDown != "" then writeScriptFile "preDown.sh" values.preDown else null;
+      postDownFile = if values.postDown != "" then writeScriptFile "postDown.sh" values.postDown else null;
+      configDir = pkgs.writeTextFile {
+        name = "config-${name}";
+        executable = false;
+        destination = "/${name}.conf";
+        text =
+        ''
+        [interface]
+        ${concatMapStringsSep "\n" (address:
+          "Address = ${address}"
+        ) values.address}
+        ${concatMapStringsSep "\n" (dns:
+          "DNS = ${dns}"
+        ) values.dns}
+        '' +
+        optionalString (values.table != null) "Table = ${values.table}\n" +
+        optionalString (values.mtu != null) "MTU = ${toString values.mtu}\n" +
+        optionalString (values.privateKey != null) "PrivateKey = ${values.privateKey}\n" +
+        optionalString (values.listenPort != null) "ListenPort = ${toString values.listenPort}\n" +
+        optionalString (preUpFile != null) "PreUp = ${preUpFile}\n" +
+        optionalString (postUpFile != null) "PostUp = ${postUpFile}\n" +
+        optionalString (preDownFile != null) "PreDown = ${preDownFile}\n" +
+        optionalString (postDownFile != null) "PostDown = ${postDownFile}\n" +
+        concatMapStringsSep "\n" (peer:
+          assert assertMsg (!((peer.presharedKeyFile != null) && (peer.presharedKey != null))) "Only one of presharedKey or presharedKeyFile may be set";
+          "[Peer]\n" +
+          "PublicKey = ${peer.publicKey}\n" +
+          optionalString (peer.presharedKey != null) "PresharedKey = ${peer.presharedKey}\n" +
+          optionalString (peer.endpoint != null) "Endpoint = ${peer.endpoint}\n" +
+          optionalString (peer.persistentKeepalive != null) "PersistentKeepalive = ${toString peer.persistentKeepalive}\n" +
+          optionalString (peer.allowedIPs != []) "AllowedIPs = ${concatStringsSep "," peer.allowedIPs}\n"
+        ) values.peers;
+      };
+      configPath = "${configDir}/${name}.conf";
+    in
+    nameValuePair "wg-quick-${name}"
+      {
+        description = "wg-quick WireGuard Tunnel - ${name}";
+        requires = [ "network-online.target" ];
+        after = [ "network.target" "network-online.target" ];
+        wantedBy = [ "multi-user.target" ];
+        environment.DEVICE = name;
+        path = [ pkgs.kmod pkgs.wireguard-tools ];
+
+        serviceConfig = {
+          Type = "oneshot";
+          RemainAfterExit = true;
+        };
+
+        script = ''
+          ${optionalString (!config.boot.isContainer) "modprobe wireguard"}
+          wg-quick up ${configPath}
+        '';
+
+        preStop = ''
+          wg-quick down ${configPath}
+        '';
+      };
+in {
+
+  ###### interface
+
+  options = {
+    networking.wg-quick = {
+      interfaces = mkOption {
+        description = "Wireguard interfaces.";
+        default = {};
+        example = {
+          wg0 = {
+            address = [ "192.168.20.4/24" ];
+            privateKey = "yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk=";
+            peers = [
+              { allowedIPs = [ "192.168.20.1/32" ];
+                publicKey  = "xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=";
+                endpoint   = "demo.wireguard.io:12913"; }
+            ];
+          };
+        };
+        type = with types; attrsOf (submodule interfaceOpts);
+      };
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf (cfg.interfaces != {}) {
+    boot.extraModulePackages = optional (versionOlder kernel.kernel.version "5.6") kernel.wireguard;
+    environment.systemPackages = [ pkgs.wireguard-tools ];
+    # This is forced to false for now because the default "--validmark" rpfilter we apply on reverse path filtering
+    # breaks the wg-quick routing because wireguard packets leave with a fwmark from wireguard.
+    networking.firewall.checkReversePath = false;
+    systemd.services = mapAttrs' generateUnit cfg.interfaces;
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/wicd.nix b/nixpkgs/nixos/modules/services/networking/wicd.nix
new file mode 100644
index 000000000000..aa10a50f876a
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/wicd.nix
@@ -0,0 +1,40 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    networking.wicd.enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether to start <command>wicd</command>. Wired and
+        wireless network configurations can then be managed by
+        wicd-client.
+      '';
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.networking.wicd.enable {
+
+    environment.systemPackages = [pkgs.wicd];
+
+    systemd.services.wicd = {
+      after = [ "network-pre.target" ];
+      before = [ "network.target" ];
+      wants = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      script = "${pkgs.wicd}/sbin/wicd -f";
+    };
+
+    services.dbus.enable = true;
+    services.dbus.packages = [pkgs.wicd];
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/wireguard.nix b/nixpkgs/nixos/modules/services/networking/wireguard.nix
new file mode 100644
index 000000000000..e8f83f6dd8bf
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/wireguard.nix
@@ -0,0 +1,452 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.networking.wireguard;
+
+  kernel = config.boot.kernelPackages;
+
+  # interface options
+
+  interfaceOpts = { ... }: {
+
+    options = {
+
+      ips = mkOption {
+        example = [ "192.168.2.1/24" ];
+        default = [];
+        type = with types; listOf str;
+        description = "The IP addresses of the interface.";
+      };
+
+      privateKey = mkOption {
+        example = "yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk=";
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          Base64 private key generated by <command>wg genkey</command>.
+
+          Warning: Consider using privateKeyFile instead if you do not
+          want to store the key in the world-readable Nix store.
+        '';
+      };
+
+      generatePrivateKeyFile = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Automatically generate a private key with
+          <command>wg genkey</command>, at the privateKeyFile location.
+        '';
+      };
+
+      privateKeyFile = mkOption {
+        example = "/private/wireguard_key";
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          Private key file as generated by <command>wg genkey</command>.
+        '';
+      };
+
+      listenPort = mkOption {
+        default = null;
+        type = with types; nullOr int;
+        example = 51820;
+        description = ''
+          16-bit port for listening. Optional; if not specified,
+          automatically generated based on interface name.
+        '';
+      };
+
+      preSetup = mkOption {
+        example = literalExample ''
+          ${pkgs.iproute}/bin/ip netns add foo
+        '';
+        default = "";
+        type = with types; coercedTo (listOf str) (concatStringsSep "\n") lines;
+        description = ''
+          Commands called at the start of the interface setup.
+        '';
+      };
+
+      postSetup = mkOption {
+        example = literalExample ''
+          printf "nameserver 10.200.100.1" | ${pkgs.openresolv}/bin/resolvconf -a wg0 -m 0
+        '';
+        default = "";
+        type = with types; coercedTo (listOf str) (concatStringsSep "\n") lines;
+        description = "Commands called at the end of the interface setup.";
+      };
+
+      postShutdown = mkOption {
+        example = literalExample "${pkgs.openresolv}/bin/resolvconf -d wg0";
+        default = "";
+        type = with types; coercedTo (listOf str) (concatStringsSep "\n") lines;
+        description = "Commands called after shutting down the interface.";
+      };
+
+      table = mkOption {
+        default = "main";
+        type = types.str;
+        description = ''The kernel routing table to add this interface's
+        associated routes to. Setting this is useful for e.g. policy routing
+        ("ip rule") or virtual routing and forwarding ("ip vrf"). Both numeric
+        table IDs and table names (/etc/rt_tables) can be used. Defaults to
+        "main".'';
+      };
+
+      peers = mkOption {
+        default = [];
+        description = "Peers linked to the interface.";
+        type = with types; listOf (submodule peerOpts);
+      };
+
+      allowedIPsAsRoutes = mkOption {
+        example = false;
+        default = true;
+        type = types.bool;
+        description = ''
+          Determines whether to add allowed IPs as routes or not.
+        '';
+      };
+
+      socketNamespace = mkOption {
+        default = null;
+        type = with types; nullOr str;
+        example = "container";
+        description = ''The pre-existing network namespace in which the
+        WireGuard interface is created, and which retains the socket even if the
+        interface is moved via <option>interfaceNamespace</option>. When
+        <literal>null</literal>, the interface is created in the init namespace.
+        See <link
+        xlink:href="https://www.wireguard.com/netns/">documentation</link>.
+        '';
+      };
+
+      interfaceNamespace = mkOption {
+        default = null;
+        type = with types; nullOr str;
+        example = "init";
+        description = ''The pre-existing network namespace the WireGuard
+        interface is moved to. The special value <literal>init</literal> means
+        the init namespace. When <literal>null</literal>, the interface is not
+        moved.
+        See <link
+        xlink:href="https://www.wireguard.com/netns/">documentation</link>.
+        '';
+      };
+    };
+
+  };
+
+  # peer options
+
+  peerOpts = {
+
+    options = {
+
+      publicKey = mkOption {
+        example = "xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=";
+        type = types.str;
+        description = "The base64 public key of the peer.";
+      };
+
+      presharedKey = mkOption {
+        default = null;
+        example = "rVXs/Ni9tu3oDBLS4hOyAUAa1qTWVA3loR8eL20os3I=";
+        type = with types; nullOr str;
+        description = ''
+          Base64 preshared key generated by <command>wg genpsk</command>.
+          Optional, and may be omitted. This option adds an additional layer of
+          symmetric-key cryptography to be mixed into the already existing
+          public-key cryptography, for post-quantum resistance.
+
+          Warning: Consider using presharedKeyFile instead if you do not
+          want to store the key in the world-readable Nix store.
+        '';
+      };
+
+      presharedKeyFile = mkOption {
+        default = null;
+        example = "/private/wireguard_psk";
+        type = with types; nullOr str;
+        description = ''
+          File pointing to preshared key as generated by <command>wg pensk</command>.
+          Optional, and may be omitted. This option adds an additional layer of
+          symmetric-key cryptography to be mixed into the already existing
+          public-key cryptography, for post-quantum resistance.
+        '';
+      };
+
+      allowedIPs = mkOption {
+        example = [ "10.192.122.3/32" "10.192.124.1/24" ];
+        type = with types; listOf str;
+        description = ''List of IP (v4 or v6) addresses with CIDR masks from
+        which this peer is allowed to send incoming traffic and to which
+        outgoing traffic for this peer is directed. The catch-all 0.0.0.0/0 may
+        be specified for matching all IPv4 addresses, and ::/0 may be specified
+        for matching all IPv6 addresses.'';
+      };
+
+      endpoint = mkOption {
+        default = null;
+        example = "demo.wireguard.io:12913";
+        type = with types; nullOr str;
+        description = ''Endpoint IP or hostname of the peer, followed by a colon,
+        and then a port number of the peer.'';
+      };
+
+      persistentKeepalive = mkOption {
+        default = null;
+        type = with types; nullOr int;
+        example = 25;
+        description = ''This is optional and is by default off, because most
+        users will not need it. It represents, in seconds, between 1 and 65535
+        inclusive, how often to send an authenticated empty packet to the peer,
+        for the purpose of keeping a stateful firewall or NAT mapping valid
+        persistently. For example, if the interface very rarely sends traffic,
+        but it might at anytime receive traffic from a peer, and it is behind
+        NAT, the interface might benefit from having a persistent keepalive
+        interval of 25 seconds; however, most users will not need this.'';
+      };
+
+    };
+
+  };
+
+
+  generatePathUnit = name: values:
+    assert (values.privateKey == null);
+    assert (values.privateKeyFile != null);
+    nameValuePair "wireguard-${name}"
+      {
+        description = "WireGuard Tunnel - ${name} - Private Key";
+        requiredBy = [ "wireguard-${name}.service" ];
+        before = [ "wireguard-${name}.service" ];
+        pathConfig.PathExists = values.privateKeyFile;
+      };
+
+  generateKeyServiceUnit = name: values:
+    assert values.generatePrivateKeyFile;
+    nameValuePair "wireguard-${name}-key"
+      {
+        description = "WireGuard Tunnel - ${name} - Key Generator";
+        wantedBy = [ "wireguard-${name}.service" ];
+        requiredBy = [ "wireguard-${name}.service" ];
+        before = [ "wireguard-${name}.service" ];
+        path = with pkgs; [ wireguard ];
+
+        serviceConfig = {
+          Type = "oneshot";
+          RemainAfterExit = true;
+        };
+
+        script = ''
+          mkdir --mode 0644 -p "${dirOf values.privateKeyFile}"
+          if [ ! -f "${values.privateKeyFile}" ]; then
+            touch "${values.privateKeyFile}"
+            chmod 0600 "${values.privateKeyFile}"
+            wg genkey > "${values.privateKeyFile}"
+            chmod 0400 "${values.privateKeyFile}"
+          fi
+        '';
+      };
+
+  generatePeerUnit = { interfaceName, interfaceCfg, peer }:
+    let
+      keyToUnitName = replaceChars
+        [ "/" "-"    " "     "+"     "="      ]
+        [ "-" "\\x2d" "\\x20" "\\x2b" "\\x3d" ];
+      unitName = keyToUnitName peer.publicKey;
+      psk =
+        if peer.presharedKey != null
+          then pkgs.writeText "wg-psk" peer.presharedKey
+          else peer.presharedKeyFile;
+      src = interfaceCfg.socketNamespace;
+      dst = interfaceCfg.interfaceNamespace;
+      ip = nsWrap "ip" src dst;
+      wg = nsWrap "wg" src dst;
+    in nameValuePair "wireguard-${interfaceName}-peer-${unitName}"
+      {
+        description = "WireGuard Peer - ${interfaceName} - ${peer.publicKey}";
+        requires = [ "wireguard-${interfaceName}.service" ];
+        after = [ "wireguard-${interfaceName}.service" ];
+        wantedBy = [ "multi-user.target" "wireguard-${interfaceName}.service" ];
+        environment.DEVICE = interfaceName;
+        environment.WG_ENDPOINT_RESOLUTION_RETRIES = "infinity";
+        path = with pkgs; [ iproute wireguard-tools ];
+
+        serviceConfig = {
+          Type = "oneshot";
+          RemainAfterExit = true;
+        };
+
+        script = let
+          wg_setup = "${wg} set ${interfaceName} peer ${peer.publicKey}" +
+            optionalString (psk != null) " preshared-key ${psk}" +
+            optionalString (peer.endpoint != null) " endpoint ${peer.endpoint}" +
+            optionalString (peer.persistentKeepalive != null) " persistent-keepalive ${toString peer.persistentKeepalive}" +
+            optionalString (peer.allowedIPs != []) " allowed-ips ${concatStringsSep "," peer.allowedIPs}";
+          route_setup =
+            optionalString interfaceCfg.allowedIPsAsRoutes
+              (concatMapStringsSep "\n"
+                (allowedIP:
+                  "${ip} route replace ${allowedIP} dev ${interfaceName} table ${interfaceCfg.table}"
+                ) peer.allowedIPs);
+        in ''
+          ${wg_setup}
+          ${route_setup}
+        '';
+
+        postStop = let
+          route_destroy = optionalString interfaceCfg.allowedIPsAsRoutes
+            (concatMapStringsSep "\n"
+              (allowedIP:
+                "${ip} route delete ${allowedIP} dev ${interfaceName} table ${interfaceCfg.table}"
+              ) peer.allowedIPs);
+        in ''
+          ${wg} set ${interfaceName} peer ${peer.publicKey} remove
+          ${route_destroy}
+        '';
+      };
+
+  generateInterfaceUnit = name: values:
+    # exactly one way to specify the private key must be set
+    #assert (values.privateKey != null) != (values.privateKeyFile != null);
+    let privKey = if values.privateKeyFile != null then values.privateKeyFile else pkgs.writeText "wg-key" values.privateKey;
+        src = values.socketNamespace;
+        dst = values.interfaceNamespace;
+        ipPreMove  = nsWrap "ip" src null;
+        ipPostMove = nsWrap "ip" src dst;
+        wg = nsWrap "wg" src dst;
+        ns = if dst == "init" then "1" else dst;
+
+    in
+    nameValuePair "wireguard-${name}"
+      {
+        description = "WireGuard Tunnel - ${name}";
+        requires = [ "network-online.target" ];
+        after = [ "network.target" "network-online.target" ];
+        wantedBy = [ "multi-user.target" ];
+        environment.DEVICE = name;
+        path = with pkgs; [ kmod iproute wireguard-tools ];
+
+        serviceConfig = {
+          Type = "oneshot";
+          RemainAfterExit = true;
+        };
+
+        script = ''
+          ${optionalString (!config.boot.isContainer) "modprobe wireguard || true"}
+
+          ${values.preSetup}
+
+          ${ipPreMove} link add dev ${name} type wireguard
+          ${optionalString (values.interfaceNamespace != null && values.interfaceNamespace != values.socketNamespace) "${ipPreMove} link set ${name} netns ${ns}"}
+
+          ${concatMapStringsSep "\n" (ip:
+            "${ipPostMove} address add ${ip} dev ${name}"
+          ) values.ips}
+
+          ${wg} set ${name} private-key ${privKey} ${
+            optionalString (values.listenPort != null) " listen-port ${toString values.listenPort}"}
+
+          ${ipPostMove} link set up dev ${name}
+
+          ${values.postSetup}
+        '';
+
+        postStop = ''
+          ${ipPostMove} link del dev ${name}
+          ${values.postShutdown}
+        '';
+      };
+
+  nsWrap = cmd: src: dst:
+    let
+      nsList = filter (ns: ns != null) [ src dst ];
+      ns = last nsList;
+    in
+      if (length nsList > 0 && ns != "init") then "ip netns exec ${ns} ${cmd}" else cmd;
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    networking.wireguard = {
+
+      enable = mkOption {
+        description = "Whether to enable WireGuard.";
+        type = types.bool;
+        # 2019-05-25: Backwards compatibility.
+        default = cfg.interfaces != {};
+        example = true;
+      };
+
+      interfaces = mkOption {
+        description = "WireGuard interfaces.";
+        default = {};
+        example = {
+          wg0 = {
+            ips = [ "192.168.20.4/24" ];
+            privateKey = "yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk=";
+            peers = [
+              { allowedIPs = [ "192.168.20.1/32" ];
+                publicKey  = "xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=";
+                endpoint   = "demo.wireguard.io:12913"; }
+            ];
+          };
+        };
+        type = with types; attrsOf (submodule interfaceOpts);
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable (let
+    all_peers = flatten
+      (mapAttrsToList (interfaceName: interfaceCfg:
+        map (peer: { inherit interfaceName interfaceCfg peer;}) interfaceCfg.peers
+      ) cfg.interfaces);
+  in {
+
+    assertions = (attrValues (
+        mapAttrs (name: value: {
+          assertion = (value.privateKey != null) != (value.privateKeyFile != null);
+          message = "Either networking.wireguard.interfaces.${name}.privateKey or networking.wireguard.interfaces.${name}.privateKeyFile must be set.";
+        }) cfg.interfaces))
+      ++ (attrValues (
+        mapAttrs (name: value: {
+          assertion = value.generatePrivateKeyFile -> (value.privateKey == null);
+          message = "networking.wireguard.interfaces.${name}.generatePrivateKeyFile must not be set if networking.wireguard.interfaces.${name}.privateKey is set.";
+        }) cfg.interfaces))
+        ++ map ({ interfaceName, peer, ... }: {
+          assertion = (peer.presharedKey == null) || (peer.presharedKeyFile == null);
+          message = "networking.wireguard.interfaces.${interfaceName} peer «${peer.publicKey}» has both presharedKey and presharedKeyFile set, but only one can be used.";
+        }) all_peers;
+
+    boot.extraModulePackages = optional (versionOlder kernel.kernel.version "5.6") kernel.wireguard;
+    environment.systemPackages = [ pkgs.wireguard-tools ];
+
+    systemd.services =
+      (mapAttrs' generateInterfaceUnit cfg.interfaces)
+      // (listToAttrs (map generatePeerUnit all_peers))
+      // (mapAttrs' generateKeyServiceUnit
+      (filterAttrs (name: value: value.generatePrivateKeyFile) cfg.interfaces));
+
+    systemd.paths = mapAttrs' generatePathUnit
+      (filterAttrs (name: value: value.privateKeyFile != null) cfg.interfaces);
+
+  });
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix b/nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix
new file mode 100644
index 000000000000..a7dea95056a0
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix
@@ -0,0 +1,266 @@
+{ config, lib, pkgs, utils, ... }:
+
+with lib;
+
+let
+  cfg = config.networking.wireless;
+  configFile = if cfg.networks != {} then pkgs.writeText "wpa_supplicant.conf" ''
+    ${optionalString cfg.userControlled.enable ''
+      ctrl_interface=DIR=/run/wpa_supplicant GROUP=${cfg.userControlled.group}
+      update_config=1''}
+    ${cfg.extraConfig}
+    ${concatStringsSep "\n" (mapAttrsToList (ssid: config: with config; let
+      key = if psk != null
+        then ''"${psk}"''
+        else pskRaw;
+      baseAuth = if key != null
+        then ''psk=${key}''
+        else ''key_mgmt=NONE'';
+    in ''
+      network={
+        ssid="${ssid}"
+        ${optionalString (priority != null) ''priority=${toString priority}''}
+        ${optionalString hidden "scan_ssid=1"}
+        ${if (auth != null) then auth else baseAuth}
+        ${extraConfig}
+      }
+    '') cfg.networks)}
+  '' else "/etc/wpa_supplicant.conf";
+in {
+  options = {
+    networking.wireless = {
+      enable = mkEnableOption "wpa_supplicant";
+
+      interfaces = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "wlan0" "wlan1" ];
+        description = ''
+          The interfaces <command>wpa_supplicant</command> will use. If empty, it will
+          automatically use all wireless interfaces.
+        '';
+      };
+
+      driver = mkOption {
+        type = types.str;
+        default = "nl80211,wext";
+        description = "Force a specific wpa_supplicant driver.";
+      };
+
+      networks = mkOption {
+        type = types.attrsOf (types.submodule {
+          options = {
+            psk = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              description = ''
+                The network's pre-shared key in plaintext defaulting
+                to being a network without any authentication.
+
+                Be aware that these will be written to the nix store
+                in plaintext!
+
+                Mutually exclusive with <varname>pskRaw</varname>.
+              '';
+            };
+
+            pskRaw = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              description = ''
+                The network's pre-shared key in hex defaulting
+                to being a network without any authentication.
+
+                Mutually exclusive with <varname>psk</varname>.
+              '';
+            };
+
+            auth = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              example = ''
+                key_mgmt=WPA-EAP
+                eap=PEAP
+                identity="user@example.com"
+                password="secret"
+              '';
+              description = ''
+                Use this option to configure advanced authentication methods like EAP.
+                See
+                <citerefentry>
+                  <refentrytitle>wpa_supplicant.conf</refentrytitle>
+                  <manvolnum>5</manvolnum>
+                </citerefentry>
+                for example configurations.
+
+                Mutually exclusive with <varname>psk</varname> and <varname>pskRaw</varname>.
+              '';
+            };
+
+            hidden = mkOption {
+              type = types.bool;
+              default = false;
+              description = ''
+                Set this to <literal>true</literal> if the SSID of the network is hidden.
+              '';
+              example = literalExample ''
+                { echelon = {
+                    hidden = true;
+                    psk = "abcdefgh";
+                  };
+                }
+              '';
+            };
+
+            priority = mkOption {
+              type = types.nullOr types.int;
+              default = null;
+              description = ''
+                By default, all networks will get same priority group (0). If some of the
+                networks are more desirable, this field can be used to change the order in
+                which wpa_supplicant goes through the networks when selecting a BSS. The
+                priority groups will be iterated in decreasing priority (i.e., the larger the
+                priority value, the sooner the network is matched against the scan results).
+                Within each priority group, networks will be selected based on security
+                policy, signal strength, etc.
+              '';
+            };
+
+            extraConfig = mkOption {
+              type = types.str;
+              default = "";
+              example = ''
+                bssid_blacklist=02:11:22:33:44:55 02:22:aa:44:55:66
+              '';
+              description = ''
+                Extra configuration lines appended to the network block.
+                See
+                <citerefentry>
+                  <refentrytitle>wpa_supplicant.conf</refentrytitle>
+                  <manvolnum>5</manvolnum>
+                </citerefentry>
+                for available options.
+              '';
+            };
+
+          };
+        });
+        description = ''
+          The network definitions to automatically connect to when
+           <command>wpa_supplicant</command> is running. If this
+           parameter is left empty wpa_supplicant will use
+          /etc/wpa_supplicant.conf as the configuration file.
+        '';
+        default = {};
+        example = literalExample ''
+          { echelon = {                   # SSID with no spaces or special characters
+              psk = "abcdefgh";
+            };
+            "echelon's AP" = {            # SSID with spaces and/or special characters
+               psk = "ijklmnop";
+            };
+            "free.wifi" = {};             # Public wireless network
+          }
+        '';
+      };
+
+      userControlled = {
+        enable = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Allow normal users to control wpa_supplicant through wpa_gui or wpa_cli.
+            This is useful for laptop users that switch networks a lot and don't want
+            to depend on a large package such as NetworkManager just to pick nearby
+            access points.
+
+            When using a declarative network specification you cannot persist any
+            settings via wpa_gui or wpa_cli.
+          '';
+        };
+
+        group = mkOption {
+          type = types.str;
+          default = "wheel";
+          example = "network";
+          description = "Members of this group can control wpa_supplicant.";
+        };
+      };
+      extraConfig = mkOption {
+        type = types.str;
+        default = "";
+        example = ''
+          p2p_disabled=1
+        '';
+        description = ''
+          Extra lines appended to the configuration file.
+          See
+          <citerefentry>
+            <refentrytitle>wpa_supplicant.conf</refentrytitle>
+            <manvolnum>5</manvolnum>
+          </citerefentry>
+          for available options.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    assertions = flip mapAttrsToList cfg.networks (name: cfg: {
+      assertion = with cfg; count (x: x != null) [ psk pskRaw auth ] <= 1;
+      message = ''options networking.wireless."${name}".{psk,pskRaw,auth} are mutually exclusive'';
+    });
+
+    environment.systemPackages =  [ pkgs.wpa_supplicant ];
+
+    services.dbus.packages = [ pkgs.wpa_supplicant ];
+    services.udev.packages = [ pkgs.crda ];
+
+    # FIXME: start a separate wpa_supplicant instance per interface.
+    systemd.services.wpa_supplicant = let
+      ifaces = cfg.interfaces;
+      deviceUnit = interface: [ "sys-subsystem-net-devices-${utils.escapeSystemdPath interface}.device" ];
+    in {
+      description = "WPA Supplicant";
+
+      after = lib.concatMap deviceUnit ifaces;
+      before = [ "network.target" ];
+      wants = [ "network.target" ];
+      requires = lib.concatMap deviceUnit ifaces;
+      wantedBy = [ "multi-user.target" ];
+      stopIfChanged = false;
+
+      path = [ pkgs.wpa_supplicant ];
+
+      script = ''
+        iface_args="-s -u -D${cfg.driver} -c ${configFile}"
+        ${if ifaces == [] then ''
+          for i in $(cd /sys/class/net && echo *); do
+            DEVTYPE=
+            UEVENT_PATH=/sys/class/net/$i/uevent
+            if [ -e "$UEVENT_PATH" ]; then
+              source "$UEVENT_PATH"
+              if [ "$DEVTYPE" = "wlan" -o -e /sys/class/net/$i/wireless ]; then
+                args+="''${args:+ -N} -i$i $iface_args"
+              fi
+            fi
+          done
+        '' else ''
+          args="${concatMapStringsSep " -N " (i: "-i${i} $iface_args") ifaces}"
+        ''}
+        exec wpa_supplicant $args
+      '';
+    };
+
+    powerManagement.resumeCommands = ''
+      /run/current-system/systemd/bin/systemctl try-restart wpa_supplicant
+    '';
+
+    # Restart wpa_supplicant when a wlan device appears or disappears.
+    services.udev.extraRules = ''
+      ACTION=="add|remove", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", RUN+="/run/current-system/systemd/bin/systemctl try-restart wpa_supplicant.service"
+    '';
+  };
+
+  meta.maintainers = with lib.maintainers; [ globin ];
+}
diff --git a/nixpkgs/nixos/modules/services/networking/xandikos.nix b/nixpkgs/nixos/modules/services/networking/xandikos.nix
new file mode 100644
index 000000000000..87c029156b9e
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/xandikos.nix
@@ -0,0 +1,148 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.xandikos;
+in
+{
+
+  options = {
+    services.xandikos = {
+      enable = mkEnableOption "Xandikos CalDAV and CardDAV server";
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.xandikos;
+        defaultText = "pkgs.xandikos";
+        description = "The Xandikos package to use.";
+      };
+
+      address = mkOption {
+        type = types.str;
+        default = "localhost";
+        description = ''
+          The IP address on which Xandikos will listen.
+          By default listens on localhost.
+        '';
+      };
+
+      port = mkOption {
+        type = types.port;
+        default = 8080;
+        description = "The port of the Xandikos web application";
+      };
+
+      routePrefix = mkOption {
+        type = types.str;
+        default = "/";
+        description = ''
+          Path to Xandikos.
+          Useful when Xandikos is behind a reverse proxy.
+        '';
+      };
+
+      extraOptions = mkOption {
+        default = [];
+        type = types.listOf types.str;
+        example = literalExample ''
+          [ "--autocreate"
+            "--defaults"
+            "--current-user-principal user"
+            "--dump-dav-xml"
+          ]
+        '';
+        description = ''
+          Extra command line arguments to pass to xandikos.
+        '';
+      };
+
+      nginx = mkOption {
+        default = {};
+        description = ''
+          Configuration for nginx reverse proxy.
+        '';
+
+        type = types.submodule {
+          options = {
+            enable = mkOption {
+              type = types.bool;
+              default = false;
+              description = ''
+                Configure the nginx reverse proxy settings.
+              '';
+            };
+
+            hostName = mkOption {
+              type = types.str;
+              description = ''
+                The hostname use to setup the virtualhost configuration
+              '';
+            };
+          };
+        };
+      };
+
+    };
+
+  };
+
+  config = mkIf cfg.enable (
+    mkMerge [
+      {
+        meta.maintainers = [ lib.maintainers."0x4A6F" ];
+
+        systemd.services.xandikos = {
+          description = "A Simple Calendar and Contact Server";
+          after = [ "network.target" ];
+          wantedBy = [ "multi-user.target" ];
+
+          serviceConfig = {
+            User = "xandikos";
+            Group = "xandikos";
+            DynamicUser = "yes";
+            RuntimeDirectory = "xandikos";
+            StateDirectory = "xandikos";
+            StateDirectoryMode = "0700";
+            PrivateDevices = true;
+            # Sandboxing
+            CapabilityBoundingSet = "CAP_NET_RAW CAP_NET_ADMIN";
+            ProtectSystem = "strict";
+            ProtectHome = true;
+            PrivateTmp = true;
+            ProtectKernelTunables = true;
+            ProtectKernelModules = true;
+            ProtectControlGroups = true;
+            RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX AF_PACKET AF_NETLINK";
+            RestrictNamespaces = true;
+            LockPersonality = true;
+            MemoryDenyWriteExecute = true;
+            RestrictRealtime = true;
+            RestrictSUIDSGID = true;
+            ExecStart = ''
+              ${cfg.package}/bin/xandikos \
+                --directory /var/lib/xandikos \
+                --listen_address ${cfg.address} \
+                --port ${toString cfg.port} \
+                --route-prefix ${cfg.routePrefix} \
+                ${lib.concatStringsSep " " cfg.extraOptions}
+            '';
+          };
+        };
+      }
+
+      (
+        mkIf cfg.nginx.enable {
+          services.nginx = {
+            enable = true;
+            virtualHosts."${cfg.nginx.hostName}" = {
+              locations."/" = {
+                proxyPass = "http://${cfg.address}:${toString cfg.port}/";
+              };
+            };
+          };
+        }
+      )
+    ]
+  );
+}
diff --git a/nixpkgs/nixos/modules/services/networking/xinetd.nix b/nixpkgs/nixos/modules/services/networking/xinetd.nix
new file mode 100644
index 000000000000..2f527ab156aa
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/xinetd.nix
@@ -0,0 +1,147 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.xinetd;
+
+  configFile = pkgs.writeText "xinetd.conf"
+    ''
+      defaults
+      {
+        log_type       = SYSLOG daemon info
+        log_on_failure = HOST
+        log_on_success = PID HOST DURATION EXIT
+        ${cfg.extraDefaults}
+      }
+
+      ${concatMapStrings makeService cfg.services}
+    '';
+
+  makeService = srv:
+    ''
+      service ${srv.name}
+      {
+        protocol    = ${srv.protocol}
+        ${optionalString srv.unlisted "type        = UNLISTED"}
+        ${optionalString (srv.flags != "") "flags = ${srv.flags}"}
+        socket_type = ${if srv.protocol == "udp" then "dgram" else "stream"}
+        ${if srv.port != 0 then "port        = ${toString srv.port}" else ""}
+        wait        = ${if srv.protocol == "udp" then "yes" else "no"}
+        user        = ${srv.user}
+        server      = ${srv.server}
+        ${optionalString (srv.serverArgs != "") "server_args = ${srv.serverArgs}"}
+        ${srv.extraConfig}
+      }
+    '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.xinetd.enable = mkEnableOption "the xinetd super-server daemon";
+
+    services.xinetd.extraDefaults = mkOption {
+      default = "";
+      type = types.lines;
+      description = ''
+        Additional configuration lines added to the default section of xinetd's configuration.
+      '';
+    };
+
+    services.xinetd.services = mkOption {
+      default = [];
+      description = ''
+        A list of services provided by xinetd.
+      '';
+
+      type = with types; listOf (submodule ({
+
+        options = {
+
+          name = mkOption {
+            type = types.str;
+            example = "login";
+            description = "Name of the service.";
+          };
+
+          protocol = mkOption {
+            type = types.str;
+            default = "tcp";
+            description =
+              "Protocol of the service.  Usually <literal>tcp</literal> or <literal>udp</literal>.";
+          };
+
+          port = mkOption {
+            type = types.int;
+            default = 0;
+            example = 123;
+            description = "Port number of the service.";
+          };
+
+          user = mkOption {
+            type = types.str;
+            default = "nobody";
+            description = "User account for the service";
+          };
+
+          server = mkOption {
+            type = types.str;
+            example = "/foo/bin/ftpd";
+            description = "Path of the program that implements the service.";
+          };
+
+          serverArgs = mkOption {
+            type = types.separatedString " ";
+            default = "";
+            description = "Command-line arguments for the server program.";
+          };
+
+          flags = mkOption {
+            type = types.str;
+            default = "";
+            description = "";
+          };
+
+          unlisted = mkOption {
+            type = types.bool;
+            default = false;
+            description = ''
+              Whether this server is listed in
+              <filename>/etc/services</filename>.  If so, the port
+              number can be omitted.
+            '';
+          };
+
+          extraConfig = mkOption {
+            type = types.lines;
+            default = "";
+            description = "Extra configuration-lines added to the section of the service.";
+          };
+
+        };
+
+      }));
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    systemd.services.xinetd = {
+      description = "xinetd server";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      path = [ pkgs.xinetd ];
+      script = "exec xinetd -syslog daemon -dontfork -stayalive -f ${configFile}";
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/xl2tpd.nix b/nixpkgs/nixos/modules/services/networking/xl2tpd.nix
new file mode 100644
index 000000000000..7dbe51422d96
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/xl2tpd.nix
@@ -0,0 +1,143 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+{
+  options = {
+    services.xl2tpd = {
+      enable = mkEnableOption "xl2tpd, the Layer 2 Tunnelling Protocol Daemon";
+
+      serverIp = mkOption {
+        type        = types.str;
+        description = "The server-side IP address.";
+        default     = "10.125.125.1";
+      };
+
+      clientIpRange = mkOption {
+        type        = types.str;
+        description = "The range from which client IPs are drawn.";
+        default     = "10.125.125.2-11";
+      };
+
+      extraXl2tpOptions = mkOption {
+        type        = types.lines;
+        description = "Adds extra lines to the xl2tpd configuration file.";
+        default     = "";
+      };
+
+      extraPppdOptions = mkOption {
+        type        = types.lines;
+        description = "Adds extra lines to the pppd options file.";
+        default     = "";
+        example     = ''
+          ms-dns 8.8.8.8
+          ms-dns 8.8.4.4
+        '';
+      };
+    };
+  };
+
+  config = mkIf config.services.xl2tpd.enable {
+    systemd.services.xl2tpd = let
+      cfg = config.services.xl2tpd;
+
+      # Config files from https://help.ubuntu.com/community/L2TPServer
+      xl2tpd-conf = pkgs.writeText "xl2tpd.conf" ''
+        [global]
+        ipsec saref = no
+
+        [lns default]
+        local ip = ${cfg.serverIp}
+        ip range = ${cfg.clientIpRange}
+        pppoptfile = ${pppd-options}
+        length bit = yes
+
+        ; Extra
+        ${cfg.extraXl2tpOptions}
+      '';
+
+      pppd-options = pkgs.writeText "ppp-options-xl2tpd.conf" ''
+        refuse-pap
+        refuse-chap
+        refuse-mschap
+        require-mschap-v2
+        # require-mppe-128
+        asyncmap 0
+        auth
+        crtscts
+        idle 1800
+        mtu 1200
+        mru 1200
+        lock
+        hide-password
+        local
+        # debug
+        name xl2tpd
+        # proxyarp
+        lcp-echo-interval 30
+        lcp-echo-failure 4
+
+        # Extra:
+        ${cfg.extraPppdOptions}
+      '';
+
+      xl2tpd-ppp-wrapped = pkgs.stdenv.mkDerivation {
+        name         = "xl2tpd-ppp-wrapped";
+        phases       = [ "installPhase" ];
+        buildInputs  = with pkgs; [ makeWrapper ];
+        installPhase = ''
+          mkdir -p $out/bin
+
+          makeWrapper ${pkgs.ppp}/sbin/pppd $out/bin/pppd \
+            --set LD_PRELOAD    "${pkgs.libredirect}/lib/libredirect.so" \
+            --set NIX_REDIRECTS "/etc/ppp=/etc/xl2tpd/ppp"
+
+          makeWrapper ${pkgs.xl2tpd}/bin/xl2tpd $out/bin/xl2tpd \
+            --set LD_PRELOAD    "${pkgs.libredirect}/lib/libredirect.so" \
+            --set NIX_REDIRECTS "${pkgs.ppp}/sbin/pppd=$out/bin/pppd"
+        '';
+      };
+    in {
+      description = "xl2tpd server";
+
+      requires = [ "network-online.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      preStart = ''
+        mkdir -p -m 700 /etc/xl2tpd
+
+        pushd /etc/xl2tpd > /dev/null
+
+        mkdir -p -m 700 ppp
+
+        [ -f ppp/chap-secrets ] || cat > ppp/chap-secrets << EOF
+        # Secrets for authentication using CHAP
+        # client	server	secret		IP addresses
+        #username	xl2tpd	password	*
+        EOF
+
+        chown root.root ppp/chap-secrets
+        chmod 600 ppp/chap-secrets
+
+        # The documentation says this file should be present but doesn't explain why and things work even if not there:
+        [ -f l2tp-secrets ] || (echo -n "* * "; ${pkgs.apg}/bin/apg -n 1 -m 32 -x 32 -a 1 -M LCN) > l2tp-secrets
+        chown root.root l2tp-secrets
+        chmod 600 l2tp-secrets
+
+        popd > /dev/null
+
+        mkdir -p /run/xl2tpd
+        chown root.root /run/xl2tpd
+        chmod 700       /run/xl2tpd
+      '';
+
+      serviceConfig = {
+        ExecStart = "${xl2tpd-ppp-wrapped}/bin/xl2tpd -D -c ${xl2tpd-conf} -s /etc/xl2tpd/l2tp-secrets -p /run/xl2tpd/pid -C /run/xl2tpd/control";
+        KillMode  = "process";
+        Restart   = "on-success";
+        Type      = "simple";
+        PIDFile   = "/run/xl2tpd/pid";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/xrdp.nix b/nixpkgs/nixos/modules/services/networking/xrdp.nix
new file mode 100644
index 000000000000..b7dd1c5d99dd
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/xrdp.nix
@@ -0,0 +1,171 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.xrdp;
+  confDir = pkgs.runCommand "xrdp.conf" { preferLocalBuild = true; } ''
+    mkdir $out
+
+    cp ${cfg.package}/etc/xrdp/{km-*,xrdp,sesman,xrdp_keyboard}.ini $out
+
+    cat > $out/startwm.sh <<EOF
+    #!/bin/sh
+    . /etc/profile
+    ${cfg.defaultWindowManager}
+    EOF
+    chmod +x $out/startwm.sh
+
+    substituteInPlace $out/xrdp.ini \
+      --replace "#rsakeys_ini=" "rsakeys_ini=/run/xrdp/rsakeys.ini" \
+      --replace "certificate=" "certificate=${cfg.sslCert}" \
+      --replace "key_file=" "key_file=${cfg.sslKey}" \
+      --replace LogFile=xrdp.log LogFile=/dev/null \
+      --replace EnableSyslog=true EnableSyslog=false
+
+    substituteInPlace $out/sesman.ini \
+      --replace LogFile=xrdp-sesman.log LogFile=/dev/null \
+      --replace EnableSyslog=1 EnableSyslog=0
+
+    # Ensure that clipboard works for non-ASCII characters
+    sed -i -e '/.*SessionVariables.*/ a\
+    LANG=${config.i18n.defaultLocale}\
+    LOCALE_ARCHIVE=${config.i18n.glibcLocales}/lib/locale/locale-archive
+    ' $out/sesman.ini
+  '';
+in
+{
+
+  ###### interface
+
+  options = {
+
+    services.xrdp = {
+
+      enable = mkEnableOption "xrdp, the Remote Desktop Protocol server";
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.xrdp;
+        defaultText = "pkgs.xrdp";
+        description = ''
+          The package to use for the xrdp daemon's binary.
+        '';
+      };
+
+      port = mkOption {
+        type = types.int;
+        default = 3389;
+        description = ''
+          Specifies on which port the xrdp daemon listens.
+        '';
+      };
+
+      sslKey = mkOption {
+        type = types.str;
+        default = "/etc/xrdp/key.pem";
+        example = "/path/to/your/key.pem";
+        description = ''
+          ssl private key path
+          A self-signed certificate will be generated if file not exists.
+        '';
+      };
+
+      sslCert = mkOption {
+        type = types.str;
+        default = "/etc/xrdp/cert.pem";
+        example = "/path/to/your/cert.pem";
+        description = ''
+          ssl certificate path
+          A self-signed certificate will be generated if file not exists.
+        '';
+      };
+
+      defaultWindowManager = mkOption {
+        type = types.str;
+        default = "xterm";
+        example = "xfce4-session";
+        description = ''
+          The script to run when user log in, usually a window manager, e.g. "icewm", "xfce4-session"
+          This is per-user overridable, if file ~/startwm.sh exists it will be used instead.
+        '';
+      };
+
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    # xrdp can run X11 program even if "services.xserver.enable = false"
+    xdg = {
+      autostart.enable = true;
+      menus.enable = true;
+      mime.enable = true;
+      icons.enable = true;
+    };
+
+    fonts.enableDefaultFonts = mkDefault true;
+
+    systemd = {
+      services.xrdp = {
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ];
+        description = "xrdp daemon";
+        requires = [ "xrdp-sesman.service" ];
+        preStart = ''
+          # prepare directory for unix sockets (the sockets will be owned by loggedinuser:xrdp)
+          mkdir -p /tmp/.xrdp || true
+          chown xrdp:xrdp /tmp/.xrdp
+          chmod 3777 /tmp/.xrdp
+
+          # generate a self-signed certificate
+          if [ ! -s ${cfg.sslCert} -o ! -s ${cfg.sslKey} ]; then
+            mkdir -p $(dirname ${cfg.sslCert}) || true
+            mkdir -p $(dirname ${cfg.sslKey}) || true
+            ${pkgs.openssl.bin}/bin/openssl req -x509 -newkey rsa:2048 -sha256 -nodes -days 365 \
+              -subj /C=US/ST=CA/L=Sunnyvale/O=xrdp/CN=www.xrdp.org \
+              -config ${cfg.package}/share/xrdp/openssl.conf \
+              -keyout ${cfg.sslKey} -out ${cfg.sslCert}
+            chown root:xrdp ${cfg.sslKey} ${cfg.sslCert}
+            chmod 440 ${cfg.sslKey} ${cfg.sslCert}
+          fi
+          if [ ! -s /run/xrdp/rsakeys.ini ]; then
+            mkdir -p /run/xrdp
+            ${cfg.package}/bin/xrdp-keygen xrdp /run/xrdp/rsakeys.ini
+          fi
+        '';
+        serviceConfig = {
+          User = "xrdp";
+          Group = "xrdp";
+          PermissionsStartOnly = true;
+          ExecStart = "${cfg.package}/bin/xrdp --nodaemon --port ${toString cfg.port} --config ${confDir}/xrdp.ini";
+        };
+      };
+
+      services.xrdp-sesman = {
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ];
+        description = "xrdp session manager";
+        restartIfChanged = false; # do not restart on "nixos-rebuild switch". like "display-manager", it can have many interactive programs as children
+        serviceConfig = {
+          ExecStart = "${cfg.package}/bin/xrdp-sesman --nodaemon --config ${confDir}/sesman.ini";
+          ExecStop  = "${pkgs.coreutils}/bin/kill -INT $MAINPID";
+        };
+      };
+
+    };
+
+    users.users.xrdp = {
+      description   = "xrdp daemon user";
+      isSystemUser  = true;
+      group         = "xrdp";
+    };
+    users.groups.xrdp = {};
+
+    security.pam.services.xrdp-sesman = { allowNullPassword = true; startSession = true; };
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/yggdrasil.nix b/nixpkgs/nixos/modules/services/networking/yggdrasil.nix
new file mode 100644
index 000000000000..0fe9a200a1b6
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/yggdrasil.nix
@@ -0,0 +1,199 @@
+{ config, lib, pkgs, ... }:
+with lib;
+let
+  keysPath = "/var/lib/yggdrasil/keys.json";
+
+  cfg = config.services.yggdrasil;
+  configProvided = cfg.config != { };
+  configFileProvided = cfg.configFile != null;
+
+in {
+  options = with types; {
+    services.yggdrasil = {
+      enable = mkEnableOption "the yggdrasil system service";
+
+      config = mkOption {
+        type = attrs;
+        default = {};
+        example = {
+          Peers = [
+            "tcp://aa.bb.cc.dd:eeeee"
+            "tcp://[aaaa:bbbb:cccc:dddd::eeee]:fffff"
+          ];
+          Listen = [
+            "tcp://0.0.0.0:xxxxx"
+          ];
+        };
+        description = ''
+          Configuration for yggdrasil, as a Nix attribute set.
+
+          Warning: this is stored in the WORLD-READABLE Nix store!
+          Therefore, it is not appropriate for private keys. If you
+          wish to specify the keys, use <option>configFile</option>.
+
+          If the <option>persistentKeys</option> is enabled then the
+          keys that are generated during activation will override
+          those in <option>config</option> or
+          <option>configFile</option>.
+
+          If no keys are specified then ephemeral keys are generated
+          and the Yggdrasil interface will have a random IPv6 address
+          each time the service is started, this is the default.
+
+          If both <option>configFile</option> and <option>config</option>
+          are supplied, they will be combined, with values from
+          <option>configFile</option> taking precedence.
+
+          You can use the command <code>nix-shell -p yggdrasil --run
+          "yggdrasil -genconf"</code> to generate default
+          configuration values with documentation.
+        '';
+      };
+
+      configFile = mkOption {
+        type = nullOr path;
+        default = null;
+        example = "/run/keys/yggdrasil.conf";
+        description = ''
+          A file which contains JSON configuration for yggdrasil.
+          See the <option>config</option> option for more information.
+        '';
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "root";
+        example = "wheel";
+        description = "Group to grant acces to the Yggdrasil control socket.";
+      };
+
+      openMulticastPort = mkOption {
+        type = bool;
+        default = false;
+        description = ''
+          Whether to open the UDP port used for multicast peer
+          discovery. The NixOS firewall blocks link-local
+          communication, so in order to make local peering work you
+          will also need to set <code>LinkLocalTCPPort</code> in your
+          yggdrasil configuration (<option>config</option> or
+          <option>configFile</option>) to a port number other than 0,
+          and then add that port to
+          <option>networking.firewall.allowedTCPPorts</option>.
+        '';
+      };
+
+      denyDhcpcdInterfaces = mkOption {
+        type = listOf str;
+        default = [];
+        example = [ "tap*" ];
+        description = ''
+          Disable the DHCP client for any interface whose name matches
+          any of the shell glob patterns in this list.  Use this
+          option to prevent the DHCP client from broadcasting requests
+          on the yggdrasil network.  It is only necessary to do so
+          when yggdrasil is running in TAP mode, because TUN
+          interfaces do not support broadcasting.
+        '';
+      };
+
+      package = mkOption {
+        type = package;
+        default = pkgs.yggdrasil;
+        defaultText = "pkgs.yggdrasil";
+        description = "Yggdrasil package to use.";
+      };
+
+      persistentKeys = mkEnableOption ''
+        If enabled then keys will be generated once and Yggdrasil
+        will retain the same IPv6 address when the service is
+        restarted. Keys are stored at ${keysPath}.
+      '';
+
+    };
+  };
+
+  config = mkIf cfg.enable (let binYggdrasil = cfg.package + "/bin/yggdrasil";
+  in {
+    assertions = [{
+      assertion = config.networking.enableIPv6;
+      message = "networking.enableIPv6 must be true for yggdrasil to work";
+    }];
+
+    system.activationScripts.yggdrasil = mkIf cfg.persistentKeys ''
+      if [ ! -e ${keysPath} ]
+      then
+        mkdir -p ${builtins.dirOf keysPath}
+        ${binYggdrasil} -genconf -json \
+          | ${pkgs.jq}/bin/jq \
+              'to_entries|map(select(.key|endswith("Key")))|from_entries' \
+          > ${keysPath}
+        chmod 600 ${keysPath}
+      fi
+    '';
+
+    systemd.services.yggdrasil = {
+      description = "Yggdrasil Network Service";
+      bindsTo = [ "network-online.target" ];
+      after = [ "network-online.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      preStart =
+        (if configProvided || configFileProvided || cfg.persistentKeys then
+          "echo "
+
+          + (lib.optionalString configProvided
+            "'${builtins.toJSON cfg.config}'")
+          + (lib.optionalString configFileProvided "$(cat ${cfg.configFile})")
+          + (lib.optionalString cfg.persistentKeys "$(cat ${keysPath})")
+          + " | ${pkgs.jq}/bin/jq -s add | ${binYggdrasil} -normaliseconf -useconf"
+        else
+          "${binYggdrasil} -genconf") + " > /run/yggdrasil/yggdrasil.conf";
+
+      serviceConfig = {
+        ExecStart =
+          "${binYggdrasil} -useconffile /run/yggdrasil/yggdrasil.conf";
+        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+        Restart = "always";
+
+        Group = cfg.group;
+        RuntimeDirectory = "yggdrasil";
+        RuntimeDirectoryMode = "0750";
+        BindReadOnlyPaths = lib.optional configFileProvided cfg.configFile
+          ++ lib.optional cfg.persistentKeys keysPath;
+
+        # TODO: as of yggdrasil 0.3.8 and systemd 243, yggdrasil fails
+        # to set up the network adapter when DynamicUser is set.  See
+        # github.com/yggdrasil-network/yggdrasil-go/issues/557.  The
+        # following options are implied by DynamicUser according to
+        # the systemd.exec documentation, and can be removed if the
+        # upstream issue is fixed and DynamicUser is set to true:
+        PrivateTmp = true;
+        RemoveIPC = true;
+        NoNewPrivileges = true;
+        ProtectSystem = "strict";
+        RestrictSUIDSGID = true;
+        # End of list of options implied by DynamicUser.
+
+        AmbientCapabilities = "CAP_NET_ADMIN";
+        CapabilityBoundingSet = "CAP_NET_ADMIN";
+        MemoryDenyWriteExecute = true;
+        ProtectControlGroups = true;
+        ProtectHome = "tmpfs";
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6 AF_NETLINK";
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        SystemCallArchitectures = "native";
+        SystemCallFilter = "~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io @resources";
+      };
+    };
+
+    networking.dhcpcd.denyInterfaces = cfg.denyDhcpcdInterfaces;
+    networking.firewall.allowedUDPPorts = mkIf cfg.openMulticastPort [ 9001 ];
+
+    # Make yggdrasilctl available on the command line.
+    environment.systemPackages = [ cfg.package ];
+  });
+  meta.maintainers = with lib.maintainers; [ gazally ehmry ];
+}
diff --git a/nixpkgs/nixos/modules/services/networking/zerobin.nix b/nixpkgs/nixos/modules/services/networking/zerobin.nix
new file mode 100644
index 000000000000..78de246a816f
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/zerobin.nix
@@ -0,0 +1,102 @@
+{ config, pkgs, lib, ... }:
+with lib;
+let
+  cfg = config.services.zerobin;
+
+  zerobin_config = pkgs.writeText "zerobin-config.py" ''
+  PASTE_FILES_ROOT = "${cfg.dataDir}"
+  ${cfg.extraConfig}
+  '';
+
+in
+  {
+    options = {
+      services.zerobin = {
+        enable = mkEnableOption "0bin";
+
+        dataDir = mkOption {
+          type = types.str;
+          default = "/var/lib/zerobin";
+          description = ''
+          Path to the 0bin data directory
+          '';
+        };
+
+        user = mkOption {
+          type = types.str;
+          default = "zerobin";
+          description = ''
+          The user 0bin should run as
+          '';
+        };
+
+        group = mkOption {
+          type = types.str;
+          default = "zerobin";
+          description = ''
+          The group 0bin should run as
+          '';
+        };
+
+        listenPort = mkOption {
+          type = types.int;
+          default = 8000;
+          example = 1357;
+          description = ''
+          The port zerobin should listen on
+          '';
+        };
+
+        listenAddress = mkOption {
+          type = types.str;
+          default = "localhost";
+          example = "127.0.0.1";
+          description = ''
+          The address zerobin should listen to
+          '';
+        };
+
+        extraConfig = mkOption {
+          type = types.lines;
+          default = "";
+          example = ''
+          MENU = (
+          ('Home', '/'),
+          )
+          COMPRESSED_STATIC_FILE = True
+          '';
+          description = ''
+          Extra configuration to be appended to the 0bin config file
+          (see https://0bin.readthedocs.org/en/latest/en/options.html)
+          '';
+        };
+      };
+    };
+
+    config = mkIf (cfg.enable) {
+      users.users.${cfg.user} =
+      if cfg.user == "zerobin" then {
+        isSystemUser = true;
+        group = cfg.group;
+        home = cfg.dataDir;
+        createHome = true;
+      }
+      else {};
+      users.groups.${cfg.group} = {};
+
+      systemd.services.zerobin = {
+        enable = true;
+        after = [ "network.target" ];
+        wantedBy = [ "multi-user.target" ];
+        serviceConfig.ExecStart = "${pkgs.pythonPackages.zerobin}/bin/zerobin ${cfg.listenAddress} ${toString cfg.listenPort} false ${cfg.user} ${cfg.group} ${zerobin_config}";
+        serviceConfig.PrivateTmp="yes";
+        serviceConfig.User = cfg.user;
+        serviceConfig.Group = cfg.group;
+        preStart = ''
+          mkdir -p ${cfg.dataDir}
+          chown ${cfg.user} ${cfg.dataDir}
+        '';
+      };
+    };
+  }
+
diff --git a/nixpkgs/nixos/modules/services/networking/zeronet.nix b/nixpkgs/nixos/modules/services/networking/zeronet.nix
new file mode 100644
index 000000000000..f354a9d42c79
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/zeronet.nix
@@ -0,0 +1,96 @@
+{ config, lib, pkgs, ... }:
+
+let
+  inherit (lib) generators literalExample mkEnableOption mkIf mkOption recursiveUpdate types;
+  cfg = config.services.zeronet;
+  dataDir = "/var/lib/zeronet";
+  configFile = pkgs.writeText "zeronet.conf" (generators.toINI {} (recursiveUpdate defaultSettings cfg.settings));
+
+  defaultSettings = {
+    global = {
+      data_dir = dataDir;
+      log_dir = dataDir;
+      ui_port = cfg.port;
+      fileserver_port = cfg.fileserverPort;
+      tor = if !cfg.tor then "disable" else if cfg.torAlways then "always" else "enable";
+    };
+  };
+in with lib; {
+  options.services.zeronet = {
+    enable = mkEnableOption "zeronet";
+
+    settings = mkOption {
+      type = with types; attrsOf (oneOf [ str int bool (listOf str) ]);
+      default = {};
+      example = literalExample "global.tor = enable;";
+
+      description = ''
+        <filename>zeronet.conf</filename> configuration. Refer to
+        <link xlink:href="https://zeronet.readthedocs.io/en/latest/faq/#is-it-possible-to-use-a-configuration-file"/>
+        for details on supported values;
+      '';
+    };
+
+    port = mkOption {
+      type = types.int;
+      default = 43110;
+      example = 43110;
+      description = "Optional zeronet web UI port.";
+    };
+
+    fileserverPort = mkOption {
+      # Not optional: when absent zeronet tries to write one to the
+      # read-only config file and crashes
+      type = types.int;
+      default = 12261;
+      example = 12261;
+      description = "Zeronet fileserver port.";
+    };
+
+    tor = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Use TOR for zeronet traffic where possible.";
+    };
+
+    torAlways = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Use TOR for all zeronet traffic.";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.tor = mkIf cfg.tor {
+      enable = true;
+      controlPort = 9051;
+
+      extraConfig = ''
+        CacheDirectoryGroupReadable 1
+        CookieAuthentication 1
+        CookieAuthFileGroupReadable 1
+      '';
+    };
+
+    systemd.services.zeronet = {
+      description = "zeronet";
+      after = [ "network.target" (optionalString cfg.tor "tor.service") ];
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        User = "zeronet";
+        DynamicUser = true;
+        StateDirectory = "zeronet";
+        SupplementaryGroups = mkIf cfg.tor [ "tor" ];
+        ExecStart = "${pkgs.zeronet}/bin/zeronet --config_file ${configFile}";
+      };
+    };
+  };
+
+  imports = [
+    (mkRemovedOptionModule [ "services" "zeronet" "dataDir" ] "Zeronet will store data by default in /var/lib/zeronet")
+    (mkRemovedOptionModule [ "services" "zeronet" "logDir" ] "Zeronet will log by default in /var/lib/zeronet")
+  ];
+
+  meta.maintainers = with maintainers; [ chiiruno ];
+}
diff --git a/nixpkgs/nixos/modules/services/networking/zerotierone.nix b/nixpkgs/nixos/modules/services/networking/zerotierone.nix
new file mode 100644
index 000000000000..cf39ed065a76
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/zerotierone.nix
@@ -0,0 +1,82 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.zerotierone;
+in
+{
+  options.services.zerotierone.enable = mkEnableOption "ZeroTierOne";
+
+  options.services.zerotierone.joinNetworks = mkOption {
+    default = [];
+    example = [ "a8a2c3c10c1a68de" ];
+    type = types.listOf types.str;
+    description = ''
+      List of ZeroTier Network IDs to join on startup
+    '';
+  };
+
+  options.services.zerotierone.port = mkOption {
+    default = 9993;
+    example = 9993;
+    type = types.int;
+    description = ''
+      Network port used by ZeroTier.
+    '';
+  };
+
+  options.services.zerotierone.package = mkOption {
+    default = pkgs.zerotierone;
+    defaultText = "pkgs.zerotierone";
+    type = types.package;
+    description = ''
+      ZeroTier One package to use.
+    '';
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.zerotierone = {
+      description = "ZeroTierOne";
+
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+      wants = [ "network-online.target" ];
+
+      path = [ cfg.package ];
+
+      preStart = ''
+        mkdir -p /var/lib/zerotier-one/networks.d
+        chmod 700 /var/lib/zerotier-one
+        chown -R root:root /var/lib/zerotier-one
+      '' + (concatMapStrings (netId: ''
+        touch "/var/lib/zerotier-one/networks.d/${netId}.conf"
+      '') cfg.joinNetworks);
+      serviceConfig = {
+        ExecStart = "${cfg.package}/bin/zerotier-one -p${toString cfg.port}";
+        Restart = "always";
+        KillMode = "process";
+        TimeoutStopSec = 5;
+      };
+    };
+
+    # ZeroTier does not issue DHCP leases, but some strangers might...
+    networking.dhcpcd.denyInterfaces = [ "zt*" ];
+
+    # ZeroTier receives UDP transmissions
+    networking.firewall.allowedUDPPorts = [ cfg.port ];
+
+    environment.systemPackages = [ cfg.package ];
+
+    # Prevent systemd from potentially changing the MAC address
+    systemd.network.links."50-zerotier" = {
+      matchConfig = {
+        OriginalName = "zt*";
+      };
+      linkConfig = {
+        AutoNegotiation = false;
+        MACAddressPolicy = "none";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/znc/default.nix b/nixpkgs/nixos/modules/services/networking/znc/default.nix
new file mode 100644
index 000000000000..a7315896c506
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/znc/default.nix
@@ -0,0 +1,308 @@
+{ config, lib, pkgs, ...}:
+
+with lib;
+
+let
+
+  cfg = config.services.znc;
+
+  defaultUser = "znc";
+
+  modules = pkgs.buildEnv {
+    name = "znc-modules";
+    paths = cfg.modulePackages;
+  };
+
+  listenerPorts = concatMap (l: optional (l ? Port) l.Port)
+    (attrValues (cfg.config.Listener or {}));
+
+  # Converts the config option to a string
+  semanticString = let
+
+      sortedAttrs = set: sort (l: r:
+        if l == "extraConfig" then false # Always put extraConfig last
+        else if isAttrs set.${l} == isAttrs set.${r} then l < r
+        else isAttrs set.${r} # Attrsets should be last, makes for a nice config
+        # This last case occurs when any side (but not both) is an attrset
+        # The order of these is correct when the attrset is on the right
+        # which we're just returning
+      ) (attrNames set);
+
+      # Specifies an attrset that encodes the value according to its type
+      encode = name: value: {
+          null = [];
+          bool = [ "${name} = ${boolToString value}" ];
+          int = [ "${name} = ${toString value}" ];
+
+          # extraConfig should be inserted verbatim
+          string = [ (if name == "extraConfig" then value else "${name} = ${value}") ];
+
+          # Values like `Foo = [ "bar" "baz" ];` should be transformed into
+          #   Foo=bar
+          #   Foo=baz
+          list = concatMap (encode name) value;
+
+          # Values like `Foo = { bar = { Baz = "baz"; Qux = "qux"; Florps = null; }; };` should be transmed into
+          #   <Foo bar>
+          #     Baz=baz
+          #     Qux=qux
+          #   </Foo>
+          set = concatMap (subname: optionals (value.${subname} != null) ([
+              "<${name} ${subname}>"
+            ] ++ map (line: "\t${line}") (toLines value.${subname}) ++ [
+              "</${name}>"
+            ])) (filter (v: v != null) (attrNames value));
+
+        }.${builtins.typeOf value};
+
+      # One level "above" encode, acts upon a set and uses encode on each name,value pair
+      toLines = set: concatMap (name: encode name set.${name}) (sortedAttrs set);
+
+    in
+      concatStringsSep "\n" (toLines cfg.config);
+
+  semanticTypes = with types; rec {
+    zncAtom = nullOr (oneOf [ int bool str ]);
+    zncAttr = attrsOf (nullOr zncConf);
+    zncAll = oneOf [ zncAtom (listOf zncAtom) zncAttr ];
+    zncConf = attrsOf (zncAll // {
+      # Since this is a recursive type and the description by default contains
+      # the description of its subtypes, infinite recursion would occur without
+      # explicitly breaking this cycle
+      description = "znc values (null, atoms (str, int, bool), list of atoms, or attrsets of znc values)";
+    });
+  };
+
+in
+
+{
+
+  imports = [ ./options.nix ];
+
+  options = {
+    services.znc = {
+      enable = mkEnableOption "ZNC";
+
+      user = mkOption {
+        default = "znc";
+        example = "john";
+        type = types.str;
+        description = ''
+          The name of an existing user account to use to own the ZNC server
+          process. If not specified, a default user will be created.
+        '';
+      };
+
+      group = mkOption {
+        default = defaultUser;
+        example = "users";
+        type = types.str;
+        description = ''
+          Group to own the ZNC process.
+        '';
+      };
+
+      dataDir = mkOption {
+        default = "/var/lib/znc/";
+        example = "/home/john/.znc/";
+        type = types.path;
+        description = ''
+          The state directory for ZNC. The config and the modules will be linked
+          to from this directory as well.
+        '';
+      };
+
+      openFirewall = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to open ports in the firewall for ZNC. Does work with
+          ports for listeners specified in
+          <option>services.znc.config.Listener</option>.
+        '';
+      };
+
+      config = mkOption {
+        type = semanticTypes.zncConf;
+        default = {};
+        example = literalExample ''
+          {
+            LoadModule = [ "webadmin" "adminlog" ];
+            User.paul = {
+              Admin = true;
+              Nick = "paul";
+              AltNick = "paul1";
+              LoadModule = [ "chansaver" "controlpanel" ];
+              Network.freenode = {
+                Server = "chat.freenode.net +6697";
+                LoadModule = [ "simple_away" ];
+                Chan = {
+                  "#nixos" = { Detached = false; };
+                  "##linux" = { Disabled = true; };
+                };
+              };
+              Pass.password = {
+                Method = "sha256";
+                Hash = "e2ce303c7ea75c571d80d8540a8699b46535be6a085be3414947d638e48d9e93";
+                Salt = "l5Xryew4g*!oa(ECfX2o";
+              };
+            };
+          }
+        '';
+        description = ''
+          Configuration for ZNC, see
+          <link xlink:href="https://wiki.znc.in/Configuration"/> for details. The
+          Nix value declared here will be translated directly to the xml-like
+          format ZNC expects. This is much more flexible than the legacy options
+          under <option>services.znc.confOptions.*</option>, but also can't do
+          any type checking.
+          </para>
+          <para>
+          You can use <command>nix-instantiate --eval --strict '&lt;nixpkgs/nixos&gt;' -A config.services.znc.config</command>
+          to view the current value. By default it contains a listener for port
+          5000 with SSL enabled.
+          </para>
+          <para>
+          Nix attributes called <literal>extraConfig</literal> will be inserted
+          verbatim into the resulting config file.
+          </para>
+          <para>
+          If <option>services.znc.useLegacyConfig</option> is turned on, the
+          option values in <option>services.znc.confOptions.*</option> will be
+          gracefully be applied to this option.
+          </para>
+          <para>
+          If you intend to update the configuration through this option, be sure
+          to enable <option>services.znc.mutable</option>, otherwise none of the
+          changes here will be applied after the initial deploy.
+        '';
+      };
+
+      configFile = mkOption {
+        type = types.path;
+        example = "~/.znc/configs/znc.conf";
+        description = ''
+          Configuration file for ZNC. It is recommended to use the
+          <option>config</option> option instead.
+          </para>
+          <para>
+          Setting this option will override any auto-generated config file
+          through the <option>confOptions</option> or <option>config</option>
+          options.
+        '';
+      };
+
+      modulePackages = mkOption {
+        type = types.listOf types.package;
+        default = [ ];
+        example = literalExample "[ pkgs.zncModules.fish pkgs.zncModules.push ]";
+        description = ''
+          A list of global znc module packages to add to znc.
+        '';
+      };
+
+      mutable = mkOption {
+        default = true; # TODO: Default to true when config is set, make sure to not delete the old config if present
+        type = types.bool;
+        description = ''
+          Indicates whether to allow the contents of the
+          <literal>dataDir</literal> directory to be changed by the user at
+          run-time.
+          </para>
+          <para>
+          If enabled, modifications to the ZNC configuration after its initial
+          creation are not overwritten by a NixOS rebuild. If disabled, the
+          ZNC configuration is rebuilt on every NixOS rebuild.
+          </para>
+          <para>
+          If the user wants to manage the ZNC service using the web admin
+          interface, this option should be enabled.
+        '';
+      };
+
+      extraFlags = mkOption {
+        default = [ ];
+        example = [ "--debug" ];
+        type = types.listOf types.str;
+        description = ''
+          Extra arguments to use for executing znc.
+        '';
+      };
+    };
+  };
+
+
+  ###### Implementation
+
+  config = mkIf cfg.enable {
+
+    services.znc = {
+      configFile = mkDefault (pkgs.writeText "znc-generated.conf" semanticString);
+      config = {
+        Version = lib.getVersion pkgs.znc;
+        Listener.l.Port = mkDefault 5000;
+        Listener.l.SSL = mkDefault true;
+      };
+    };
+
+    networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall listenerPorts;
+
+    systemd.services.znc = {
+      description = "ZNC Server";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network-online.target" ];
+      serviceConfig = {
+        User = cfg.user;
+        Group = cfg.group;
+        Restart = "always";
+        ExecStart = "${pkgs.znc}/bin/znc --foreground --datadir ${cfg.dataDir} ${escapeShellArgs cfg.extraFlags}";
+        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+        ExecStop = "${pkgs.coreutils}/bin/kill -INT $MAINPID";
+      };
+      preStart = ''
+        mkdir -p ${cfg.dataDir}/configs
+
+        # If mutable, regenerate conf file every time.
+        ${optionalString (!cfg.mutable) ''
+          echo "znc is set to be system-managed. Now deleting old znc.conf file to be regenerated."
+          rm -f ${cfg.dataDir}/configs/znc.conf
+        ''}
+
+        # Ensure essential files exist.
+        if [[ ! -f ${cfg.dataDir}/configs/znc.conf ]]; then
+            echo "No znc.conf file found in ${cfg.dataDir}. Creating one now."
+            cp --no-clobber ${cfg.configFile} ${cfg.dataDir}/configs/znc.conf
+            chmod u+rw ${cfg.dataDir}/configs/znc.conf
+            chown ${cfg.user} ${cfg.dataDir}/configs/znc.conf
+        fi
+
+        if [[ ! -f ${cfg.dataDir}/znc.pem ]]; then
+          echo "No znc.pem file found in ${cfg.dataDir}. Creating one now."
+          ${pkgs.znc}/bin/znc --makepem --datadir ${cfg.dataDir}
+        fi
+
+        # Symlink modules
+        rm ${cfg.dataDir}/modules || true
+        ln -fs ${modules}/lib/znc ${cfg.dataDir}/modules
+      '';
+    };
+
+    users.users = optionalAttrs (cfg.user == defaultUser) {
+      ${defaultUser} =
+        { description = "ZNC server daemon owner";
+          group = defaultUser;
+          uid = config.ids.uids.znc;
+          home = cfg.dataDir;
+          createHome = true;
+        };
+      };
+
+    users.groups = optionalAttrs (cfg.user == defaultUser) {
+      ${defaultUser} =
+        { gid = config.ids.gids.znc;
+          members = [ defaultUser ];
+        };
+    };
+
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/znc/options.nix b/nixpkgs/nixos/modules/services/networking/znc/options.nix
new file mode 100644
index 000000000000..048dbd738630
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/znc/options.nix
@@ -0,0 +1,270 @@
+{ lib, config, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.znc;
+
+  networkOpts = {
+    options = {
+
+      server = mkOption {
+        type = types.str;
+        example = "chat.freenode.net";
+        description = ''
+          IRC server address.
+        '';
+      };
+
+      port = mkOption {
+        type = types.ints.u16;
+        default = 6697;
+        description = ''
+          IRC server port.
+        '';
+      };
+
+      password = mkOption {
+        type = types.str;
+        default = "";
+        description = ''
+          IRC server password, such as for a Slack gateway.
+        '';
+      };
+
+      useSSL = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to use SSL to connect to the IRC server.
+        '';
+      };
+
+      modules = mkOption {
+        type = types.listOf types.str;
+        default = [ "simple_away" ];
+        example = literalExample "[ simple_away sasl ]";
+        description = ''
+          ZNC network modules to load.
+        '';
+      };
+
+      channels = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "nixos" ];
+        description = ''
+          IRC channels to join.
+        '';
+      };
+
+      hasBitlbeeControlChannel = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to add the special Bitlbee operations channel.
+        '';
+      };
+
+      extraConf = mkOption {
+        default = "";
+        type = types.lines;
+        example = ''
+          Encoding = ^UTF-8
+          FloodBurst = 4
+          FloodRate = 1.00
+          IRCConnectEnabled = true
+          Ident = johntron
+          JoinDelay = 0
+          Nick = johntron
+        '';
+        description = ''
+          Extra config for the network. Consider using
+          <option>services.znc.config</option> instead.
+        '';
+      };
+    };
+  };
+
+in
+
+{
+
+  options = {
+    services.znc = {
+
+      useLegacyConfig = mkOption {
+        default = true;
+        type = types.bool;
+        description = ''
+          Whether to propagate the legacy options under
+          <option>services.znc.confOptions.*</option> to the znc config. If this
+          is turned on, the znc config will contain a user with the default name
+          "znc", global modules "webadmin" and "adminlog" will be enabled by
+          default, and more, all controlled through the
+          <option>services.znc.confOptions.*</option> options.
+          You can use <command>nix-instantiate --eval --strict '&lt;nixpkgs/nixos&gt;' -A config.services.znc.config</command>
+          to view the current value of the config.
+          </para>
+          <para>
+          In any case, if you need more flexibility,
+          <option>services.znc.config</option> can be used to override/add to
+          all of the legacy options.
+        '';
+      };
+
+      confOptions = {
+        modules = mkOption {
+          type = types.listOf types.str;
+          default = [ "webadmin" "adminlog" ];
+          example = [ "partyline" "webadmin" "adminlog" "log" ];
+          description = ''
+            A list of modules to include in the `znc.conf` file.
+          '';
+        };
+
+        userModules = mkOption {
+          type = types.listOf types.str;
+          default = [ "chansaver" "controlpanel" ];
+          example = [ "chansaver" "controlpanel" "fish" "push" ];
+          description = ''
+            A list of user modules to include in the `znc.conf` file.
+          '';
+        };
+
+        userName = mkOption {
+          default = "znc";
+          example = "johntron";
+          type = types.str;
+          description = ''
+            The user name used to log in to the ZNC web admin interface.
+          '';
+        };
+
+        networks = mkOption {
+          default = { };
+          type = with types; attrsOf (submodule networkOpts);
+          description = ''
+            IRC networks to connect the user to.
+          '';
+          example = literalExample ''
+            {
+              "freenode" = {
+                server = "chat.freenode.net";
+                port = 6697;
+                useSSL = true;
+                modules = [ "simple_away" ];
+              };
+            };
+          '';
+        };
+
+        nick = mkOption {
+          default = "znc-user";
+          example = "john";
+          type = types.str;
+          description = ''
+            The IRC nick.
+          '';
+        };
+
+        passBlock = mkOption {
+          example = literalExample ''
+            &lt;Pass password&gt;
+               Method = sha256
+               Hash = e2ce303c7ea75c571d80d8540a8699b46535be6a085be3414947d638e48d9e93
+               Salt = l5Xryew4g*!oa(ECfX2o
+            &lt;/Pass&gt;
+          '';
+          type = types.str;
+          description = ''
+            Generate with `nix-shell -p znc --command "znc --makepass"`.
+            This is the password used to log in to the ZNC web admin interface.
+            You can also set this through
+            <option>services.znc.config.User.&lt;username&gt;.Pass.Method</option>
+            and co.
+          '';
+        };
+
+        port = mkOption {
+          default = 5000;
+          type = types.int;
+          description = ''
+            Specifies the port on which to listen.
+          '';
+        };
+
+        useSSL = mkOption {
+          default = true;
+          type = types.bool;
+          description = ''
+            Indicates whether the ZNC server should use SSL when listening on
+            the specified port. A self-signed certificate will be generated.
+          '';
+        };
+
+        uriPrefix = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          example = "/znc/";
+          description = ''
+            An optional URI prefix for the ZNC web interface. Can be
+            used to make ZNC available behind a reverse proxy.
+          '';
+        };
+
+        extraZncConf = mkOption {
+          default = "";
+          type = types.lines;
+          description = ''
+            Extra config to `znc.conf` file.
+          '';
+        };
+      };
+
+    };
+  };
+
+  config = mkIf cfg.useLegacyConfig {
+
+    services.znc.config = let
+      c = cfg.confOptions;
+      # defaults here should override defaults set in the non-legacy part
+      mkDefault = mkOverride 900;
+    in {
+      LoadModule = mkDefault c.modules;
+      Listener.l = {
+        Port = mkDefault c.port;
+        IPv4 = mkDefault true;
+        IPv6 = mkDefault true;
+        SSL = mkDefault c.useSSL;
+        URIPrefix = c.uriPrefix;
+      };
+      User.${c.userName} = {
+        Admin = mkDefault true;
+        Nick = mkDefault c.nick;
+        AltNick = mkDefault "${c.nick}_";
+        Ident = mkDefault c.nick;
+        RealName = mkDefault c.nick;
+        LoadModule = mkDefault c.userModules;
+        Network = mapAttrs (name: net: {
+          LoadModule = mkDefault net.modules;
+          Server = mkDefault "${net.server} ${optionalString net.useSSL "+"}${toString net.port} ${net.password}";
+          Chan = optionalAttrs net.hasBitlbeeControlChannel { "&bitlbee" = mkDefault {}; } //
+            listToAttrs (map (n: nameValuePair "#${n}" (mkDefault {})) net.channels);
+          extraConfig = if net.extraConf == "" then mkDefault null else net.extraConf;
+        }) c.networks;
+        extraConfig = [ c.passBlock ];
+      };
+      extraConfig = optional (c.extraZncConf != "") c.extraZncConf;
+    };
+  };
+
+  imports = [
+    (mkRemovedOptionModule ["services" "znc" "zncConf"] ''
+      Instead of `services.znc.zncConf = "... foo ...";`, use
+      `services.znc.configFile = pkgs.writeText "znc.conf" "... foo ...";`.
+    '')
+  ];
+}