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/biboumi.nix269
-rw-r--r--nixpkgs/nixos/modules/services/networking/bitcoind.nix206
-rw-r--r--nixpkgs/nixos/modules/services/networking/blockbook-frontend.nix275
-rw-r--r--nixpkgs/nixos/modules/services/networking/corerad.nix3
-rw-r--r--nixpkgs/nixos/modules/services/networking/dhcpd.nix12
-rw-r--r--nixpkgs/nixos/modules/services/networking/gateone.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/heyefi.nix82
-rw-r--r--nixpkgs/nixos/modules/services/networking/hylafax/options.nix4
-rw-r--r--nixpkgs/nixos/modules/services/networking/jicofo.nix152
-rw-r--r--nixpkgs/nixos/modules/services/networking/jitsi-videobridge.nix276
-rw-r--r--nixpkgs/nixos/modules/services/networking/kresd.nix13
-rw-r--r--nixpkgs/nixos/modules/services/networking/monero.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/mstpd.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/namecoind.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/ncdns.nix278
-rw-r--r--nixpkgs/nixos/modules/services/networking/networkmanager.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/nextdns.nix44
-rw-r--r--nixpkgs/nixos/modules/services/networking/nghttpx/default.nix6
-rw-r--r--nixpkgs/nixos/modules/services/networking/nsd.nix7
-rw-r--r--nixpkgs/nixos/modules/services/networking/ntp/chrony.nix1
-rw-r--r--nixpkgs/nixos/modules/services/networking/nylon.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/onedrive.nix72
-rw-r--r--nixpkgs/nixos/modules/services/networking/onedrive.xml34
-rw-r--r--nixpkgs/nixos/modules/services/networking/openvpn.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/prosody.nix4
-rw-r--r--nixpkgs/nixos/modules/services/networking/prosody.xml13
-rw-r--r--nixpkgs/nixos/modules/services/networking/radicale.nix11
-rw-r--r--nixpkgs/nixos/modules/services/networking/resilio.nix20
-rw-r--r--nixpkgs/nixos/modules/services/networking/robustirc-bridge.nix47
-rw-r--r--nixpkgs/nixos/modules/services/networking/seeks.nix75
-rw-r--r--nixpkgs/nixos/modules/services/networking/shadowsocks.nix54
-rw-r--r--nixpkgs/nixos/modules/services/networking/skydns.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/ssh/sshd.nix12
-rw-r--r--nixpkgs/nixos/modules/services/networking/sslh.nix18
-rw-r--r--nixpkgs/nixos/modules/services/networking/supplicant.nix26
-rw-r--r--nixpkgs/nixos/modules/services/networking/syncthing.nix13
-rw-r--r--nixpkgs/nixos/modules/services/networking/tinc.nix16
-rw-r--r--nixpkgs/nixos/modules/services/networking/trickster.nix5
-rw-r--r--nixpkgs/nixos/modules/services/networking/unifi.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/wasabibackend.nix158
-rw-r--r--nixpkgs/nixos/modules/services/networking/websockify.nix6
-rw-r--r--nixpkgs/nixos/modules/services/networking/wg-quick.nix20
-rw-r--r--nixpkgs/nixos/modules/services/networking/wireguard.nix15
-rw-r--r--nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix5
-rw-r--r--nixpkgs/nixos/modules/services/networking/xandikos.nix4
-rw-r--r--nixpkgs/nixos/modules/services/networking/yggdrasil.nix5
-rw-r--r--nixpkgs/nixos/modules/services/networking/yggdrasil.xml157
47 files changed, 2112 insertions, 324 deletions
diff --git a/nixpkgs/nixos/modules/services/networking/biboumi.nix b/nixpkgs/nixos/modules/services/networking/biboumi.nix
new file mode 100644
index 000000000000..66ddca93d818
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/biboumi.nix
@@ -0,0 +1,269 @@
+{ config, lib, pkgs, options, ... }:
+with lib;
+let
+  cfg = config.services.biboumi;
+  inherit (config.environment) etc;
+  rootDir = "/run/biboumi/mnt-root";
+  stateDir = "/var/lib/biboumi";
+  settingsFile = pkgs.writeText "biboumi.cfg" (
+    generators.toKeyValue {
+      mkKeyValue = k: v:
+        if v == null then ""
+        else generators.mkKeyValueDefault {} "=" k v;
+    } cfg.settings);
+  need_CAP_NET_BIND_SERVICE = cfg.settings.identd_port != 0 && cfg.settings.identd_port < 1024;
+in
+{
+  options = {
+    services.biboumi = {
+      enable = mkEnableOption "the Biboumi XMPP gateway to IRC";
+
+      settings = mkOption {
+        description = ''
+          See <link xlink:href="https://lab.louiz.org/louiz/biboumi/blob/8.5/doc/biboumi.1.rst">biboumi 8.5</link>
+          for documentation.
+        '';
+        default = {};
+        type = types.submodule {
+          freeformType = with types;
+            (attrsOf (nullOr (oneOf [str int bool]))) // {
+              description = "settings option";
+            };
+          options.admin = mkOption {
+            type = with types; listOf str;
+            default = [];
+            example = ["admin@example.org"];
+            apply = concatStringsSep ":";
+            description = ''
+              The bare JID of the gateway administrator. This JID will have more
+              privileges than other standard users, for example some administration
+              ad-hoc commands will only be available to that JID.
+            '';
+          };
+          options.ca_file = mkOption {
+            type = types.path;
+            default = "/etc/ssl/certs/ca-certificates.crt";
+            description = ''
+              Specifies which file should be used as the list of trusted CA
+              when negociating a TLS session.
+            '';
+          };
+          options.db_name = mkOption {
+            type = with types; either path str;
+            default = "${stateDir}/biboumi.sqlite";
+            description = ''
+              The name of the database to use.
+            '';
+            example = "postgresql://user:secret@localhost";
+          };
+          options.hostname = mkOption {
+            type = types.str;
+            example = "biboumi.example.org";
+            description = ''
+              The hostname served by the XMPP gateway.
+              This domain must be configured in the XMPP server
+              as an external component.
+            '';
+          };
+          options.identd_port = mkOption {
+            type = types.port;
+            default = 113;
+            example = 0;
+            description = ''
+              The TCP port on which to listen for identd queries.
+            '';
+          };
+          options.log_level = mkOption {
+            type = types.ints.between 0 3;
+            default = 1;
+            description = ''
+              Indicate what type of log messages to write in the logs.
+              0 is debug, 1 is info, 2 is warning, 3 is error.
+            '';
+          };
+          options.password = mkOption {
+            type = with types; nullOr str;
+            description = ''
+              The password used to authenticate the XMPP component to your XMPP server.
+              This password must be configured in the XMPP server,
+              associated with the external component on
+              <link linkend="opt-services.biboumi.settings.hostname">hostname</link>.
+
+              Set it to null and use <link linkend="opt-services.biboumi.credentialsFile">credentialsFile</link>
+              if you do not want this password to go into the Nix store.
+            '';
+          };
+          options.persistent_by_default = mkOption {
+            type = types.bool;
+            default = false;
+            description = ''
+              Whether all rooms will be persistent by default:
+              the value of the “persistent” option in the global configuration of each
+              user will be “true”, but the value of each individual room will still
+              default to false. This means that a user just needs to change the global
+              “persistent” configuration option to false in order to override this.
+            '';
+          };
+          options.policy_directory = mkOption {
+            type = types.path;
+            default = "${pkgs.biboumi}/etc/biboumi";
+            description = ''
+              A directory that should contain the policy files,
+              used to customize Botan’s behaviour
+              when negociating the TLS connections with the IRC servers.
+            '';
+          };
+          options.port = mkOption {
+            type = types.port;
+            default = 5347;
+            description = ''
+              The TCP port to use to connect to the local XMPP component.
+            '';
+          };
+          options.realname_customization = mkOption {
+            type = types.bool;
+            default = true;
+            description = ''
+              Whether the users will be able to use
+              the ad-hoc commands that lets them configure
+              their realname and username.
+            '';
+          };
+          options.realname_from_jid = mkOption {
+            type = types.bool;
+            default = false;
+            description = ''
+              Whether the realname and username of each biboumi
+              user will be extracted from their JID.
+              Otherwise they will be set to the nick
+              they used to connect to the IRC server.
+            '';
+          };
+          options.xmpp_server_ip = mkOption {
+            type = types.str;
+            default = "127.0.0.1";
+            description = ''
+              The IP address to connect to the XMPP server on.
+              The connection to the XMPP server is unencrypted,
+              so the biboumi instance and the server should
+              normally be on the same host.
+            '';
+          };
+        };
+      };
+
+      credentialsFile = mkOption {
+        type = types.path;
+        description = ''
+          Path to a configuration file to be merged with the settings.
+          Beware not to surround "=" with spaces when setting biboumi's options in this file.
+          Useful to merge a file which is better kept out of the Nix store
+          because it contains sensible data like
+          <link linkend="opt-services.biboumi.settings.password">password</link>.
+        '';
+        default = "/dev/null";
+        example = "/run/keys/biboumi.cfg";
+      };
+
+      openFirewall = mkEnableOption "opening of the identd port in the firewall";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    networking.firewall = mkIf (cfg.openFirewall && cfg.settings.identd_port != 0)
+      { allowedTCPPorts = [ cfg.settings.identd_port ]; };
+
+    systemd.services.biboumi = {
+      description = "Biboumi, XMPP to IRC gateway";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        Type = "notify";
+        # Biboumi supports systemd's watchdog.
+        WatchdogSec = 20;
+        Restart = "always";
+        # Use "+" because credentialsFile may not be accessible to User= or Group=.
+        ExecStartPre = [("+" + pkgs.writeShellScript "biboumi-prestart" ''
+          set -eux
+          cat ${settingsFile} '${cfg.credentialsFile}' |
+          install -m 644 /dev/stdin /run/biboumi/biboumi.cfg
+        '')];
+        ExecStart = "${pkgs.biboumi}/bin/biboumi /run/biboumi/biboumi.cfg";
+        ExecReload = "${pkgs.coreutils}/bin/kill -USR1 $MAINPID";
+        # Firewalls needing opening for output connections can still do that
+        # selectively for biboumi with:
+        # users.users.biboumi.isSystemUser = true;
+        # and, for example:
+        # networking.nftables.ruleset = ''
+        #   add rule inet filter output meta skuid biboumi tcp accept
+        # '';
+        DynamicUser = true;
+        RootDirectory = rootDir;
+        RootDirectoryStartOnly = true;
+        InaccessiblePaths = [ "-+${rootDir}" ];
+        RuntimeDirectory = [ "biboumi" (removePrefix "/run/" rootDir) ];
+        RuntimeDirectoryMode = "700";
+        StateDirectory = "biboumi";
+        StateDirectoryMode = "700";
+        MountAPIVFS = true;
+        UMask = "0066";
+        BindPaths = [
+          stateDir
+          # This is for Type="notify"
+          # See https://github.com/systemd/systemd/issues/3544
+          "/run/systemd/notify"
+          "/run/systemd/journal/socket"
+        ];
+        BindReadOnlyPaths = [
+          builtins.storeDir
+          "/etc"
+        ];
+        # The following options are only for optimizing:
+        # systemd-analyze security biboumi
+        AmbientCapabilities = [ (optionalString need_CAP_NET_BIND_SERVICE "CAP_NET_BIND_SERVICE") ];
+        CapabilityBoundingSet = [ (optionalString need_CAP_NET_BIND_SERVICE "CAP_NET_BIND_SERVICE") ];
+        # ProtectClock= adds DeviceAllow=char-rtc r
+        DeviceAllow = "";
+        LockPersonality = true;
+        MemoryDenyWriteExecute = true;
+        NoNewPrivileges = true;
+        PrivateDevices = true;
+        PrivateMounts = true;
+        PrivateNetwork = mkDefault false;
+        PrivateTmp = true;
+        # PrivateUsers=true breaks AmbientCapabilities=CAP_NET_BIND_SERVICE
+        # See https://bugs.archlinux.org/task/65921
+        PrivateUsers = !need_CAP_NET_BIND_SERVICE;
+        ProtectClock = true;
+        ProtectControlGroups = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectSystem = "strict";
+        RemoveIPC = true;
+        # AF_UNIX is for /run/systemd/notify
+        RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+        SystemCallFilter = [
+          "@system-service"
+          # Groups in @system-service which do not contain a syscall
+          # listed by perf stat -e 'syscalls:sys_enter_*' biboumi biboumi.cfg
+          # in tests, and seem likely not necessary for biboumi.
+          # To run such a perf in ExecStart=, you have to:
+          # - AmbientCapabilities="CAP_SYS_ADMIN"
+          # - mount -o remount,mode=755 /sys/kernel/debug/{,tracing}
+          "~@aio" "~@chown" "~@ipc" "~@keyring" "~@resources" "~@setuid" "~@timer"
+        ];
+        SystemCallArchitectures = "native";
+        SystemCallErrorNumber = "EPERM";
+      };
+    };
+  };
+
+  meta.maintainers = with maintainers; [ julm ];
+}
diff --git a/nixpkgs/nixos/modules/services/networking/bitcoind.nix b/nixpkgs/nixos/modules/services/networking/bitcoind.nix
index 4e00a8865474..bc9aa53f49aa 100644
--- a/nixpkgs/nixos/modules/services/networking/bitcoind.nix
+++ b/nixpkgs/nixos/modules/services/networking/bitcoind.nix
@@ -3,31 +3,8 @@
 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}"
-  ];
+  eachBitcoind = config.services.bitcoind;
 
   rpcUserOpts = { name, ... }: {
     options = {
@@ -39,11 +16,14 @@ let
         '';
       };
       passwordHMAC = mkOption {
-        type = with types; uniq (strMatching "[0-9a-f]+\\$[0-9a-f]{64}");
+        type = types.uniq (types.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;.
+
+          Tool (Python script) for HMAC generation is available here:
+          <link xlink:href="https://github.com/bitcoin/bitcoin/blob/master/share/rpcauth/rpcauth.py"/>
         '';
       };
     };
@@ -51,10 +31,10 @@ let
       name = mkDefault name;
     };
   };
-in {
-  options = {
 
-    services.bitcoind = {
+  bitcoindOpts = { config, lib, name, ...}: {
+    options = {
+
       enable = mkEnableOption "Bitcoin daemon";
 
       package = mkOption {
@@ -63,12 +43,14 @@ in {
         defaultText = "pkgs.bitcoind";
         description = "The package providing bitcoin binaries.";
       };
+
       configFile = mkOption {
-        type = types.path;
-        default = configFile;
-        example = "/etc/bitcoind.conf";
+        type = types.nullOr types.path;
+        default = null;
+        example = "/var/lib/${name}/bitcoin.conf";
         description = "The configuration file path to supply bitcoind.";
       };
+
       extraConfig = mkOption {
         type = types.lines;
         default = "";
@@ -79,20 +61,22 @@ in {
         '';
         description = "Additional configurations to be appended to <filename>bitcoin.conf</filename>.";
       };
+
       dataDir = mkOption {
         type = types.path;
-        default = "/var/lib/bitcoind";
+        default = "/var/lib/bitcoind-${name}";
         description = "The data directory for bitcoind.";
       };
 
       user = mkOption {
         type = types.str;
-        default = "bitcoin";
+        default = "bitcoind-${name}";
         description = "The user as which to run bitcoind.";
       };
+
       group = mkOption {
         type = types.str;
-        default = cfg.user;
+        default = config.user;
         description = "The group as which to run bitcoind.";
       };
 
@@ -110,29 +94,36 @@ in {
               bob.passwordHMAC = "b2dd077cb54591a2f3139e69a897ac$4e71f08d48b4347cf8eff3815c0e25ae2e9a4340474079f55705f40574f4ec99";
             }
           '';
-          type = with types; loaOf (submodule rpcUserOpts);
-          description = ''
-            RPC user information for JSON-RPC connnections.
-          '';
+          type = types.attrsOf (types.submodule rpcUserOpts);
+          description = "RPC user information for JSON-RPC connnections.";
         };
       };
 
+      pidFile = mkOption {
+        type = types.path;
+        default = "${config.dataDir}/bitcoind.pid";
+        description = "Location of bitcoind pid file.";
+      };
+
       testnet = mkOption {
         type = types.bool;
         default = false;
-        description = "Whether to use the test chain.";
+        description = "Whether to use the testnet instead of mainnet.";
       };
+
       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.";
+        description = "Override the default database cache size in MiB.";
       };
+
       prune = mkOption {
         type = types.nullOr (types.coercedTo
           (types.enum [ "disable" "manual" ])
@@ -149,45 +140,122 @@ in {
           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)
+          to stay under the specified target size in MiB).
+        '';
+      };
+
+      extraCmdlineOptions = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          Extra command line options to pass to bitcoind.
+          Run bitcoind --help to list all available options.
         '';
       };
     };
   };
+in
+{
 
-  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";
-      };
+  options = {
+    services.bitcoind = mkOption {
+      type = types.attrsOf (types.submodule bitcoindOpts);
+      default = {};
+      description = "Specification of one or more bitcoind instances.";
     };
-    users.users.${cfg.user} = {
+  };
+
+  config = mkIf (eachBitcoind != {}) {
+
+    assertions = flatten (mapAttrsToList (bitcoindName: cfg: [
+    {
+      assertion = (cfg.prune != null) -> (builtins.elem cfg.prune [ "disable" "manual" 0 1 ] || (builtins.isInt cfg.prune && cfg.prune >= 550));
+      message = ''
+        If set, services.bitcoind.${bitcoindName}.prune has to be "disable", "manual", 0 , 1 or >= 550.
+      '';
+    }
+    {
+      assertion = (cfg.rpc.users != {}) -> (cfg.configFile == null);
+      message = ''
+        You cannot set both services.bitcoind.${bitcoindName}.rpc.users and services.bitcoind.${bitcoindName}.configFile
+        as they are exclusive. RPC user setting would have no effect if custom configFile would be used.
+      '';
+    }
+    ]) eachBitcoind);
+
+    environment.systemPackages = flatten (mapAttrsToList (bitcoindName: cfg: [
+      cfg.package
+    ]) eachBitcoind);
+
+    systemd.services = mapAttrs' (bitcoindName: cfg: (
+      nameValuePair "bitcoind-${bitcoindName}" (
+      let
+        configFile = pkgs.writeText "bitcoin.conf" ''
+          # If Testnet is enabled, we need to add [test] section
+          # otherwise, some options (e.g.: custom RPC port) will not work
+          ${optionalString cfg.testnet "[test]"}
+          # RPC users
+          ${concatMapStringsSep  "\n"
+            (rpcUser: "rpcauth=${rpcUser.name}:${rpcUser.passwordHMAC}")
+            (attrValues cfg.rpc.users)
+          }
+          # Extra config options (from bitcoind nixos service)
+          ${cfg.extraConfig}
+        '';
+      in {
+        description = "Bitcoin daemon";
+        after = [ "network.target" ];
+        wantedBy = [ "multi-user.target" ];
+        serviceConfig = {
+          User = cfg.user;
+          Group = cfg.group;
+          ExecStart = ''
+            ${cfg.package}/bin/bitcoind \
+            ${if (cfg.configFile != null) then
+              "-conf=${cfg.configFile}"
+            else
+              "-conf=${configFile}"
+            } \
+            -datadir=${cfg.dataDir} \
+            -pid=${cfg.pidFile} \
+            ${optionalString cfg.testnet "-testnet"}\
+            ${optionalString (cfg.port != null) "-port=${toString cfg.port}"}\
+            ${optionalString (cfg.prune != null) "-prune=${toString cfg.prune}"}\
+            ${optionalString (cfg.dbCache != null) "-dbcache=${toString cfg.dbCache}"}\
+            ${optionalString (cfg.rpc.port != null) "-rpcport=${toString cfg.rpc.port}"}\
+            ${toString cfg.extraCmdlineOptions}
+          '';
+          Restart = "on-failure";
+
+          # Hardening measures
+          PrivateTmp = "true";
+          ProtectSystem = "full";
+          NoNewPrivileges = "true";
+          PrivateDevices = "true";
+          MemoryDenyWriteExecute = "true";
+        };
+      }
+    ))) eachBitcoind;
+
+    systemd.tmpfiles.rules = flatten (mapAttrsToList (bitcoindName: cfg: [
+      "d '${cfg.dataDir}' 0770 '${cfg.user}' '${cfg.group}' - -"
+    ]) eachBitcoind);
+
+    users.users = mapAttrs' (bitcoindName: cfg: (
+      nameValuePair "bitcoind-${bitcoindName}" {
       name = cfg.user;
       group = cfg.group;
       description = "Bitcoin daemon user";
       home = cfg.dataDir;
       isSystemUser = true;
-    };
-    users.groups.${cfg.group} = {
-      name = cfg.group;
-    };
+    })) eachBitcoind;
+
+    users.groups = mapAttrs' (bitcoindName: cfg: (
+      nameValuePair "${cfg.group}" { }
+    )) eachBitcoind;
+
   };
+
+  meta.maintainers = with maintainers; [ _1000101 ];
+
 }
diff --git a/nixpkgs/nixos/modules/services/networking/blockbook-frontend.nix b/nixpkgs/nixos/modules/services/networking/blockbook-frontend.nix
new file mode 100644
index 000000000000..dde24522756a
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/blockbook-frontend.nix
@@ -0,0 +1,275 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  eachBlockbook = config.services.blockbook-frontend;
+
+  blockbookOpts = { config, lib, name, ...}: {
+
+    options = {
+
+      enable = mkEnableOption "blockbook-frontend application.";
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.blockbook;
+        description = "Which blockbook package to use.";
+      };
+
+      user = mkOption {
+        type = types.str;
+        default = "blockbook-frontend-${name}";
+        description = "The user as which to run blockbook-frontend-${name}.";
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "${config.user}";
+        description = "The group as which to run blockbook-frontend-${name}.";
+      };
+
+      certFile = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        example = "/etc/secrets/blockbook-frontend-${name}/certFile";
+        description = ''
+          To enable SSL, specify path to the name of certificate files without extension.
+          Expecting <filename>certFile.crt</filename> and <filename>certFile.key</filename>.
+        '';
+      };
+
+      configFile = mkOption {
+        type = with types; nullOr path;
+        default = null;
+        example = "${config.dataDir}/config.json";
+        description = "Location of the blockbook configuration file.";
+      };
+
+      coinName = mkOption {
+        type = types.str;
+        default = "Bitcoin";
+        example = "Bitcoin";
+        description = ''
+          See <link xlink:href="https://github.com/trezor/blockbook/blob/master/bchain/coins/blockchain.go#L61"/>
+          for current of coins supported in master (Note: may differ from release).
+        '';
+      };
+
+      cssDir = mkOption {
+        type = types.path;
+        default = "${config.package}/share/css/";
+        example = "${config.dataDir}/static/css/";
+        description = ''
+          Location of the dir with <filename>main.css</filename> CSS file.
+          By default, the one shipped with the package is used.
+        '';
+      };
+
+      dataDir = mkOption {
+        type = types.path;
+        default = "/var/lib/blockbook-frontend-${name}";
+        description = "Location of blockbook-frontend-${name} data directory.";
+      };
+
+      debug = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Debug mode, return more verbose errors, reload templates on each request.";
+      };
+
+      internal = mkOption {
+        type = types.nullOr types.str;
+        default = ":9030";
+        example = ":9030";
+        description = "Internal http server binding <literal>[address]:port</literal>.";
+      };
+
+      messageQueueBinding = mkOption {
+        type = types.str;
+        default = "tcp://127.0.0.1:38330";
+        example = "tcp://127.0.0.1:38330";
+        description = "Message Queue Binding <literal>address:port</literal>.";
+      };
+
+      public = mkOption {
+        type = types.nullOr types.str;
+        default = ":9130";
+        example = ":9130";
+        description = "Public http server binding <literal>[address]:port</literal>.";
+      };
+
+      rpc = {
+        url = mkOption {
+          type = types.str;
+          default = "http://127.0.0.1";
+          description = "URL for JSON-RPC connections.";
+        };
+
+        port = mkOption {
+          type = types.port;
+          default = 8030;
+          description = "Port for JSON-RPC connections.";
+        };
+
+        user = mkOption {
+          type = types.str;
+          default = "rpc";
+          example = "rpc";
+          description = "Username for JSON-RPC connections.";
+        };
+
+        password = mkOption {
+          type = types.str;
+          default = "rpc";
+          example = "rpc";
+          description = ''
+            RPC password for JSON-RPC connections.
+            Warning: this is stored in cleartext in the Nix store!!!
+            Use <literal>configFile</literal> or <literal>passwordFile</literal> if needed.
+          '';
+        };
+
+        passwordFile = mkOption {
+          type = types.nullOr types.path;
+          default = null;
+          description = ''
+            File containing password of the RPC user.
+            Note: This options is ignored when <literal>configFile</literal> is used.
+          '';
+        };
+      };
+
+      sync = mkOption {
+        type = types.bool;
+        default = true;
+        description = "Synchronizes until tip, if together with zeromq, keeps index synchronized.";
+      };
+
+      templateDir = mkOption {
+        type = types.path;
+        default = "${config.package}/share/templates/";
+        example = "${config.dataDir}/templates/static/";
+        description = "Location of the HTML templates. By default, ones shipped with the package are used.";
+      };
+
+      extraConfig = mkOption {
+        type = types.attrs;
+        default = {};
+        example = literalExample '' {
+          alternative_estimate_fee = "whatthefee-disabled";
+          alternative_estimate_fee_params = "{\"url\": \"https://whatthefee.io/data.json\", \"periodSeconds\": 60}";
+          fiat_rates = "coingecko";
+          fiat_rates_params = "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"bitcoin\", \"periodSeconds\": 60}";
+          coin_shortcut = "BTC";
+          coin_label = "Bitcoin";
+          xpub_magic = 76067358;
+          xpub_magic_segwit_p2sh = 77429938;
+          xpub_magic_segwit_native = 78792518;
+        }'';
+        description = ''
+          Additional configurations to be appended to <filename>coin.conf</filename>.
+          Overrides any already defined configuration options.
+          See <link xlink:href="https://github.com/trezor/blockbook/tree/master/configs/coins"/>
+          for current configuration options supported in master (Note: may differ from release).
+        '';
+      };
+
+      extraCmdLineOptions = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "-workers=1" "-dbcache=0" "-logtosderr" ];
+        description = ''
+          Extra command line options to pass to Blockbook.
+          Run blockbook --help to list all available options.
+        '';
+      };
+    };
+  };
+in
+{
+  # interface
+
+  options = {
+    services.blockbook-frontend = mkOption {
+      type = types.attrsOf (types.submodule blockbookOpts);
+      default = {};
+      description = "Specification of one or more blockbook-frontend instances.";
+    };
+  };
+
+  # implementation
+
+  config = mkIf (eachBlockbook != {}) {
+
+    systemd.services = mapAttrs' (blockbookName: cfg: (
+      nameValuePair "blockbook-frontend-${blockbookName}" (
+        let
+          configFile = if cfg.configFile != null then cfg.configFile else
+            pkgs.writeText "config.conf" (builtins.toJSON ( {
+                coin_name = "${cfg.coinName}";
+                rpc_user = "${cfg.rpc.user}";
+                rpc_pass = "${cfg.rpc.password}";
+                rpc_url = "${cfg.rpc.url}:${toString cfg.rpc.port}";
+                message_queue_binding = "${cfg.messageQueueBinding}";
+              } // cfg.extraConfig)
+            );
+        in {
+          description = "blockbook-frontend-${blockbookName} daemon";
+          after = [ "network.target" ];
+          wantedBy = [ "multi-user.target" ];
+          preStart = ''
+            ln -sf ${cfg.templateDir} ${cfg.dataDir}/static/
+            ln -sf ${cfg.cssDir} ${cfg.dataDir}/static/
+            ${optionalString (cfg.rpc.passwordFile != null && cfg.configFile == null) ''
+              CONFIGTMP=$(mktemp)
+              ${pkgs.jq}/bin/jq ".rpc_pass = \"$(cat ${cfg.rpc.passwordFile})\"" ${configFile} > $CONFIGTMP
+              mv $CONFIGTMP ${cfg.dataDir}/${blockbookName}-config.json
+            ''}
+          '';
+          serviceConfig = {
+            User = cfg.user;
+            Group = cfg.group;
+            ExecStart = ''
+               ${cfg.package}/bin/blockbook \
+               ${if (cfg.rpc.passwordFile != null && cfg.configFile == null) then
+               "-blockchaincfg=${cfg.dataDir}/${blockbookName}-config.json"
+               else
+               "-blockchaincfg=${configFile}"
+               } \
+               -datadir=${cfg.dataDir} \
+               ${optionalString (cfg.sync != false) "-sync"} \
+               ${optionalString (cfg.certFile != null) "-certfile=${toString cfg.certFile}"} \
+               ${optionalString (cfg.debug != false) "-debug"} \
+               ${optionalString (cfg.internal != null) "-internal=${toString cfg.internal}"} \
+               ${optionalString (cfg.public != null) "-public=${toString cfg.public}"} \
+               ${toString cfg.extraCmdLineOptions}
+            '';
+            Restart = "on-failure";
+            WorkingDirectory = cfg.dataDir;
+            LimitNOFILE = 65536;
+          };
+        }
+    ) )) eachBlockbook;
+
+    systemd.tmpfiles.rules = flatten (mapAttrsToList (blockbookName: cfg: [
+      "d ${cfg.dataDir} 0750 ${cfg.user} ${cfg.group} - -"
+      "d ${cfg.dataDir}/static 0750 ${cfg.user} ${cfg.group} - -"
+    ]) eachBlockbook);
+
+    users.users = mapAttrs' (blockbookName: cfg: (
+      nameValuePair "blockbook-frontend-${blockbookName}" {
+      name = cfg.user;
+      group = cfg.group;
+      home = cfg.dataDir;
+      isSystemUser = true;
+    })) eachBlockbook;
+
+    users.groups = mapAttrs' (instanceName: cfg: (
+      nameValuePair "${cfg.group}" { })) eachBlockbook;
+  };
+
+  meta.maintainers = with maintainers; [ _1000101 ];
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/corerad.nix b/nixpkgs/nixos/modules/services/networking/corerad.nix
index 5d73c0a0d779..d90a5923bc62 100644
--- a/nixpkgs/nixos/modules/services/networking/corerad.nix
+++ b/nixpkgs/nixos/modules/services/networking/corerad.nix
@@ -77,8 +77,11 @@ in {
         AmbientCapabilities = "CAP_NET_ADMIN CAP_NET_RAW";
         NoNewPrivileges = true;
         DynamicUser = true;
+        Type = "notify";
+        NotifyAccess = "main";
         ExecStart = "${getBin cfg.package}/bin/corerad -c=${cfg.configFile}";
         Restart = "on-failure";
+        RestartKillSignal = "SIGHUP";
       };
     };
   };
diff --git a/nixpkgs/nixos/modules/services/networking/dhcpd.nix b/nixpkgs/nixos/modules/services/networking/dhcpd.nix
index 67f7d8118870..8966deac76cb 100644
--- a/nixpkgs/nixos/modules/services/networking/dhcpd.nix
+++ b/nixpkgs/nixos/modules/services/networking/dhcpd.nix
@@ -11,7 +11,7 @@ let
     ''
       default-lease-time 600;
       max-lease-time 7200;
-      authoritative;
+      ${optionalString (!cfg.authoritative) "not "}authoritative;
       ddns-update-style interim;
       log-facility local1; # see dhcpd.nix
 
@@ -176,6 +176,16 @@ let
       '';
     };
 
+    authoritative = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Whether the DHCP server shall send DHCPNAK messages to misconfigured
+        clients. If this is not done, clients may be unable to get a correct
+        IP address after changing subnets until their old lease has expired.
+      '';
+    };
+
   };
 
 in
diff --git a/nixpkgs/nixos/modules/services/networking/gateone.nix b/nixpkgs/nixos/modules/services/networking/gateone.nix
index 4456a95402ed..56f2ba21a125 100644
--- a/nixpkgs/nixos/modules/services/networking/gateone.nix
+++ b/nixpkgs/nixos/modules/services/networking/gateone.nix
@@ -56,4 +56,4 @@ config = mkIf cfg.enable {
   };
 };
 }
-  
+
diff --git a/nixpkgs/nixos/modules/services/networking/heyefi.nix b/nixpkgs/nixos/modules/services/networking/heyefi.nix
deleted file mode 100644
index fc2b5a848578..000000000000
--- a/nixpkgs/nixos/modules/services/networking/heyefi.nix
+++ /dev/null
@@ -1,82 +0,0 @@
-{ 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/hylafax/options.nix b/nixpkgs/nixos/modules/services/networking/hylafax/options.nix
index 4ac6d3fa8432..9e28d09dffca 100644
--- a/nixpkgs/nixos/modules/services/networking/hylafax/options.nix
+++ b/nixpkgs/nixos/modules/services/networking/hylafax/options.nix
@@ -3,7 +3,7 @@
 let
 
   inherit (lib.options) literalExample mkEnableOption mkOption;
-  inherit (lib.types) bool enum int lines loaOf nullOr path str submodule;
+  inherit (lib.types) bool enum int lines attrsOf nullOr path str submodule;
   inherit (lib.modules) mkDefault mkIf mkMerge;
 
   commonDescr = ''
@@ -248,7 +248,7 @@ in
     };
 
     modems = mkOption {
-      type = loaOf (submodule [ modemConfigOptions ]);
+      type = attrsOf (submodule [ modemConfigOptions ]);
       default = {};
       example.ttyS1 = {
         type = "cirrus";
diff --git a/nixpkgs/nixos/modules/services/networking/jicofo.nix b/nixpkgs/nixos/modules/services/networking/jicofo.nix
new file mode 100644
index 000000000000..160a5fea91a0
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/jicofo.nix
@@ -0,0 +1,152 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.jicofo;
+in
+{
+  options.services.jicofo = with types; {
+    enable = mkEnableOption "Jitsi Conference Focus - component of Jitsi Meet";
+
+    xmppHost = mkOption {
+      type = str;
+      example = "localhost";
+      description = ''
+        Hostname of the XMPP server to connect to.
+      '';
+    };
+
+    xmppDomain = mkOption {
+      type = nullOr str;
+      example = "meet.example.org";
+      description = ''
+        Domain name of the XMMP server to which to connect as a component.
+
+        If null, <option>xmppHost</option> is used.
+      '';
+    };
+
+    componentPasswordFile = mkOption {
+      type = str;
+      example = "/run/keys/jicofo-component";
+      description = ''
+        Path to file containing component secret.
+      '';
+    };
+
+    userName = mkOption {
+      type = str;
+      default = "focus";
+      description = ''
+        User part of the JID for XMPP user connection.
+      '';
+    };
+
+    userDomain = mkOption {
+      type = str;
+      example = "auth.meet.example.org";
+      description = ''
+        Domain part of the JID for XMPP user connection.
+      '';
+    };
+
+    userPasswordFile = mkOption {
+      type = str;
+      example = "/run/keys/jicofo-user";
+      description = ''
+        Path to file containing password for XMPP user connection.
+      '';
+    };
+
+    bridgeMuc = mkOption {
+      type = str;
+      example = "jvbbrewery@internal.meet.example.org";
+      description = ''
+        JID of the internal MUC used to communicate with Videobridges.
+      '';
+    };
+
+    config = mkOption {
+      type = attrsOf str;
+      default = { };
+      example = literalExample ''
+        {
+          "org.jitsi.jicofo.auth.URL" = "XMPP:jitsi-meet.example.com";
+        }
+      '';
+      description = ''
+        Contents of the <filename>sip-communicator.properties</filename> configuration file for jicofo.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.jicofo.config = mapAttrs (_: v: mkDefault v) {
+      "org.jitsi.jicofo.BRIDGE_MUC" = cfg.bridgeMuc;
+    };
+
+    users.groups.jitsi-meet = {};
+
+    systemd.services.jicofo = let
+      jicofoProps = {
+        "-Dnet.java.sip.communicator.SC_HOME_DIR_LOCATION" = "/etc/jitsi";
+        "-Dnet.java.sip.communicator.SC_HOME_DIR_NAME" = "jicofo";
+        "-Djava.util.logging.config.file" = "/etc/jitsi/jicofo/logging.properties";
+      };
+    in
+    {
+      description = "JItsi COnference FOcus";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+
+      restartTriggers = [
+        config.environment.etc."jitsi/jicofo/sip-communicator.properties".source
+      ];
+      environment.JAVA_SYS_PROPS = concatStringsSep " " (mapAttrsToList (k: v: "${k}=${toString v}") jicofoProps);
+
+      script = ''
+        ${pkgs.jicofo}/bin/jicofo \
+          --host=${cfg.xmppHost} \
+          --domain=${if cfg.xmppDomain == null then cfg.xmppHost else cfg.xmppDomain} \
+          --secret=$(cat ${cfg.componentPasswordFile}) \
+          --user_name=${cfg.userName} \
+          --user_domain=${cfg.userDomain} \
+          --user_password=$(cat ${cfg.userPasswordFile})
+      '';
+
+      serviceConfig = {
+        Type = "exec";
+
+        DynamicUser = true;
+        User = "jicofo";
+        Group = "jitsi-meet";
+
+        CapabilityBoundingSet = "";
+        NoNewPrivileges = true;
+        ProtectSystem = "strict";
+        ProtectHome = true;
+        PrivateTmp = true;
+        PrivateDevices = true;
+        ProtectHostname = true;
+        ProtectKernelTunables = true;
+        ProtectKernelModules = true;
+        ProtectControlGroups = true;
+        RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ];
+        RestrictNamespaces = true;
+        LockPersonality = true;
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+      };
+    };
+
+    environment.etc."jitsi/jicofo/sip-communicator.properties".source =
+      pkgs.writeText "sip-communicator.properties" (
+        generators.toKeyValue {} cfg.config
+      );
+    environment.etc."jitsi/jicofo/logging.properties".source =
+      mkDefault "${pkgs.jicofo}/etc/jitsi/jicofo/logging.properties-journal";
+  };
+
+  meta.maintainers = lib.teams.jitsi.members;
+}
diff --git a/nixpkgs/nixos/modules/services/networking/jitsi-videobridge.nix b/nixpkgs/nixos/modules/services/networking/jitsi-videobridge.nix
new file mode 100644
index 000000000000..5482e997a401
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/jitsi-videobridge.nix
@@ -0,0 +1,276 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.jitsi-videobridge;
+  attrsToArgs = a: concatStringsSep " " (mapAttrsToList (k: v: "${k}=${toString v}") a);
+
+  # HOCON is a JSON superset that videobridge2 uses for configuration.
+  # It can substitute environment variables which we use for passwords here.
+  # https://github.com/lightbend/config/blob/master/README.md
+  #
+  # Substitution for environment variable FOO is represented as attribute set
+  # { __hocon_envvar = "FOO"; }
+  toHOCON = x: if isAttrs x && x ? __hocon_envvar then ("\${" + x.__hocon_envvar + "}")
+    else if isAttrs x then "{${ concatStringsSep "," (mapAttrsToList (k: v: ''"${k}":${toHOCON v}'') x) }}"
+    else if isList x then "[${ concatMapStringsSep "," toHOCON x }]"
+    else builtins.toJSON x;
+
+  # We're passing passwords in environment variables that have names generated
+  # from an attribute name, which may not be a valid bash identifier.
+  toVarName = s: "XMPP_PASSWORD_" + stringAsChars (c: if builtins.match "[A-Za-z0-9]" c != null then c else "_") s;
+
+  defaultJvbConfig = {
+    videobridge = {
+      ice = {
+        tcp = {
+          enabled = true;
+          port = 4443;
+        };
+        udp.port = 10000;
+      };
+      stats = {
+        enabled = true;
+        transports = [ { type = "muc"; } ];
+      };
+      apis.xmpp-client.configs = flip mapAttrs cfg.xmppConfigs (name: xmppConfig: {
+        hostname = xmppConfig.hostName;
+        domain = xmppConfig.domain;
+        username = xmppConfig.userName;
+        password = { __hocon_envvar = toVarName name; };
+        muc_jids = xmppConfig.mucJids;
+        muc_nickname = xmppConfig.mucNickname;
+        disable_certificate_verification = xmppConfig.disableCertificateVerification;
+      });
+    };
+  };
+
+  # Allow overriding leaves of the default config despite types.attrs not doing any merging.
+  jvbConfig = recursiveUpdate defaultJvbConfig cfg.config;
+in
+{
+  options.services.jitsi-videobridge = with types; {
+    enable = mkEnableOption "Jitsi Videobridge, a WebRTC compatible video router";
+
+    config = mkOption {
+      type = attrs;
+      default = { };
+      example = literalExample ''
+        {
+          videobridge = {
+            ice.udp.port = 5000;
+            websockets = {
+              enabled = true;
+              server-id = "jvb1";
+            };
+          };
+        }
+      '';
+      description = ''
+        Videobridge configuration.
+
+        See <link xlink:href="https://github.com/jitsi/jitsi-videobridge/blob/master/src/main/resources/reference.conf" />
+        for default configuration with comments.
+      '';
+    };
+
+    xmppConfigs = mkOption {
+      description = ''
+        XMPP servers to connect to.
+
+        See <link xlink:href="https://github.com/jitsi/jitsi-videobridge/blob/master/doc/muc.md" /> for more information.
+      '';
+      default = { };
+      example = literalExample ''
+        {
+          "localhost" = {
+            hostName = "localhost";
+            userName = "jvb";
+            domain = "auth.xmpp.example.org";
+            passwordFile = "/var/lib/jitsi-meet/videobridge-secret";
+            mucJids = "jvbbrewery@internal.xmpp.example.org";
+          };
+        }
+      '';
+      type = attrsOf (submodule ({ name, ... }: {
+        options = {
+          hostName = mkOption {
+            type = str;
+            example = "xmpp.example.org";
+            description = ''
+              Hostname of the XMPP server to connect to. Name of the attribute set is used by default.
+            '';
+          };
+          domain = mkOption {
+            type = nullOr str;
+            default = null;
+            example = "auth.xmpp.example.org";
+            description = ''
+              Domain part of JID of the XMPP user, if it is different from hostName.
+            '';
+          };
+          userName = mkOption {
+            type = str;
+            default = "jvb";
+            description = ''
+              User part of the JID.
+            '';
+          };
+          passwordFile = mkOption {
+            type = str;
+            example = "/run/keys/jitsi-videobridge-xmpp1";
+            description = ''
+              File containing the password for the user.
+            '';
+          };
+          mucJids = mkOption {
+            type = str;
+            example = "jvbbrewery@internal.xmpp.example.org";
+            description = ''
+              JID of the MUC to join. JiCoFo needs to be configured to join the same MUC.
+            '';
+          };
+          mucNickname = mkOption {
+            # Upstream DEBs use UUID, let's use hostname instead.
+            type = str;
+            description = ''
+              Videobridges use the same XMPP account and need to be distinguished by the
+              nickname (aka resource part of the JID). By default, system hostname is used.
+            '';
+          };
+          disableCertificateVerification = mkOption {
+            type = bool;
+            default = false;
+            description = ''
+              Whether to skip validation of the server's certificate.
+            '';
+          };
+        };
+        config = {
+          hostName = mkDefault name;
+          mucNickname = mkDefault (builtins.replaceStrings [ "." ] [ "-" ] (
+            config.networking.hostName + optionalString (config.networking.domain != null) ".${config.networking.domain}"
+          ));
+        };
+      }));
+    };
+
+    nat = {
+      localAddress = mkOption {
+        type = nullOr str;
+        default = null;
+        example = "192.168.1.42";
+        description = ''
+          Local address when running behind NAT.
+        '';
+      };
+
+      publicAddress = mkOption {
+        type = nullOr str;
+        default = null;
+        example = "1.2.3.4";
+        description = ''
+          Public address when running behind NAT.
+        '';
+      };
+    };
+
+    extraProperties = mkOption {
+      type = attrsOf str;
+      default = { };
+      description = ''
+        Additional Java properties passed to jitsi-videobridge.
+      '';
+    };
+
+    openFirewall = mkOption {
+      type = bool;
+      default = false;
+      description = ''
+        Whether to open ports in the firewall for the videobridge.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.groups.jitsi-meet = {};
+
+    services.jitsi-videobridge.extraProperties = optionalAttrs (cfg.nat.localAddress != null) {
+      "org.ice4j.ice.harvest.NAT_HARVESTER_LOCAL_ADDRESS" = cfg.nat.localAddress;
+      "org.ice4j.ice.harvest.NAT_HARVESTER_PUBLIC_ADDRESS" = cfg.nat.publicAddress;
+    };
+
+    systemd.services.jitsi-videobridge2 = let
+      jvbProps = {
+        "-Dnet.java.sip.communicator.SC_HOME_DIR_LOCATION" = "/etc/jitsi";
+        "-Dnet.java.sip.communicator.SC_HOME_DIR_NAME" = "videobridge";
+        "-Djava.util.logging.config.file" = "/etc/jitsi/videobridge/logging.properties";
+        "-Dconfig.file" = pkgs.writeText "jvb.conf" (toHOCON jvbConfig);
+      } // (mapAttrs' (k: v: nameValuePair "-D${k}" v) cfg.extraProperties);
+    in
+    {
+      aliases = [ "jitsi-videobridge.service" ];
+      description = "Jitsi Videobridge";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      environment.JAVA_SYS_PROPS = attrsToArgs jvbProps;
+
+      script = (concatStrings (mapAttrsToList (name: xmppConfig:
+        "export ${toVarName name}=$(cat ${xmppConfig.passwordFile})\n"
+      ) cfg.xmppConfigs))
+      + ''
+        ${pkgs.jitsi-videobridge}/bin/jitsi-videobridge --apis=none
+      '';
+
+      serviceConfig = {
+        Type = "exec";
+
+        DynamicUser = true;
+        User = "jitsi-videobridge";
+        Group = "jitsi-meet";
+
+        CapabilityBoundingSet = "";
+        NoNewPrivileges = true;
+        ProtectSystem = "strict";
+        ProtectHome = true;
+        PrivateTmp = true;
+        PrivateDevices = true;
+        ProtectHostname = true;
+        ProtectKernelTunables = true;
+        ProtectKernelModules = true;
+        ProtectControlGroups = true;
+        RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ];
+        RestrictNamespaces = true;
+        LockPersonality = true;
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+
+        TasksMax = 65000;
+        LimitNPROC = 65000;
+        LimitNOFILE = 65000;
+      };
+    };
+
+    environment.etc."jitsi/videobridge/logging.properties".source =
+      mkDefault "${pkgs.jitsi-videobridge}/etc/jitsi/videobridge/logging.properties-journal";
+
+    # (from videobridge2 .deb)
+    # this sets the max, so that we can bump the JVB UDP single port buffer size.
+    boot.kernel.sysctl."net.core.rmem_max" = mkDefault 10485760;
+    boot.kernel.sysctl."net.core.netdev_max_backlog" = mkDefault 100000;
+
+    networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall
+      [ jvbConfig.videobridge.ice.tcp.port ];
+    networking.firewall.allowedUDPPorts = mkIf cfg.openFirewall
+      [ jvbConfig.videobridge.ice.udp.port ];
+
+    assertions = [{
+      message = "publicAddress must be set if and only if localAddress is set";
+      assertion = (cfg.nat.publicAddress == null) == (cfg.nat.localAddress == null);
+    }];
+  };
+
+  meta.maintainers = lib.teams.jitsi.members;
+}
diff --git a/nixpkgs/nixos/modules/services/networking/kresd.nix b/nixpkgs/nixos/modules/services/networking/kresd.nix
index c5a84eebd46f..ccb34163d5f3 100644
--- a/nixpkgs/nixos/modules/services/networking/kresd.nix
+++ b/nixpkgs/nixos/modules/services/networking/kresd.nix
@@ -129,14 +129,17 @@ in {
     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.
+      # Ensure /run/knot-resolver exists
+      RuntimeDirectory = "knot-resolver";
+      RuntimeDirectoryMode = "0770";
+      # Ensure /var/lib/knot-resolver exists
+      StateDirectory = "knot-resolver";
+      StateDirectoryMode = "0770";
+      # Ensure /var/cache/knot-resolver exists
       CacheDirectory = "knot-resolver";
-      CacheDirectoryMode = "0750";
+      CacheDirectoryMode = "0770";
     };
 
-    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
diff --git a/nixpkgs/nixos/modules/services/networking/monero.nix b/nixpkgs/nixos/modules/services/networking/monero.nix
index 97af29978397..fde3293fc131 100644
--- a/nixpkgs/nixos/modules/services/networking/monero.nix
+++ b/nixpkgs/nixos/modules/services/networking/monero.nix
@@ -87,7 +87,7 @@ in
       };
 
       rpc.password = mkOption {
-        type = types.str;
+        type = types.nullOr types.str;
         default = null;
         description = ''
           Password for RPC connections.
diff --git a/nixpkgs/nixos/modules/services/networking/mstpd.nix b/nixpkgs/nixos/modules/services/networking/mstpd.nix
index 5d1fc4a65427..bd71010ce549 100644
--- a/nixpkgs/nixos/modules/services/networking/mstpd.nix
+++ b/nixpkgs/nixos/modules/services/networking/mstpd.nix
@@ -5,7 +5,7 @@ in
 with lib;
 {
   options.services.mstpd = {
-    
+
     enable = mkOption {
       default = false;
       type = types.bool;
diff --git a/nixpkgs/nixos/modules/services/networking/namecoind.nix b/nixpkgs/nixos/modules/services/networking/namecoind.nix
index 6ca99e1321bd..16f85df2e77c 100644
--- a/nixpkgs/nixos/modules/services/networking/namecoind.nix
+++ b/nixpkgs/nixos/modules/services/networking/namecoind.nix
@@ -89,7 +89,7 @@ in
       };
 
       rpc.password = mkOption {
-        type = types.str;
+        type = types.nullOr types.str;
         default = null;
         description = ''
           Password for RPC connections.
diff --git a/nixpkgs/nixos/modules/services/networking/ncdns.nix b/nixpkgs/nixos/modules/services/networking/ncdns.nix
new file mode 100644
index 000000000000..c1832ad17520
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/ncdns.nix
@@ -0,0 +1,278 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfgs = config.services;
+  cfg  = cfgs.ncdns;
+
+  dataDir  = "/var/lib/ncdns";
+  username = "ncdns";
+
+  valueType = with types; oneOf [ int str bool path ]
+    // { description = "setting type (integer, string, bool or path)"; };
+
+  configType = with types; attrsOf (nullOr (either valueType configType))
+    // { description = ''
+          ncdns.conf configuration type. The format consists of an
+          attribute set of settings. Each setting can be either `null`,
+          a value or an attribute set. The allowed values are integers,
+          strings, booleans or paths.
+         '';
+       };
+
+  configFile = pkgs.runCommand "ncdns.conf"
+    { json = builtins.toJSON cfg.settings;
+      passAsFile = [ "json" ];
+    }
+    "${pkgs.remarshal}/bin/json2toml < $jsonPath > $out";
+
+  defaultFiles = {
+    public  = "${dataDir}/bit.key";
+    private = "${dataDir}/bit.private";
+    zonePublic  = "${dataDir}/bit-zone.key";
+    zonePrivate = "${dataDir}/bit-zone.private";
+  };
+
+  # if all keys are the default value
+  needsKeygen = all id (flip mapAttrsToList cfg.dnssec.keys
+    (n: v: v == getAttr n defaultFiles));
+
+  mkDefaultAttrs = mapAttrs (n: v: mkDefault v);
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.ncdns = {
+
+      enable = mkEnableOption ''
+        ncdns, a Go daemon to bridge Namecoin to DNS.
+        To resolve .bit domains set <literal>services.namecoind.enable = true;</literal>
+        and an RPC username/password
+      '';
+
+      address = mkOption {
+        type = types.str;
+        default = "127.0.0.1";
+        description = ''
+          The IP address the ncdns resolver will bind to.  Leave this unchanged
+          if you do not wish to directly expose the resolver.
+        '';
+      };
+
+      port = mkOption {
+        type = types.port;
+        default = 5333;
+        description = ''
+          The port the ncdns resolver will bind to.
+        '';
+      };
+
+      identity.hostname = mkOption {
+        type = types.str;
+        default = config.networking.hostName;
+        example = "example.com";
+        description = ''
+          The hostname of this ncdns instance, which defaults to the machine
+          hostname. If specified, ncdns lists the hostname as an NS record at
+          the zone apex:
+          <programlisting>
+          bit. IN NS ns1.example.com.
+          </programlisting>
+          If unset ncdns will generate an internal psuedo-hostname under the
+          zone, which will resolve to the value of
+          <option>services.ncdns.identity.address</option>.
+          If you are only using ncdns locally you can ignore this.
+        '';
+      };
+
+      identity.hostmaster = mkOption {
+        type = types.str;
+        default = "";
+        example = "root@example.com";
+        description = ''
+          An email address for the SOA record at the bit zone.
+          If you are only using ncdns locally you can ignore this.
+        '';
+      };
+
+      identity.address = mkOption {
+        type = types.str;
+        default = "127.127.127.127";
+        description = ''
+          The IP address the hostname specified in
+          <option>services.ncdns.identity.hostname</option> should resolve to.
+          If you are only using ncdns locally you can ignore this.
+        '';
+      };
+
+      dnssec.enable = mkEnableOption ''
+        DNSSEC support in ncdns. This will generate KSK and ZSK keypairs
+        (unless provided via the options
+        <option>services.ncdns.dnssec.publicKey</option>,
+        <option>services.ncdns.dnssec.privateKey</option> etc.) and add a trust
+        anchor to recursive resolvers
+      '';
+
+      dnssec.keys.public = mkOption {
+        type = types.path;
+        default = defaultFiles.public;
+        description = ''
+          Path to the file containing the KSK public key.
+          The key can be generated using the <literal>dnssec-keygen</literal>
+          command, provided by the package <package>bind</package> as follows:
+          <programlisting>
+          $ dnssec-keygen -a RSASHA256 -3 -b 2048 -f KSK bit
+          </programlisting>
+        '';
+      };
+
+      dnssec.keys.private = mkOption {
+        type = types.path;
+        default = defaultFiles.private;
+        description = ''
+          Path to the file containing the KSK private key.
+        '';
+      };
+
+      dnssec.keys.zonePublic = mkOption {
+        type = types.path;
+        default = defaultFiles.zonePublic;
+        description = ''
+          Path to the file containing the ZSK public key.
+          The key can be generated using the <literal>dnssec-keygen</literal>
+          command, provided by the package <package>bind</package> as follows:
+          <programlisting>
+          $ dnssec-keygen -a RSASHA256 -3 -b 2048 bit
+          </programlisting>
+        '';
+      };
+
+      dnssec.keys.zonePrivate = mkOption {
+        type = types.path;
+        default = defaultFiles.zonePrivate;
+        description = ''
+          Path to the file containing the ZSK private key.
+        '';
+      };
+
+      settings = mkOption {
+        type = configType;
+        default = { };
+        example = literalExample ''
+          { # enable webserver
+            ncdns.httplistenaddr = ":8202";
+
+            # synchronize TLS certs
+            certstore.nss = true;
+            # note: all paths are relative to the config file
+            certstore.nsscertdir =  "../../var/lib/ncdns";
+            certstore.nssdbdir = "../../home/alice/.pki/nssdb";
+          }
+        '';
+        description = ''
+          ncdns settings. Use this option to configure ncds
+          settings not exposed in a NixOS option or to bypass one.
+          See the example ncdns.conf file at <link xlink:href="
+          https://git.io/JfX7g"/> for the available options.
+        '';
+      };
+
+    };
+
+    services.pdns-recursor.resolveNamecoin = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Resolve <literal>.bit</literal> top-level domains using ncdns and namecoin.
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    services.pdns-recursor = mkIf cfgs.pdns-recursor.resolveNamecoin {
+      forwardZonesRecurse.bit = "127.0.0.1:${toString cfg.port}";
+      luaConfig =
+        if cfg.dnssec.enable
+          then ''readTrustAnchorsFromFile("${cfg.dnssec.keys.public}")''
+          else ''addNTA("bit", "namecoin DNSSEC disabled")'';
+    };
+
+    # Avoid pdns-recursor not finding the DNSSEC keys
+    systemd.services.pdns-recursor = mkIf cfgs.pdns-recursor.resolveNamecoin {
+      after = [ "ncdns.service" ];
+      wants = [ "ncdns.service" ];
+    };
+
+    services.ncdns.settings = mkDefaultAttrs {
+      ncdns =
+        { # Namecoin RPC
+          namecoinrpcaddress =
+            "${cfgs.namecoind.rpc.address}:${toString cfgs.namecoind.rpc.port}";
+          namecoinrpcusername = cfgs.namecoind.rpc.user;
+          namecoinrpcpassword = cfgs.namecoind.rpc.password;
+
+          # Identity
+          selfname = cfg.identity.hostname;
+          hostmaster = cfg.identity.hostmaster;
+          selfip = cfg.identity.address;
+
+          # Other
+          bind = "${cfg.address}:${toString cfg.port}";
+        }
+        // optionalAttrs cfg.dnssec.enable
+        { # DNSSEC
+          publickey  = "../.." + cfg.dnssec.keys.public;
+          privatekey = "../.." + cfg.dnssec.keys.private;
+          zonepublickey  = "../.." + cfg.dnssec.keys.zonePublic;
+          zoneprivatekey = "../.." + cfg.dnssec.keys.zonePrivate;
+        };
+
+        # Daemon
+        service.daemon = true;
+        xlog.journal = true;
+    };
+
+    users.users.ncdns =
+      { description = "ncdns daemon user"; };
+
+    systemd.services.ncdns = {
+      description = "ncdns daemon";
+      after    = [ "namecoind.service" ];
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        User = "ncdns";
+        StateDirectory = "ncdns";
+        Restart = "on-failure";
+        ExecStart = "${pkgs.ncdns}/bin/ncdns -conf=${configFile}";
+      };
+
+      preStart = optionalString (cfg.dnssec.enable && needsKeygen) ''
+        cd ${dataDir}
+        if [ ! -e bit.key ]; then
+          ${pkgs.bind}/bin/dnssec-keygen -a RSASHA256 -3 -b 2048 bit
+          mv Kbit.*.key bit-zone.key
+          mv Kbit.*.private bit-zone.private
+          ${pkgs.bind}/bin/dnssec-keygen -a RSASHA256 -3 -b 2048 -f KSK bit
+          mv Kbit.*.key bit.key
+          mv Kbit.*.private bit.private
+        fi
+      '';
+    };
+
+  };
+
+  meta.maintainers = with lib.maintainers; [ rnhmjoj ];
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/networkmanager.nix b/nixpkgs/nixos/modules/services/networking/networkmanager.nix
index cc789897b29f..17c549d42c32 100644
--- a/nixpkgs/nixos/modules/services/networking/networkmanager.nix
+++ b/nixpkgs/nixos/modules/services/networking/networkmanager.nix
@@ -458,7 +458,7 @@ in {
 
     systemd.services.NetworkManager-dispatcher = {
       wantedBy = [ "network.target" ];
-      restartTriggers = [ configFile ];
+      restartTriggers = [ configFile overrideNameserversScript ];
 
       # useful binaries for user-specified hooks
       path = [ pkgs.iproute pkgs.utillinux pkgs.coreutils ];
diff --git a/nixpkgs/nixos/modules/services/networking/nextdns.nix b/nixpkgs/nixos/modules/services/networking/nextdns.nix
new file mode 100644
index 000000000000..a633bff62ec7
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/nextdns.nix
@@ -0,0 +1,44 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.nextdns;
+in {
+  options = {
+    services.nextdns = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to enable the NextDNS DNS/53 to DoH Proxy service.";
+      };
+      arguments = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "-config" "10.0.3.0/24=abcdef" ];
+        description = "Additional arguments to be passed to nextdns run.";
+      };
+    };
+  };
+
+  # https://github.com/nextdns/nextdns/blob/628ea509eaaccd27adb66337db03e5b56f6f38a8/host/service/systemd/service.go
+  config = mkIf cfg.enable {
+    systemd.services.nextdns = {
+      description = "NextDNS DNS/53 to DoH Proxy";
+      environment = {
+        SERVICE_RUN_MODE = "1";
+      };
+      serviceConfig = {
+        StartLimitInterval = 5;
+        StartLimitBurst = 10;
+        ExecStart = "${pkgs.nextdns}/bin/nextdns run ${escapeShellArgs config.services.nextdns.arguments}";
+        RestartSec = 120;
+        LimitMEMLOCK = "infinity";
+      };
+      after = [ "network.target" ];
+      before = [ "nss-lookup.target" ];
+      wants = [ "nss-lookup.target" ];
+      wantedBy = [ "multi-user.target" ];
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/nghttpx/default.nix b/nixpkgs/nixos/modules/services/networking/nghttpx/default.nix
index 881a2670f5db..b8a0a24e3aad 100644
--- a/nixpkgs/nixos/modules/services/networking/nghttpx/default.nix
+++ b/nixpkgs/nixos/modules/services/networking/nghttpx/default.nix
@@ -60,7 +60,7 @@ let
       # 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
@@ -90,7 +90,7 @@ in
 { imports = [
     ./nghttpx-options.nix
   ];
-  
+
   config = lib.mkIf cfg.enable {
 
     users.groups.nghttpx = { };
@@ -98,7 +98,7 @@ in
       group = config.users.groups.nghttpx.name;
       isSystemUser = true;
     };
-      
+
 
     systemd.services = {
       nghttpx = {
diff --git a/nixpkgs/nixos/modules/services/networking/nsd.nix b/nixpkgs/nixos/modules/services/networking/nsd.nix
index 6e3eed0c5570..3ecbd06ee416 100644
--- a/nixpkgs/nixos/modules/services/networking/nsd.nix
+++ b/nixpkgs/nixos/modules/services/networking/nsd.nix
@@ -11,8 +11,6 @@ let
 
   # 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;
@@ -897,7 +895,10 @@ in
               + "want, please enable 'services.nsd.rootServer'.";
     };
 
-    environment.systemPackages = [ nsdPkg ];
+    environment = {
+      systemPackages = [ nsdPkg ];
+      etc."nsd/nsd.conf".source = "${configFile}/nsd.conf";
+    };
 
     users.groups.${username}.gid = config.ids.gids.nsd;
 
diff --git a/nixpkgs/nixos/modules/services/networking/ntp/chrony.nix b/nixpkgs/nixos/modules/services/networking/ntp/chrony.nix
index b7e4c89a155c..78de50583f34 100644
--- a/nixpkgs/nixos/modules/services/networking/ntp/chrony.nix
+++ b/nixpkgs/nixos/modules/services/networking/ntp/chrony.nix
@@ -117,7 +117,6 @@ in
             ProtectHome = "yes";
             ProtectSystem = "full";
             PrivateTmp = "yes";
-            StateDirectory = "chrony";
           };
 
       };
diff --git a/nixpkgs/nixos/modules/services/networking/nylon.nix b/nixpkgs/nixos/modules/services/networking/nylon.nix
index 7c171281a926..bfc358cb12fb 100644
--- a/nixpkgs/nixos/modules/services/networking/nylon.nix
+++ b/nixpkgs/nixos/modules/services/networking/nylon.nix
@@ -140,7 +140,7 @@ in
     services.nylon = mkOption {
       default = {};
       description = "Collection of named nylon instances";
-      type = with types; loaOf (submodule nylonOpts);
+      type = with types; attrsOf (submodule nylonOpts);
       internal = true;
     };
 
diff --git a/nixpkgs/nixos/modules/services/networking/onedrive.nix b/nixpkgs/nixos/modules/services/networking/onedrive.nix
new file mode 100644
index 000000000000..c52f920bae25
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/onedrive.nix
@@ -0,0 +1,72 @@
+{ config, lib, pkgs, ... }:
+let
+  cfg = config.services.onedrive;
+
+  onedriveLauncher =  pkgs.writeShellScriptBin
+    "onedrive-launcher"
+    ''
+      # XDG_CONFIG_HOME is not recognized in the environment here.
+      if [ -f $HOME/.config/onedrive-launcher ]
+      then
+        # Hopefully using underscore boundary helps locate variables
+        for _onedrive_config_dirname_ in $(cat $HOME/.config/onedrive-launcher | grep -v '[ \t]*#' )
+        do
+          systemctl --user start onedrive@$_onedrive_config_dirname_
+        done
+      else
+        systemctl --user start onedrive@onedrive
+      fi
+    ''
+  ;
+
+in {
+  ### Documentation
+  # meta.doc = ./onedrive.xml;
+
+  ### Interface
+
+  options.services.onedrive = {
+    enable = lib.mkOption {
+      type = lib.types.bool;
+      default = false;
+      description = "Enable OneDrive service";
+    };
+
+     package = lib.mkOption {
+       type = lib.types.package;
+       default = pkgs.onedrive;
+       defaultText = "pkgs.onedrive";
+       example = lib.literalExample "pkgs.onedrive";
+       description = ''
+         OneDrive package to use.
+       '';
+     };
+  };
+### Implementation
+
+  config = lib.mkIf cfg.enable {
+    environment.systemPackages = [ cfg.package ];
+
+    systemd.user.services."onedrive@" = {
+      description = "Onedrive sync service";
+
+      serviceConfig = {
+        Type = "simple";
+        ExecStart = ''
+          ${cfg.package}/bin/onedrive --monitor --confdir=%h/.config/%i
+        '';
+        Restart="on-failure";
+        RestartSec=3;
+        RestartPreventExitStatus=3;
+      };
+    };
+
+    systemd.user.services.onedrive-launcher = {
+      wantedBy = [ "default.target" ];
+      serviceConfig = {
+        Type = "oneshot";
+        ExecStart = "${onedriveLauncher}/bin/onedrive-launcher";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/onedrive.xml b/nixpkgs/nixos/modules/services/networking/onedrive.xml
new file mode 100644
index 000000000000..5a9dcf01aeee
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/onedrive.xml
@@ -0,0 +1,34 @@
+<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="onedrive">
+ <title>Microsoft OneDrive</title>
+ <para>
+  Microsoft Onedrive is a popular cloud file-hosting service, used by 85% of Fortune 500 companies. NixOS uses a popular OneDrive client for Linux maintained by github user abraunegg. The Linux client is excellent and allows customization of which files or paths to download, not much unlike the default Windows OneDrive client by Microsoft itself. The client allows syncing with multiple onedrive accounts at the same time, of any type- OneDrive personal, OneDrive business, Office365 and Sharepoint libraries, without any additional charge.
+ </para>
+ <para>
+  For more information, guides and documentation, see <link xlink:href="https://abraunegg.github.io/"/>.
+ </para>
+ <para>
+  To enable OneDrive support, add the following to your <filename>configuration.nix</filename>:
+<programlisting>
+<xref linkend="opt-services.onedrive.enable"/> = true;
+</programlisting>
+  This installs the <literal>onedrive</literal> package and a service <literal>onedriveLauncher</literal> which will instantiate a <literal>onedrive</literal> service for all your OneDrive accounts. Follow the steps in documentation of the onedrive client to setup your accounts. To use the service with multiple accounts, create a file named <filename>onedrive-launcher</filename> in <filename>~/.config</filename> and add the filename of the config directory, relative to <filename>~/.config</filename>. For example, if you have two OneDrive accounts with configs in <filename>~/.config/onedrive_bob_work</filename> and <filename>~/.config/onedrive_bob_personal</filename>, add the following lines:
+<programlisting>
+onedrive_bob_work
+# Not in use:
+# onedrive_bob_office365
+onedrive_bob_personal
+</programlisting>
+  No such file needs to be created if you are using only a single OneDrive account with config in the default location <filename>~/.config/onedrive</filename>, in the absence of <filename>~/.config/onedrive-launcher</filename>, only a single service is instantiated, with default config path.
+</para>
+
+  <para>
+  If you wish to use a custom OneDrive package, say from another channel, add the following line:
+<programlisting>
+<xref linkend="opt-services.onedrive.package"/> = pkgs.unstable.onedrive;
+</programlisting>
+ </para>
+</chapter>
diff --git a/nixpkgs/nixos/modules/services/networking/openvpn.nix b/nixpkgs/nixos/modules/services/networking/openvpn.nix
index dcd7e9e5fa4c..650f9c84ac72 100644
--- a/nixpkgs/nixos/modules/services/networking/openvpn.nix
+++ b/nixpkgs/nixos/modules/services/networking/openvpn.nix
@@ -11,7 +11,7 @@ let
   makeOpenVPNJob = cfg: name:
     let
 
-      path = (getAttr "openvpn-${name}" config.systemd.services).path;
+      path = makeBinPath (getAttr "openvpn-${name}" config.systemd.services).path;
 
       upScript = ''
         #! /bin/sh
diff --git a/nixpkgs/nixos/modules/services/networking/prosody.nix b/nixpkgs/nixos/modules/services/networking/prosody.nix
index cdd341c9fb62..a6c1cb0f4797 100644
--- a/nixpkgs/nixos/modules/services/networking/prosody.nix
+++ b/nixpkgs/nixos/modules/services/networking/prosody.nix
@@ -655,7 +655,7 @@ in
 
         description = "Define the virtual hosts";
 
-        type = with types; loaOf (submodule vHostOpts);
+        type = with types; attrsOf (submodule vHostOpts);
 
         example = {
           myhost = {
@@ -772,7 +772,7 @@ in
       };
 
       disco_items = {
-      ${ lib.concatStringsSep "\n" (builtins.map (x: ''{ "${x.url}", "${x.description}"};'') discoItems)} 
+      ${ lib.concatStringsSep "\n" (builtins.map (x: ''{ "${x.url}", "${x.description}"};'') discoItems)}
       };
 
       allow_registration = ${toLua cfg.allowRegistration}
diff --git a/nixpkgs/nixos/modules/services/networking/prosody.xml b/nixpkgs/nixos/modules/services/networking/prosody.xml
index 7859cb1578b7..471240cd1475 100644
--- a/nixpkgs/nixos/modules/services/networking/prosody.xml
+++ b/nixpkgs/nixos/modules/services/networking/prosody.xml
@@ -43,10 +43,10 @@ services.prosody = {
   <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.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";
@@ -65,7 +65,7 @@ services.prosody = {
    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.
+   <link linkend="opt-security.acme.certs._name_.extraDomainNames">extraDomainNames</link> module option.
  </para>
  <para>
    Provided the setup detailed in the previous section, you'll need the following acme configuration to generate
@@ -78,8 +78,7 @@ security.acme = {
     "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;
+      <link linkend="opt-security.acme.certs._name_.extraDomainNames">extraDomainNames</link> = [ "conference.example.org" "upload.example.org" ];
     };
   };
 };</programlisting>
diff --git a/nixpkgs/nixos/modules/services/networking/radicale.nix b/nixpkgs/nixos/modules/services/networking/radicale.nix
index 30bf22586f86..5af035fd59e0 100644
--- a/nixpkgs/nixos/modules/services/networking/radicale.nix
+++ b/nixpkgs/nixos/modules/services/networking/radicale.nix
@@ -8,8 +8,10 @@ let
 
   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 {
+  defaultPackage = if versionAtLeast config.system.stateVersion "20.09" then {
+    pkg = pkgs.radicale3;
+    text = "pkgs.radicale3";
+  } else if versionAtLeast config.system.stateVersion "17.09" then {
     pkg = pkgs.radicale2;
     text = "pkgs.radicale2";
   } else {
@@ -35,8 +37,9 @@ in
       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.
+        <literal>system.stateVersion &lt; 17.09</literal>, version 2.x if
+        <literal>17.09 ≤ system.stateVersion &lt; 20.09</literal>, and
+        version 3.x otherwise.
       '';
     };
 
diff --git a/nixpkgs/nixos/modules/services/networking/resilio.nix b/nixpkgs/nixos/modules/services/networking/resilio.nix
index e74e03fc0b07..6193d7340fc4 100644
--- a/nixpkgs/nixos/modules/services/networking/resilio.nix
+++ b/nixpkgs/nixos/modules/services/networking/resilio.nix
@@ -30,12 +30,12 @@ let
     download_limit = cfg.downloadLimit;
     upload_limit = cfg.uploadLimit;
     lan_encrypt_data = cfg.encryptLAN;
-  } // optionalAttrs cfg.enableWebUI {
+  } // optionalAttrs (cfg.directoryRoot != "") { directory_root = cfg.directoryRoot; }
+    // 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 (cfg.apiKey != "") { api_key = cfg.apiKey; });
   } // optionalAttrs (sharedFoldersRecord != []) {
     shared_folders = sharedFoldersRecord;
   }));
@@ -109,8 +109,8 @@ in
 
       httpListenAddr = mkOption {
         type = types.str;
-        default = "0.0.0.0";
-        example = "1.2.3.4";
+        default = "[::1]";
+        example = "0.0.0.0";
         description = ''
           HTTP address to bind to.
         '';
@@ -206,16 +206,16 @@ in
 
           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.
+          member of the <literal>rslsync</literal> group.
 
           Directories in this list should be in the
-          <literal>resilio</literal> group, and that group must have
+          <literal>rslsync</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
+          the <literal>rslsync</literal> group. Also,
+          <literal>setfacl -d -m group:rslsync:rwx</literal> and
+          <literal>setfacl -m group:rslsync:rwx</literal> should also
           be applied so that the sub directories are writable by
           the group.
         '';
diff --git a/nixpkgs/nixos/modules/services/networking/robustirc-bridge.nix b/nixpkgs/nixos/modules/services/networking/robustirc-bridge.nix
new file mode 100644
index 000000000000..255af79ec04b
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/robustirc-bridge.nix
@@ -0,0 +1,47 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.robustirc-bridge;
+in
+{
+  options = {
+    services.robustirc-bridge = {
+      enable = mkEnableOption "RobustIRC bridge";
+
+      extraFlags = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''Extra flags passed to the <command>robustirc-bridge</command> command. See <link xlink:href="https://robustirc.net/docs/adminguide.html#_bridge">RobustIRC Documentation</link> or robustirc-bridge(1) for details.'';
+        example = [
+          "-network robustirc.net"
+        ];
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.robustirc-bridge = {
+      description = "RobustIRC bridge";
+      documentation = [
+        "man:robustirc-bridge(1)"
+        "https://robustirc.net/"
+      ];
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+
+      serviceConfig = {
+        DynamicUser = true;
+        ExecStart = "${pkgs.robustirc-bridge}/bin/robustirc-bridge ${concatStringsSep " " cfg.extraFlags}";
+        Restart = "on-failure";
+
+        # Hardening
+        PrivateDevices = true;
+        ProtectSystem = true;
+        ProtectHome = true;
+        PrivateTmp = true;
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/seeks.nix b/nixpkgs/nixos/modules/services/networking/seeks.nix
deleted file mode 100644
index 40729225b6d0..000000000000
--- a/nixpkgs/nixos/modules/services/networking/seeks.nix
+++ /dev/null
@@ -1,75 +0,0 @@
-{ 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
index af12db590f00..d2541f9a6dff 100644
--- a/nixpkgs/nixos/modules/services/networking/shadowsocks.nix
+++ b/nixpkgs/nixos/modules/services/networking/shadowsocks.nix
@@ -11,8 +11,13 @@ let
     method = cfg.encryptionMethod;
     mode = cfg.mode;
     user = "nobody";
-    fast_open = true;
-  } // optionalAttrs (cfg.password != null) { password = cfg.password; };
+    fast_open = cfg.fastOpen;
+  } // optionalAttrs (cfg.plugin != null) {
+    plugin = cfg.plugin;
+    plugin_opts = cfg.pluginOpts;
+  } // optionalAttrs (cfg.password != null) {
+    password = cfg.password;
+  } // cfg.extraConfig;
 
   configFile = pkgs.writeText "shadowsocks.json" (builtins.toJSON opts);
 
@@ -74,6 +79,14 @@ in
         '';
       };
 
+      fastOpen = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          use TCP fast-open
+        '';
+      };
+
       encryptionMethod = mkOption {
         type = types.str;
         default = "chacha20-ietf-poly1305";
@@ -82,6 +95,41 @@ in
         '';
       };
 
+      plugin = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "\${pkgs.shadowsocks-v2ray-plugin}/bin/v2ray-plugin";
+        description = ''
+          SIP003 plugin for shadowsocks
+        '';
+      };
+
+      pluginOpts = mkOption {
+        type = types.str;
+        default = "";
+        example = "server;host=example.com";
+        description = ''
+          Options to pass to the plugin if one was specified
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.attrs;
+        default = {};
+        example = ''
+          {
+            nameserver = "8.8.8.8";
+          }
+        '';
+        description = ''
+          Additional configuration for shadowsocks that is not covered by the
+          provided options. The provided attrset will be serialized to JSON and
+          has to contain valid shadowsocks options. Unfortunately most
+          additional options are undocumented but it's easy to find out what is
+          available by looking into the source code of
+          <link xlink:href="https://github.com/shadowsocks/shadowsocks-libev/blob/master/src/jconf.c"/>
+        '';
+      };
     };
 
   };
@@ -99,7 +147,7 @@ in
       description = "shadowsocks-libev Daemon";
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
-      path = [ pkgs.shadowsocks-libev ] ++ optional (cfg.passwordFile != null) pkgs.jq;
+      path = [ pkgs.shadowsocks-libev ] ++ optional (cfg.plugin != null) cfg.plugin ++ optional (cfg.passwordFile != null) pkgs.jq;
       serviceConfig.PrivateTmp = true;
       script = ''
         ${optionalString (cfg.passwordFile != null) ''
diff --git a/nixpkgs/nixos/modules/services/networking/skydns.nix b/nixpkgs/nixos/modules/services/networking/skydns.nix
index e79d6de92644..ea466de93275 100644
--- a/nixpkgs/nixos/modules/services/networking/skydns.nix
+++ b/nixpkgs/nixos/modules/services/networking/skydns.nix
@@ -64,7 +64,7 @@ in {
     extraConfig = mkOption {
       default = {};
       type = types.attrsOf types.str;
-      description = "Skydns attribute set of extra config options passed as environemnt variables.";
+      description = "Skydns attribute set of extra config options passed as environment variables.";
     };
   };
 
diff --git a/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix b/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix
index a294bbfba0aa..d0251277e9e8 100644
--- a/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix
+++ b/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix
@@ -232,6 +232,14 @@ in
         '';
       };
 
+      banner = mkOption {
+        type = types.nullOr types.lines;
+        default = null;
+        description = ''
+          Message to display to the remote user before authentication is allowed.
+        '';
+      };
+
       authorizedKeysFiles = mkOption {
         type = types.listOf types.str;
         default = [];
@@ -368,7 +376,7 @@ in
     };
 
     users.users = mkOption {
-      type = with types; loaOf (submodule userOptions);
+      type = with types; attrsOf (submodule userOptions);
     };
 
   };
@@ -485,6 +493,8 @@ in
       ''
         UsePAM yes
 
+        Banner ${if cfg.banner == null then "none" else pkgs.writeText "ssh_banner" cfg.banner}
+
         AddressFamily ${if config.networking.enableIPv6 then "any" else "inet"}
         ${concatMapStrings (port: ''
           Port ${toString port}
diff --git a/nixpkgs/nixos/modules/services/networking/sslh.nix b/nixpkgs/nixos/modules/services/networking/sslh.nix
index c4fa370a5fef..0921febba668 100644
--- a/nixpkgs/nixos/modules/services/networking/sslh.nix
+++ b/nixpkgs/nixos/modules/services/networking/sslh.nix
@@ -15,7 +15,11 @@ let
 
     listen:
     (
-      { host: "${cfg.listenAddress}"; port: "${toString cfg.port}"; }
+      ${
+        concatMapStringsSep ",\n"
+        (addr: ''{ host: "${addr}"; port: "${toString cfg.port}"; }'')
+        cfg.listenAddresses
+      }
     );
 
     ${cfg.appendConfig}
@@ -33,6 +37,10 @@ let
   '';
 in
 {
+  imports = [
+    (mkRenamedOptionModule [ "services" "sslh" "listenAddress" ] [ "services" "sslh" "listenAddresses" ])
+  ];
+
   options = {
     services.sslh = {
       enable = mkEnableOption "sslh";
@@ -55,10 +63,10 @@ in
         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.";
+      listenAddresses = mkOption {
+        type = types.coercedTo types.str singleton (types.listOf types.str);
+        default = [ "0.0.0.0" "[::]" ];
+        description = "Listening addresses or hostnames.";
       };
 
       port = mkOption {
diff --git a/nixpkgs/nixos/modules/services/networking/supplicant.nix b/nixpkgs/nixos/modules/services/networking/supplicant.nix
index b5b9989ce186..20704be9b36f 100644
--- a/nixpkgs/nixos/modules/services/networking/supplicant.nix
+++ b/nixpkgs/nixos/modules/services/networking/supplicant.nix
@@ -76,9 +76,9 @@ in
     networking.supplicant = mkOption {
       type = with types; attrsOf (submodule {
         options = {
-  
+
           configFile = {
-  
+
             path = mkOption {
               type = types.nullOr types.path;
               default = null;
@@ -89,7 +89,7 @@ in
                 precedence over options defined in <literal>configFile</literal>.
               '';
             };
-  
+
             writable = mkOption {
               type = types.bool;
               default = false;
@@ -98,9 +98,9 @@ in
                 <literal>wpa_supplicant</literal>.
               '';
             };
-  
+
           };
-  
+
           extraConf = mkOption {
             type = types.lines;
             default = "";
@@ -126,7 +126,7 @@ in
               use the <literal>configFile</literal> instead.
             '';
           };
-  
+
           extraCmdArgs = mkOption {
             type = types.str;
             default = "";
@@ -134,21 +134,21 @@ in
             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;
@@ -159,20 +159,20 @@ in
                 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.";
             };
-  
+
           };
         };
       });
diff --git a/nixpkgs/nixos/modules/services/networking/syncthing.nix b/nixpkgs/nixos/modules/services/networking/syncthing.nix
index e717d78feed5..28348c7893a0 100644
--- a/nixpkgs/nixos/modules/services/networking/syncthing.nix
+++ b/nixpkgs/nixos/modules/services/networking/syncthing.nix
@@ -18,6 +18,7 @@ let
     fsWatcherEnabled = folder.watch;
     fsWatcherDelayS = folder.watchDelay;
     ignorePerms = folder.ignorePerms;
+    ignoreDelete = folder.ignoreDelete;
     versioning = folder.versioning;
   }) (filterAttrs (
     _: folder:
@@ -284,8 +285,6 @@ in {
                 });
               };
 
-
-
               rescanInterval = mkOption {
                 type = types.int;
                 default = 3600;
@@ -327,6 +326,16 @@ in {
                 '';
               };
 
+              ignoreDelete = mkOption {
+                type = types.bool;
+                default = false;
+                description = ''
+                  Whether to delete files in destination. See <link
+                  xlink:href="https://docs.syncthing.net/advanced/folder-ignoredelete.html">
+                  upstream's docs</link>.
+                '';
+              };
+
             };
           }));
         };
diff --git a/nixpkgs/nixos/modules/services/networking/tinc.nix b/nixpkgs/nixos/modules/services/networking/tinc.nix
index e98aafc20937..725bd9bf9403 100644
--- a/nixpkgs/nixos/modules/services/networking/tinc.nix
+++ b/nixpkgs/nixos/modules/services/networking/tinc.nix
@@ -48,6 +48,14 @@ in
               '';
             };
 
+            rsaPrivateKeyFile = mkOption {
+              default = null;
+              type = types.nullOr types.path;
+              description = ''
+                Path of the private RSA keyfile.
+              '';
+            };
+
             debugLevel = mkOption {
               default = 0;
               type = types.addCheck types.int (l: l >= 0 && l <= 5);
@@ -139,6 +147,7 @@ in
               Name = ${if data.name == null then "$HOST" else data.name}
               DeviceType = ${data.interfaceType}
               ${optionalString (data.ed25519PrivateKeyFile != null) "Ed25519PrivateKeyFile = ${data.ed25519PrivateKeyFile}"}
+              ${optionalString (data.rsaPrivateKeyFile != null) "PrivateKeyFile = ${data.rsaPrivateKeyFile}"}
               ${optionalString (data.listenAddress != null) "ListenAddress = ${data.listenAddress}"}
               ${optionalString (data.bindToAddress != null) "BindToAddress = ${data.bindToAddress}"}
               Interface = tinc.${network}
@@ -170,12 +179,15 @@ in
           # 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 ''
+          ${if data.ed25519PrivateKeyFile != null then "  # ed25519 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
+          ${if data.rsaPrivateKeyFile != null then "  # RSA Keyfile managed by nix" else ''
             [ -f "/etc/tinc/${network}/rsa_key.priv" ] || tinc -n ${network} generate-rsa-keys 4096
+          ''}
+            # In case there isn't anything to do
+            true
           else
             # Tinc 1.0 uses the tincd application
             [ -f "/etc/tinc/${network}/rsa_key.priv" ] || tincd -n ${network} -K 4096
diff --git a/nixpkgs/nixos/modules/services/networking/trickster.nix b/nixpkgs/nixos/modules/services/networking/trickster.nix
index 8760dd5a9382..49c945adb80f 100644
--- a/nixpkgs/nixos/modules/services/networking/trickster.nix
+++ b/nixpkgs/nixos/modules/services/networking/trickster.nix
@@ -106,7 +106,8 @@ in
         Restart = "always";
       };
     };
+  };
 
-  };  
-}
+  meta.maintainers = with maintainers; [ _1000101 ];
 
+}
diff --git a/nixpkgs/nixos/modules/services/networking/unifi.nix b/nixpkgs/nixos/modules/services/networking/unifi.nix
index 4bdfa8143dce..62bcf7a14972 100644
--- a/nixpkgs/nixos/modules/services/networking/unifi.nix
+++ b/nixpkgs/nixos/modules/services/networking/unifi.nix
@@ -162,6 +162,8 @@ in
       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";
+      # Make sure package upgrades trigger a service restart
+      restartTriggers = [ cfg.unifiPackage cfg.mongodbPackage ];
 
       serviceConfig = {
         Type = "simple";
diff --git a/nixpkgs/nixos/modules/services/networking/wasabibackend.nix b/nixpkgs/nixos/modules/services/networking/wasabibackend.nix
new file mode 100644
index 000000000000..6eacffe709b0
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/wasabibackend.nix
@@ -0,0 +1,158 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.services.wasabibackend;
+
+  inherit (lib) mkEnableOption mkIf mkOption optionalAttrs optionalString types;
+
+  confOptions = {
+      BitcoinRpcConnectionString = "${cfg.rpc.user}:${cfg.rpc.password}";
+  } // optionalAttrs (cfg.network == "mainnet") {
+      Network = "Main";
+      MainNetBitcoinP2pEndPoint = "${cfg.endpoint.ip}:${toString cfg.endpoint.port}";
+      MainNetBitcoinCoreRpcEndPoint = "${cfg.rpc.ip}:${toString cfg.rpc.port}";
+  } // optionalAttrs (cfg.network == "testnet") {
+      Network = "TestNet";
+      TestNetBitcoinP2pEndPoint = "${cfg.endpoint.ip}:${toString cfg.endpoint.port}";
+      TestNetBitcoinCoreRpcEndPoint = "${cfg.rpc.ip}:${toString cfg.rpc.port}";
+  } // optionalAttrs (cfg.network == "regtest") {
+      Network = "RegTest";
+      RegTestBitcoinP2pEndPoint = "${cfg.endpoint.ip}:${toString cfg.endpoint.port}";
+      RegTestBitcoinCoreRpcEndPoint = "${cfg.rpc.ip}:${toString cfg.rpc.port}";
+  };
+
+	configFile = pkgs.writeText "wasabibackend.conf" (builtins.toJSON confOptions);
+
+in {
+
+  options = {
+
+    services.wasabibackend = {
+      enable = mkEnableOption "Wasabi backend service";
+
+      dataDir = mkOption {
+        type = types.path;
+        default = "/var/lib/wasabibackend";
+        description = "The data directory for the Wasabi backend node.";
+      };
+
+      customConfigFile = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        description = "Defines the path to a custom configuration file that is copied to the user's directory. Overrides any config options.";
+      };
+
+      network = mkOption {
+        type = types.enum [ "mainnet" "testnet" "regtest" ];
+        default = "mainnet";
+        description = "The network to use for the Wasabi backend service.";
+      };
+
+      endpoint = {
+        ip = mkOption {
+          type = types.str;
+          default = "127.0.0.1";
+          description = "IP address for P2P connection to bitcoind.";
+        };
+
+        port = mkOption {
+          type = types.port;
+          default = 8333;
+          description = "Port for P2P connection to bitcoind.";
+        };
+      };
+
+      rpc = {
+        ip = mkOption {
+          type = types.str;
+          default = "127.0.0.1";
+          description = "IP address for RPC connection to bitcoind.";
+        };
+
+        port = mkOption {
+          type = types.port;
+          default = 8332;
+          description = "Port for RPC connection to bitcoind.";
+        };
+
+        user = mkOption {
+          type = types.str;
+          default = "bitcoin";
+          description = "RPC user for the bitcoin endpoint.";
+        };
+
+        password = mkOption {
+          type = types.str;
+          default = "password";
+          description = "RPC password for the bitcoin endpoint. Warning: this is stored in cleartext in the Nix store! Use <literal>configFile</literal> or <literal>passwordFile</literal> if needed.";
+        };
+
+        passwordFile = mkOption {
+          type = types.nullOr types.path;
+          default = null;
+          description = "File that contains the password of the RPC user.";
+        };
+      };
+
+      user = mkOption {
+        type = types.str;
+        default = "wasabibackend";
+        description = "The user as which to run the wasabibackend node.";
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = cfg.user;
+        description = "The group as which to run the wasabibackend node.";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    systemd.tmpfiles.rules = [
+      "d '${cfg.dataDir}' 0770 '${cfg.user}' '${cfg.group}' - -"
+    ];
+
+    systemd.services.wasabibackend = {
+      description = "wasabibackend server";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network-online.target" ];
+      environment = {
+        DOTNET_PRINT_TELEMETRY_MESSAGE = "false";
+        DOTNET_CLI_TELEMETRY_OPTOUT = "true";
+      };
+      preStart = ''
+        mkdir -p ${cfg.dataDir}/.walletwasabi/backend
+        ${if cfg.customConfigFile != null then ''
+          cp -v ${cfg.customConfigFile} ${cfg.dataDir}/.walletwasabi/backend/Config.json
+        '' else ''
+          cp -v ${configFile} ${cfg.dataDir}/.walletwasabi/backend/Config.json
+          ${optionalString (cfg.rpc.passwordFile != null) ''
+            CONFIGTMP=$(mktemp)
+            cat ${cfg.dataDir}/.walletwasabi/backend/Config.json | ${pkgs.jq}/bin/jq --arg rpconnection "${cfg.rpc.user}:$(cat "${cfg.rpc.passwordFile}")" '. + { BitcoinRpcConnectionString: $rpconnection }' > $CONFIGTMP
+            mv $CONFIGTMP ${cfg.dataDir}/.walletwasabi/backend/Config.json
+          ''}
+        ''}
+        chmod ug+w ${cfg.dataDir}/.walletwasabi/backend/Config.json
+      '';
+      serviceConfig = {
+        User = cfg.user;
+        Group = cfg.group;
+        ExecStart = "${pkgs.wasabibackend}/bin/WasabiBackend";
+        ProtectSystem = "full";
+      };
+    };
+
+    users.users.${cfg.user} = {
+      name = cfg.user;
+      group = cfg.group;
+      description = "wasabibackend daemon user";
+      home = cfg.dataDir;
+      isSystemUser = true;
+    };
+
+    users.groups.${cfg.group} = {};
+
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/websockify.nix b/nixpkgs/nixos/modules/services/networking/websockify.nix
index d9177df65bd6..27cb47be12f7 100644
--- a/nixpkgs/nixos/modules/services/networking/websockify.nix
+++ b/nixpkgs/nixos/modules/services/networking/websockify.nix
@@ -5,12 +5,12 @@ with lib;
 let cfg = config.services.networking.websockify; in {
   options = {
     services.networking.websockify = {
-      enable = mkOption {  
+      enable = mkOption {
         description = "Whether to enable websockify to forward websocket connections to TCP connections.";
 
-        default = false;   
+        default = false;
 
-        type = types.bool; 
+        type = types.bool;
       };
 
       sslCert = mkOption {
diff --git a/nixpkgs/nixos/modules/services/networking/wg-quick.nix b/nixpkgs/nixos/modules/services/networking/wg-quick.nix
index ff1bdeed9f48..02fe40a22a10 100644
--- a/nixpkgs/nixos/modules/services/networking/wg-quick.nix
+++ b/nixpkgs/nixos/modules/services/networking/wg-quick.nix
@@ -29,7 +29,7 @@ let
         type = with types; nullOr str;
         default = null;
         description = ''
-          Base64 private key generated by wg genkey.
+          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.
@@ -41,7 +41,7 @@ let
         type = with types; nullOr str;
         default = null;
         description = ''
-          Private key file as generated by wg genkey.
+          Private key file as generated by <command>wg genkey</command>.
         '';
       };
 
@@ -106,9 +106,9 @@ let
         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".
+          ("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".
         '';
       };
 
@@ -139,7 +139,7 @@ let
       publicKey = mkOption {
         example = "xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=";
         type = types.str;
-        description = "The base64 public key the peer.";
+        description = "The base64 public key to the peer.";
       };
 
       presharedKey = mkOption {
@@ -147,8 +147,8 @@ let
         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
+          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.
 
@@ -162,8 +162,8 @@ let
         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
+          File pointing to preshared key as 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.
         '';
diff --git a/nixpkgs/nixos/modules/services/networking/wireguard.nix b/nixpkgs/nixos/modules/services/networking/wireguard.nix
index e8f83f6dd8bf..e07020349cf4 100644
--- a/nixpkgs/nixos/modules/services/networking/wireguard.nix
+++ b/nixpkgs/nixos/modules/services/networking/wireguard.nix
@@ -91,11 +91,13 @@ let
       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".'';
+        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 {
@@ -174,7 +176,7 @@ let
         example = "/private/wireguard_psk";
         type = with types; nullOr str;
         description = ''
-          File pointing to preshared key as generated by <command>wg pensk</command>.
+          File pointing to preshared key as 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.
@@ -217,7 +219,6 @@ let
 
   };
 
-
   generatePathUnit = name: values:
     assert (values.privateKey == null);
     assert (values.privateKeyFile != null);
diff --git a/nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix b/nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix
index a7dea95056a0..395139879036 100644
--- a/nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix
+++ b/nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix
@@ -4,7 +4,7 @@ with lib;
 
 let
   cfg = config.networking.wireless;
-  configFile = if cfg.networks != {} then pkgs.writeText "wpa_supplicant.conf" ''
+  configFile = if cfg.networks != {} || cfg.extraConfig != "" || cfg.userControlled.enable then pkgs.writeText "wpa_supplicant.conf" ''
     ${optionalString cfg.userControlled.enable ''
       ctrl_interface=DIR=/run/wpa_supplicant GROUP=${cfg.userControlled.group}
       update_config=1''}
@@ -233,6 +233,9 @@ in {
       path = [ pkgs.wpa_supplicant ];
 
       script = ''
+        if [ -f /etc/wpa_supplicant.conf -a "/etc/wpa_supplicant.conf" != "${configFile}" ]
+        then echo >&2 "<3>/etc/wpa_supplicant.conf present but ignored. Generated ${configFile} is used instead."
+        fi
         iface_args="-s -u -D${cfg.driver} -c ${configFile}"
         ${if ifaces == [] then ''
           for i in $(cd /sys/class/net && echo *); do
diff --git a/nixpkgs/nixos/modules/services/networking/xandikos.nix b/nixpkgs/nixos/modules/services/networking/xandikos.nix
index 87c029156b9e..3c40bb956f57 100644
--- a/nixpkgs/nixos/modules/services/networking/xandikos.nix
+++ b/nixpkgs/nixos/modules/services/networking/xandikos.nix
@@ -90,7 +90,7 @@ in
   config = mkIf cfg.enable (
     mkMerge [
       {
-        meta.maintainers = [ lib.maintainers."0x4A6F" ];
+        meta.maintainers = with lib.maintainers; [ _0x4A6F ];
 
         systemd.services.xandikos = {
           description = "A Simple Calendar and Contact Server";
@@ -122,7 +122,7 @@ in
             ExecStart = ''
               ${cfg.package}/bin/xandikos \
                 --directory /var/lib/xandikos \
-                --listen_address ${cfg.address} \
+                --listen-address ${cfg.address} \
                 --port ${toString cfg.port} \
                 --route-prefix ${cfg.routePrefix} \
                 ${lib.concatStringsSep " " cfg.extraOptions}
diff --git a/nixpkgs/nixos/modules/services/networking/yggdrasil.nix b/nixpkgs/nixos/modules/services/networking/yggdrasil.nix
index 0fe9a200a1b6..a71c635c9f6e 100644
--- a/nixpkgs/nixos/modules/services/networking/yggdrasil.nix
+++ b/nixpkgs/nixos/modules/services/networking/yggdrasil.nix
@@ -195,5 +195,8 @@ in {
     # Make yggdrasilctl available on the command line.
     environment.systemPackages = [ cfg.package ];
   });
-  meta.maintainers = with lib.maintainers; [ gazally ehmry ];
+  meta = {
+    doc = ./yggdrasil.xml;
+    maintainers = with lib.maintainers; [ gazally ehmry ];
+  };
 }
diff --git a/nixpkgs/nixos/modules/services/networking/yggdrasil.xml b/nixpkgs/nixos/modules/services/networking/yggdrasil.xml
new file mode 100644
index 000000000000..c012cd4a9294
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/yggdrasil.xml
@@ -0,0 +1,157 @@
+<?xml version="1.0"?>
+<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-networking-yggdrasil">
+  <title>Yggdrasil</title>
+  <para>
+    <emphasis>Source:</emphasis>
+    <filename>modules/services/networking/yggdrasil/default.nix</filename>
+  </para>
+  <para>
+    <emphasis>Upstream documentation:</emphasis>
+    <link xlink:href="https://yggdrasil-network.github.io/"/>
+  </para>
+  <para>
+Yggdrasil is an early-stage implementation of a fully end-to-end encrypted,
+self-arranging IPv6 network.
+</para>
+  <section xml:id="module-services-networking-yggdrasil-configuration">
+    <title>Configuration</title>
+    <section xml:id="module-services-networking-yggdrasil-configuration-simple">
+      <title>Simple ephemeral node</title>
+      <para>
+An annotated example of a simple configuration:
+<programlisting>
+{
+  services.yggdrasil = {
+    enable = true;
+    persistentKeys = false;
+      # The NixOS module will generate new keys and a new IPv6 address each time
+      # it is started if persistentKeys is not enabled.
+
+    config = {
+      Peers = [
+        # Yggdrasil will automatically connect and "peer" with other nodes it
+        # discovers via link-local multicast annoucements. Unless this is the
+        # case (it probably isn't) a node needs peers within the existing
+        # network that it can tunnel to.
+        "tcp://1.2.3.4:1024"
+        "tcp://1.2.3.5:1024"
+        # Public peers can be found at
+        # https://github.com/yggdrasil-network/public-peers
+      ];
+    };
+  };
+}
+</programlisting>
+   </para>
+    </section>
+    <section xml:id="module-services-networking-yggdrasil-configuration-prefix">
+      <title>Persistent node with prefix</title>
+      <para>
+A node with a fixed address that announces a prefix:
+<programlisting>
+let
+  address = "210:5217:69c0:9afc:1b95:b9f:8718:c3d2";
+  prefix = "310:5217:69c0:9afc";
+  # taken from the output of "yggdrasilctl getself".
+in {
+
+  services.yggdrasil = {
+    enable = true;
+    persistentKeys = true; # Maintain a fixed public key and IPv6 address.
+    config = {
+      Peers = [ "tcp://1.2.3.4:1024" "tcp://1.2.3.5:1024" ];
+      NodeInfo = {
+        # This information is visible to the network.
+        name = config.networking.hostName;
+        location = "The North Pole";
+      };
+    };
+  };
+
+  boot.kernel.sysctl."net.ipv6.conf.all.forwarding" = 1;
+    # Forward traffic under the prefix.
+
+  networking.interfaces.${eth0}.ipv6.addresses = [{
+    # Set a 300::/8 address on the local physical device.
+    address = prefix + "::1";
+    prefixLength = 64;
+  }];
+
+  services.radvd = {
+    # Annouce the 300::/8 prefix to eth0.
+    enable = true;
+    config = ''
+      interface eth0
+      {
+        AdvSendAdvert on;
+        AdvDefaultLifetime 0;
+        prefix ${prefix}::/64 {
+          AdvOnLink on;
+          AdvAutonomous on;
+        };
+        route 200::/8 {};
+      };
+    '';
+  };
+}
+</programlisting>
+  </para>
+    </section>
+    <section xml:id="module-services-networking-yggdrasil-configuration-container">
+      <title>Yggdrasil attached Container</title>
+      <para>
+A NixOS container attached to the Yggdrasil network via a node running on the
+host:
+        <programlisting>
+let
+  yggPrefix64 = "310:5217:69c0:9afc";
+    # Again, taken from the output of "yggdrasilctl getself".
+in
+{
+  boot.kernel.sysctl."net.ipv6.conf.all.forwarding" = 1;
+  # Enable IPv6 forwarding.
+
+  networking = {
+    bridges.br0.interfaces = [ ];
+    # A bridge only to containers&#x2026;
+
+    interfaces.br0 = {
+      # &#x2026; configured with a prefix address.
+      ipv6.addresses = [{
+        address = "${yggPrefix64}::1";
+        prefixLength = 64;
+      }];
+    };
+  };
+
+  containers.foo = {
+    autoStart = true;
+    privateNetwork = true;
+    hostBridge = "br0";
+    # Attach the container to the bridge only.
+    config = { config, pkgs, ... }: {
+      networking.interfaces.eth0.ipv6 = {
+        addresses = [{
+          # Configure a prefix address.
+          address = "${yggPrefix64}::2";
+          prefixLength = 64;
+        }];
+        routes = [{
+          # Configure the prefix route.
+          address = "200::";
+          prefixLength = 7;
+          via = "${yggPrefix64}::1";
+        }];
+      };
+
+      services.httpd.enable = true;
+      networking.firewall.allowedTCPPorts = [ 80 ];
+    };
+  };
+
+}
+</programlisting>
+      </para>
+    </section>
+  </section>
+</chapter>