about summary refs log tree commit diff
path: root/nixos/modules
diff options
context:
space:
mode:
authorFrederik Rietdijk <fridh@fridh.nl>2020-06-04 15:25:54 +0200
committerFrederik Rietdijk <fridh@fridh.nl>2020-06-04 15:25:54 +0200
commit08900c0554657442c60c86e54081325c0f4cedce (patch)
tree4e7a2e330991ee3eb08cf6b51d3f44ae47ae03f3 /nixos/modules
parent458b2cb7d67561c796c6f0660a97eea9b685fc65 (diff)
parent3327b9a385f5ec2e219bf93b48896772b3f66afc (diff)
downloadnixlib-08900c0554657442c60c86e54081325c0f4cedce.tar
nixlib-08900c0554657442c60c86e54081325c0f4cedce.tar.gz
nixlib-08900c0554657442c60c86e54081325c0f4cedce.tar.bz2
nixlib-08900c0554657442c60c86e54081325c0f4cedce.tar.lz
nixlib-08900c0554657442c60c86e54081325c0f4cedce.tar.xz
nixlib-08900c0554657442c60c86e54081325c0f4cedce.tar.zst
nixlib-08900c0554657442c60c86e54081325c0f4cedce.zip
Merge master into staging-next
Diffstat (limited to 'nixos/modules')
-rw-r--r--nixos/modules/installer/cd-dvd/sd-image.nix2
-rw-r--r--nixos/modules/installer/tools/nix-fallback-paths.nix8
-rw-r--r--nixos/modules/module-list.nix2
-rw-r--r--nixos/modules/programs/chromium.nix19
-rw-r--r--nixos/modules/programs/shadow.nix5
-rw-r--r--nixos/modules/services/misc/gitlab.nix2
-rw-r--r--nixos/modules/services/misc/home-assistant.nix10
-rw-r--r--nixos/modules/services/misc/matrix-appservice-discord.nix162
-rw-r--r--nixos/modules/services/misc/mautrix-telegram.nix163
-rw-r--r--nixos/modules/services/networking/haproxy.nix32
-rw-r--r--nixos/modules/services/networking/v2ray.nix8
-rw-r--r--nixos/modules/services/networking/vsftpd.nix3
-rw-r--r--nixos/modules/services/system/nscd.conf2
-rw-r--r--nixos/modules/tasks/network-interfaces.nix8
-rw-r--r--nixos/modules/virtualisation/kvmgt.nix52
-rw-r--r--nixos/modules/virtualisation/libvirtd.nix2
-rw-r--r--nixos/modules/virtualisation/qemu-vm.nix2
17 files changed, 430 insertions, 52 deletions
diff --git a/nixos/modules/installer/cd-dvd/sd-image.nix b/nixos/modules/installer/cd-dvd/sd-image.nix
index 901c60befb6c..4187c01d70eb 100644
--- a/nixos/modules/installer/cd-dvd/sd-image.nix
+++ b/nixos/modules/installer/cd-dvd/sd-image.nix
@@ -68,7 +68,7 @@ in
       default = null;
       example = "14e19a7b-0ae0-484d-9d54-43bd6fdc20c7";
       description = ''
-        UUID for the main NixOS partition on the SD card.
+        UUID for the filesystem on the main NixOS partition on the SD card.
       '';
     };
 
diff --git a/nixos/modules/installer/tools/nix-fallback-paths.nix b/nixos/modules/installer/tools/nix-fallback-paths.nix
index bfd8970d2b2b..35a7b382b7cf 100644
--- a/nixos/modules/installer/tools/nix-fallback-paths.nix
+++ b/nixos/modules/installer/tools/nix-fallback-paths.nix
@@ -1,6 +1,6 @@
 {
-  x86_64-linux = "/nix/store/xb0nl3z356n0sfrhswfli2g19a19slys-nix-2.3.5";
-  i686-linux = "/nix/store/k8kdd4yy1yap6lai5idyhmzcwsjh1fik-nix-2.3.5";
-  aarch64-linux = "/nix/store/dr86cbipxqjcb8pf2k0v8wvw0h0adfpz-nix-2.3.5";
-  x86_64-darwin = "/nix/store/n6dqdndkv9kac66kdr988kaiyavl44x8-nix-2.3.5";
+  x86_64-linux = "/nix/store/j8dbv5w6jl34caywh2ygdy88knx1mdf7-nix-2.3.6";
+  i686-linux = "/nix/store/9fqvbdisahqp0238vrs7wn5anpri0a65-nix-2.3.6";
+  aarch64-linux = "/nix/store/72pwn0nm9bjqx9vpi8sgh4bl6g5wh814-nix-2.3.6";
+  x86_64-darwin = "/nix/store/g37vk77m90p5zcl5nixjlzp3vqpisfn5-nix-2.3.6";
 }
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index d4e6f753e9f9..e8c4a4c14cf0 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -462,7 +462,9 @@
   ./services/misc/lidarr.nix
   ./services/misc/mame.nix
   ./services/misc/mathics.nix
+  ./services/misc/matrix-appservice-discord.nix
   ./services/misc/matrix-synapse.nix
+  ./services/misc/mautrix-telegram.nix
   ./services/misc/mbpfan.nix
   ./services/misc/mediatomb.nix
   ./services/misc/mesos-master.nix
diff --git a/nixos/modules/programs/chromium.nix b/nixos/modules/programs/chromium.nix
index 16c063ebc89d..3f0429136190 100644
--- a/nixos/modules/programs/chromium.nix
+++ b/nixos/modules/programs/chromium.nix
@@ -69,11 +69,24 @@ in
       extraOpts = mkOption {
         type = types.attrs;
         description = ''
-          Extra chromium policy options, see
-          <link xlink:href="https://www.chromium.org/administrators/policy-list-3">https://www.chromium.org/administrators/policy-list-3</link>
-          for a list of avalible options
+          Extra chromium policy options. A list of available policies
+          can be found in the Chrome Enterprise documentation:
+          <link xlink:href="https://cloud.google.com/docs/chrome-enterprise/policies/">https://cloud.google.com/docs/chrome-enterprise/policies/</link>
+          Make sure the selected policy is supported on Linux and your browser version.
         '';
         default = {};
+        example = literalExample ''
+          {
+            "BrowserSignin" = 0;
+            "SyncDisabled" = true;
+            "PasswordManagerEnabled" = false;
+            "SpellcheckEnabled" = true;
+            "SpellcheckLanguage" = [
+                                     "de"
+                                     "en-US"
+                                   ];
+          }
+        '';
       };
     };
   };
diff --git a/nixos/modules/programs/shadow.nix b/nixos/modules/programs/shadow.nix
index fc352795c017..386ded9d98b6 100644
--- a/nixos/modules/programs/shadow.nix
+++ b/nixos/modules/programs/shadow.nix
@@ -114,8 +114,9 @@ in
       newgrp.source    = "${pkgs.shadow.out}/bin/newgrp";
       newuidmap.source = "${pkgs.shadow.out}/bin/newuidmap";
       newgidmap.source = "${pkgs.shadow.out}/bin/newgidmap";
-    } // (if config.users.mutableUsers then {
+    } // lib.optionalAttrs config.users.mutableUsers {
+      chsh.source      = "${pkgs.shadow.out}/bin/chsh";
       passwd.source    = "${pkgs.shadow.out}/bin/passwd";
-    } else {});
+    };
   };
 }
diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix
index 730166b04d20..7b2bbf89a446 100644
--- a/nixos/modules/services/misc/gitlab.nix
+++ b/nixos/modules/services/misc/gitlab.nix
@@ -129,7 +129,7 @@ let
     HOME = "${cfg.statePath}/home";
     UNICORN_PATH = "${cfg.statePath}/";
     GITLAB_PATH = "${cfg.packages.gitlab}/share/gitlab/";
-    SCHEMA = "${cfg.statePath}/db/schema.rb";
+    SCHEMA = "${cfg.statePath}/db/structure.sql";
     GITLAB_UPLOADS_PATH = "${cfg.statePath}/uploads";
     GITLAB_LOG_PATH = "${cfg.statePath}/log";
     GITLAB_REDIS_CONFIG_FILE = pkgs.writeText "redis.yml" (builtins.toJSON redisConfig);
diff --git a/nixos/modules/services/misc/home-assistant.nix b/nixos/modules/services/misc/home-assistant.nix
index 86033d02bf3f..8ce2437841b0 100644
--- a/nixos/modules/services/misc/home-assistant.nix
+++ b/nixos/modules/services/misc/home-assistant.nix
@@ -11,9 +11,9 @@ let
     (recursiveUpdate defaultConfig cfg.config) else cfg.config));
   configFile = pkgs.runCommand "configuration.yaml" { preferLocalBuild = true; } ''
     ${pkgs.remarshal}/bin/json2yaml -i ${configJSON} -o $out
-    # Hack to support secrets, that are encoded as custom yaml objects,
-    # https://www.home-assistant.io/docs/configuration/secrets/
-    sed -i -e "s/'\!secret \(.*\)'/\!secret \1/" $out
+    # Hack to support custom yaml objects,
+    # i.e. secrets: https://www.home-assistant.io/docs/configuration/secrets/
+    sed -i -e "s/'\!\([a-z_]\+\) \(.*\)'/\!\1 \2/;s/^\!\!/\!/;" $out
   '';
 
   lovelaceConfigJSON = pkgs.writeText "ui-lovelace.json"
@@ -120,7 +120,9 @@ in {
             unit_system = "metric";
             time_zone = "UTC";
           };
-          frontend = { };
+          frontend = {
+            themes = "!include_dir_merge_named themes";
+          };
           http = { };
           feedreader.urls = [ "https://nixos.org/blogs.xml" ];
         }
diff --git a/nixos/modules/services/misc/matrix-appservice-discord.nix b/nixos/modules/services/misc/matrix-appservice-discord.nix
new file mode 100644
index 000000000000..49c41ff637a8
--- /dev/null
+++ b/nixos/modules/services/misc/matrix-appservice-discord.nix
@@ -0,0 +1,162 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  dataDir = "/var/lib/matrix-appservice-discord";
+  registrationFile = "${dataDir}/discord-registration.yaml";
+  appDir = "${pkgs.matrix-appservice-discord}/lib/node_modules/matrix-appservice-discord";
+  cfg = config.services.matrix-appservice-discord;
+  # TODO: switch to configGen.json once RFC42 is implemented
+  settingsFile = pkgs.writeText "matrix-appservice-discord-settings.json" (builtins.toJSON cfg.settings);
+
+in {
+  options = {
+    services.matrix-appservice-discord = {
+      enable = mkEnableOption "a bridge between Matrix and Discord";
+
+      settings = mkOption rec {
+        # TODO: switch to types.config.json as prescribed by RFC42 once it's implemented
+        type = types.attrs;
+        apply = recursiveUpdate default;
+        default = {
+          database = {
+            filename = "${dataDir}/discord.db";
+
+            # TODO: remove those old config keys once the following issues are solved:
+            # * https://github.com/Half-Shot/matrix-appservice-discord/issues/490
+            # * https://github.com/Half-Shot/matrix-appservice-discord/issues/498
+            userStorePath = "${dataDir}/user-store.db";
+            roomStorePath = "${dataDir}/room-store.db";
+          };
+
+          # empty values necessary for registration file generation
+          # actual values defined in environmentFile
+          auth = {
+            clientID = "";
+            botToken = "";
+          };
+        };
+        example = literalExample ''
+          {
+            bridge = {
+              domain = "public-domain.tld";
+              homeserverUrl = "http://public-domain.tld:8008";
+            };
+          }
+        '';
+        description = ''
+          <filename>config.yaml</filename> configuration as a Nix attribute set.
+          </para>
+
+          <para>
+          Configuration options should match those described in
+          <link xlink:href="https://github.com/Half-Shot/matrix-appservice-discord/blob/master/config/config.sample.yaml">
+          config.sample.yaml</link>.
+          </para>
+
+          <para>
+          <option>config.bridge.domain</option> and <option>config.bridge.homeserverUrl</option>
+          should be set to match the public host name of the Matrix homeserver for webhooks and avatars to work.
+          </para>
+
+          <para>
+          Secret tokens should be specified using <option>environmentFile</option>
+          instead of this world-readable attribute set.
+        '';
+      };
+
+      environmentFile = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        description = ''
+          File containing environment variables to be passed to the matrix-appservice-discord service,
+          in which secret tokens can be specified securely by defining values for
+          <literal>APPSERVICE_DISCORD_AUTH_CLIENT_I_D</literal> and
+          <literal>APPSERVICE_DISCORD_AUTH_BOT_TOKEN</literal>.
+        '';
+      };
+
+      url = mkOption {
+        type = types.str;
+        default = "http://localhost:${toString cfg.port}";
+        description = ''
+          The URL where the application service is listening for HS requests.
+        '';
+      };
+
+      port = mkOption {
+        type = types.port;
+        default = 9005; # from https://github.com/Half-Shot/matrix-appservice-discord/blob/master/package.json#L11
+        description = ''
+          Port number on which the bridge should listen for internal communication with the Matrix homeserver.
+        '';
+      };
+
+      localpart = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          The user_id localpart to assign to the AS.
+        '';
+      };
+
+      serviceDependencies = mkOption {
+        type = with types; listOf str;
+        default = optional config.services.matrix-synapse.enable "matrix-synapse.service";
+        description = ''
+          List of Systemd services to require and wait for when starting the application service,
+          such as the Matrix homeserver if it's running on the same host.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.matrix-appservice-discord = {
+      description = "A bridge between Matrix and Discord.";
+
+      wantedBy = [ "multi-user.target" ];
+      wants = [ "network-online.target" ] ++ cfg.serviceDependencies;
+      after = [ "network-online.target" ] ++ cfg.serviceDependencies;
+
+      preStart = ''
+        if [ ! -f '${registrationFile}' ]; then
+          ${pkgs.matrix-appservice-discord}/bin/matrix-appservice-discord \
+            --generate-registration \
+            --url=${escapeShellArg cfg.url} \
+            ${optionalString (cfg.localpart != null) "--localpart=${escapeShellArg cfg.localpart}"} \
+            --config='${settingsFile}' \
+            --file='${registrationFile}'
+        fi
+      '';
+
+      serviceConfig = {
+        Type = "simple";
+        Restart = "always";
+
+        ProtectSystem = "strict";
+        ProtectHome = true;
+        ProtectKernelTunables = true;
+        ProtectKernelModules = true;
+        ProtectControlGroups = true;
+
+        DynamicUser = true;
+        PrivateTmp = true;
+        WorkingDirectory = appDir;
+        StateDirectory = baseNameOf dataDir;
+        UMask = 0027;
+        EnvironmentFile = cfg.environmentFile;
+
+        ExecStart = ''
+          ${pkgs.matrix-appservice-discord}/bin/matrix-appservice-discord \
+            --file='${registrationFile}' \
+            --config='${settingsFile}' \
+            --port='${toString cfg.port}'
+        '';
+      };
+    };
+  };
+
+  meta.maintainers = with maintainers; [ pacien ];
+}
diff --git a/nixos/modules/services/misc/mautrix-telegram.nix b/nixos/modules/services/misc/mautrix-telegram.nix
new file mode 100644
index 000000000000..78a42fbb574b
--- /dev/null
+++ b/nixos/modules/services/misc/mautrix-telegram.nix
@@ -0,0 +1,163 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  dataDir = "/var/lib/mautrix-telegram";
+  registrationFile = "${dataDir}/telegram-registration.yaml";
+  cfg = config.services.mautrix-telegram;
+  # TODO: switch to configGen.json once RFC42 is implemented
+  settingsFile = pkgs.writeText "mautrix-telegram-settings.json" (builtins.toJSON cfg.settings);
+
+in {
+  options = {
+    services.mautrix-telegram = {
+      enable = mkEnableOption "Mautrix-Telegram, a Matrix-Telegram hybrid puppeting/relaybot bridge";
+
+      settings = mkOption rec {
+        # TODO: switch to types.config.json as prescribed by RFC42 once it's implemented
+        type = types.attrs;
+        apply = recursiveUpdate default;
+        default = {
+          appservice = rec {
+            database = "sqlite:///${dataDir}/mautrix-telegram.db";
+            hostname = "0.0.0.0";
+            port = 8080;
+            address = "http://localhost:${toString port}";
+          };
+
+          bridge = {
+            permissions."*" = "relaybot";
+            relaybot.whitelist = [ ];
+          };
+
+          logging = {
+            version = 1;
+
+            formatters.precise.format = "[%(levelname)s@%(name)s] %(message)s";
+
+            handlers.console = {
+              class = "logging.StreamHandler";
+              formatter = "precise";
+            };
+
+            loggers = {
+              mau.level = "INFO";
+              telethon.level = "INFO";
+
+              # prevent tokens from leaking in the logs:
+              # https://github.com/tulir/mautrix-telegram/issues/351
+              aiohttp.level = "WARNING";
+            };
+
+            # log to console/systemd instead of file
+            root = {
+              level = "INFO";
+              handlers = [ "console" ];
+            };
+          };
+        };
+        example = literalExample ''
+          {
+            homeserver = {
+              address = "http://localhost:8008";
+              domain = "public-domain.tld";
+            };
+
+            appservice.public = {
+              prefix = "/public";
+              external = "https://public-appservice-address/public";
+            };
+
+            bridge.permissions = {
+              "example.com" = "full";
+              "@admin:example.com" = "admin";
+            };
+          }
+        '';
+        description = ''
+          <filename>config.yaml</filename> configuration as a Nix attribute set.
+          Configuration options should match those described in
+          <link xlink:href="https://github.com/tulir/mautrix-telegram/blob/master/example-config.yaml">
+          example-config.yaml</link>.
+          </para>
+
+          <para>
+          Secret tokens should be specified using <option>environmentFile</option>
+          instead of this world-readable attribute set.
+        '';
+      };
+
+      environmentFile = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        description = ''
+          File containing environment variables to be passed to the mautrix-telegram service,
+          in which secret tokens can be specified securely by defining values for
+          <literal>MAUTRIX_TELEGRAM_APPSERVICE_AS_TOKEN</literal>,
+          <literal>MAUTRIX_TELEGRAM_APPSERVICE_HS_TOKEN</literal>,
+          <literal>MAUTRIX_TELEGRAM_TELEGRAM_API_ID</literal>,
+          <literal>MAUTRIX_TELEGRAM_TELEGRAM_API_HASH</literal> and optionally
+          <literal>MAUTRIX_TELEGRAM_TELEGRAM_BOT_TOKEN</literal>.
+        '';
+      };
+
+      serviceDependencies = mkOption {
+        type = with types; listOf str;
+        default = optional config.services.matrix-synapse.enable "matrix-synapse.service";
+        description = ''
+          List of Systemd services to require and wait for when starting the application service.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.mautrix-telegram = {
+      description = "Mautrix-Telegram, a Matrix-Telegram hybrid puppeting/relaybot bridge.";
+
+      wantedBy = [ "multi-user.target" ];
+      wants = [ "network-online.target" ] ++ cfg.serviceDependencies;
+      after = [ "network-online.target" ] ++ cfg.serviceDependencies;
+
+      preStart = ''
+        # generate the appservice's registration file if absent
+        if [ ! -f '${registrationFile}' ]; then
+          ${pkgs.mautrix-telegram}/bin/mautrix-telegram \
+            --generate-registration \
+            --base-config='${pkgs.mautrix-telegram}/example-config.yaml' \
+            --config='${settingsFile}' \
+            --registration='${registrationFile}'
+        fi
+
+        # run automatic database init and migration scripts
+        ${pkgs.mautrix-telegram.alembic}/bin/alembic -x config='${settingsFile}' upgrade head
+      '';
+
+      serviceConfig = {
+        Type = "simple";
+        Restart = "always";
+
+        ProtectSystem = "strict";
+        ProtectHome = true;
+        ProtectKernelTunables = true;
+        ProtectKernelModules = true;
+        ProtectControlGroups = true;
+
+        DynamicUser = true;
+        PrivateTmp = true;
+        WorkingDirectory = pkgs.mautrix-telegram; # necessary for the database migration scripts to be found
+        StateDirectory = baseNameOf dataDir;
+        UMask = 0027;
+        EnvironmentFile = cfg.environmentFile;
+
+        ExecStart = ''
+          ${pkgs.mautrix-telegram}/bin/mautrix-telegram \
+            --config='${settingsFile}'
+        '';
+      };
+    };
+  };
+
+  meta.maintainers = with maintainers; [ pacien vskilet ];
+}
diff --git a/nixos/modules/services/networking/haproxy.nix b/nixos/modules/services/networking/haproxy.nix
index 4678829986c6..e9d72b35499d 100644
--- a/nixos/modules/services/networking/haproxy.nix
+++ b/nixos/modules/services/networking/haproxy.nix
@@ -56,6 +56,9 @@ with lib;
       message = "You must provide services.haproxy.config.";
     }];
 
+    # configuration file indirection is needed to support reloading
+    environment.etc."haproxy.cfg".source = haproxyCfg;
+
     systemd.services.haproxy = {
       description = "HAProxy";
       after = [ "network.target" ];
@@ -64,11 +67,32 @@ with lib;
         User = cfg.user;
         Group = cfg.group;
         Type = "notify";
-        # when running the config test, don't be quiet so we can see what goes wrong
-        ExecStartPre = "${pkgs.haproxy}/sbin/haproxy -c -f ${haproxyCfg}";
-        ExecStart = "${pkgs.haproxy}/sbin/haproxy -Ws -f ${haproxyCfg}";
-        Restart = "on-failure";
+        ExecStartPre = [
+          # when the master process receives USR2, it reloads itself using exec(argv[0]),
+          # so we create a symlink there and update it before reloading
+          "${pkgs.coreutils}/bin/ln -sf ${pkgs.haproxy}/sbin/haproxy /run/haproxy/haproxy"
+          # when running the config test, don't be quiet so we can see what goes wrong
+          "/run/haproxy/haproxy -c -f ${haproxyCfg}"
+        ];
+        ExecStart = "/run/haproxy/haproxy -Ws -f /etc/haproxy.cfg -p /run/haproxy/haproxy.pid";
+        # support reloading
+        ExecReload = [
+          "${pkgs.haproxy}/sbin/haproxy -c -f ${haproxyCfg}"
+          "${pkgs.coreutils}/bin/ln -sf ${pkgs.haproxy}/sbin/haproxy /run/haproxy/haproxy"
+          "${pkgs.coreutils}/bin/kill -USR2 $MAINPID"
+        ];
+        KillMode = "mixed";
+        SuccessExitStatus = "143";
+        Restart = "always";
         RuntimeDirectory = "haproxy";
+        # upstream hardening options
+        NoNewPrivileges = true;
+        ProtectHome = true;
+        ProtectSystem = "strict";
+        ProtectKernelTunables = true;
+        ProtectKernelModules = true;
+        ProtectControlGroups = true;
+        SystemCallFilter= "~@cpu-emulation @keyring @module @obsolete @raw-io @reboot @swap @sync";
         # needed in case we bind to port < 1024
         AmbientCapabilities = "CAP_NET_BIND_SERVICE";
       };
diff --git a/nixos/modules/services/networking/v2ray.nix b/nixos/modules/services/networking/v2ray.nix
index a1774cdffbb9..6a924a16449a 100644
--- a/nixos/modules/services/networking/v2ray.nix
+++ b/nixos/modules/services/networking/v2ray.nix
@@ -58,7 +58,13 @@ with lib;
     cfg = config.services.v2ray;
     configFile = if cfg.configFile != null
       then cfg.configFile
-      else (pkgs.writeText "v2ray.json" (builtins.toJSON cfg.config));
+      else pkgs.writeTextFile {
+        name = "v2ray.json";
+        text = builtins.toJSON cfg.config;
+        checkPhase = ''
+          ${pkgs.v2ray}/bin/v2ray -test -config $out
+        '';
+      };
 
   in mkIf cfg.enable {
     assertions = [
diff --git a/nixos/modules/services/networking/vsftpd.nix b/nixos/modules/services/networking/vsftpd.nix
index b3e201844236..c57994533c17 100644
--- a/nixos/modules/services/networking/vsftpd.nix
+++ b/nixos/modules/services/networking/vsftpd.nix
@@ -116,7 +116,8 @@ let
         userlist_file=${cfg.userlistFile}
       ''}
       background=YES
-      listen=YES
+      listen=NO
+      listen_ipv6=YES
       nopriv_user=vsftpd
       secure_chroot_dir=/var/empty
       ${optionalString (cfg.localRoot != null) ''
diff --git a/nixos/modules/services/system/nscd.conf b/nixos/modules/services/system/nscd.conf
index 2b7523a7346d..722b883ba420 100644
--- a/nixos/modules/services/system/nscd.conf
+++ b/nixos/modules/services/system/nscd.conf
@@ -24,7 +24,7 @@ negative-time-to-live   netgroup        0
 shared                  netgroup        yes
 
 enable-cache            hosts           yes
-positive-time-to-live   hosts           600
+positive-time-to-live   hosts           0
 negative-time-to-live   hosts           0
 shared                  hosts           yes
 
diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix
index 12cff6b038f8..78d669669499 100644
--- a/nixos/modules/tasks/network-interfaces.nix
+++ b/nixos/modules/tasks/network-interfaces.nix
@@ -283,7 +283,7 @@ let
         default = false;
         type = types.bool;
         description = ''
-          Turn on proxy_arp for this device (and proxy_ndp for ipv6).
+          Turn on proxy_arp for this device.
           This is mainly useful for creating pseudo-bridges between a real
           interface and a virtual network such as VPN or a virtual machine for
           interfaces that don't support real bridging (most wlan interfaces).
@@ -381,7 +381,7 @@ in
       # syntax). Note: We also allow underscores for compatibility/legacy
       # reasons (as undocumented feature):
       type = types.strMatching
-        "^[[:alpha:]]([[:alnum:]_-]{0,61}[[:alnum:]])?$";
+        "^$|^[[:alpha:]]([[:alnum:]_-]{0,61}[[:alnum:]])?$";
       description = ''
         The name of the machine. Leave it empty if you want to obtain it from a
         DHCP server (if using DHCP). The hostname must be a valid DNS label (see
@@ -1065,11 +1065,11 @@ in
       optionalString hasBonds "options bonding max_bonds=0";
 
     boot.kernel.sysctl = {
+      "net.ipv4.conf.all.forwarding" = mkDefault (any (i: i.proxyARP) interfaces);
       "net.ipv6.conf.all.disable_ipv6" = mkDefault (!cfg.enableIPv6);
       "net.ipv6.conf.default.disable_ipv6" = mkDefault (!cfg.enableIPv6);
-      "net.ipv6.conf.all.forwarding" = mkDefault (any (i: i.proxyARP) interfaces);
     } // listToAttrs (flip concatMap (filter (i: i.proxyARP) interfaces)
-        (i: forEach [ "4" "6" ] (v: nameValuePair "net.ipv${v}.conf.${replaceChars ["."] ["/"] i.name}.proxy_arp" true)))
+        (i: [(nameValuePair "net.ipv4.conf.${replaceChars ["."] ["/"] i.name}.proxy_arp" true)]))
       // listToAttrs (forEach interfaces
         (i: let
           opt = i.tempAddress;
diff --git a/nixos/modules/virtualisation/kvmgt.nix b/nixos/modules/virtualisation/kvmgt.nix
index 0902d2dc2cb0..e08ad3446281 100644
--- a/nixos/modules/virtualisation/kvmgt.nix
+++ b/nixos/modules/virtualisation/kvmgt.nix
@@ -9,8 +9,8 @@ let
 
   vgpuOptions = {
     uuid = mkOption {
-      type = types.str;
-      description = "UUID of VGPU device. You can generate one with <package>libossp_uuid</package>.";
+      type = with types; listOf str;
+      description = "UUID(s) of VGPU device. You can generate one with <package>libossp_uuid</package>.";
     };
   };
 
@@ -36,7 +36,7 @@ in {
           and find info about device via <command>cat /sys/bus/pci/devices/*/mdev_supported_types/i915-GVTg_V5_4/description</command>
         '';
         example = {
-          i915-GVTg_V5_8.uuid = "a297db4a-f4c2-11e6-90f6-d3b88d6c9525";
+          i915-GVTg_V5_8.uuid = [ "a297db4a-f4c2-11e6-90f6-d3b88d6c9525" ];
         };
       };
     };
@@ -51,31 +51,35 @@ in {
     boot.kernelModules = [ "kvmgt" ];
     boot.kernelParams = [ "i915.enable_gvt=1" ];
 
-    systemd.paths = mapAttrs' (name: value:
-      nameValuePair "kvmgt-${name}" {
-        description = "KVMGT VGPU ${name} path";
-        wantedBy = [ "multi-user.target" ];
-        pathConfig = {
-          PathExists = "/sys/bus/pci/devices/${cfg.device}/mdev_supported_types/${name}/create";
-        };
-      }
-    ) cfg.vgpus;
-
     services.udev.extraRules = ''
       SUBSYSTEM=="vfio", OWNER="root", GROUP="kvm"
     '';
 
-    systemd.services = mapAttrs' (name: value:
-      nameValuePair "kvmgt-${name}" {
-        description = "KVMGT VGPU ${name}";
-        serviceConfig = {
-          Type = "oneshot";
-          RemainAfterExit = true;
-          ExecStart = "${pkgs.runtimeShell} -c 'echo ${value.uuid} > /sys/bus/pci/devices/${cfg.device}/mdev_supported_types/${name}/create'";
-          ExecStop = "${pkgs.runtimeShell} -c 'echo 1 > /sys/bus/pci/devices/${cfg.device}/${value.uuid}/remove'";
-        };
-      }
-    ) cfg.vgpus;
+    systemd = let
+      vgpus = listToAttrs (flatten (mapAttrsToList
+        (mdev: opt: map (id: nameValuePair "kvmgt-${id}" { inherit mdev; uuid = id; }) opt.uuid)
+        cfg.vgpus));
+    in {
+      paths = mapAttrs (_: opt:
+        {
+          description = "KVMGT VGPU ${opt.uuid} path";
+          wantedBy = [ "multi-user.target" ];
+          pathConfig = {
+            PathExists = "/sys/bus/pci/devices/${cfg.device}/mdev_supported_types/${opt.mdev}/create";
+          };
+        }) vgpus;
+
+      services = mapAttrs (_: opt:
+        {
+          description = "KVMGT VGPU ${opt.uuid}";
+          serviceConfig = {
+            Type = "oneshot";
+            RemainAfterExit = true;
+            ExecStart = "${pkgs.runtimeShell} -c 'echo ${opt.uuid} > /sys/bus/pci/devices/${cfg.device}/mdev_supported_types/${opt.mdev}/create'";
+            ExecStop = "${pkgs.runtimeShell} -c 'echo 1 > /sys/bus/pci/devices/${cfg.device}/${opt.uuid}/remove'";
+          };
+        }) vgpus;
+    };
   };
 
   meta.maintainers = with maintainers; [ gnidorah ];
diff --git a/nixos/modules/virtualisation/libvirtd.nix b/nixos/modules/virtualisation/libvirtd.nix
index f89e5d544b22..43b5fcfa8fae 100644
--- a/nixos/modules/virtualisation/libvirtd.nix
+++ b/nixos/modules/virtualisation/libvirtd.nix
@@ -114,7 +114,7 @@ in {
         Specifies the action to be done to / on the guests when the host boots.
         The "start" option starts all guests that were running prior to shutdown
         regardless of their autostart settings. The "ignore" option will not
-        start the formally running guest on boot. However, any guest marked as
+        start the formerly running guest on boot. However, any guest marked as
         autostart will still be automatically started by libvirtd.
       '';
     };
diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix
index ac86330c098b..4592ffcfe4db 100644
--- a/nixos/modules/virtualisation/qemu-vm.nix
+++ b/nixos/modules/virtualisation/qemu-vm.nix
@@ -576,7 +576,7 @@ in
         "/tmp/xchg" =
           { device = "xchg";
             fsType = "9p";
-            options = [ "trans=virtio" "version=9p2000.L" "cache=loose" ];
+            options = [ "trans=virtio" "version=9p2000.L" ];
             neededForBoot = true;
           };
         "/tmp/shared" =