summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
authorOrivej Desh <orivej@gmx.fr>2020-03-23 00:42:14 +0000
committerOrivej Desh <orivej@gmx.fr>2020-03-23 00:53:16 +0000
commit1b89aa3f7a472cba32c0464046a904c0724d59b0 (patch)
tree4a65acca5fee3353775f10a3679640ce06dffcb4 /nixos
parent805f6235df79936e96ab3cebe17aea0af9b1b197 (diff)
parente1c308535442a95f8494d0dde12ff13e47276f64 (diff)
downloadnixpkgs-1b89aa3f7a472cba32c0464046a904c0724d59b0.tar
nixpkgs-1b89aa3f7a472cba32c0464046a904c0724d59b0.tar.gz
nixpkgs-1b89aa3f7a472cba32c0464046a904c0724d59b0.tar.bz2
nixpkgs-1b89aa3f7a472cba32c0464046a904c0724d59b0.tar.lz
nixpkgs-1b89aa3f7a472cba32c0464046a904c0724d59b0.tar.xz
nixpkgs-1b89aa3f7a472cba32c0464046a904c0724d59b0.tar.zst
nixpkgs-1b89aa3f7a472cba32c0464046a904c0724d59b0.zip
Merge branch 'master' into staging
Diffstat (limited to 'nixos')
-rw-r--r--nixos/doc/manual/configuration/configuration.xml1
-rw-r--r--nixos/doc/manual/release-notes/rl-2003.xml74
-rw-r--r--nixos/doc/manual/release-notes/rl-2009.xml19
-rw-r--r--nixos/lib/eval-config.nix4
-rw-r--r--nixos/maintainers/scripts/ec2/amazon-image.nix11
-rw-r--r--nixos/modules/hardware/wooting.nix12
-rw-r--r--nixos/modules/installer/tools/tools.nix8
-rw-r--r--nixos/modules/misc/documentation.nix14
-rw-r--r--nixos/modules/misc/nixpkgs.nix4
-rw-r--r--nixos/modules/module-list.nix10
-rw-r--r--nixos/modules/programs/ssmtp.nix12
-rw-r--r--nixos/modules/security/google_oslogin.nix6
-rw-r--r--nixos/modules/security/tpm2.nix185
-rw-r--r--nixos/modules/services/computing/foldingathome/client.nix81
-rw-r--r--nixos/modules/services/misc/folding-at-home.nix67
-rw-r--r--nixos/modules/services/misc/matrix-synapse.nix46
-rw-r--r--nixos/modules/services/misc/matrix-synapse.xml (renamed from nixos/doc/manual/configuration/matrix.xml)97
-rw-r--r--nixos/modules/services/misc/sssd.nix6
-rw-r--r--nixos/modules/services/monitoring/netdata.nix17
-rw-r--r--nixos/modules/services/networking/firewall.nix10
-rw-r--r--nixos/modules/services/networking/iodine.nix163
-rw-r--r--nixos/modules/services/networking/resilio.nix2
-rw-r--r--nixos/modules/services/networking/smartdns.nix61
-rw-r--r--nixos/modules/services/networking/ssh/sshd.nix26
-rw-r--r--nixos/modules/services/networking/stubby.nix1
-rw-r--r--nixos/modules/services/networking/supplicant.nix2
-rw-r--r--nixos/modules/services/networking/supybot.nix109
-rw-r--r--nixos/modules/services/networking/tailscale.nix46
-rw-r--r--nixos/modules/services/networking/zerotierone.nix17
-rw-r--r--nixos/modules/services/security/fail2ban.nix4
-rw-r--r--nixos/modules/services/x11/desktop-managers/kodi.nix2
-rw-r--r--nixos/modules/system/activation/top-level.nix2
-rw-r--r--nixos/modules/system/boot/kernel.nix265
-rw-r--r--nixos/modules/system/boot/networkd.nix75
-rw-r--r--nixos/modules/system/boot/stage-1.nix13
-rw-r--r--nixos/tests/all-tests.nix1
-rw-r--r--nixos/tests/fenics.nix50
-rw-r--r--nixos/tests/iodine.nix63
-rw-r--r--nixos/tests/matrix-synapse.nix21
-rw-r--r--nixos/tests/networking.nix25
40 files changed, 1218 insertions, 414 deletions
diff --git a/nixos/doc/manual/configuration/configuration.xml b/nixos/doc/manual/configuration/configuration.xml
index 5961209bc13..507d28814ea 100644
--- a/nixos/doc/manual/configuration/configuration.xml
+++ b/nixos/doc/manual/configuration/configuration.xml
@@ -21,7 +21,6 @@
  <xi:include href="xfce.xml" />
  <xi:include href="networking.xml" />
  <xi:include href="linux-kernel.xml" />
- <xi:include href="matrix.xml" />
  <xi:include href="../generated/modules.xml" xpointer="xpointer(//section[@id='modules']/*)" />
  <xi:include href="profiles.xml" />
  <xi:include href="kubernetes.xml" />
diff --git a/nixos/doc/manual/release-notes/rl-2003.xml b/nixos/doc/manual/release-notes/rl-2003.xml
index 918906d27e7..e5351519f8d 100644
--- a/nixos/doc/manual/release-notes/rl-2003.xml
+++ b/nixos/doc/manual/release-notes/rl-2003.xml
@@ -196,10 +196,10 @@ services.xserver.displayManager.defaultSession = "xfce+icewm";
    </listitem>
    <listitem>
     <para>
-     There is now only one Xfce package-set and module. This means attributes, <literal>xfce4-14</literal>
-     <literal>xfce4-12</literal>, and <literal>xfceUnstable</literal> all now point to the latest Xfce 4.14
-     packages. And in future NixOS releases will be the latest released version of Xfce available at the
-     time during the releases development (if viable).
+     There is now only one Xfce package-set and module. This means that attributes <literal>xfce4-14</literal>
+     and <literal>xfceUnstable</literal> all now point to the latest Xfce 4.14
+     packages. And in the future NixOS releases will be the latest released version of Xfce available at the
+     time of the release's development (if viable).
     </para>
    </listitem>
    <listitem>
@@ -235,7 +235,7 @@ services.xserver.displayManager.defaultSession = "xfce+icewm";
    <listitem>
     <para>
       The <literal>buildRustCrate</literal> infrastructure now produces <literal>lib</literal> outputs in addition to the <literal>out</literal> output.
-      This has led to drastically reduced closed sizes for some rust crates since development dependencies are now in the <literal>lib</literal> output.
+      This has led to drastically reduced closure sizes for some rust crates since development dependencies are now in the <literal>lib</literal> output.
     </para>
     </listitem>
    <listitem>
@@ -641,6 +641,13 @@ auth required pam_succeed_if.so uid >= 1000 quiet
      The previous behavior can be restored by setting <literal>config.riot-web.conf = { disable_guests = false; piwik = true; }</literal>.
     </para>
    </listitem>
+   <listitem>
+     <para>
+       Stand-alone usage of <literal>Upower</literal> now requires
+       <option>services.upower.enable</option> instead of just installing into
+       <xref linkend="opt-environment.systemPackages"/>.
+     </para>
+   </listitem>
   </itemizedlist>
  </section>
 
@@ -712,6 +719,63 @@ auth required pam_succeed_if.so uid >= 1000 quiet
      For further reference, please read <link xlink:href="https://github.com/NixOS/nixpkgs/pull/68953">#68953</link> or the corresponding <link xlink:href="https://discourse.nixos.org/t/predictable-network-interface-names-in-initrd/4055">discourse thread</link>.
     </para>
    </listitem>
+   <listitem>
+    <para>
+     The <package>matrix-synapse</package>-package has been updated to
+     <link xlink:href="https://github.com/matrix-org/synapse/releases/tag/v1.11.1">v1.11.1</link>.
+     Due to <link xlink:href="https://github.com/matrix-org/synapse/releases/tag/v1.10.0rc1">stricter requirements</link>
+     for database configuration when using <package>postgresql</package>, the automated database setup
+     of the module has been removed to avoid any further edge-cases.
+    </para>
+    <para>
+     <package>matrix-synapse</package> expects <literal>postgresql</literal>-databases to have the options
+     <literal>LC_COLLATE</literal> and <literal>LC_CTYPE</literal> set to
+     <link xlink:href="https://www.postgresql.org/docs/12/locale.html"><literal>'C'</literal></link> which basically
+     instructs <literal>postgresql</literal> to ignore any locale-based preferences.
+    </para>
+    <para>
+     Depending on your setup, you need to incorporate one of the following changes in your setup to
+     upgrade to 20.03:
+     <itemizedlist>
+      <listitem><para>If you use <literal>sqlite3</literal> you don't need to do anything.</para></listitem>
+      <listitem><para>If you use <literal>postgresql</literal> on a different server, you don't need
+       to change anything as well since this module was never designed to configure remote databases.
+      </para></listitem>
+      <listitem><para>If you use <literal>postgresql</literal> and configured your synapse initially on
+       <literal>19.09</literal> or older, you simply need to enable <package>postgresql</package>-support
+        explicitly:
+<programlisting>{ ... }: {
+  services.matrix-synapse = {
+    <link linkend="opt-services.matrix-synapse.enable">enable</link> = true;
+    /* and all the other config you've defined here */
+  };
+  <link linkend="opt-services.postgresql.enable">services.postgresql.enable</link> = true;
+}</programlisting>
+      </para></listitem>
+      <listitem><para>If you deploy a fresh <package>matrix-synapse</package>, you need to configure
+       the database yourself (e.g. by using the
+       <link linkend="opt-services.postgresql.initialScript">services.postgresql.initialScript</link>
+       option). An example for this can be found in the
+       <link linkend="module-services-matrix">documentation of the Matrix module</link>.
+      </para></listitem>
+      <listitem><para>If you initially deployed your <package>matrix-synapse</package> on
+       <literal>nixos-unstable</literal> <emphasis>after</emphasis> the <literal>19.09</literal>-release,
+       your database is misconfigured due to a regression in NixOS. For now, <package>matrix-synapse</package> will
+       startup with a warning, but it's recommended to reconfigure the database to set the values
+       <literal>LC_COLLATE</literal> and <literal>LC_CTYPE</literal> to
+       <link xlink:href="https://www.postgresql.org/docs/12/locale.html"><literal>'C'</literal></link>.
+      </para></listitem>
+     </itemizedlist>
+    </para>
+  </listitem>
+  <listitem>
+   <para>
+    The <link linkend="opt-systemd.network.links">systemd.network.links</link> option is now respected
+    even when <link linkend="opt-systemd.network.enable">systemd-networkd</link> is disabled.
+    This mirrors the behaviour of systemd - It's udev that parses <literal>.link</literal> files,
+    not <command>systemd-networkd</command>.
+   </para>
+  </listitem>
   </itemizedlist>
  </section>
 </section>
diff --git a/nixos/doc/manual/release-notes/rl-2009.xml b/nixos/doc/manual/release-notes/rl-2009.xml
index e236b230332..2f61ee5ae2e 100644
--- a/nixos/doc/manual/release-notes/rl-2009.xml
+++ b/nixos/doc/manual/release-notes/rl-2009.xml
@@ -28,6 +28,15 @@
      PHP now defaults to PHP 7.4, updated from 7.3.
     </para>
    </listitem>
+   <listitem>
+    <para>
+     Two new options, <link linkend="opt-services.openssh.authorizedKeysCommand">authorizedKeysCommand</link>
+     and <link linkend="opt-services.openssh.authorizedKeysCommandUser">authorizedKeysCommandUser</link>, have
+     been added to the <literal>openssh</literal> module. If you have <literal>AuthorizedKeysCommand</literal>
+     in your <link linkend="opt-services.openssh.extraConfig">services.openssh.extraConfig</link> you should
+     make use of these new options instead.
+    </para>
+   </listitem>
   </itemizedlist>
  </section>
 
@@ -77,6 +86,16 @@
 }</programlisting>
     </para>
    </listitem>
+   <listitem>
+    <para>
+      The <link linkend="opt-services.supybot.enable">supybot</link> module now uses <literal>/var/lib/supybot</literal>
+      as its default <link linkend="opt-services.supybot.stateDir">stateDir</link> path if <literal>stateVersion</literal>
+      is 20.09 or higher. It also enables number of
+      <link xlink:href="https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Sandboxing">systemd sandboxing options</link>
+      which may possibly interfere with some plugins. If this is the case you can disable the options through attributes in
+      <option>systemd.services.supybot.serviceConfig</option>.
+    </para>
+   </listitem>
   </itemizedlist>
  </section>
 
diff --git a/nixos/lib/eval-config.nix b/nixos/lib/eval-config.nix
index 9892d6f160f..c8824c2690d 100644
--- a/nixos/lib/eval-config.nix
+++ b/nixos/lib/eval-config.nix
@@ -61,7 +61,7 @@ in rec {
     args = extraArgs;
     specialArgs =
       { modulesPath = builtins.toString ../modules; } // specialArgs;
-  }) config options;
+  }) config options _module;
 
   # These are the extra arguments passed to every module.  In
   # particular, Nixpkgs is passed through the "pkgs" argument.
@@ -69,5 +69,5 @@ in rec {
     inherit baseModules extraModules modules;
   };
 
-  inherit (config._module.args) pkgs;
+  inherit (_module.args) pkgs;
 }
diff --git a/nixos/maintainers/scripts/ec2/amazon-image.nix b/nixos/maintainers/scripts/ec2/amazon-image.nix
index 31e15537179..36f3e7af873 100644
--- a/nixos/maintainers/scripts/ec2/amazon-image.nix
+++ b/nixos/maintainers/scripts/ec2/amazon-image.nix
@@ -8,10 +8,15 @@ in {
 
   imports = [ ../../../modules/virtualisation/amazon-image.nix ];
 
-  # Required to provide good EBS experience,
+  # Amazon recomments setting this to the highest possible value for a good EBS
+  # experience, which prior to 4.15 was 255.
   # https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/nvme-ebs-volumes.html#timeout-nvme-ebs-volumes
-  # TODO change value to 4294967295 when kernel is updated to 4.15 or later
-  config.boot.kernelParams = [ "nvme_core.io_timeout=255" ];
+  config.boot.kernelParams =
+    let timeout =
+      if pkgs.lib.versionAtLeast config.boot.kernelPackages.kernel.version "4.15"
+      then "4294967295"
+      else  "255";
+    in [ "nvme_core.io_timeout=${timeout}" ];
 
   options.amazonImage = {
     name = mkOption {
diff --git a/nixos/modules/hardware/wooting.nix b/nixos/modules/hardware/wooting.nix
new file mode 100644
index 00000000000..ee550cbbf6b
--- /dev/null
+++ b/nixos/modules/hardware/wooting.nix
@@ -0,0 +1,12 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+{
+  options.hardware.wooting.enable =
+    mkEnableOption "Enable support for Wooting keyboards";
+
+  config = mkIf config.hardware.wooting.enable {
+    environment.systemPackages = [ pkgs.wootility ];
+    services.udev.packages = [ pkgs.wooting-udev-rules ];
+  };
+}
diff --git a/nixos/modules/installer/tools/tools.nix b/nixos/modules/installer/tools/tools.nix
index 833865e99bb..655d77db157 100644
--- a/nixos/modules/installer/tools/tools.nix
+++ b/nixos/modules/installer/tools/tools.nix
@@ -42,7 +42,10 @@ let
     inherit (config.system.nixos-generate-config) configuration;
   };
 
-  nixos-option = pkgs.callPackage ./nixos-option { };
+  nixos-option =
+    if lib.versionAtLeast (lib.getVersion pkgs.nix) "2.4pre"
+    then null
+    else pkgs.callPackage ./nixos-option { };
 
   nixos-version = makeProg {
     name = "nixos-version";
@@ -184,10 +187,9 @@ in
         nixos-install
         nixos-rebuild
         nixos-generate-config
-        nixos-option
         nixos-version
         nixos-enter
-      ];
+      ] ++ lib.optional (nixos-option != null) nixos-option;
 
     system.build = {
       inherit nixos-install nixos-generate-config nixos-option nixos-rebuild nixos-enter;
diff --git a/nixos/modules/misc/documentation.nix b/nixos/modules/misc/documentation.nix
index d09afadd609..7b3f9c0fe9c 100644
--- a/nixos/modules/misc/documentation.nix
+++ b/nixos/modules/misc/documentation.nix
@@ -17,6 +17,7 @@ let
     inherit pkgs config;
     version = config.system.nixos.release;
     revision = "release-${version}";
+    extraSources = cfg.nixos.extraModuleSources;
     options =
       let
         scrubbedEval = evalModules {
@@ -163,6 +164,19 @@ in
         '';
       };
 
+      nixos.extraModuleSources = mkOption {
+        type = types.listOf (types.either types.path types.str);
+        default = [ ];
+        description = ''
+          Which extra NixOS module paths the generated NixOS's documentation should strip
+          from options.
+        '';
+        example = literalExample ''
+          # e.g. with options from modules in ''${pkgs.customModules}/nix:
+          [ pkgs.customModules ]
+        '';
+      };
+
     };
 
   };
diff --git a/nixos/modules/misc/nixpkgs.nix b/nixos/modules/misc/nixpkgs.nix
index 011d493c153..4f5a9250eaa 100644
--- a/nixos/modules/misc/nixpkgs.nix
+++ b/nixos/modules/misc/nixpkgs.nix
@@ -236,8 +236,8 @@ in
         let
           nixosExpectedSystem =
             if config.nixpkgs.crossSystem != null
-            then config.nixpkgs.crossSystem.system
-            else config.nixpkgs.localSystem.system;
+            then config.nixpkgs.crossSystem.system or (lib.systems.parse.doubleFromSystem (lib.systems.parse.mkSystemFromString config.nixpkgs.crossSystem.config))
+            else config.nixpkgs.localSystem.system or (lib.systems.parse.doubleFromSystem (lib.systems.parse.mkSystemFromString config.nixpkgs.localSystem.config));
           nixosOption =
             if config.nixpkgs.crossSystem != null
             then "nixpkgs.crossSystem"
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index dba2593bbef..ccdc39eecd8 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -64,6 +64,7 @@
   ./hardware/tuxedo-keyboard.nix
   ./hardware/usb-wwan.nix
   ./hardware/onlykey.nix
+  ./hardware/wooting.nix
   ./hardware/video/amdgpu.nix
   ./hardware/video/amdgpu-pro.nix
   ./hardware/video/ati.nix
@@ -200,6 +201,7 @@
   ./security/wrappers/default.nix
   ./security/sudo.nix
   ./security/systemd-confinement.nix
+  ./security/tpm2.nix
   ./services/admin/oxidized.nix
   ./services/admin/salt/master.nix
   ./services/admin/salt/minion.nix
@@ -247,9 +249,10 @@
   ./services/cluster/kubernetes/proxy.nix
   ./services/cluster/kubernetes/scheduler.nix
   ./services/computing/boinc/client.nix
-  ./services/computing/torque/server.nix
-  ./services/computing/torque/mom.nix
+  ./services/computing/foldingathome/client.nix
   ./services/computing/slurm/slurm.nix
+  ./services/computing/torque/mom.nix
+  ./services/computing/torque/server.nix
   ./services/continuous-integration/buildbot/master.nix
   ./services/continuous-integration/buildbot/worker.nix
   ./services/continuous-integration/buildkite-agents.nix
@@ -432,7 +435,6 @@
   ./services/misc/ethminer.nix
   ./services/misc/exhibitor.nix
   ./services/misc/felix.nix
-  ./services/misc/folding-at-home.nix
   ./services/misc/freeswitch.nix
   ./services/misc/fstrim.nix
   ./services/misc/gammu-smsd.nix
@@ -709,6 +711,7 @@
   ./services/networking/shorewall6.nix
   ./services/networking/shout.nix
   ./services/networking/sniproxy.nix
+  ./services/networking/smartdns.nix
   ./services/networking/smokeping.nix
   ./services/networking/softether.nix
   ./services/networking/spacecookie.nix
@@ -726,6 +729,7 @@
   ./services/networking/syncthing.nix
   ./services/networking/syncthing-relay.nix
   ./services/networking/syncplay.nix
+  ./services/networking/tailscale.nix
   ./services/networking/tcpcrypt.nix
   ./services/networking/teamspeak3.nix
   ./services/networking/tedicross.nix
diff --git a/nixos/modules/programs/ssmtp.nix b/nixos/modules/programs/ssmtp.nix
index f794eac8af0..c7a94739349 100644
--- a/nixos/modules/programs/ssmtp.nix
+++ b/nixos/modules/programs/ssmtp.nix
@@ -14,8 +14,16 @@ in
 {
 
   imports = [
-    (mkRenamedOptionModule [ "networking" "defaultMailServer" ] [ "services" "ssmtp" ])
-    (mkRenamedOptionModule [ "services" "ssmtp" "directDelivery" ] [ "services" "ssmtp" "enable" ])
+    (mkRenamedOptionModule [ "networking" "defaultMailServer" "directDelivery" ] [ "services" "ssmtp" "enable" ])
+    (mkRenamedOptionModule [ "networking" "defaultMailServer" "hostName" ] [ "services" "ssmtp" "hostName" ])
+    (mkRenamedOptionModule [ "networking" "defaultMailServer" "domain" ] [ "services" "ssmtp" "domain" ])
+    (mkRenamedOptionModule [ "networking" "defaultMailServer" "root" ] [ "services" "ssmtp" "root" ])
+    (mkRenamedOptionModule [ "networking" "defaultMailServer" "useTLS" ] [ "services" "ssmtp" "useTLS" ])
+    (mkRenamedOptionModule [ "networking" "defaultMailServer" "useSTARTTLS" ] [ "services" "ssmtp" "useSTARTTLS" ])
+    (mkRenamedOptionModule [ "networking" "defaultMailServer" "authUser" ] [ "services" "ssmtp" "authUser" ])
+    (mkRenamedOptionModule [ "networking" "defaultMailServer" "authPass" ] [ "services" "ssmtp" "authPass" ])
+    (mkRenamedOptionModule [ "networking" "defaultMailServer" "authPassFile" ] [ "services" "ssmtp" "authPassFile" ])
+    (mkRenamedOptionModule [ "networking" "defaultMailServer" "setSendmail" ] [ "services" "ssmtp" "setSendmail" ])
   ];
 
   options = {
diff --git a/nixos/modules/security/google_oslogin.nix b/nixos/modules/security/google_oslogin.nix
index 246419b681a..6f9962e1d62 100644
--- a/nixos/modules/security/google_oslogin.nix
+++ b/nixos/modules/security/google_oslogin.nix
@@ -59,10 +59,8 @@ in
         exec ${package}/bin/google_authorized_keys "$@"
       '';
     };
-    services.openssh.extraConfig = ''
-      AuthorizedKeysCommand /etc/ssh/authorized_keys_command_google_oslogin %u
-      AuthorizedKeysCommandUser nobody
-    '';
+    services.openssh.authorizedKeysCommand = "/etc/ssh/authorized_keys_command_google_oslogin %u";
+    services.openssh.authorizedKeysCommandUser = "nobody";
   };
 
 }
diff --git a/nixos/modules/security/tpm2.nix b/nixos/modules/security/tpm2.nix
new file mode 100644
index 00000000000..13804fb82cb
--- /dev/null
+++ b/nixos/modules/security/tpm2.nix
@@ -0,0 +1,185 @@
+{ lib, pkgs, config, ... }:
+let
+  cfg = config.security.tpm2;
+
+  # This snippet is taken from tpm2-tss/dist/tpm-udev.rules, but modified to allow custom user/groups
+  # The idea is that the tssUser is allowed to acess the TPM and kernel TPM resource manager, while
+  # the tssGroup is only allowed to access the kernel resource manager
+  # Therefore, if either of the two are null, the respective part isn't generated
+  udevRules = tssUser: tssGroup: ''
+    ${lib.optionalString (tssUser != null) ''KERNEL=="tpm[0-9]*", MODE="0660", OWNER="${tssUser}"''}
+    ${lib.optionalString (tssUser != null || tssGroup != null)
+      ''KERNEL=="tpmrm[0-9]*", MODE="0660"''
+      + lib.optionalString (tssUser != null) '', OWNER="${tssUser}"''
+      + lib.optionalString (tssGroup != null) '', GROUP="${tssGroup}"''
+     }
+  '';
+
+in {
+  options.security.tpm2 = {
+    enable = lib.mkEnableOption "Trusted Platform Module 2 support";
+
+    tssUser = lib.mkOption {
+      description = ''
+        Name of the tpm device-owner and service user, set if applyUdevRules is
+        set.
+      '';
+      type = lib.types.nullOr lib.types.str;
+      default = if cfg.abrmd.enable then "tss" else "root";
+      defaultText = ''"tss" when using the userspace resource manager,'' +
+                    ''"root" otherwise'';
+    };
+
+    tssGroup = lib.mkOption {
+      description = ''
+        Group of the tpm kernel resource manager (tpmrm) device-group, set if
+        applyUdevRules is set.
+      '';
+      type = lib.types.nullOr lib.types.str;
+      default = "tss";
+    };
+
+    applyUdevRules = lib.mkOption {
+      description = ''
+        Whether to make the /dev/tpm[0-9] devices accessible by the tssUser, or
+        the /dev/tpmrm[0-9] by tssGroup respectively
+      '';
+      type = lib.types.bool;
+      default = true;
+    };
+
+    abrmd = {
+      enable = lib.mkEnableOption ''
+        Trusted Platform 2 userspace resource manager daemon
+      '';
+
+      package = lib.mkOption {
+        description = "tpm2-abrmd package to use";
+        type = lib.types.package;
+        default = pkgs.tpm2-abrmd;
+        defaultText = "pkgs.tpm2-abrmd";
+      };
+    };
+
+    pkcs11 = {
+      enable = lib.mkEnableOption ''
+        TPM2 PKCS#11 tool and shared library in system path
+        (<literal>/run/current-system/sw/lib/libtpm2_pkcs11.so</literal>)
+      '';
+
+      package = lib.mkOption {
+        description = "tpm2-pkcs11 package to use";
+        type = lib.types.package;
+        default = pkgs.tpm2-pkcs11;
+        defaultText = "pkgs.tpm2-pkcs11";
+      };
+    };
+
+    tctiEnvironment = {
+      enable = lib.mkOption {
+        description = ''
+          Set common TCTI environment variables to the specified value.
+          The variables are
+          <itemizedlist>
+            <listitem>
+              <para>
+                <literal>TPM2TOOLS_TCTI</literal>
+              </para>
+            </listitem>
+            <listitem>
+              <para>
+                <literal>TPM2_PKCS11_TCTI</literal>
+              </para>
+            </listitem>
+          </itemizedlist>
+        '';
+        type = lib.types.bool;
+        default = false;
+      };
+
+      interface = lib.mkOption {
+        description = ''
+          The name of the TPM command transmission interface (TCTI) library to
+          use.
+        '';
+        type = lib.types.enum [ "tabrmd" "device" ];
+        default = "device";
+      };
+
+      deviceConf = lib.mkOption {
+        description = ''
+          Configuration part of the device TCTI, e.g. the path to the TPM device.
+          Applies if interface is set to "device".
+          The format is specified in the
+          <link xlink:href="https://github.com/tpm2-software/tpm2-tools/blob/master/man/common/tcti.md#tcti-options">
+          tpm2-tools repository</link>.
+        '';
+        type = lib.types.str;
+        default = "/dev/tpmrm0";
+      };
+
+      tabrmdConf = lib.mkOption {
+        description = ''
+          Configuration part of the tabrmd TCTI, like the D-Bus bus name.
+          Applies if interface is set to "tabrmd".
+          The format is specified in the
+          <link xlink:href="https://github.com/tpm2-software/tpm2-tools/blob/master/man/common/tcti.md#tcti-options">
+          tpm2-tools repository</link>.
+        '';
+        type = lib.types.str;
+        default = "bus_name=com.intel.tss2.Tabrmd";
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable (lib.mkMerge [
+    {
+      # PKCS11 tools and library
+      environment.systemPackages = lib.mkIf cfg.pkcs11.enable [
+        (lib.getBin cfg.pkcs11.package)
+        (lib.getLib cfg.pkcs11.package)
+      ];
+
+      services.udev.extraRules = lib.mkIf cfg.applyUdevRules
+        (udevRules cfg.tssUser cfg.tssGroup);
+
+      # Create the tss user and group only if the default value is used
+      users.users.${cfg.tssUser} = lib.mkIf (cfg.tssUser == "tss") {
+        isSystemUser = true;
+      };
+      users.groups.${cfg.tssGroup} = lib.mkIf (cfg.tssGroup == "tss") {};
+
+      environment.variables = lib.mkIf cfg.tctiEnvironment.enable (
+        lib.attrsets.genAttrs [
+          "TPM2TOOLS_TCTI"
+          "TPM2_PKCS11_TCTI"
+        ] (_: ''${cfg.tctiEnvironment.interface}:${
+          if cfg.tctiEnvironment.interface == "tabrmd" then
+            cfg.tctiEnvironment.tabrmdConf
+          else
+            cfg.tctiEnvironment.deviceConf
+        }'')
+      );
+    }
+
+    (lib.mkIf cfg.abrmd.enable {
+      systemd.services."tpm2-abrmd" = {
+        wantedBy = [ "multi-user.target" ];
+        serviceConfig = {
+          Type = "dbus";
+          Restart = "always";
+          RestartSec = 30;
+          BusName = "com.intel.tss2.Tabrmd";
+          StandardOutput = "syslog";
+          ExecStart = "${cfg.abrmd.package}/bin/tpm2-abrmd";
+          User = "tss";
+          Group = "nogroup";
+        };
+      };
+
+      services.dbus.packages = lib.singleton cfg.abrmd.package;
+    })
+  ]);
+
+  meta.maintainers = with lib.maintainers; [ lschuermann ];
+}
diff --git a/nixos/modules/services/computing/foldingathome/client.nix b/nixos/modules/services/computing/foldingathome/client.nix
new file mode 100644
index 00000000000..9f99af48c48
--- /dev/null
+++ b/nixos/modules/services/computing/foldingathome/client.nix
@@ -0,0 +1,81 @@
+{ config, lib, pkgs, ... }:
+with lib;
+let
+  cfg = config.services.foldingathome;
+
+  args =
+    ["--team" "${toString cfg.team}"]
+    ++ lib.optionals (cfg.user != null) ["--user" cfg.user]
+    ++ cfg.extraArgs
+    ;
+in
+{
+  imports = [
+    (mkRenamedOptionModule [ "services" "foldingAtHome" ] [ "services" "foldingathome" ])
+    (mkRenamedOptionModule [ "services" "foldingathome" "nickname" ] [ "services" "foldingathome" "user" ])
+    (mkRemovedOptionModule [ "services" "foldingathome" "config" ] ''
+      Use <literal>services.foldingathome.extraArgs instead<literal>
+    '')
+  ];
+  options.services.foldingathome = {
+    enable = mkEnableOption "Enable the Folding@home client";
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.fahclient;
+      defaultText = "pkgs.fahclient";
+      description = ''
+        Which Folding@home client to use.
+      '';
+    };
+
+    user = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        The user associated with the reported computation results. This will
+        be used in the ranking statistics.
+      '';
+    };
+
+    team = mkOption {
+      type = types.int;
+      default = 236565;
+      description = ''
+        The team ID associated with the reported computation results. This
+        will be used in the ranking statistics.
+
+        By default, use the NixOS folding@home team ID is being used.
+      '';
+    };
+
+    extraArgs = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      description = ''
+        Extra startup options for the FAHClient. Run
+        <literal>FAHClient --help</literal> to find all the available options.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.foldingathome = {
+      description = "Folding@home client";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      script = ''
+        exec ${cfg.package}/bin/FAHClient ${lib.escapeShellArgs args}
+      '';
+      serviceConfig = {
+        DynamicUser = true;
+        StateDirectory = "foldingathome";
+        WorkingDirectory = "%S/foldingathome";
+      };
+    };
+  };
+
+  meta = {
+    maintainers = with lib.maintainers; [ zimbatm ];
+  };
+}
diff --git a/nixos/modules/services/misc/folding-at-home.nix b/nixos/modules/services/misc/folding-at-home.nix
deleted file mode 100644
index fd2ea3948f6..00000000000
--- a/nixos/modules/services/misc/folding-at-home.nix
+++ /dev/null
@@ -1,67 +0,0 @@
-{ config, lib, pkgs, ... }:
-with lib;
-let
-  stateDir = "/var/lib/foldingathome";
-  cfg = config.services.foldingAtHome;
-  fahUser = "foldingathome";
-in {
-
-  ###### interface
-
-  options = {
-
-    services.foldingAtHome = {
-
-      enable = mkOption {
-        default = false;
-        description = ''
-          Whether to enable the Folding@Home to use idle CPU time.
-        '';
-      };
-
-      nickname = mkOption {
-        default = "Anonymous";
-        description = ''
-          A unique handle for statistics.
-        '';
-      };
-
-      config = mkOption {
-        default = "";
-        description = ''
-          Extra configuration. Contents will be added verbatim to the
-          configuration file.
-        '';
-      };
-
-    };
-
-  };
-
-  ###### implementation
-
-  config = mkIf cfg.enable {
-
-    users.users.${fahUser} =
-      { uid = config.ids.uids.foldingathome;
-        description = "Folding@Home user";
-        home = stateDir;
-      };
-
-    systemd.services.foldingathome = {
-      after = [ "network.target" ];
-      wantedBy = [ "multi-user.target" ];
-      preStart = ''
-        mkdir -m 0755 -p ${stateDir}
-        chown ${fahUser} ${stateDir}
-        cp -f ${pkgs.writeText "client.cfg" cfg.config} ${stateDir}/client.cfg
-      '';
-      script = "${pkgs.su}/bin/su -s ${pkgs.runtimeShell} ${fahUser} -c 'cd ${stateDir}; ${pkgs.foldingathome}/bin/fah6'";
-    };
-
-    services.foldingAtHome.config = ''
-        [settings]
-        username=${cfg.nickname}
-    '';
-  };
-}
diff --git a/nixos/modules/services/misc/matrix-synapse.nix b/nixos/modules/services/misc/matrix-synapse.nix
index 750f4a292fb..d02fa13bb99 100644
--- a/nixos/modules/services/misc/matrix-synapse.nix
+++ b/nixos/modules/services/misc/matrix-synapse.nix
@@ -111,6 +111,9 @@ app_service_config_files: ${builtins.toJSON cfg.app_service_config_files}
 
 ${cfg.extraConfig}
 '';
+
+  hasLocalPostgresDB = let args = cfg.database_args; in
+    usePostgresql && (!(args ? host) || (elem args.host [ "localhost" "127.0.0.1" "::1" ]));
 in {
   options = {
     services.matrix-synapse = {
@@ -354,13 +357,6 @@ in {
           The database engine name. Can be sqlite or psycopg2.
         '';
       };
-      create_local_database = mkOption {
-        type = types.bool;
-        default = true;
-        description = ''
-          Whether to create a local database automatically.
-        '';
-      };
       database_name = mkOption {
         type = types.str;
         default = "matrix-synapse";
@@ -657,6 +653,25 @@ in {
   };
 
   config = mkIf cfg.enable {
+    assertions = [
+      { assertion = hasLocalPostgresDB -> config.services.postgresql.enable;
+        message = ''
+          Cannot deploy matrix-synapse with a configuration for a local postgresql database
+            and a missing postgresql service. Since 20.03 it's mandatory to manually configure the
+            database (please read the thread in https://github.com/NixOS/nixpkgs/pull/80447 for
+            further reference).
+
+            If you
+            - try to deploy a fresh synapse, you need to configure the database yourself. An example
+              for this can be found in <nixpkgs/nixos/tests/matrix-synapse.nix>
+            - update your existing matrix-synapse instance, you simply need to add `services.postgresql.enable = true`
+              to your configuration.
+
+          For further information about this update, please read the release-notes of 20.03 carefully.
+        '';
+      }
+    ];
+
     users.users.matrix-synapse = { 
         group = "matrix-synapse";
         home = cfg.dataDir;
@@ -669,18 +684,9 @@ in {
       gid = config.ids.gids.matrix-synapse;
     };
 
-    services.postgresql = mkIf (usePostgresql && cfg.create_local_database) {
-      enable = mkDefault true;
-      ensureDatabases = [ cfg.database_name ];
-      ensureUsers = [{
-        name = cfg.database_user;
-        ensurePermissions = { "DATABASE \"${cfg.database_name}\"" = "ALL PRIVILEGES"; };
-      }];
-    };
-
     systemd.services.matrix-synapse = {
       description = "Synapse Matrix homeserver";
-      after = [ "network.target" ] ++ lib.optional config.services.postgresql.enable "postgresql.service" ;
+      after = [ "network.target" ] ++ optional hasLocalPostgresDB "postgresql.service";
       wantedBy = [ "multi-user.target" ];
       preStart = ''
         ${cfg.package}/bin/homeserver \
@@ -709,6 +715,12 @@ in {
       The `trusted_third_party_id_servers` option as been removed in `matrix-synapse` v1.4.0
       as the behavior is now obsolete.
     '')
+    (mkRemovedOptionModule [ "services" "matrix-synapse" "create_local_database" ] ''
+      Database configuration must be done manually. An exemplary setup is demonstrated in
+      <nixpkgs/nixos/tests/matrix-synapse.nix>
+    '')
   ];
 
+  meta.doc = ./matrix-synapse.xml;
+
 }
diff --git a/nixos/doc/manual/configuration/matrix.xml b/nixos/modules/services/misc/matrix-synapse.xml
index ef8d5cbda88..053a3b2a563 100644
--- a/nixos/doc/manual/configuration/matrix.xml
+++ b/nixos/modules/services/misc/matrix-synapse.xml
@@ -40,26 +40,35 @@ let
     in join config.networking.hostName config.networking.domain;
 in {
   networking = {
-    hostName = "myhostname";
-    domain = "example.org";
+    <link linkend="opt-networking.hostName">hostName</link> = "myhostname";
+    <link linkend="opt-networking.domain">domain</link> = "example.org";
   };
-  networking.firewall.allowedTCPPorts = [ 80 443 ];
+  <link linkend="opt-networking.firewall.allowedTCPPorts">networking.firewall.allowedTCPPorts</link> = [ 80 443 ];
+
+  <link linkend="opt-services.postgresql.enable">services.postgresql.enable</link> = true;
+  <link linkend="opt-services.postgresql.initialScript">services.postgresql.initialScript</link> = ''
+    CREATE ROLE "matrix-synapse" WITH LOGIN PASSWORD 'synapse';
+    CREATE DATABASE "matrix-synapse" WITH OWNER "matrix-synapse"
+      TEMPLATE template0
+      LC_COLLATE = "C"
+      LC_CTYPE = "C";
+  '';
 
   services.nginx = {
-    enable = true;
+    <link linkend="opt-services.nginx.enable">enable</link> = true;
     # only recommendedProxySettings and recommendedGzipSettings are strictly required,
     # but the rest make sense as well
-    recommendedTlsSettings = true;
-    recommendedOptimisation = true;
-    recommendedGzipSettings = true;
-    recommendedProxySettings = true;
+    <link linkend="opt-services.nginx.recommendedTlsSettings">recommendedTlsSettings</link> = true;
+    <link linkend="opt-services.nginx.recommendedOptimisation">recommendedOptimisation</link> = true;
+    <link linkend="opt-services.nginx.recommendedGzipSettings">recommendedGzipSettings</link> = true;
+    <link linkend="opt-services.nginx.recommendedProxySettings">recommendedProxySettings</link> = true;
 
-    virtualHosts = {
+    <link linkend="opt-services.nginx.virtualHosts">virtualHosts</link> = {
       # This host section can be placed on a different host than the rest,
       # i.e. to delegate from the host being accessible as ${config.networking.domain}
       # to another host actually running the Matrix homeserver.
       "${config.networking.domain}" = {
-        locations."= /.well-known/matrix/server".extraConfig =
+        <link linkend="opt-services.nginx.virtualHosts._name_.locations._name_.extraConfig">locations."= /.well-known/matrix/server".extraConfig</link> =
           let
             # use 443 instead of the default 8448 port to unite
             # the client-server and server-server port for simplicity
@@ -68,7 +77,7 @@ in {
             add_header Content-Type application/json;
             return 200 '${builtins.toJSON server}';
           '';
-        locations."= /.well-known/matrix/client".extraConfig =
+        <link linkend="opt-services.nginx.virtualHosts._name_.locations._name_.extraConfig">locations."= /.well-known/matrix/client".extraConfig</link> =
           let
             client = {
               "m.homeserver" =  { "base_url" = "https://${fqdn}"; };
@@ -84,34 +93,37 @@ in {
 
       # Reverse proxy for Matrix client-server and server-server communication
       ${fqdn} = {
-        enableACME = true;
-        forceSSL = true;
+        <link linkend="opt-services.nginx.virtualHosts._name_.enableACME">enableACME</link> = true;
+        <link linkend="opt-services.nginx.virtualHosts._name_.forceSSL">forceSSL</link> = true;
 
         # Or do a redirect instead of the 404, or whatever is appropriate for you.
         # But do not put a Matrix Web client here! See the Riot Web section below.
-        locations."/".extraConfig = ''
+        <link linkend="opt-services.nginx.virtualHosts._name_.locations._name_.extraConfig">locations."/".extraConfig</link> = ''
           return 404;
         '';
 
         # forward all Matrix API calls to the synapse Matrix homeserver
         locations."/_matrix" = {
-          proxyPass = "http://[::1]:8008"; # without a trailing /
+          <link linkend="opt-services.nginx.virtualHosts._name_.locations._name_.proxyPass">proxyPass</link> = "http://[::1]:8008"; # without a trailing /
         };
       };
     };
   };
   services.matrix-synapse = {
-    enable = true;
-    server_name = config.networking.domain;
-    listeners = [
+    <link linkend="opt-services.matrix-synapse.enable">enable</link> = true;
+    <link linkend="opt-services.matrix-synapse.server_name">server_name</link> = config.networking.domain;
+    <link linkend="opt-services.matrix-synapse.listeners">listeners</link> = [
       {
-        port = 8008;
-        bind_address = "::1";
-        type = "http";
-        tls = false;
-        x_forwarded = true;
-        resources = [
-          { names = [ "client" "federation" ]; compress = false; }
+        <link linkend="opt-services.matrix-synapse.listeners._.port">port</link> = 8008;
+        <link linkend="opt-services.matrix-synapse.listeners._.bind_address">bind_address</link> = "::1";
+        <link linkend="opt-services.matrix-synapse.listeners._.type">type</link> = "http";
+        <link linkend="opt-services.matrix-synapse.listeners._.tls">tls</link> = false;
+        <link linkend="opt-services.matrix-synapse.listeners._.x_forwarded">x_forwarded</link> = true;
+        <link linkend="opt-services.matrix-synapse.listeners._.resources">resources</link> = [
+          {
+            <link linkend="opt-services.matrix-synapse.listeners._.resources._.names">names</link> = [ "client" "federation" ];
+            <link linkend="opt-services.matrix-synapse.listeners._.resources._.compress">compress</link> = false;
+          }
         ];
       }
     ];
@@ -135,10 +147,10 @@ in {
 
   <para>
    If you want to run a server with public registration by anybody, you can
-   then enable <option>services.matrix-synapse.enable_registration =
-   true;</option>. Otherwise, or you can generate a registration secret with
+   then enable <literal><link linkend="opt-services.matrix-synapse.enable_registration">services.matrix-synapse.enable_registration</link> =
+   true;</literal>. Otherwise, or you can generate a registration secret with
    <command>pwgen -s 64 1</command> and set it with
-   <option>services.matrix-synapse.registration_shared_secret</option>. To
+   <option><link linkend="opt-services.matrix-synapse.registration_shared_secret">services.matrix-synapse.registration_shared_secret</link></option>. To
    create a new user or admin, run the following after you have set the secret
    and have rebuilt NixOS:
 <screen>
@@ -154,8 +166,8 @@ Success!
    <literal>@your-username:example.org</literal>. Note that the registration
    secret ends up in the nix store and therefore is world-readable by any user
    on your machine, so it makes sense to only temporarily activate the
-   <option>registration_shared_secret</option> option until a better solution
-   for NixOS is in place.
+   <link linkend="opt-services.matrix-synapse.registration_shared_secret">registration_shared_secret</link>
+   option until a better solution for NixOS is in place.
   </para>
  </section>
  <section xml:id="module-services-matrix-riot-web">
@@ -177,15 +189,24 @@ Success!
    Matrix Now!</link> for a list of existing clients and their supported
    featureset.
 <programlisting>
-services.nginx.virtualHosts."riot.${fqdn}" = {
-  enableACME = true;
-  forceSSL = true;
-  serverAliases = [
-    "riot.${config.networking.domain}"
-  ];
+{
+  services.nginx.virtualHosts."riot.${fqdn}" = {
+    <link linkend="opt-services.nginx.virtualHosts._name_.enableACME">enableACME</link> = true;
+    <link linkend="opt-services.nginx.virtualHosts._name_.forceSSL">forceSSL</link> = true;
+    <link linkend="opt-services.nginx.virtualHosts._name_.serverAliases">serverAliases</link> = [
+      "riot.${config.networking.domain}"
+    ];
 
-  root = pkgs.riot-web;
-};
+    <link linkend="opt-services.nginx.virtualHosts._name_.root">root</link> = pkgs.riot-web.override {
+      conf = {
+        default_server_config."m.homeserver" = {
+          "base_url" = "${config.networking.domain}";
+          "server_name" = "${fqdn}";
+        };
+      };
+    };
+  };
+}
 </programlisting>
   </para>
 
diff --git a/nixos/modules/services/misc/sssd.nix b/nixos/modules/services/misc/sssd.nix
index 6b64045dde8..36008d25741 100644
--- a/nixos/modules/services/misc/sssd.nix
+++ b/nixos/modules/services/misc/sssd.nix
@@ -88,9 +88,7 @@ in {
         exec ${pkgs.sssd}/bin/sss_ssh_authorizedkeys "$@"
       '';
     };
-    services.openssh.extraConfig = ''
-      AuthorizedKeysCommand /etc/ssh/authorized_keys_command
-      AuthorizedKeysCommandUser nobody
-    '';
+    services.openssh.authorizedKeysCommand = "/etc/ssh/authorized_keys_command";
+    services.openssh.authorizedKeysCommandUser = "nobody";
   })];
 }
diff --git a/nixos/modules/services/monitoring/netdata.nix b/nixos/modules/services/monitoring/netdata.nix
index f8225af2042..7589fd0e67b 100644
--- a/nixos/modules/services/monitoring/netdata.nix
+++ b/nixos/modules/services/monitoring/netdata.nix
@@ -12,7 +12,7 @@ let
   '';
 
   plugins = [
-    "${pkgs.netdata}/libexec/netdata/plugins.d"
+    "${cfg.package}/libexec/netdata/plugins.d"
     "${wrappedPlugins}/libexec/netdata/plugins.d"
   ] ++ cfg.extraPluginPaths;
 
@@ -35,6 +35,13 @@ in {
     services.netdata = {
       enable = mkEnableOption "netdata";
 
+      package = mkOption {
+        type = types.package;
+        default = pkgs.netdata;
+        defaultText = "pkgs.netdata";
+        description = "Netdata package to use.";
+      };
+
       user = mkOption {
         type = types.str;
         default = "netdata";
@@ -141,8 +148,8 @@ in {
       path = (with pkgs; [ curl gawk which ]) ++ lib.optional cfg.python.enable
         (pkgs.python3.withPackages cfg.python.extraPackages);
       serviceConfig = {
-        Environment="PYTHONPATH=${pkgs.netdata}/libexec/netdata/python.d/python_modules";
-        ExecStart = "${pkgs.netdata}/bin/netdata -P /run/netdata/netdata.pid -D -c ${configFile}";
+        Environment="PYTHONPATH=${cfg.package}/libexec/netdata/python.d/python_modules";
+        ExecStart = "${cfg.package}/bin/netdata -P /run/netdata/netdata.pid -D -c ${configFile}";
         ExecReload = "${pkgs.utillinux}/bin/kill -s HUP -s USR1 -s USR2 $MAINPID";
         TimeoutStopSec = 60;
         # User and group
@@ -159,7 +166,7 @@ in {
     systemd.enableCgroupAccounting = true;
 
     security.wrappers."apps.plugin" = {
-      source = "${pkgs.netdata}/libexec/netdata/plugins.d/apps.plugin.org";
+      source = "${cfg.package}/libexec/netdata/plugins.d/apps.plugin.org";
       capabilities = "cap_dac_read_search,cap_sys_ptrace+ep";
       owner = cfg.user;
       group = cfg.group;
@@ -167,7 +174,7 @@ in {
     };
 
     security.wrappers."freeipmi.plugin" = {
-      source = "${pkgs.netdata}/libexec/netdata/plugins.d/freeipmi.plugin.org";
+      source = "${cfg.package}/libexec/netdata/plugins.d/freeipmi.plugin.org";
       capabilities = "cap_dac_override,cap_fowner+ep";
       owner = cfg.user;
       group = cfg.group;
diff --git a/nixos/modules/services/networking/firewall.nix b/nixos/modules/services/networking/firewall.nix
index b0045ff795e..cdc3a172ea7 100644
--- a/nixos/modules/services/networking/firewall.nix
+++ b/nixos/modules/services/networking/firewall.nix
@@ -546,9 +546,13 @@ in
       options nf_conntrack nf_conntrack_helper=1
     '';
 
-    assertions = [ { assertion = cfg.checkReversePath -> kernelHasRPFilter;
-                     message = "This kernel does not support rpfilter"; }
-                 ];
+    assertions = [
+      # This is approximately "checkReversePath -> kernelHasRPFilter",
+      # but the checkReversePath option can include non-boolean
+      # values.
+      { assertion = cfg.checkReversePath == false || kernelHasRPFilter;
+        message = "This kernel does not support rpfilter"; }
+    ];
 
     systemd.services.firewall = {
       description = "Firewall";
diff --git a/nixos/modules/services/networking/iodine.nix b/nixos/modules/services/networking/iodine.nix
index f9ca26c2796..46051d7044b 100644
--- a/nixos/modules/services/networking/iodine.nix
+++ b/nixos/modules/services/networking/iodine.nix
@@ -9,6 +9,8 @@ let
 
   iodinedUser = "iodined";
 
+  /* is this path made unreadable by ProtectHome = true ? */
+  isProtected = x: hasPrefix "/root" x || hasPrefix "/home" x;
 in
 {
   imports = [
@@ -35,45 +37,48 @@ in
           corresponding attribute name.
         '';
         example = literalExample ''
-        {
-          foo = {
-            server = "tunnel.mdomain.com";
-            relay = "8.8.8.8";
-            extraConfig = "-v";
+          {
+            foo = {
+              server = "tunnel.mdomain.com";
+              relay = "8.8.8.8";
+              extraConfig = "-v";
+            }
           }
-        }
         '';
-        type = types.attrsOf (types.submodule (
-        {
-          options = {
-            server = mkOption {
-              type = types.str;
-              default = "";
-              description = "Domain or Subdomain of server running iodined";
-              example = "tunnel.mydomain.com";
-            };
-
-            relay = mkOption {
-              type = types.str;
-              default = "";
-              description = "DNS server to use as a intermediate relay to the iodined server";
-              example = "8.8.8.8";
-            };
-
-            extraConfig = mkOption {
-              type = types.str;
-              default = "";
-              description = "Additional command line parameters";
-              example = "-l 192.168.1.10 -p 23";
-            };
-
-            passwordFile = mkOption {
-              type = types.str;
-              default = "";
-              description = "File that contains password";
-            };
-          };
-        }));
+        type = types.attrsOf (
+          types.submodule (
+            {
+              options = {
+                server = mkOption {
+                  type = types.str;
+                  default = "";
+                  description = "Hostname of server running iodined";
+                  example = "tunnel.mydomain.com";
+                };
+
+                relay = mkOption {
+                  type = types.str;
+                  default = "";
+                  description = "DNS server to use as an intermediate relay to the iodined server";
+                  example = "8.8.8.8";
+                };
+
+                extraConfig = mkOption {
+                  type = types.str;
+                  default = "";
+                  description = "Additional command line parameters";
+                  example = "-l 192.168.1.10 -p 23";
+                };
+
+                passwordFile = mkOption {
+                  type = types.str;
+                  default = "";
+                  description = "Path to a file containing the password.";
+                };
+              };
+            }
+          )
+        );
       };
 
       server = {
@@ -121,31 +126,67 @@ in
     boot.kernelModules = [ "tun" ];
 
     systemd.services =
-    let
-      createIodineClientService = name: cfg:
-      {
-        description = "iodine client - ${name}";
-        after = [ "network.target" ];
-        wantedBy = [ "multi-user.target" ];
-        script = "exec ${pkgs.iodine}/bin/iodine -f -u ${iodinedUser} ${cfg.extraConfig} ${optionalString (cfg.passwordFile != "") "< \"${cfg.passwordFile}\""} ${cfg.relay} ${cfg.server}";
-        serviceConfig = {
-          RestartSec = "30s";
-          Restart = "always";
+      let
+        createIodineClientService = name: cfg:
+          {
+            description = "iodine client - ${name}";
+            after = [ "network.target" ];
+            wantedBy = [ "multi-user.target" ];
+            script = "exec ${pkgs.iodine}/bin/iodine -f -u ${iodinedUser} ${cfg.extraConfig} ${optionalString (cfg.passwordFile != "") "< \"${builtins.toString cfg.passwordFile}\""} ${cfg.relay} ${cfg.server}";
+            serviceConfig = {
+              RestartSec = "30s";
+              Restart = "always";
+
+              # hardening :
+              # Filesystem access
+              ProtectSystem = "strict";
+              ProtectHome = if isProtected cfg.passwordFile then "read-only" else "true" ;
+              PrivateTmp = true;
+              ReadWritePaths = "/dev/net/tun";
+              PrivateDevices = false;
+              ProtectKernelTunables = true;
+              ProtectKernelModules = true;
+              ProtectControlGroups = true;
+              # Caps
+              NoNewPrivileges = true;
+              # Misc.
+              LockPersonality = true;
+              RestrictRealtime = true;
+              PrivateMounts = true;
+              MemoryDenyWriteExecute = true;
+            };
+          };
+      in
+        listToAttrs (
+          mapAttrsToList
+            (name: value: nameValuePair "iodine-${name}" (createIodineClientService name value))
+            cfg.clients
+        ) // {
+          iodined = mkIf (cfg.server.enable) {
+            description = "iodine, ip over dns server daemon";
+            after = [ "network.target" ];
+            wantedBy = [ "multi-user.target" ];
+            script = "exec ${pkgs.iodine}/bin/iodined -f -u ${iodinedUser} ${cfg.server.extraConfig} ${optionalString (cfg.server.passwordFile != "") "< \"${builtins.toString cfg.server.passwordFile}\""} ${cfg.server.ip} ${cfg.server.domain}";
+            serviceConfig = {
+              # Filesystem access
+              ProtectSystem = "strict";
+              ProtectHome = if isProtected cfg.server.passwordFile then "read-only" else "true" ;
+              PrivateTmp = true;
+              ReadWritePaths = "/dev/net/tun";
+              PrivateDevices = false;
+              ProtectKernelTunables = true;
+              ProtectKernelModules = true;
+              ProtectControlGroups = true;
+              # Caps
+              NoNewPrivileges = true;
+              # Misc.
+              LockPersonality = true;
+              RestrictRealtime = true;
+              PrivateMounts = true;
+              MemoryDenyWriteExecute = true;
+            };
+          };
         };
-      };
-    in
-    listToAttrs (
-      mapAttrsToList
-        (name: value: nameValuePair "iodine-${name}" (createIodineClientService name value))
-        cfg.clients
-    ) // {
-      iodined = mkIf (cfg.server.enable) {
-        description = "iodine, ip over dns server daemon";
-        after = [ "network.target" ];
-        wantedBy = [ "multi-user.target" ];
-        script = "exec ${pkgs.iodine}/bin/iodined -f -u ${iodinedUser} ${cfg.server.extraConfig} ${optionalString (cfg.server.passwordFile != "") "< \"${cfg.server.passwordFile}\""} ${cfg.server.ip} ${cfg.server.domain}";
-      };
-    };
 
     users.users.${iodinedUser} = {
       uid = config.ids.uids.iodined;
diff --git a/nixos/modules/services/networking/resilio.nix b/nixos/modules/services/networking/resilio.nix
index 9b25aa57583..e74e03fc0b0 100644
--- a/nixos/modules/services/networking/resilio.nix
+++ b/nixos/modules/services/networking/resilio.nix
@@ -244,7 +244,7 @@ in
       group           = "rslsync";
     };
 
-    users.groups = [ { name = "rslsync"; } ];
+    users.groups.rslsync = {};
 
     systemd.services.resilio = with pkgs; {
       description = "Resilio Sync Service";
diff --git a/nixos/modules/services/networking/smartdns.nix b/nixos/modules/services/networking/smartdns.nix
new file mode 100644
index 00000000000..f1888af7041
--- /dev/null
+++ b/nixos/modules/services/networking/smartdns.nix
@@ -0,0 +1,61 @@
+{ lib, pkgs, config, ... }:
+
+with lib;
+
+let
+  inherit (lib.types) attrsOf coercedTo listOf oneOf str int bool;
+  cfg = config.services.smartdns;
+
+  confFile = pkgs.writeText "smartdns.conf" (with generators;
+    toKeyValue {
+      mkKeyValue = mkKeyValueDefault {
+        mkValueString = v:
+          if isBool v then
+            if v then "yes" else "no"
+          else
+            mkValueStringDefault { } v;
+      } " ";
+      listsAsDuplicateKeys =
+        true; # Allowing duplications because we need to deal with multiple entries with the same key.
+    } cfg.settings);
+in {
+  options.services.smartdns = {
+    enable = mkEnableOption "SmartDNS DNS server";
+
+    bindPort = mkOption {
+      type = types.port;
+      default = 53;
+      description = "DNS listening port number.";
+    };
+
+    settings = mkOption {
+      type =
+      let atom = oneOf [ str int bool ];
+      in attrsOf (coercedTo atom toList (listOf atom));
+      example = literalExample ''
+        {
+          bind = ":5353 -no-rule -group example";
+          cache-size = 4096;
+          server-tls = [ "8.8.8.8:853" "1.1.1.1:853" ];
+          server-https = "https://cloudflare-dns.com/dns-query -exclude-default-group";
+          prefetch-domain = true;
+          speed-check-mode = "ping,tcp:80";
+        };
+      '';
+      description = ''
+        A set that will be generated into configuration file, see the <link xlink:href="https://github.com/pymumu/smartdns/blob/master/ReadMe_en.md#configuration-parameter">SmartDNS README</link> for details of configuration parameters.
+        You could override the options here like <option>services.smartdns.bindPort</option> by writing <literal>settings.bind = ":5353 -no-rule -group example";</literal>.
+      '';
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    services.smartdns.settings.bind = mkDefault ":${toString cfg.bindPort}";
+
+    systemd.packages = [ pkgs.smartdns ];
+    systemd.services.smartdns.wantedBy = [ "multi-user.target" ];
+    environment.etc."smartdns/smartdns.conf".source = confFile;
+    environment.etc."default/smartdns".source =
+      "${pkgs.smartdns}/etc/default/smartdns";
+  };
+}
diff --git a/nixos/modules/services/networking/ssh/sshd.nix b/nixos/modules/services/networking/ssh/sshd.nix
index b0e2e303cbc..464e9ed38c4 100644
--- a/nixos/modules/services/networking/ssh/sshd.nix
+++ b/nixos/modules/services/networking/ssh/sshd.nix
@@ -17,7 +17,7 @@ let
     ${cfg.extraConfig}
     EOL
 
-    ssh-keygen -f mock-hostkey -N ""
+    ssh-keygen -q -f mock-hostkey -N ""
     sshd -t -f $out -h mock-hostkey
   '';
 
@@ -238,6 +238,26 @@ in
         description = "Files from which authorized keys are read.";
       };
 
+      authorizedKeysCommand = mkOption {
+        type = types.str;
+        default = "none";
+        description = ''
+          Specifies a program to be used to look up the user's public
+          keys. The program must be owned by root, not writable by group
+          or others and specified by an absolute path.
+        '';
+      };
+
+      authorizedKeysCommandUser = mkOption {
+        type = types.str;
+        default = "nobody";
+        description = ''
+          Specifies the user under whose account the AuthorizedKeysCommand
+          is run. It is recommended to use a dedicated user that has no
+          other role on the host than running authorized keys commands.
+        '';
+      };
+
       kexAlgorithms = mkOption {
         type = types.listOf types.str;
         default = [
@@ -485,6 +505,10 @@ in
         PrintMotd no # handled by pam_motd
 
         AuthorizedKeysFile ${toString cfg.authorizedKeysFiles}
+        ${optionalString (cfg.authorizedKeysCommand != "none") ''
+          AuthorizedKeysCommand ${cfg.authorizedKeysCommand}
+          AuthorizedKeysCommandUser ${cfg.authorizedKeysCommandUser}
+        ''}
 
         ${flip concatMapStrings cfg.hostKeys (k: ''
           HostKey ${k.path}
diff --git a/nixos/modules/services/networking/stubby.nix b/nixos/modules/services/networking/stubby.nix
index 849d266576d..c5e0f929a12 100644
--- a/nixos/modules/services/networking/stubby.nix
+++ b/nixos/modules/services/networking/stubby.nix
@@ -205,6 +205,7 @@ in
       wantedBy = [ "multi-user.target" ];
 
       serviceConfig = {
+        Type = "notify";
         AmbientCapabilities = "CAP_NET_BIND_SERVICE";
         CapabilityBoundingSet = "CAP_NET_BIND_SERVICE";
         ExecStart = "${pkgs.stubby}/bin/stubby -C ${confFile} ${optionalString cfg.debugLogging "-l"}";
diff --git a/nixos/modules/services/networking/supplicant.nix b/nixos/modules/services/networking/supplicant.nix
index 35c1e649e2e..b5b9989ce18 100644
--- a/nixos/modules/services/networking/supplicant.nix
+++ b/nixos/modules/services/networking/supplicant.nix
@@ -39,8 +39,6 @@ let
         bindsTo = deps;
         after = deps;
         before = [ "network.target" ];
-        # Receive restart event after resume
-        partOf = [ "post-resume.target" ];
 
         path = [ pkgs.coreutils ];
 
diff --git a/nixos/modules/services/networking/supybot.nix b/nixos/modules/services/networking/supybot.nix
index d5b9a97a1c1..dc9fb31ffd0 100644
--- a/nixos/modules/services/networking/supybot.nix
+++ b/nixos/modules/services/networking/supybot.nix
@@ -3,32 +3,35 @@
 with lib;
 
 let
-
   cfg  = config.services.supybot;
-
+  isStateDirHome = hasPrefix "/home/" cfg.stateDir;
+  isStateDirVar = cfg.stateDir == "/var/lib/supybot";
+  pyEnv = pkgs.python3.withPackages (p: [ p.limnoria ] ++ (cfg.extraPackages p));
 in
-
 {
-
   options = {
 
     services.supybot = {
 
       enable = mkOption {
+        type = types.bool;
         default = false;
-        description = "Enable Supybot, an IRC bot";
+        description = "Enable Supybot, an IRC bot (also known as Limnoria).";
       };
 
       stateDir = mkOption {
-        # Setting this to /var/lib/supybot caused useradd to fail
-        default = "/home/supybot";
+        type = types.path;
+        default = if versionAtLeast config.system.stateVersion "20.09"
+          then "/var/lib/supybot"
+          else "/home/supybot";
+        defaultText = "/var/lib/supybot";
         description = "The root directory, logs and plugins are stored here";
       };
 
       configFile = mkOption {
         type = types.path;
         description = ''
-          Path to a supybot config file. This can be generated by
+          Path to initial supybot config file. This can be generated by
           running supybot-wizard.
 
           Note: all paths should include the full path to the stateDir
@@ -36,21 +39,54 @@ in
         '';
       };
 
+      plugins = mkOption {
+        type = types.attrsOf types.path;
+        default = {};
+        description = ''
+          Attribute set of additional plugins that will be symlinked to the
+          <filename>plugin</filename> subdirectory.
+
+          Please note that you still need to add the plugins to the config
+          file (or with <literal>!load</literal>) using their attribute name.
+        '';
+        example = literalExample ''
+          let
+            plugins = pkgs.fetchzip {
+              url = "https://github.com/ProgVal/Supybot-plugins/archive/57c2450c.zip";
+              sha256 = "077snf84ibnva3sbpzdfpfma6hcdw7dflwnhg6pw7mgnf0nd84qd";
+            };
+          in
+          {
+            Wikipedia = "''${plugins}/Wikipedia";
+            Decide = ./supy-decide;
+          }
+        '';
+      };
+
+      extraPackages = mkOption {
+        default = p: [];
+        description = ''
+          Extra Python packages available to supybot plugins. The
+          value must be a function which receives the attrset defined
+          in <varname>python3Packages</varname> as the sole argument.
+        '';
+        example = literalExample ''p: [ p.lxml p.requests ]'';
+      };
+
     };
 
   };
 
-
   config = mkIf cfg.enable {
 
-    environment.systemPackages = [ pkgs.pythonPackages.limnoria ];
+    environment.systemPackages = [ pkgs.python3Packages.limnoria ];
 
     users.users.supybot = {
       uid = config.ids.uids.supybot;
       group = "supybot";
       description = "Supybot IRC bot user";
       home = cfg.stateDir;
-      createHome = true;
+      isSystemUser = true;
     };
 
     users.groups.supybot = {
@@ -59,19 +95,16 @@ in
 
     systemd.services.supybot = {
       description = "Supybot, an IRC bot";
+      documentation = [ "https://limnoria.readthedocs.io/" ];
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
-      path = [ pkgs.pythonPackages.limnoria ];
       preStart = ''
-        cd ${cfg.stateDir}
-        mkdir -p backup conf data plugins logs/plugins tmp web
-        ln -sf ${cfg.configFile} supybot.cfg
         # This needs to be created afresh every time
-        rm -f supybot.cfg.bak
+        rm -f '${cfg.stateDir}/supybot.cfg.bak'
       '';
 
       serviceConfig = {
-        ExecStart = "${pkgs.pythonPackages.limnoria}/bin/supybot ${cfg.stateDir}/supybot.cfg";
+        ExecStart = "${pyEnv}/bin/supybot ${cfg.stateDir}/supybot.cfg";
         PIDFile = "/run/supybot.pid";
         User = "supybot";
         Group = "supybot";
@@ -79,8 +112,50 @@ in
         Restart = "on-abort";
         StartLimitInterval = "5m";
         StartLimitBurst = "1";
+
+        NoNewPrivileges = true;
+        PrivateDevices = true;
+        PrivateMounts = true;
+        PrivateTmp = true;
+        ProtectControlGroups = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
+        RestrictSUIDSGID = true;
+        SystemCallArchitectures = "native";
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        LockPersonality = true;
+        MemoryDenyWriteExecute = true;
+        RemoveIPC = true;
+        ProtectHostname = true;
+        CapabilityBoundingSet = "";
+        ProtectSystem = "full";
+      }
+      // optionalAttrs isStateDirVar {
+        StateDirectory = "supybot";
+        ProtectSystem = "strict";
+      }
+      // optionalAttrs (!isStateDirHome) {
+        ProtectHome = true;
       };
     };
 
+    systemd.tmpfiles.rules = [
+      "d '${cfg.stateDir}'              0700 supybot supybot - -"
+      "d '${cfg.stateDir}/backup'       0750 supybot supybot - -"
+      "d '${cfg.stateDir}/conf'         0750 supybot supybot - -"
+      "d '${cfg.stateDir}/data'         0750 supybot supybot - -"
+      "d '${cfg.stateDir}/plugins'      0750 supybot supybot - -"
+      "d '${cfg.stateDir}/logs'         0750 supybot supybot - -"
+      "d '${cfg.stateDir}/logs/plugins' 0750 supybot supybot - -"
+      "d '${cfg.stateDir}/tmp'          0750 supybot supybot - -"
+      "d '${cfg.stateDir}/web'          0750 supybot supybot - -"
+      "L '${cfg.stateDir}/supybot.cfg'  -    -       -       - ${cfg.configFile}"
+    ]
+    ++ (flip mapAttrsToList cfg.plugins (name: dest:
+      "L+ '${cfg.stateDir}/plugins/${name}' - - - - ${dest}"
+    ));
+
   };
 }
diff --git a/nixos/modules/services/networking/tailscale.nix b/nixos/modules/services/networking/tailscale.nix
new file mode 100644
index 00000000000..513c42b4011
--- /dev/null
+++ b/nixos/modules/services/networking/tailscale.nix
@@ -0,0 +1,46 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let cfg = config.services.tailscale;
+in {
+  meta.maintainers = with maintainers; [ danderson mbaillie ];
+
+  options.services.tailscale = {
+    enable = mkEnableOption "Tailscale client daemon";
+
+    port = mkOption {
+      type = types.port;
+      default = 41641;
+      description = "The port to listen on for tunnel traffic (0=autoselect).";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.tailscale = {
+      description = "Tailscale client daemon";
+
+      after = [ "network-pre.target" ];
+      wants = [ "network-pre.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      unitConfig = {
+        StartLimitIntervalSec = 0;
+        StartLimitBurst = 0;
+      };
+
+      serviceConfig = {
+        ExecStart =
+          "${pkgs.tailscale}/bin/tailscaled --port ${toString cfg.port}";
+
+        RuntimeDirectory = "tailscale";
+        RuntimeDirectoryMode = 755;
+
+        StateDirectory = "tailscale";
+        StateDirectoryMode = 700;
+
+        Restart = "on-failure";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/zerotierone.nix b/nixos/modules/services/networking/zerotierone.nix
index 042c4d5addd..cf39ed065a7 100644
--- a/nixos/modules/services/networking/zerotierone.nix
+++ b/nixos/modules/services/networking/zerotierone.nix
@@ -69,13 +69,14 @@ in
     environment.systemPackages = [ cfg.package ];
 
     # Prevent systemd from potentially changing the MAC address
-    environment.etc."systemd/network/50-zerotier.link".text = ''
-      [Match]
-      OriginalName=zt*
-
-      [Link]
-      AutoNegotiation=false
-      MACAddressPolicy=none
-    '';
+    systemd.network.links."50-zerotier" = {
+      matchConfig = {
+        OriginalName = "zt*";
+      };
+      linkConfig = {
+        AutoNegotiation = false;
+        MACAddressPolicy = "none";
+      };
+    };
   };
 }
diff --git a/nixos/modules/services/security/fail2ban.nix b/nixos/modules/services/security/fail2ban.nix
index cb748c93d24..3f84f9c2560 100644
--- a/nixos/modules/services/security/fail2ban.nix
+++ b/nixos/modules/services/security/fail2ban.nix
@@ -216,6 +216,10 @@ in
 
   config = mkIf cfg.enable {
 
+    warnings = mkIf (config.networking.firewall.enable == false && config.networking.nftables.enable == false) [
+      "fail2ban can not be used without a firewall"
+    ];
+
     environment.systemPackages = [ cfg.package ];
 
     environment.etc = {
diff --git a/nixos/modules/services/x11/desktop-managers/kodi.nix b/nixos/modules/services/x11/desktop-managers/kodi.nix
index 65a7b9c628e..e997b9a1134 100644
--- a/nixos/modules/services/x11/desktop-managers/kodi.nix
+++ b/nixos/modules/services/x11/desktop-managers/kodi.nix
@@ -20,7 +20,7 @@ in
     services.xserver.desktopManager.session = [{
       name = "kodi";
       start = ''
-        ${pkgs.kodi}/bin/kodi --lircdev /run/lirc/lircd --standalone &
+        LIRC_SOCKET_PATH=/run/lirc/lircd ${pkgs.kodi}/bin/kodi --standalone &
         waitPID=$!
       '';
     }];
diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix
index 14bd751ce32..49693b6f1be 100644
--- a/nixos/modules/system/activation/top-level.nix
+++ b/nixos/modules/system/activation/top-level.nix
@@ -75,7 +75,7 @@ let
       echo -n "$configurationName" > $out/configuration-name
       echo -n "systemd ${toString config.systemd.package.interfaceVersion}" > $out/init-interface-version
       echo -n "$nixosLabel" > $out/nixos-version
-      echo -n "${pkgs.stdenv.hostPlatform.system}" > $out/system
+      echo -n "${config.boot.kernelPackages.stdenv.hostPlatform.system}" > $out/system
 
       mkdir $out/fine-tune
       childCount=0
diff --git a/nixos/modules/system/boot/kernel.nix b/nixos/modules/system/boot/kernel.nix
index c247f334c23..43871f439f7 100644
--- a/nixos/modules/system/boot/kernel.nix
+++ b/nixos/modules/system/boot/kernel.nix
@@ -192,139 +192,144 @@ in
 
   ###### implementation
 
-  config = mkIf (!config.boot.isContainer) {
-
-    system.build = { inherit kernel; };
-
-    system.modulesTree = [ kernel ] ++ config.boot.extraModulePackages;
-
-    # Implement consoleLogLevel both in early boot and using sysctl
-    # (so you don't need to reboot to have changes take effect).
-    boot.kernelParams =
-      [ "loglevel=${toString config.boot.consoleLogLevel}" ] ++
-      optionals config.boot.vesa [ "vga=0x317" "nomodeset" ];
-
-    boot.kernel.sysctl."kernel.printk" = mkDefault config.boot.consoleLogLevel;
-
-    boot.kernelModules = [ "loop" "atkbd" ];
-
-    boot.initrd.availableKernelModules =
-      [ # Note: most of these (especially the SATA/PATA modules)
-        # shouldn't be included by default since nixos-generate-config
-        # detects them, but I'm keeping them for now for backwards
-        # compatibility.
-
-        # Some SATA/PATA stuff.
-        "ahci"
-        "sata_nv"
-        "sata_via"
-        "sata_sis"
-        "sata_uli"
-        "ata_piix"
-        "pata_marvell"
-
-        # Standard SCSI stuff.
-        "sd_mod"
-        "sr_mod"
-
-        # SD cards and internal eMMC drives.
-        "mmc_block"
-
-        # Support USB keyboards, in case the boot fails and we only have
-        # a USB keyboard, or for LUKS passphrase prompt.
-        "uhci_hcd"
-        "ehci_hcd"
-        "ehci_pci"
-        "ohci_hcd"
-        "ohci_pci"
-        "xhci_hcd"
-        "xhci_pci"
-        "usbhid"
-        "hid_generic" "hid_lenovo" "hid_apple" "hid_roccat"
-        "hid_logitech_hidpp" "hid_logitech_dj"
-
-      ] ++ optionals (pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64) [
-        # Misc. x86 keyboard stuff.
-        "pcips2" "atkbd" "i8042"
-
-        # x86 RTC needed by the stage 2 init script.
-        "rtc_cmos"
-      ];
-
-    boot.initrd.kernelModules =
-      [ # For LVM.
-        "dm_mod"
-      ];
-
-    # The Linux kernel >= 2.6.27 provides firmware.
-    hardware.firmware = [ kernel ];
-
-    # Create /etc/modules-load.d/nixos.conf, which is read by
-    # systemd-modules-load.service to load required kernel modules.
-    environment.etc =
-      { "modules-load.d/nixos.conf".source = kernelModulesConf;
-      };
-
-    systemd.services.systemd-modules-load =
-      { wantedBy = [ "multi-user.target" ];
-        restartTriggers = [ kernelModulesConf ];
-        serviceConfig =
-          { # Ignore failed module loads.  Typically some of the
-            # modules in ‘boot.kernelModules’ are "nice to have but
-            # not required" (e.g. acpi-cpufreq), so we don't want to
-            # barf on those.
-            SuccessExitStatus = "0 1";
+  config = mkMerge
+    [ (mkIf config.boot.initrd.enable {
+        boot.initrd.availableKernelModules =
+          [ # Note: most of these (especially the SATA/PATA modules)
+            # shouldn't be included by default since nixos-generate-config
+            # detects them, but I'm keeping them for now for backwards
+            # compatibility.
+
+            # Some SATA/PATA stuff.
+            "ahci"
+            "sata_nv"
+            "sata_via"
+            "sata_sis"
+            "sata_uli"
+            "ata_piix"
+            "pata_marvell"
+
+            # Standard SCSI stuff.
+            "sd_mod"
+            "sr_mod"
+
+            # SD cards and internal eMMC drives.
+            "mmc_block"
+
+            # Support USB keyboards, in case the boot fails and we only have
+            # a USB keyboard, or for LUKS passphrase prompt.
+            "uhci_hcd"
+            "ehci_hcd"
+            "ehci_pci"
+            "ohci_hcd"
+            "ohci_pci"
+            "xhci_hcd"
+            "xhci_pci"
+            "usbhid"
+            "hid_generic" "hid_lenovo" "hid_apple" "hid_roccat"
+            "hid_logitech_hidpp" "hid_logitech_dj"
+
+          ] ++ optionals (pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64) [
+            # Misc. x86 keyboard stuff.
+            "pcips2" "atkbd" "i8042"
+
+            # x86 RTC needed by the stage 2 init script.
+            "rtc_cmos"
+          ];
+
+        boot.initrd.kernelModules =
+          [ # For LVM.
+            "dm_mod"
+          ];
+      })
+
+      (mkIf (!config.boot.isContainer) {
+        system.build = { inherit kernel; };
+
+        system.modulesTree = [ kernel ] ++ config.boot.extraModulePackages;
+
+        # Implement consoleLogLevel both in early boot and using sysctl
+        # (so you don't need to reboot to have changes take effect).
+        boot.kernelParams =
+          [ "loglevel=${toString config.boot.consoleLogLevel}" ] ++
+          optionals config.boot.vesa [ "vga=0x317" "nomodeset" ];
+
+        boot.kernel.sysctl."kernel.printk" = mkDefault config.boot.consoleLogLevel;
+
+        boot.kernelModules = [ "loop" "atkbd" ];
+
+        # The Linux kernel >= 2.6.27 provides firmware.
+        hardware.firmware = [ kernel ];
+
+        # Create /etc/modules-load.d/nixos.conf, which is read by
+        # systemd-modules-load.service to load required kernel modules.
+        environment.etc =
+          { "modules-load.d/nixos.conf".source = kernelModulesConf;
           };
-      };
-
-    lib.kernelConfig = {
-      isYes = option: {
-        assertion = config: config.isYes option;
-        message = "CONFIG_${option} is not yes!";
-        configLine = "CONFIG_${option}=y";
-      };
-
-      isNo = option: {
-        assertion = config: config.isNo option;
-        message = "CONFIG_${option} is not no!";
-        configLine = "CONFIG_${option}=n";
-      };
-
-      isModule = option: {
-        assertion = config: config.isModule option;
-        message = "CONFIG_${option} is not built as a module!";
-        configLine = "CONFIG_${option}=m";
-      };
-
-      ### Usually you will just want to use these two
-      # True if yes or module
-      isEnabled = option: {
-        assertion = config: config.isEnabled option;
-        message = "CONFIG_${option} is not enabled!";
-        configLine = "CONFIG_${option}=y";
-      };
-
-      # True if no or omitted
-      isDisabled = option: {
-        assertion = config: config.isDisabled option;
-        message = "CONFIG_${option} is not disabled!";
-        configLine = "CONFIG_${option}=n";
-      };
-    };
 
-    # The config options that all modules can depend upon
-    system.requiredKernelConfig = with config.lib.kernelConfig; [
-      # !!! Should this really be needed?
-      (isYes "MODULES")
-      (isYes "BINFMT_ELF")
-    ] ++ (optional (randstructSeed != "") (isYes "GCC_PLUGIN_RANDSTRUCT"));
+        systemd.services.systemd-modules-load =
+          { wantedBy = [ "multi-user.target" ];
+            restartTriggers = [ kernelModulesConf ];
+            serviceConfig =
+              { # Ignore failed module loads.  Typically some of the
+                # modules in ‘boot.kernelModules’ are "nice to have but
+                # not required" (e.g. acpi-cpufreq), so we don't want to
+                # barf on those.
+                SuccessExitStatus = "0 1";
+              };
+          };
 
-    # nixpkgs kernels are assumed to have all required features
-    assertions = if config.boot.kernelPackages.kernel ? features then [] else
-      let cfg = config.boot.kernelPackages.kernel.config; in map (attrs:
-        { assertion = attrs.assertion cfg; inherit (attrs) message; }
-      ) config.system.requiredKernelConfig;
+        lib.kernelConfig = {
+          isYes = option: {
+            assertion = config: config.isYes option;
+            message = "CONFIG_${option} is not yes!";
+            configLine = "CONFIG_${option}=y";
+          };
 
-  };
+          isNo = option: {
+            assertion = config: config.isNo option;
+            message = "CONFIG_${option} is not no!";
+            configLine = "CONFIG_${option}=n";
+          };
+
+          isModule = option: {
+            assertion = config: config.isModule option;
+            message = "CONFIG_${option} is not built as a module!";
+            configLine = "CONFIG_${option}=m";
+          };
+
+          ### Usually you will just want to use these two
+          # True if yes or module
+          isEnabled = option: {
+            assertion = config: config.isEnabled option;
+            message = "CONFIG_${option} is not enabled!";
+            configLine = "CONFIG_${option}=y";
+          };
+
+          # True if no or omitted
+          isDisabled = option: {
+            assertion = config: config.isDisabled option;
+            message = "CONFIG_${option} is not disabled!";
+            configLine = "CONFIG_${option}=n";
+          };
+        };
+
+        # The config options that all modules can depend upon
+        system.requiredKernelConfig = with config.lib.kernelConfig;
+          [
+            # !!! Should this really be needed?
+            (isYes "MODULES")
+            (isYes "BINFMT_ELF")
+          ] ++ (optional (randstructSeed != "") (isYes "GCC_PLUGIN_RANDSTRUCT"));
+
+        # nixpkgs kernels are assumed to have all required features
+        assertions = if config.boot.kernelPackages.kernel ? features then [] else
+          let cfg = config.boot.kernelPackages.kernel.config; in map (attrs:
+            { assertion = attrs.assertion cfg; inherit (attrs) message; }
+          ) config.system.requiredKernelConfig;
+
+      })
+
+    ];
 
 }
diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix
index 6dfbe66fc64..3078f84f6e9 100644
--- a/nixos/modules/system/boot/networkd.nix
+++ b/nixos/modules/system/boot/networkd.nix
@@ -355,6 +355,14 @@ let
   };
 
   linkOptions = commonNetworkOptions // {
+    # overwrite enable option from above
+    enable = mkOption {
+      default = true;
+      type = types.bool;
+      description = ''
+        Whether to enable this .link unit. It's handled by udev no matter if <command>systemd-networkd</command> is enabled or not
+      '';
+    };
 
     linkConfig = mkOption {
       default = {};
@@ -1045,44 +1053,49 @@ in
 
   };
 
-  config = mkIf config.systemd.network.enable {
+  config = mkMerge [
+    # .link units are honored by udev, no matter if systemd-networkd is enabled or not.
+    {
+      systemd.network.units = mapAttrs' (n: v: nameValuePair "${n}.link" (linkToUnit n v)) cfg.links;
+      environment.etc = unitFiles;
+    }
 
-    users.users.systemd-network.group = "systemd-network";
+    (mkIf config.systemd.network.enable {
 
-    systemd.additionalUpstreamSystemUnits = [
-      "systemd-networkd.service" "systemd-networkd-wait-online.service"
-    ];
+      users.users.systemd-network.group = "systemd-network";
 
-    systemd.network.units = mapAttrs' (n: v: nameValuePair "${n}.link" (linkToUnit n v)) cfg.links
-      // mapAttrs' (n: v: nameValuePair "${n}.netdev" (netdevToUnit n v)) cfg.netdevs
-      // mapAttrs' (n: v: nameValuePair "${n}.network" (networkToUnit n v)) cfg.networks;
+      systemd.additionalUpstreamSystemUnits = [
+        "systemd-networkd.service" "systemd-networkd-wait-online.service"
+      ];
 
-    environment.etc = unitFiles;
+      systemd.network.units = mapAttrs' (n: v: nameValuePair "${n}.netdev" (netdevToUnit n v)) cfg.netdevs
+        // mapAttrs' (n: v: nameValuePair "${n}.network" (networkToUnit n v)) cfg.networks;
 
-    systemd.services.systemd-networkd = {
-      wantedBy = [ "multi-user.target" ];
-      restartTriggers = attrNames unitFiles;
-      # prevent race condition with interface renaming (#39069)
-      requires = [ "systemd-udev-settle.service" ];
-      after = [ "systemd-udev-settle.service" ];
-    };
+      systemd.services.systemd-networkd = {
+        wantedBy = [ "multi-user.target" ];
+        restartTriggers = attrNames unitFiles;
+        # prevent race condition with interface renaming (#39069)
+        requires = [ "systemd-udev-settle.service" ];
+        after = [ "systemd-udev-settle.service" ];
+      };
 
-    systemd.services.systemd-networkd-wait-online = {
-      wantedBy = [ "network-online.target" ];
-    };
+      systemd.services.systemd-networkd-wait-online = {
+        wantedBy = [ "network-online.target" ];
+      };
 
-    systemd.services."systemd-network-wait-online@" = {
-      description = "Wait for Network Interface %I to be Configured";
-      conflicts = [ "shutdown.target" ];
-      requisite = [ "systemd-networkd.service" ];
-      after = [ "systemd-networkd.service" ];
-      serviceConfig = {
-        Type = "oneshot";
-        RemainAfterExit = true;
-        ExecStart = "${config.systemd.package}/lib/systemd/systemd-networkd-wait-online -i %I";
+      systemd.services."systemd-network-wait-online@" = {
+        description = "Wait for Network Interface %I to be Configured";
+        conflicts = [ "shutdown.target" ];
+        requisite = [ "systemd-networkd.service" ];
+        after = [ "systemd-networkd.service" ];
+        serviceConfig = {
+          Type = "oneshot";
+          RemainAfterExit = true;
+          ExecStart = "${config.systemd.package}/lib/systemd/systemd-networkd-wait-online -i %I";
+        };
       };
-    };
 
-    services.resolved.enable = mkDefault true;
-  };
+      services.resolved.enable = mkDefault true;
+    })
+  ];
 }
diff --git a/nixos/modules/system/boot/stage-1.nix b/nixos/modules/system/boot/stage-1.nix
index 26117cffeda..93cd801ef80 100644
--- a/nixos/modules/system/boot/stage-1.nix
+++ b/nixos/modules/system/boot/stage-1.nix
@@ -390,6 +390,17 @@ in
       '';
     };
 
+    boot.initrd.enable = mkOption {
+      type = types.bool;
+      default = !config.boot.isContainer;
+      defaultText = "!config.boot.isContainer";
+      description = ''
+        Whether to enable the NixOS initial RAM disk (initrd). This may be
+        needed to perform some initialisation tasks (like mounting
+        network/encrypted file systems) before continuing the boot process.
+      '';
+    };
+
     boot.initrd.prepend = mkOption {
       default = [ ];
       type = types.listOf types.str;
@@ -555,7 +566,7 @@ in
 
   };
 
-  config = mkIf (!config.boot.isContainer) {
+  config = mkIf config.boot.initrd.enable {
     assertions = [
       { assertion = any (fs: fs.mountPoint == "/") fileSystems;
         message = "The ‘fileSystems’ option does not specify your root file system.";
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index 7dd0f23df65..51b463747b0 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -135,6 +135,7 @@ in
   initrd-network-ssh = handleTest ./initrd-network-ssh {};
   initrdNetwork = handleTest ./initrd-network.nix {};
   installer = handleTest ./installer.nix {};
+  iodine = handleTest ./iodine.nix {};
   ipv6 = handleTest ./ipv6.nix {};
   jackett = handleTest ./jackett.nix {};
   jellyfin = handleTest ./jellyfin.nix {};
diff --git a/nixos/tests/fenics.nix b/nixos/tests/fenics.nix
new file mode 100644
index 00000000000..7252d19e4e6
--- /dev/null
+++ b/nixos/tests/fenics.nix
@@ -0,0 +1,50 @@
+import ./make-test-python.nix ({ pkgs, ... }:
+
+let
+  fenicsScript = pkgs.writeScript "poisson.py" ''
+    #!/usr/bin/env python
+    from dolfin import *
+
+    mesh = UnitSquareMesh(4, 4)
+    V = FunctionSpace(mesh, "Lagrange", 1)
+
+    def boundary(x):
+        return x[0] < DOLFIN_EPS or x[0] > 1.0 - DOLFIN_EPS
+
+    u0 = Constant(0.0)
+    bc = DirichletBC(V, u0, boundary)
+
+    u = TrialFunction(V)
+    v = TestFunction(V)
+    f = Expression("10*exp(-(pow(x[0] - 0.5, 2) + pow(x[1] - 0.5, 2)) / 0.02)", degree=2)
+    g = Expression("sin(5*x[0])", degree=2)
+    a = inner(grad(u), grad(v))*dx
+    L = f*v*dx + g*v*ds
+
+    u = Function(V)
+    solve(a == L, u, bc)
+    print(u)
+  '';
+in
+{
+  name = "fenics";
+  meta = {
+    maintainers = with pkgs.stdenv.lib.maintainers; [ knedlsepp ];
+  };
+
+  nodes = {
+    fenicsnode = { pkgs, ... }: {
+      environment.systemPackages = with pkgs; [
+        gcc
+        (python3.withPackages (ps: with ps; [ fenics ]))
+      ];
+      virtualisation.memorySize = 512;
+    };
+  };
+  testScript =
+    { nodes, ... }:
+    ''
+      start_all()
+      node1.succeed("${fenicsScript}")
+    '';
+})
diff --git a/nixos/tests/iodine.nix b/nixos/tests/iodine.nix
new file mode 100644
index 00000000000..8bd9603a6d6
--- /dev/null
+++ b/nixos/tests/iodine.nix
@@ -0,0 +1,63 @@
+import ./make-test-python.nix (
+  { pkgs, ... }: let
+    domain = "whatever.example.com";
+  in
+    {
+      name = "iodine";
+      nodes = {
+        server =
+          { ... }:
+
+            {
+              networking.firewall = {
+                allowedUDPPorts = [ 53 ];
+                trustedInterfaces = [ "dns0" ];
+              };
+              boot.kernel.sysctl = {
+                "net.ipv4.ip_forward" = 1;
+                "net.ipv6.ip_forward" = 1;
+              };
+
+              services.iodine.server = {
+                enable = true;
+                ip = "10.53.53.1/24";
+                passwordFile = "${builtins.toFile "password" "foo"}";
+                inherit domain;
+              };
+
+              # test resource: accessible only via tunnel
+              services.openssh = {
+                enable = true;
+                openFirewall = false;
+              };
+            };
+
+        client =
+          { ... }: {
+            services.iodine.clients.testClient = {
+              # test that ProtectHome is "read-only"
+              passwordFile = "/root/pw";
+              relay = "server";
+              server = domain;
+            };
+            systemd.tmpfiles.rules = [
+              "f /root/pw 0666 root root - foo"
+            ];
+            environment.systemPackages = [
+              pkgs.nagiosPluginsOfficial
+            ];
+          };
+
+      };
+
+      testScript = ''
+        start_all()
+
+        server.wait_for_unit("sshd")
+        server.wait_for_unit("iodined")
+        client.wait_for_unit("iodine-testClient")
+
+        client.succeed("check_ssh -H 10.53.53.1")
+      '';
+    }
+)
diff --git a/nixos/tests/matrix-synapse.nix b/nixos/tests/matrix-synapse.nix
index fca53009083..f3623aa3c09 100644
--- a/nixos/tests/matrix-synapse.nix
+++ b/nixos/tests/matrix-synapse.nix
@@ -35,12 +35,31 @@ in {
 
   nodes = {
     # Since 0.33.0, matrix-synapse doesn't allow underscores in server names
-    serverpostgres = args: {
+    serverpostgres = { pkgs, ... }: {
       services.matrix-synapse = {
         enable = true;
         database_type = "psycopg2";
         tls_certificate_path = "${cert}";
         tls_private_key_path = "${key}";
+        database_args = {
+          password = "synapse";
+        };
+      };
+      services.postgresql = {
+        enable = true;
+
+        # The database name and user are configured by the following options:
+        #   - services.matrix-synapse.database_name
+        #   - services.matrix-synapse.database_user
+        #
+        # The values used here represent the default values of the module.
+        initialScript = pkgs.writeText "synapse-init.sql" ''
+          CREATE ROLE "matrix-synapse" WITH LOGIN PASSWORD 'synapse';
+          CREATE DATABASE "matrix-synapse" WITH OWNER "matrix-synapse"
+            TEMPLATE template0
+            LC_COLLATE = "C"
+            LC_CTYPE = "C";
+        '';
       };
     };
 
diff --git a/nixos/tests/networking.nix b/nixos/tests/networking.nix
index 933a4451af9..0a6507d2dc8 100644
--- a/nixos/tests/networking.nix
+++ b/nixos/tests/networking.nix
@@ -655,6 +655,31 @@ let
             ), "The IPv6 routing table has not been properly cleaned:\n{}".format(ipv6Residue)
       '';
     };
+    # even with disabled networkd, systemd.network.links should work
+    # (as it's handled by udev, not networkd)
+    link = {
+      name = "Link";
+      nodes.client = { pkgs, ... }: {
+        virtualisation.vlans = [ 1 ];
+        networking = {
+          useNetworkd = networkd;
+          useDHCP = false;
+        };
+        systemd.network.links."50-foo" = {
+          matchConfig = {
+            Name = "foo";
+            Driver = "dummy";
+          };
+          linkConfig.MTUBytes = "1442";
+        };
+      };
+      testScript = ''
+        print(client.succeed("ip l add name foo type dummy"))
+        print(client.succeed("stat /etc/systemd/network/50-foo.link"))
+        client.succeed("udevadm settle")
+        assert "mtu 1442" in client.succeed("ip l show dummy0")
+      '';
+    };
   };
 
 in mapAttrs (const (attrs: makeTest (attrs // {