summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
Diffstat (limited to 'nixos')
-rw-r--r--nixos/doc/manual/release-notes/rl-2305.section.md4
-rw-r--r--nixos/modules/config/gnu.nix1
-rw-r--r--nixos/modules/installer/cd-dvd/iso-image.nix2
-rw-r--r--nixos/modules/installer/tools/nixos-generate-config.pl1
-rw-r--r--nixos/modules/module-list.nix1
-rw-r--r--nixos/modules/security/pam.nix2
-rw-r--r--nixos/modules/services/monitoring/uptime-kuma.nix2
-rw-r--r--nixos/modules/services/network-filesystems/webdav-server-rs.nix8
-rw-r--r--nixos/modules/services/networking/harmonia.nix92
-rw-r--r--nixos/modules/services/web-apps/pict-rs.nix4
-rw-r--r--nixos/modules/system/boot/loader/grub/grub.nix52
-rw-r--r--nixos/modules/system/boot/loader/grub/install-grub.pl448
-rw-r--r--nixos/modules/system/boot/loader/grub/ipxe.nix6
-rw-r--r--nixos/modules/system/boot/loader/grub/memtest.nix14
-rw-r--r--nixos/modules/system/boot/networkd.nix2
-rw-r--r--nixos/modules/virtualisation/azure-common.nix1
-rw-r--r--nixos/modules/virtualisation/xen-domU.nix1
-rw-r--r--nixos/tests/aaaaxy.nix7
-rw-r--r--nixos/tests/all-tests.nix1
-rw-r--r--nixos/tests/geth.nix8
-rw-r--r--nixos/tests/harmonia.nix33
-rw-r--r--nixos/tests/installer.nix50
-rw-r--r--nixos/tests/nginx.nix18
-rw-r--r--nixos/tests/sssd-ldap.nix80
24 files changed, 485 insertions, 353 deletions
diff --git a/nixos/doc/manual/release-notes/rl-2305.section.md b/nixos/doc/manual/release-notes/rl-2305.section.md
index ef138813b14b6..2d37362412824 100644
--- a/nixos/doc/manual/release-notes/rl-2305.section.md
+++ b/nixos/doc/manual/release-notes/rl-2305.section.md
@@ -58,6 +58,8 @@ In addition to numerous new and upgraded packages, this release has the followin
 
 - [gmediarender](https://github.com/hzeller/gmrender-resurrect), a simple, headless UPnP/DLNA renderer.  Available as [services.gmediarender](options.html#opt-services.gmediarender.enable).
 
+- [harmonia](https://github.com/nix-community/harmonia/), Nix binary cache implemented in rust using libnix-store. Available as [services.harmonia](options.html#opt-services.harmonia.enable).
+
 - [hyprland](https://github.com/hyprwm/hyprland), a dynamic tiling Wayland compositor that doesn't sacrifice on its looks. Available as [programs.hyprland](#opt-programs.hyprland.enable).
 
 - [minipro](https://gitlab.com/DavidGriffith/minipro/), an open source program for controlling the MiniPRO TL866xx series of chip programmers. Available as [programs.minipro](options.html#opt-programs.minipro.enable).
@@ -292,6 +294,8 @@ In addition to numerous new and upgraded packages, this release has the followin
 
 - The `zplug` package changes its output path from `$out` to `$out/share/zplug`. Users should update their dependency on `${pkgs.zplug}/init.zsh` to `${pkgs.zplug}/share/zplug/init.zsh`.
 
+- The `pict-rs` package was updated from an 0.3 alpha release to 0.3 stable, and related environment variables now require two underscores instead of one.
+
 ## Other Notable Changes {#sec-release-23.05-notable-changes}
 
 <!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
diff --git a/nixos/modules/config/gnu.nix b/nixos/modules/config/gnu.nix
index d06b479e2af53..a47d299b226b5 100644
--- a/nixos/modules/config/gnu.nix
+++ b/nixos/modules/config/gnu.nix
@@ -29,7 +29,6 @@
 
     # GNU GRUB, where available.
     boot.loader.grub.enable = !pkgs.stdenv.isAarch32;
-    boot.loader.grub.version = 2;
 
     # GNU lsh.
     services.openssh.enable = false;
diff --git a/nixos/modules/installer/cd-dvd/iso-image.nix b/nixos/modules/installer/cd-dvd/iso-image.nix
index 3921bb61b91c3..e22bb866927ba 100644
--- a/nixos/modules/installer/cd-dvd/iso-image.nix
+++ b/nixos/modules/installer/cd-dvd/iso-image.nix
@@ -694,8 +694,6 @@ in
       }
     ];
 
-    boot.loader.grub.version = 2;
-
     # Don't build the GRUB menu builder script, since we don't need it
     # here and it causes a cyclic dependency.
     boot.loader.grub.enable = false;
diff --git a/nixos/modules/installer/tools/nixos-generate-config.pl b/nixos/modules/installer/tools/nixos-generate-config.pl
index c65898b261cb0..5d3d0216d20c9 100644
--- a/nixos/modules/installer/tools/nixos-generate-config.pl
+++ b/nixos/modules/installer/tools/nixos-generate-config.pl
@@ -651,7 +651,6 @@ EOF
             $bootLoaderConfig = <<EOF;
   # Use the GRUB 2 boot loader.
   boot.loader.grub.enable = true;
-  boot.loader.grub.version = 2;
   # boot.loader.grub.efiSupport = true;
   # boot.loader.grub.efiInstallAsRemovable = true;
   # boot.loader.efi.efiSysMountPoint = "/boot/efi";
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index b6679b348e395..0b0634884c718 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -876,6 +876,7 @@
   ./services/networking/gobgpd.nix
   ./services/networking/gvpe.nix
   ./services/networking/hans.nix
+  ./services/networking/harmonia.nix
   ./services/networking/haproxy.nix
   ./services/networking/headscale.nix
   ./services/networking/hostapd.nix
diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix
index 6e8be412de83c..d379265960bb3 100644
--- a/nixos/modules/security/pam.nix
+++ b/nixos/modules/security/pam.nix
@@ -638,7 +638,7 @@ let
             password sufficient ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf
           '' +
           optionalString config.services.sssd.enable ''
-            password sufficient ${pkgs.sssd}/lib/security/pam_sss.so use_authtok
+            password sufficient ${pkgs.sssd}/lib/security/pam_sss.so
           '' +
           optionalString config.security.pam.krb5.enable ''
             password sufficient ${pam_krb5}/lib/security/pam_krb5.so use_first_pass
diff --git a/nixos/modules/services/monitoring/uptime-kuma.nix b/nixos/modules/services/monitoring/uptime-kuma.nix
index 5f803d57b5e97..7027046b24253 100644
--- a/nixos/modules/services/monitoring/uptime-kuma.nix
+++ b/nixos/modules/services/monitoring/uptime-kuma.nix
@@ -43,6 +43,8 @@ in
     services.uptime-kuma.settings = {
       DATA_DIR = "/var/lib/uptime-kuma/";
       NODE_ENV = mkDefault "production";
+      HOST = mkDefault "127.0.0.1";
+      PORT = mkDefault "3001";
     };
 
     systemd.services.uptime-kuma = {
diff --git a/nixos/modules/services/network-filesystems/webdav-server-rs.nix b/nixos/modules/services/network-filesystems/webdav-server-rs.nix
index 9ea304111819b..34e717025e645 100644
--- a/nixos/modules/services/network-filesystems/webdav-server-rs.nix
+++ b/nixos/modules/services/network-filesystems/webdav-server-rs.nix
@@ -28,6 +28,12 @@ in
         description = lib.mdDoc "Group to run under when setuid is not enabled.";
       };
 
+      debug = mkOption {
+        type = types.bool;
+        default = false;
+        description = lib.mdDoc "Enable debug mode.";
+      };
+
       settings = mkOption {
         type = format.type;
         default = { };
@@ -111,7 +117,7 @@ in
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
       serviceConfig = {
-        ExecStart = "${pkgs.webdav-server-rs}/bin/webdav-server -c ${cfg.configFile}";
+        ExecStart = "${pkgs.webdav-server-rs}/bin/webdav-server ${lib.optionalString cfg.debug "--debug"} -c ${cfg.configFile}";
 
         CapabilityBoundingSet = [
           "CAP_SETUID"
diff --git a/nixos/modules/services/networking/harmonia.nix b/nixos/modules/services/networking/harmonia.nix
new file mode 100644
index 0000000000000..0f9d328d0059b
--- /dev/null
+++ b/nixos/modules/services/networking/harmonia.nix
@@ -0,0 +1,92 @@
+{ config, pkgs, lib, ... }:
+let
+  cfg = config.services.harmonia;
+
+  format = pkgs.formats.toml { };
+in
+{
+  options = {
+    services.harmonia = {
+      enable = lib.mkEnableOption (lib.mdDoc "Harmonia: Nix binary cache written in Rust");
+
+      signKeyPath = lib.mkOption {
+        type = lib.types.nullOr lib.types.path;
+        default = null;
+        description = lib.mdDoc "Path to the signing key to use for signing the cache";
+      };
+
+      package = lib.mkPackageOptionMD pkgs "harmonia" { };
+
+      settings = lib.mkOption {
+        inherit (format) type;
+        description = lib.mdDoc "Settings to merge with the default configuration";
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    services.harmonia.settings.bind = lib.mkDefault "[::]:5000";
+
+    systemd.services.harmonia = {
+      description = "harmonia binary cache service";
+
+      requires = [ "nix-daemon.socket" ];
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      environment = {
+        CONFIG_FILE = format.generate "harmonia.toml" cfg.settings;
+        SIGN_KEY_PATH = lib.mkIf (cfg.signKeyPath != null) "%d/sign-key";
+        # Note: it's important to set this for nix-store, because it wants to use
+        # $HOME in order to use a temporary cache dir. bizarre failures will occur
+        # otherwise
+        HOME = "/run/harmonia";
+      };
+
+      serviceConfig = {
+        ExecStart = lib.getExe cfg.package;
+
+        User = "harmonia";
+        Group = "harmonia";
+        DynamicUser = true;
+        PrivateUsers = true;
+        DeviceAllow = [ "" ];
+        UMask = "0066";
+
+        RuntimeDirectory = "harmonia";
+        LoadCredential = lib.optional (cfg.signKeyPath != null) "sign-key:${cfg.signKeyPath}";
+
+        SystemCallFilter = [
+          "@system-service"
+          "~@privileged"
+          "~@resources"
+        ];
+        CapabilityBoundingSet = "";
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectControlGroups = true;
+        ProtectKernelLogs = true;
+        ProtectHostname = true;
+        ProtectClock = true;
+        RestrictRealtime = true;
+        MemoryDenyWriteExecute = true;
+        ProcSubset = "pid";
+        ProtectProc = "invisible";
+        RestrictNamespaces = true;
+        SystemCallArchitectures = "native";
+
+        PrivateNetwork = false;
+        PrivateTmp = true;
+        PrivateDevices = true;
+        PrivateMounts = true;
+        NoNewPrivileges = true;
+        ProtectSystem = "strict";
+        ProtectHome = true;
+        LockPersonality = true;
+        RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6";
+
+        LimitNOFILE = 65536;
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/web-apps/pict-rs.nix b/nixos/modules/services/web-apps/pict-rs.nix
index 0f13b2ae6db13..3270715a051ba 100644
--- a/nixos/modules/services/web-apps/pict-rs.nix
+++ b/nixos/modules/services/web-apps/pict-rs.nix
@@ -34,8 +34,8 @@ in
   config = lib.mkIf cfg.enable {
     systemd.services.pict-rs = {
       environment = {
-        PICTRS_PATH = cfg.dataDir;
-        PICTRS_ADDR = "${cfg.address}:${toString cfg.port}";
+        PICTRS__PATH = cfg.dataDir;
+        PICTRS__ADDR = "${cfg.address}:${toString cfg.port}";
       };
       wantedBy = [ "multi-user.target" ];
       serviceConfig = {
diff --git a/nixos/modules/system/boot/loader/grub/grub.nix b/nixos/modules/system/boot/loader/grub/grub.nix
index 5c0a07fb51272..28254cbf2ca0d 100644
--- a/nixos/modules/system/boot/loader/grub/grub.nix
+++ b/nixos/modules/system/boot/loader/grub/grub.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, ... }:
+{ config, options, lib, pkgs, ... }:
 
 with lib;
 
@@ -12,8 +12,7 @@ let
     # Package set of targeted architecture
     if cfg.forcei686 then pkgs.pkgsi686Linux else pkgs;
 
-  realGrub = if cfg.version == 1 then grubPkgs.grub
-    else if cfg.zfsSupport then grubPkgs.grub2.override { zfsSupport = true; }
+  realGrub = if cfg.zfsSupport then grubPkgs.grub2.override { zfsSupport = true; }
     else if cfg.trustedBoot.enable
          then if cfg.trustedBoot.isHPLaptop
               then grubPkgs.trustedGrub-for-HP
@@ -28,8 +27,7 @@ let
     else realGrub;
 
   grubEfi =
-    # EFI version of Grub v2
-    if cfg.efiSupport && (cfg.version == 2)
+    if cfg.efiSupport
     then realGrub.override { efiSupport = cfg.efiSupport; }
     else null;
 
@@ -52,24 +50,24 @@ let
       fullName = lib.getName realGrub;
       fullVersion = lib.getVersion realGrub;
       grubEfi = f grubEfi;
-      grubTargetEfi = optionalString (cfg.efiSupport && (cfg.version == 2)) (f (grubEfi.grubTarget or ""));
+      grubTargetEfi = optionalString cfg.efiSupport (f (grubEfi.grubTarget or ""));
       bootPath = args.path;
       storePath = config.boot.loader.grub.storePath;
       bootloaderId = if args.efiBootloaderId == null then "${config.system.nixos.distroName}${efiSysMountPoint'}" else args.efiBootloaderId;
       timeout = if config.boot.loader.timeout == null then -1 else config.boot.loader.timeout;
-      users = if cfg.users == {} || cfg.version != 1 then cfg.users else throw "GRUB version 1 does not support user accounts.";
       theme = f cfg.theme;
       inherit efiSysMountPoint;
       inherit (args) devices;
       inherit (efi) canTouchEfiVariables;
       inherit (cfg)
-        version extraConfig extraPerEntryConfig extraEntries forceInstall useOSProber
+        extraConfig extraPerEntryConfig extraEntries forceInstall useOSProber
         extraGrubInstallArgs
         extraEntriesBeforeNixOS extraPrepareConfig configurationLimit copyKernels
-        default fsIdentifier efiSupport efiInstallAsRemovable gfxmodeEfi gfxmodeBios gfxpayloadEfi gfxpayloadBios;
+        default fsIdentifier efiSupport efiInstallAsRemovable gfxmodeEfi gfxmodeBios gfxpayloadEfi gfxpayloadBios
+        users;
       path = with pkgs; makeBinPath (
         [ coreutils gnused gnugrep findutils diffutils btrfs-progs util-linux mdadm ]
-        ++ optional (cfg.efiSupport && (cfg.version == 2)) efibootmgr
+        ++ optional cfg.efiSupport efibootmgr
         ++ optionals cfg.useOSProber [ busybox os-prober ]);
       font = if cfg.font == null then ""
         else (if lib.last (lib.splitString "." cfg.font) == "pf2"
@@ -109,14 +107,8 @@ in
       };
 
       version = mkOption {
-        default = 2;
-        example = 1;
+        visible = false;
         type = types.int;
-        description = lib.mdDoc ''
-          The version of GRUB to use: `1` for GRUB
-          Legacy (versions 0.9x), or `2` (the
-          default) for GRUB 2.
-        '';
       };
 
       device = mkOption {
@@ -724,14 +716,7 @@ in
 
   config = mkMerge [
 
-    { boot.loader.grub.splashImage = mkDefault (
-        if cfg.version == 1 then pkgs.fetchurl {
-          url = "http://www.gnome-look.org/CONTENT/content-files/36909-soft-tux.xpm.gz";
-          sha256 = "14kqdx2lfqvh40h6fjjzqgff1mwk74dmbjvmqphi6azzra7z8d59";
-        }
-        # GRUB 1.97 doesn't support gzipped XPMs.
-        else defaultSplash);
-    }
+    { boot.loader.grub.splashImage = mkDefault defaultSplash; }
 
     (mkIf (cfg.splashImage == defaultSplash) {
       boot.loader.grub.backgroundColor = mkDefault "#2F302F";
@@ -789,10 +774,6 @@ in
 
       assertions = [
         {
-          assertion = !cfg.zfsSupport || cfg.version == 2;
-          message = "Only GRUB version 2 provides ZFS support";
-        }
-        {
           assertion = cfg.mirroredBoots != [ ];
           message = "You must set the option ‘boot.loader.grub.devices’ or "
             + "'boot.loader.grub.mirroredBoots' to make the system bootable.";
@@ -802,10 +783,6 @@ in
           message = "You cannot have duplicated devices in mirroredBoots";
         }
         {
-          assertion = !cfg.trustedBoot.enable || cfg.version == 2;
-          message = "Trusted GRUB is only available for GRUB 2";
-        }
-        {
           assertion = !cfg.efiSupport || !cfg.trustedBoot.enable;
           message = "Trusted GRUB does not have EFI support";
         }
@@ -825,6 +802,10 @@ in
           assertion = cfg.efiInstallAsRemovable -> !config.boot.loader.efi.canTouchEfiVariables;
           message = "If you wish to to use boot.loader.grub.efiInstallAsRemovable, then turn off boot.loader.efi.canTouchEfiVariables";
         }
+        {
+          assertion = !(options.boot.loader.grub.version.isDefined && cfg.version == 1);
+          message = "Support for version 0.9x of GRUB was removed after being unsupported upstream for around a decade";
+        }
       ] ++ flip concatMap cfg.mirroredBoots (args: [
         {
           assertion = args.devices != [ ];
@@ -844,6 +825,11 @@ in
       }));
     })
 
+    (mkIf options.boot.loader.grub.version.isDefined {
+      warnings = [ ''
+        The boot.loader.grub.version option does not have any effect anymore, please remove it from your configuration.
+      '' ];
+    })
   ];
 
 
diff --git a/nixos/modules/system/boot/loader/grub/install-grub.pl b/nixos/modules/system/boot/loader/grub/install-grub.pl
index 2779f26aa1b62..cfccb93264bfd 100644
--- a/nixos/modules/system/boot/loader/grub/install-grub.pl
+++ b/nixos/modules/system/boot/loader/grub/install-grub.pl
@@ -61,7 +61,6 @@ sub runCommand {
 }
 
 my $grub = get("grub");
-my $grubVersion = int(get("version"));
 my $grubTarget = get("grubTarget");
 my $extraConfig = get("extraConfig");
 my $extraPrepareConfig = get("extraPrepareConfig");
@@ -96,9 +95,7 @@ my $theme = get("theme");
 my $saveDefault = $defaultEntry eq "saved";
 $ENV{'PATH'} = get("path");
 
-die "unsupported GRUB version\n" if $grubVersion != 1 && $grubVersion != 2;
-
-print STDERR "updating GRUB $grubVersion menu...\n";
+print STDERR "updating GRUB 2 menu...\n";
 
 mkpath("$bootPath/grub", 0, 0700);
 
@@ -176,76 +173,74 @@ sub GrubFs {
     }
     my $search = "";
 
-    if ($grubVersion > 1) {
-        # ZFS is completely separate logic as zpools are always identified by a label
-        # or custom UUID
-        if ($fs->type eq 'zfs') {
-            my $sid = index($fs->device, '/');
-
-            if ($sid < 0) {
-                $search = '--label ' . $fs->device;
-                $path = '/@' . $path;
-            } else {
-                $search = '--label ' . substr($fs->device, 0, $sid);
-                $path = '/' . substr($fs->device, $sid) . '/@' . $path;
+    # ZFS is completely separate logic as zpools are always identified by a label
+    # or custom UUID
+    if ($fs->type eq 'zfs') {
+        my $sid = index($fs->device, '/');
+
+        if ($sid < 0) {
+            $search = '--label ' . $fs->device;
+            $path = '/@' . $path;
+        } else {
+            $search = '--label ' . substr($fs->device, 0, $sid);
+            $path = '/' . substr($fs->device, $sid) . '/@' . $path;
+        }
+    } else {
+        my %types = ('uuid' => '--fs-uuid', 'label' => '--label');
+
+        if ($fsIdentifier eq 'provided') {
+            # If the provided dev is identifying the partition using a label or uuid,
+            # we should get the label / uuid and do a proper search
+            my @matches = $fs->device =~ m/\/dev\/disk\/by-(label|uuid)\/(.*)/;
+            if ($#matches > 1) {
+                die "Too many matched devices"
+            } elsif ($#matches == 1) {
+                $search = "$types{$matches[0]} $matches[1]"
             }
         } else {
-            my %types = ('uuid' => '--fs-uuid', 'label' => '--label');
-
-            if ($fsIdentifier eq 'provided') {
-                # If the provided dev is identifying the partition using a label or uuid,
-                # we should get the label / uuid and do a proper search
-                my @matches = $fs->device =~ m/\/dev\/disk\/by-(label|uuid)\/(.*)/;
-                if ($#matches > 1) {
-                    die "Too many matched devices"
-                } elsif ($#matches == 1) {
-                    $search = "$types{$matches[0]} $matches[1]"
-                }
-            } else {
-                # Determine the identifying type
-                $search = $types{$fsIdentifier} . ' ';
+            # Determine the identifying type
+            $search = $types{$fsIdentifier} . ' ';
 
-                # Based on the type pull in the identifier from the system
-                my ($status, @devInfo) = runCommand("@utillinux@/bin/blkid", "-o", "export", @{[$fs->device]});
-                if ($status != 0) {
-                    die "Failed to get blkid info (returned $status) for @{[$fs->mount]} on @{[$fs->device]}";
-                }
-                my @matches = join("", @devInfo) =~ m/@{[uc $fsIdentifier]}=([^\n]*)/;
-                if ($#matches != 0) {
-                    die "Couldn't find a $types{$fsIdentifier} for @{[$fs->device]}\n"
-                }
-                $search .= $matches[0];
+            # Based on the type pull in the identifier from the system
+            my ($status, @devInfo) = runCommand("@utillinux@/bin/blkid", "-o", "export", @{[$fs->device]});
+            if ($status != 0) {
+                die "Failed to get blkid info (returned $status) for @{[$fs->mount]} on @{[$fs->device]}";
+            }
+            my @matches = join("", @devInfo) =~ m/@{[uc $fsIdentifier]}=([^\n]*)/;
+            if ($#matches != 0) {
+                die "Couldn't find a $types{$fsIdentifier} for @{[$fs->device]}\n"
             }
+            $search .= $matches[0];
+        }
 
-            # BTRFS is a special case in that we need to fix the referrenced path based on subvolumes
-            if ($fs->type eq 'btrfs') {
-                my ($status, @id_info) = runCommand("@btrfsprogs@/bin/btrfs", "subvol", "show", @{[$fs->mount]});
+        # BTRFS is a special case in that we need to fix the referrenced path based on subvolumes
+        if ($fs->type eq 'btrfs') {
+            my ($status, @id_info) = runCommand("@btrfsprogs@/bin/btrfs", "subvol", "show", @{[$fs->mount]});
+            if ($status != 0) {
+                die "Failed to retrieve subvolume info for @{[$fs->mount]}\n";
+            }
+            my @ids = join("\n", @id_info) =~ m/^(?!\/\n).*Subvolume ID:[ \t\n]*([0-9]+)/s;
+            if ($#ids > 0) {
+                die "Btrfs subvol name for @{[$fs->device]} listed multiple times in mount\n"
+            } elsif ($#ids == 0) {
+                my ($status, @path_info) = runCommand("@btrfsprogs@/bin/btrfs", "subvol", "list", @{[$fs->mount]});
                 if ($status != 0) {
-                    die "Failed to retrieve subvolume info for @{[$fs->mount]}\n";
+                    die "Failed to find @{[$fs->mount]} subvolume id from btrfs\n";
                 }
-                my @ids = join("\n", @id_info) =~ m/^(?!\/\n).*Subvolume ID:[ \t\n]*([0-9]+)/s;
-                if ($#ids > 0) {
-                    die "Btrfs subvol name for @{[$fs->device]} listed multiple times in mount\n"
-                } elsif ($#ids == 0) {
-                    my ($status, @path_info) = runCommand("@btrfsprogs@/bin/btrfs", "subvol", "list", @{[$fs->mount]});
-                    if ($status != 0) {
-                        die "Failed to find @{[$fs->mount]} subvolume id from btrfs\n";
-                    }
-                    my @paths = join("", @path_info) =~ m/ID $ids[0] [^\n]* path ([^\n]*)/;
-                    if ($#paths > 0) {
-                        die "Btrfs returned multiple paths for a single subvolume id, mountpoint @{[$fs->mount]}\n";
-                    } elsif ($#paths != 0) {
-                        die "Btrfs did not return a path for the subvolume at @{[$fs->mount]}\n";
-                    }
-                    $path = "/$paths[0]$path";
+                my @paths = join("", @path_info) =~ m/ID $ids[0] [^\n]* path ([^\n]*)/;
+                if ($#paths > 0) {
+                    die "Btrfs returned multiple paths for a single subvolume id, mountpoint @{[$fs->mount]}\n";
+                } elsif ($#paths != 0) {
+                    die "Btrfs did not return a path for the subvolume at @{[$fs->mount]}\n";
                 }
+                $path = "/$paths[0]$path";
             }
         }
-        if (not $search eq "") {
-            $search = "search --set=drive$driveid " . $search;
-            $path = "(\$drive$driveid)$path";
-            $driveid += 1;
-        }
+    }
+    if (not $search eq "") {
+        $search = "search --set=drive$driveid " . $search;
+        $path = "(\$drive$driveid)$path";
+        $driveid += 1;
     }
     return Grub->new(path => $path, search => $search);
 }
@@ -258,166 +253,151 @@ if ($copyKernels == 0) {
 # Generate the header.
 my $conf .= "# Automatically generated.  DO NOT EDIT THIS FILE!\n";
 
-if ($grubVersion == 1) {
-    # $defaultEntry might be "saved", indicating that we want to use the last selected configuration as default.
-    # Incidentally this is already the correct value for the grub 1 config to achieve this behaviour.
-    $conf .= "
-        default $defaultEntry
-        timeout $timeout
-    ";
-    if ($splashImage) {
-        copy $splashImage, "$bootPath/background.xpm.gz" or die "cannot copy $splashImage to $bootPath: $!\n";
-        $conf .= "splashimage " . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/background.xpm.gz\n";
+my @users = ();
+foreach my $user ($dom->findnodes('/expr/attrs/attr[@name = "users"]/attrs/attr')) {
+    my $name = $user->findvalue('@name') or die;
+    my $hashedPassword = $user->findvalue('./attrs/attr[@name = "hashedPassword"]/string/@value');
+    my $hashedPasswordFile = $user->findvalue('./attrs/attr[@name = "hashedPasswordFile"]/string/@value');
+    my $password = $user->findvalue('./attrs/attr[@name = "password"]/string/@value');
+    my $passwordFile = $user->findvalue('./attrs/attr[@name = "passwordFile"]/string/@value');
+
+    if ($hashedPasswordFile) {
+        open(my $f, '<', $hashedPasswordFile) or die "Can't read file '$hashedPasswordFile'!";
+        $hashedPassword = <$f>;
+        chomp $hashedPassword;
+    }
+    if ($passwordFile) {
+        open(my $f, '<', $passwordFile) or die "Can't read file '$passwordFile'!";
+        $password = <$f>;
+        chomp $password;
     }
-}
-
-else {
-    my @users = ();
-    foreach my $user ($dom->findnodes('/expr/attrs/attr[@name = "users"]/attrs/attr')) {
-        my $name = $user->findvalue('@name') or die;
-        my $hashedPassword = $user->findvalue('./attrs/attr[@name = "hashedPassword"]/string/@value');
-        my $hashedPasswordFile = $user->findvalue('./attrs/attr[@name = "hashedPasswordFile"]/string/@value');
-        my $password = $user->findvalue('./attrs/attr[@name = "password"]/string/@value');
-        my $passwordFile = $user->findvalue('./attrs/attr[@name = "passwordFile"]/string/@value');
-
-        if ($hashedPasswordFile) {
-            open(my $f, '<', $hashedPasswordFile) or die "Can't read file '$hashedPasswordFile'!";
-            $hashedPassword = <$f>;
-            chomp $hashedPassword;
-        }
-        if ($passwordFile) {
-            open(my $f, '<', $passwordFile) or die "Can't read file '$passwordFile'!";
-            $password = <$f>;
-            chomp $password;
-        }
 
-        if ($hashedPassword) {
-            if (index($hashedPassword, "grub.pbkdf2.") == 0) {
-                $conf .= "\npassword_pbkdf2 $name $hashedPassword";
-            }
-            else {
-                die "Password hash for GRUB user '$name' is not valid!";
-            }
-        }
-        elsif ($password) {
-            $conf .= "\npassword $name $password";
+    if ($hashedPassword) {
+        if (index($hashedPassword, "grub.pbkdf2.") == 0) {
+            $conf .= "\npassword_pbkdf2 $name $hashedPassword";
         }
         else {
-            die "GRUB user '$name' has no password!";
+            die "Password hash for GRUB user '$name' is not valid!";
         }
-        push(@users, $name);
     }
-    if (@users) {
-        $conf .= "\nset superusers=\"" . join(' ',@users) . "\"\n";
-    }
-
-    if ($copyKernels == 0) {
-        $conf .= "
-            " . $grubStore->search;
+    elsif ($password) {
+        $conf .= "\npassword $name $password";
     }
-    # FIXME: should use grub-mkconfig.
-    my $defaultEntryText = $defaultEntry;
-    if ($saveDefault) {
-        $defaultEntryText = "\"\${saved_entry}\"";
+    else {
+        die "GRUB user '$name' has no password!";
     }
-    $conf .= "
-        " . $grubBoot->search . "
-        if [ -s \$prefix/grubenv ]; then
-          load_env
-        fi
+    push(@users, $name);
+}
+if (@users) {
+    $conf .= "\nset superusers=\"" . join(' ',@users) . "\"\n";
+}
 
-        # ‘grub-reboot’ sets a one-time saved entry, which we process here and
-        # then delete.
-        if [ \"\${next_entry}\" ]; then
-          set default=\"\${next_entry}\"
-          set next_entry=
-          save_env next_entry
-          set timeout=1
-          set boot_once=true
-        else
-          set default=$defaultEntryText
-          set timeout=$timeout
+if ($copyKernels == 0) {
+    $conf .= "
+        " . $grubStore->search;
+}
+# FIXME: should use grub-mkconfig.
+my $defaultEntryText = $defaultEntry;
+if ($saveDefault) {
+    $defaultEntryText = "\"\${saved_entry}\"";
+}
+$conf .= "
+    " . $grubBoot->search . "
+    if [ -s \$prefix/grubenv ]; then
+      load_env
+    fi
+
+    # ‘grub-reboot’ sets a one-time saved entry, which we process here and
+    # then delete.
+    if [ \"\${next_entry}\" ]; then
+      set default=\"\${next_entry}\"
+      set next_entry=
+      save_env next_entry
+      set timeout=1
+      set boot_once=true
+    else
+      set default=$defaultEntryText
+      set timeout=$timeout
+    fi
+
+    function savedefault {
+        if [ -z \"\${boot_once}\"]; then
+        saved_entry=\"\${chosen}\"
+        save_env saved_entry
         fi
+    }
 
-        function savedefault {
-            if [ -z \"\${boot_once}\"]; then
-            saved_entry=\"\${chosen}\"
-            save_env saved_entry
-            fi
-        }
-
-        # Setup the graphics stack for bios and efi systems
-        if [ \"\${grub_platform}\" = \"efi\" ]; then
-          insmod efi_gop
-          insmod efi_uga
-        else
-          insmod vbe
+    # Setup the graphics stack for bios and efi systems
+    if [ \"\${grub_platform}\" = \"efi\" ]; then
+      insmod efi_gop
+      insmod efi_uga
+    else
+      insmod vbe
+    fi
+";
+
+if ($font) {
+    copy $font, "$bootPath/converted-font.pf2" or die "cannot copy $font to $bootPath: $!\n";
+    $conf .= "
+        insmod font
+        if loadfont " . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/converted-font.pf2; then
+          insmod gfxterm
+          if [ \"\${grub_platform}\" = \"efi\" ]; then
+            set gfxmode=$gfxmodeEfi
+            set gfxpayload=$gfxpayloadEfi
+          else
+            set gfxmode=$gfxmodeBios
+            set gfxpayload=$gfxpayloadBios
+          fi
+          terminal_output gfxterm
         fi
     ";
-
-    if ($font) {
-        copy $font, "$bootPath/converted-font.pf2" or die "cannot copy $font to $bootPath: $!\n";
-        $conf .= "
-            insmod font
-            if loadfont " . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/converted-font.pf2; then
-              insmod gfxterm
-              if [ \"\${grub_platform}\" = \"efi\" ]; then
-                set gfxmode=$gfxmodeEfi
-                set gfxpayload=$gfxpayloadEfi
-              else
-                set gfxmode=$gfxmodeBios
-                set gfxpayload=$gfxpayloadBios
-              fi
-              terminal_output gfxterm
-            fi
-        ";
+}
+if ($splashImage) {
+    # Keeps the image's extension.
+    my ($filename, $dirs, $suffix) = fileparse($splashImage, qr"\..[^.]*$");
+    # The module for jpg is jpeg.
+    if ($suffix eq ".jpg") {
+        $suffix = ".jpeg";
     }
-    if ($splashImage) {
-        # Keeps the image's extension.
-        my ($filename, $dirs, $suffix) = fileparse($splashImage, qr"\..[^.]*$");
-        # The module for jpg is jpeg.
-        if ($suffix eq ".jpg") {
-            $suffix = ".jpeg";
-        }
-        if ($backgroundColor) {
-            $conf .= "
-            background_color '$backgroundColor'
-            ";
-        }
-        copy $splashImage, "$bootPath/background$suffix" or die "cannot copy $splashImage to $bootPath: $!\n";
+    if ($backgroundColor) {
         $conf .= "
-            insmod " . substr($suffix, 1) . "
-            if background_image --mode '$splashMode' " . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/background$suffix; then
-              set color_normal=white/black
-              set color_highlight=black/white
-            else
-              set menu_color_normal=cyan/blue
-              set menu_color_highlight=white/blue
-            fi
+        background_color '$backgroundColor'
         ";
     }
+    copy $splashImage, "$bootPath/background$suffix" or die "cannot copy $splashImage to $bootPath: $!\n";
+    $conf .= "
+        insmod " . substr($suffix, 1) . "
+        if background_image --mode '$splashMode' " . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/background$suffix; then
+          set color_normal=white/black
+          set color_highlight=black/white
+        else
+          set menu_color_normal=cyan/blue
+          set menu_color_highlight=white/blue
+        fi
+    ";
+}
 
-    rmtree("$bootPath/theme") or die "cannot clean up theme folder in $bootPath\n" if -e "$bootPath/theme";
+rmtree("$bootPath/theme") or die "cannot clean up theme folder in $bootPath\n" if -e "$bootPath/theme";
 
-    if ($theme) {
-        # Copy theme
-        rcopy($theme, "$bootPath/theme") or die "cannot copy $theme to $bootPath\n";
-        $conf .= "
-            # Sets theme.
-            set theme=" . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/theme/theme.txt
-            export theme
-            # Load theme fonts, if any
-        ";
+if ($theme) {
+    # Copy theme
+    rcopy($theme, "$bootPath/theme") or die "cannot copy $theme to $bootPath\n";
+    $conf .= "
+        # Sets theme.
+        set theme=" . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/theme/theme.txt
+        export theme
+        # Load theme fonts, if any
+    ";
 
-        find( { wanted => sub {
-            if ($_ =~ /\.pf2$/i) {
-                $font = File::Spec->abs2rel($File::Find::name, $theme);
-                $conf .= "
-                    loadfont " . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/theme/$font
-                ";
-            }
-        }, no_chdir => 1 }, $theme );
-    }
+    find( { wanted => sub {
+        if ($_ =~ /\.pf2$/i) {
+            $font = File::Spec->abs2rel($File::Find::name, $theme);
+            $conf .= "
+                loadfont " . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/theme/$font
+            ";
+        }
+    }, no_chdir => 1 }, $theme );
 }
 
 $conf .= "$extraConfig\n";
@@ -494,31 +474,19 @@ sub addEntry {
         readFile("$path/kernel-params");
     my $xenParams = $xen && -e "$path/xen-params" ? readFile("$path/xen-params") : "";
 
-    if ($grubVersion == 1) {
-        $conf .= "title $name\n";
-        $conf .= "  $extraPerEntryConfig\n" if $extraPerEntryConfig;
-        $conf .= "  kernel $xen $xenParams\n" if $xen;
-        $conf .= "  " . ($xen ? "module" : "kernel") . " $kernel $kernelParams\n";
-        $conf .= "  " . ($xen ? "module" : "initrd") . " $initrd\n";
-        if ($saveDefault) {
-            $conf .= "  savedefault\n";
-        }
-        $conf .= "\n";
-    } else {
-        $conf .= "menuentry \"$name\" " . $options . " {\n";
-        if ($saveDefault) {
-            $conf .= "  savedefault\n";
-        }
-        $conf .= $grubBoot->search . "\n";
-        if ($copyKernels == 0) {
-            $conf .= $grubStore->search . "\n";
-        }
-        $conf .= "  $extraPerEntryConfig\n" if $extraPerEntryConfig;
-        $conf .= "  multiboot $xen $xenParams\n" if $xen;
-        $conf .= "  " . ($xen ? "module" : "linux") . " $kernel $kernelParams\n";
-        $conf .= "  " . ($xen ? "module" : "initrd") . " $initrd\n";
-        $conf .= "}\n\n";
+    $conf .= "menuentry \"$name\" " . $options . " {\n";
+    if ($saveDefault) {
+        $conf .= "  savedefault\n";
     }
+    $conf .= $grubBoot->search . "\n";
+    if ($copyKernels == 0) {
+        $conf .= $grubStore->search . "\n";
+    }
+    $conf .= "  $extraPerEntryConfig\n" if $extraPerEntryConfig;
+    $conf .= "  multiboot $xen $xenParams\n" if $xen;
+    $conf .= "  " . ($xen ? "module" : "linux") . " $kernel $kernelParams\n";
+    $conf .= "  " . ($xen ? "module" : "initrd") . " $initrd\n";
+    $conf .= "}\n\n";
 }
 
 
@@ -562,7 +530,7 @@ sub addProfile {
     my ($profile, $description) = @_;
 
     # Add entries for all generations of this profile.
-    $conf .= "submenu \"$description\" --class submenu {\n" if $grubVersion == 2;
+    $conf .= "submenu \"$description\" --class submenu {\n";
 
     sub nrFromGen { my ($x) = @_; $x =~ /\/\w+-(\d+)-link/; return $1; }
 
@@ -585,17 +553,15 @@ sub addProfile {
         addEntry("@distroName@ - Configuration " . nrFromGen($link) . " ($date - $version)", $link, $subEntryOptions, 0);
     }
 
-    $conf .= "}\n" if $grubVersion == 2;
+    $conf .= "}\n";
 }
 
 addProfile "/nix/var/nix/profiles/system", "@distroName@ - All configurations";
 
-if ($grubVersion == 2) {
-    for my $profile (glob "/nix/var/nix/profiles/system-profiles/*") {
-        my $name = basename($profile);
-        next unless $name =~ /^\w+$/;
-        addProfile $profile, "@distroName@ - Profile '$name'";
-    }
+for my $profile (glob "/nix/var/nix/profiles/system-profiles/*") {
+    my $name = basename($profile);
+    next unless $name =~ /^\w+$/;
+    addProfile $profile, "@distroName@ - Profile '$name'";
 }
 
 # extraPrepareConfig could refer to @bootPath@, which we have to substitute
@@ -607,16 +573,14 @@ if ($extraPrepareConfig ne "") {
 }
 
 # write the GRUB config.
-my $confFile = $grubVersion == 1 ? "$bootPath/grub/menu.lst" : "$bootPath/grub/grub.cfg";
+my $confFile = "$bootPath/grub/grub.cfg";
 my $tmpFile = $confFile . ".tmp";
 writeFile($tmpFile, $conf);
 
 
 # check whether to install GRUB EFI or not
 sub getEfiTarget {
-    if ($grubVersion == 1) {
-        return "no"
-    } elsif (($grub ne "") && ($grubEfi ne "")) {
+    if (($grub ne "") && ($grubEfi ne "")) {
         # EFI can only be installed when target is set;
         # A target is also required then for non-EFI grub
         if (($grubTarget eq "") || ($grubTargetEfi eq "")) { die }
@@ -741,7 +705,7 @@ symlink "$bootPath", "$tmpDir/boot" or die "Failed to symlink $tmpDir/boot: $!";
 if (($requireNewInstall != 0) && ($efiTarget eq "no" || $efiTarget eq "both")) {
     foreach my $dev (@deviceTargets) {
         next if $dev eq "nodev";
-        print STDERR "installing the GRUB $grubVersion boot loader on $dev...\n";
+        print STDERR "installing the GRUB 2 boot loader on $dev...\n";
         my @command = ("$grub/sbin/grub-install", "--recheck", "--root-directory=$tmpDir", Cwd::abs_path($dev), @extraGrubInstallArgs);
         if ($forceInstall eq "true") {
             push @command, "--force";
@@ -756,7 +720,7 @@ if (($requireNewInstall != 0) && ($efiTarget eq "no" || $efiTarget eq "both")) {
 
 # install EFI GRUB
 if (($requireNewInstall != 0) && ($efiTarget eq "only" || $efiTarget eq "both")) {
-    print STDERR "installing the GRUB $grubVersion EFI boot loader into $efiSysMountPoint...\n";
+    print STDERR "installing the GRUB 2 boot loader into $efiSysMountPoint...\n";
     my @command = ("$grubEfi/sbin/grub-install", "--recheck", "--target=$grubTargetEfi", "--boot-directory=$bootPath", "--efi-directory=$efiSysMountPoint", @extraGrubInstallArgs);
     if ($forceInstall eq "true") {
         push @command, "--force";
diff --git a/nixos/modules/system/boot/loader/grub/ipxe.nix b/nixos/modules/system/boot/loader/grub/ipxe.nix
index adddcbee0164d..d926b7ceaa6e6 100644
--- a/nixos/modules/system/boot/loader/grub/ipxe.nix
+++ b/nixos/modules/system/boot/loader/grub/ipxe.nix
@@ -46,11 +46,7 @@ in
 
   config = mkIf (builtins.length scripts != 0) {
 
-    boot.loader.grub.extraEntries =
-      if config.boot.loader.grub.version == 2 then
-        toString (map grubEntry scripts)
-      else
-        throw "iPXE is not supported with GRUB 1.";
+    boot.loader.grub.extraEntries = toString (map grubEntry scripts);
 
     boot.loader.grub.extraFiles =
       { "ipxe.lkrn" = "${pkgs.ipxe}/ipxe.lkrn"; }
diff --git a/nixos/modules/system/boot/loader/grub/memtest.nix b/nixos/modules/system/boot/loader/grub/memtest.nix
index ccb6e8cc3caf5..ee969e9bff5bf 100644
--- a/nixos/modules/system/boot/loader/grub/memtest.nix
+++ b/nixos/modules/system/boot/loader/grub/memtest.nix
@@ -84,15 +84,11 @@ in
     })
 
     (mkIf (cfg.enable && !efiSupport) {
-      boot.loader.grub.extraEntries =
-        if config.boot.loader.grub.version == 2 then
-          ''
-            menuentry "Memtest86+" {
-              linux16 @bootRoot@/memtest.bin ${toString cfg.params}
-            }
-          ''
-        else
-          throw "Memtest86+ is not supported with GRUB 1.";
+      boot.loader.grub.extraEntries = ''
+        menuentry "Memtest86+" {
+          linux16 @bootRoot@/memtest.bin ${toString cfg.params}
+        }
+      '';
 
       boot.loader.grub.extraFiles."memtest.bin" = "${memtest86}/memtest.bin";
     })
diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix
index 52413a13f07b7..07f51f43184d9 100644
--- a/nixos/modules/system/boot/networkd.nix
+++ b/nixos/modules/system/boot/networkd.nix
@@ -2848,7 +2848,7 @@ let
         ''
         + optionalString (def.tokenBucketFilterConfig != { }) ''
           [TokenBucketFilter]
-          ${attrsToSection def.tockenBucketFilterConfig}
+          ${attrsToSection def.tokenBucketFilterConfig}
         ''
         + optionalString (def.pieConfig != { }) ''
           [PIE]
diff --git a/nixos/modules/virtualisation/azure-common.nix b/nixos/modules/virtualisation/azure-common.nix
index f29d368137ae0..cd1ffdb6cbcc3 100644
--- a/nixos/modules/virtualisation/azure-common.nix
+++ b/nixos/modules/virtualisation/azure-common.nix
@@ -12,7 +12,6 @@ with lib;
 
   # Generate a GRUB menu.
   boot.loader.grub.device = "/dev/sda";
-  boot.loader.grub.version = 2;
   boot.loader.timeout = 0;
 
   boot.growPartition = true;
diff --git a/nixos/modules/virtualisation/xen-domU.nix b/nixos/modules/virtualisation/xen-domU.nix
index c00b984c2ce04..ce5a482b1145b 100644
--- a/nixos/modules/virtualisation/xen-domU.nix
+++ b/nixos/modules/virtualisation/xen-domU.nix
@@ -3,7 +3,6 @@
 { ... }:
 
 {
-  boot.loader.grub.version = 2;
   boot.loader.grub.device = "nodev";
 
   boot.initrd.kernelModules =
diff --git a/nixos/tests/aaaaxy.nix b/nixos/tests/aaaaxy.nix
index a1e1d44773c82..90b3c85e0c5f2 100644
--- a/nixos/tests/aaaaxy.nix
+++ b/nixos/tests/aaaaxy.nix
@@ -3,14 +3,16 @@
   meta.maintainers = with lib.maintainers; [ Luflosi ];
 
   nodes.machine = {
-    hardware.opengl.enable = true;
+    imports = [
+      ./common/x11.nix
+    ];
   };
 
   # This starts the game from a known state, feeds it a prerecorded set of button presses
   # and then checks if the final game state is identical to the expected state.
   # This is also what AAAAXY's CI system does and serves as a good sanity check.
   testScript = ''
-    machine.wait_for_unit("basic.target")
+    machine.wait_for_x()
 
     machine.succeed(
       # benchmark.dem needs to be in a mutable directory,
@@ -18,7 +20,6 @@
       "mkdir -p '/tmp/aaaaxy/assets/demos/'",
       "ln -s '${pkgs.aaaaxy.testing_infra}/assets/demos/benchmark.dem' '/tmp/aaaaxy/assets/demos/'",
       """
-        '${pkgs.xvfb-run}/bin/xvfb-run' \
         '${pkgs.aaaaxy.testing_infra}/scripts/regression-test-demo.sh' \
         'aaaaxy' 'on track for Any%, All Paths and No Teleports' \
         '${pkgs.aaaaxy}/bin/aaaaxy' '/tmp/aaaaxy/assets/demos/benchmark.dem'
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index 3c8b163b1fc14..5ceb39d0be51d 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -297,6 +297,7 @@ in {
   haste-server = handleTest ./haste-server.nix {};
   haproxy = handleTest ./haproxy.nix {};
   hardened = handleTest ./hardened.nix {};
+  harmonia = handleTest ./harmonia.nix {};
   headscale = handleTest ./headscale.nix {};
   healthchecks = handleTest ./web-apps/healthchecks.nix {};
   hbase2 = handleTest ./hbase.nix { package=pkgs.hbase2; };
diff --git a/nixos/tests/geth.nix b/nixos/tests/geth.nix
index 11ad1ed2ea66f..dc6490db57c9c 100644
--- a/nixos/tests/geth.nix
+++ b/nixos/tests/geth.nix
@@ -19,6 +19,10 @@ import ./make-test-python.nix ({ pkgs, ... }: {
         enable = true;
         port = 18545;
       };
+      authrpc = {
+        enable = true;
+        port = 18551;
+      };
     };
   };
 
@@ -31,11 +35,11 @@ import ./make-test-python.nix ({ pkgs, ... }: {
     machine.wait_for_open_port(18545)
 
     machine.succeed(
-        'geth attach --exec eth.blockNumber http://localhost:8545 | grep \'^0$\' '
+        'geth attach --exec "eth.blockNumber" http://localhost:8545 | grep \'^0$\' '
     )
 
     machine.succeed(
-        'geth attach --exec "eth.chainId()" http://localhost:18545 | grep \'"0x5"\' '
+        'geth attach --exec "eth.blockNumber" http://localhost:18545 | grep \'^0$\' '
     )
   '';
 })
diff --git a/nixos/tests/harmonia.nix b/nixos/tests/harmonia.nix
new file mode 100644
index 0000000000000..95fb7ea18cf8d
--- /dev/null
+++ b/nixos/tests/harmonia.nix
@@ -0,0 +1,33 @@
+import ./make-test-python.nix ({ pkgs, ... }:
+{
+  name = "harmonia";
+  nodes = {
+    harmonia = {
+      services.harmonia = {
+        enable = true;
+        signKeyPath = pkgs.writeText "cache-key"
+          "cache.example.com-1:9FhO0w+7HjZrhvmzT1VlAZw4OSAlFGTgC24Seg3tmPl4gZBdwZClzTTHr9cVzJpwsRSYLTu7hEAQe3ljy92CWg==";
+      };
+
+      networking.firewall.allowedTCPPorts = [ 5000 ];
+      system.extraDependencies = [ pkgs.hello ];
+    };
+
+    client01 = { lib, ... }: {
+      nix.settings = {
+        substituters = lib.mkForce [ "http://harmonia:5000" ];
+        trusted-public-keys = lib.mkForce [ "cache.example.com-1:eIGQXcGQpc00x6/XFcyacLEUmC07u4RAEHt5Y8vdglo=" ];
+      };
+    };
+  };
+
+  testScript = ''
+    start_all()
+
+    client01.wait_until_succeeds("curl -f http://harmonia:5000/version")
+    client01.succeed("curl -f http://harmonia:5000/nix-cache-info")
+
+    client01.succeed("cat /etc/nix/nix.conf >&2")
+    client01.wait_until_succeeds("nix-store --realise ${pkgs.hello} --store /root/other-store")
+  '';
+})
diff --git a/nixos/tests/installer.nix b/nixos/tests/installer.nix
index 51d0d232ebbf6..28b93472263f4 100644
--- a/nixos/tests/installer.nix
+++ b/nixos/tests/installer.nix
@@ -10,7 +10,7 @@ with pkgs.lib;
 let
 
   # The configuration to install.
-  makeConfig = { bootLoader, grubVersion, grubDevice, grubIdentifier, grubUseEfi
+  makeConfig = { bootLoader, grubDevice, grubIdentifier, grubUseEfi
                , extraConfig, forceGrubReinstallCount ? 0
                }:
     pkgs.writeText "configuration.nix" ''
@@ -29,11 +29,6 @@ let
         ${optionalString systemdStage1 "boot.initrd.systemd.enable = true;"}
 
         ${optionalString (bootLoader == "grub") ''
-          boot.loader.grub.version = ${toString grubVersion};
-          ${optionalString (grubVersion == 1) ''
-            boot.loader.grub.splashImage = null;
-          ''}
-
           boot.loader.grub.extraConfig = "serial; terminal_output serial";
           ${if grubUseEfi then ''
             boot.loader.grub.device = "nodev";
@@ -70,11 +65,11 @@ let
   # disk, and then reboot from the hard disk.  It's parameterized with
   # a test script fragment `createPartitions', which must create
   # partitions and filesystems.
-  testScriptFun = { bootLoader, createPartitions, grubVersion, grubDevice, grubUseEfi
+  testScriptFun = { bootLoader, createPartitions, grubDevice, grubUseEfi
                   , grubIdentifier, preBootCommands, postBootCommands, extraConfig
                   , testSpecialisationConfig
                   }:
-    let iface = if grubVersion == 1 then "ide" else "virtio";
+    let iface = "virtio";
         isEfi = bootLoader == "systemd-boot" || (bootLoader == "grub" && grubUseEfi);
         bios  = if pkgs.stdenv.isAarch64 then "QEMU_EFI.fd" else "OVMF.fd";
     in if !isEfi && !pkgs.stdenv.hostPlatform.isx86 then ''
@@ -122,7 +117,7 @@ let
           machine.succeed("cat /mnt/etc/nixos/hardware-configuration.nix >&2")
           machine.copy_from_host(
               "${ makeConfig {
-                    inherit bootLoader grubVersion grubDevice grubIdentifier
+                    inherit bootLoader grubDevice grubIdentifier
                             grubUseEfi extraConfig;
                   }
               }",
@@ -193,7 +188,7 @@ let
           # doesn't know about the host-guest sharing mechanism.
           machine.copy_from_host_via_shell(
               "${ makeConfig {
-                    inherit bootLoader grubVersion grubDevice grubIdentifier
+                    inherit bootLoader grubDevice grubIdentifier
                             grubUseEfi extraConfig;
                     forceGrubReinstallCount = 1;
                   }
@@ -222,7 +217,7 @@ let
       # doesn't know about the host-guest sharing mechanism.
       machine.copy_from_host_via_shell(
           "${ makeConfig {
-                inherit bootLoader grubVersion grubDevice grubIdentifier
+                inherit bootLoader grubDevice grubIdentifier
                 grubUseEfi extraConfig;
                 forceGrubReinstallCount = 2;
               }
@@ -284,7 +279,7 @@ let
     { createPartitions, preBootCommands ? "", postBootCommands ? "", extraConfig ? ""
     , extraInstallerConfig ? {}
     , bootLoader ? "grub" # either "grub" or "systemd-boot"
-    , grubVersion ? 2, grubDevice ? "/dev/vda", grubIdentifier ? "uuid", grubUseEfi ? false
+    , grubDevice ? "/dev/vda", grubIdentifier ? "uuid", grubUseEfi ? false
     , enableOCR ? false, meta ? {}
     , testSpecialisationConfig ? false
     }:
@@ -316,11 +311,9 @@ let
           # installer. This ensures the target disk (/dev/vda) is
           # the same during and after installation.
           virtualisation.emptyDiskImages = [ 512 ];
-          virtualisation.rootDevice =
-            if grubVersion == 1 then "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_drive2" else "/dev/vdb";
+          virtualisation.rootDevice = "/dev/vdb";
           virtualisation.bootLoaderDevice = "/dev/vda";
-          virtualisation.qemu.diskInterface =
-            if grubVersion == 1 then "scsi" else "virtio";
+          virtualisation.qemu.diskInterface = "virtio";
 
           # We don't want to have any networking in the guest whatsoever.
           # Also, if any vlans are enabled, the guest will reboot
@@ -372,8 +365,7 @@ let
             # curl's tarball, we see what it's trying to download
             curl
           ]
-          ++ optional (bootLoader == "grub" && grubVersion == 1) pkgs.grub
-          ++ optionals (bootLoader == "grub" && grubVersion == 2) (let
+          ++ optionals (bootLoader == "grub") (let
             zfsSupport = lib.any (x: x == "zfs")
               (extraInstallerConfig.boot.supportedFilesystems or []);
           in [
@@ -392,7 +384,7 @@ let
 
       testScript = testScriptFun {
         inherit bootLoader createPartitions preBootCommands postBootCommands
-                grubVersion grubDevice grubIdentifier grubUseEfi extraConfig
+                grubDevice grubIdentifier grubUseEfi extraConfig
                 testSpecialisationConfig;
       };
     };
@@ -875,26 +867,6 @@ in {
     '';
   };
 
-  # Test a basic install using GRUB 1.
-  grub1 = makeInstallerTest "grub1" rec {
-    createPartitions = ''
-      machine.succeed(
-          "flock ${grubDevice} parted --script ${grubDevice} -- mklabel msdos"
-          + " mkpart primary linux-swap 1M 1024M"
-          + " mkpart primary ext2 1024M -1s",
-          "udevadm settle",
-          "mkswap ${grubDevice}-part1 -L swap",
-          "swapon -L swap",
-          "mkfs.ext3 -L nixos ${grubDevice}-part2",
-          "mount LABEL=nixos /mnt",
-          "mkdir -p /mnt/tmp",
-      )
-    '';
-    grubVersion = 1;
-    # /dev/sda is not stable, even when the SCSI disk number is.
-    grubDevice = "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_drive1";
-  };
-
   # Test using labels to identify volumes in grub
   simpleLabels = makeInstallerTest "simpleLabels" {
     createPartitions = ''
diff --git a/nixos/tests/nginx.nix b/nixos/tests/nginx.nix
index 2a7e0f48d868b..8b1f921ec5209 100644
--- a/nixos/tests/nginx.nix
+++ b/nixos/tests/nginx.nix
@@ -87,15 +87,23 @@ import ./make-test-python.nix ({ pkgs, ... }: {
         return etag
 
 
-    webserver.wait_for_unit("nginx")
-    webserver.wait_for_open_port(80)
+    def wait_for_nginx_on_port(port):
+        webserver.wait_for_unit("nginx")
+        webserver.wait_for_open_port(port)
+
+
+    # nginx can be ready before multi-user.target, in which case switching to
+    # a different configuration might not realize it needs to restart nginx.
+    webserver.wait_for_unit("multi-user.target")
+
+    wait_for_nginx_on_port(80)
 
     with subtest("check ETag if serving Nix store paths"):
         old_etag = check_etag()
         webserver.succeed(
             "${etagSystem}/bin/switch-to-configuration test >&2"
         )
-        webserver.sleep(1)
+        wait_for_nginx_on_port(80)
         new_etag = check_etag()
         assert old_etag != new_etag
 
@@ -103,7 +111,7 @@ import ./make-test-python.nix ({ pkgs, ... }: {
         webserver.succeed(
             "${justReloadSystem}/bin/switch-to-configuration test >&2"
         )
-        webserver.wait_for_open_port(8080)
+        wait_for_nginx_on_port(8080)
         webserver.fail("journalctl -u nginx | grep -q -i stopped")
         webserver.succeed("journalctl -u nginx | grep -q -i reloaded")
 
@@ -111,7 +119,7 @@ import ./make-test-python.nix ({ pkgs, ... }: {
         webserver.succeed(
             "${reloadRestartSystem}/bin/switch-to-configuration test >&2"
         )
-        webserver.wait_for_unit("nginx")
+        wait_for_nginx_on_port(80)
         webserver.succeed("journalctl -u nginx | grep -q -i stopped")
 
     with subtest("nixos-rebuild --switch should fail when there are configuration errors"):
diff --git a/nixos/tests/sssd-ldap.nix b/nixos/tests/sssd-ldap.nix
index ff83e96068a96..60f3b1a415daf 100644
--- a/nixos/tests/sssd-ldap.nix
+++ b/nixos/tests/sssd-ldap.nix
@@ -6,17 +6,33 @@ let
   ldapRootPassword = "foobar";
 
   testUser = "alice";
-in import ./make-test-python.nix ({pkgs, ...}: {
+  testPassword = "foobar";
+  testNewPassword = "barfoo";
+in
+import ./make-test-python.nix ({ pkgs, ... }: {
   name = "sssd-ldap";
 
   meta = with pkgs.lib.maintainers; {
-    maintainers = [ bbigras ];
+    maintainers = [ bbigras s1341 ];
   };
 
   nodes.machine = { pkgs, ... }: {
+    security.pam.services.systemd-user.makeHomeDir = true;
+    environment.etc."cert.pem".text = builtins.readFile ./common/acme/server/acme.test.cert.pem;
+    environment.etc."key.pem".text = builtins.readFile ./common/acme/server/acme.test.key.pem;
     services.openldap = {
       enable = true;
+      urlList = [ "ldap:///" "ldaps:///" ];
       settings = {
+        attrs = {
+          olcTLSCACertificateFile = "/etc/cert.pem";
+          olcTLSCertificateFile = "/etc/cert.pem";
+          olcTLSCertificateKeyFile = "/etc/key.pem";
+          olcTLSCipherSuite = "HIGH:MEDIUM:+3DES:+RC4:+aNULL";
+          olcTLSCRLCheck = "none";
+          olcTLSVerifyClient = "never";
+          olcTLSProtocolMin = "3.1";
+        };
         children = {
           "cn=schema".includes = [
             "${pkgs.openldap}/etc/schema/core.ldif"
@@ -32,6 +48,23 @@ in import ./make-test-python.nix ({pkgs, ...}: {
               olcSuffix = dbSuffix;
               olcRootDN = "cn=${ldapRootUser},${dbSuffix}";
               olcRootPW = ldapRootPassword;
+              olcAccess = [
+                /*
+                  custom access rules for userPassword attributes
+                  */
+                ''
+                  {0}to attrs=userPassword
+                                    by self write
+                                    by anonymous auth
+                                    by * none''
+
+                /*
+                  allow read on anything else
+                  */
+                ''
+                  {1}to *
+                                    by * read''
+              ];
             };
           };
         };
@@ -55,7 +88,7 @@ in import ./make-test-python.nix ({pkgs, ...}: {
           dn: uid=${testUser},ou=accounts,ou=posix,${dbSuffix}
           objectClass: person
           objectClass: posixAccount
-          # userPassword: somePasswordHash
+          userPassword: ${testPassword}
           homeDirectory: /home/${testUser}
           uidNumber: 1234
           gidNumber: 1234
@@ -78,7 +111,9 @@ in import ./make-test-python.nix ({pkgs, ...}: {
         [domain/${dbDomain}]
         auth_provider = ldap
         id_provider = ldap
-        ldap_uri = ldap://127.0.0.1:389
+        ldap_uri = ldaps://127.0.0.1:636
+        ldap_tls_reqcert = allow
+        ldap_tls_cacert = /etc/cert.pem
         ldap_search_base = ${dbSuffix}
         ldap_default_bind_dn = cn=${ldapRootUser},${dbSuffix}
         ldap_default_authtok_type = password
@@ -97,5 +132,42 @@ in import ./make-test-python.nix ({pkgs, ...}: {
     else:
       machine.wait_for_console_text("Backend is online")
       machine.succeed("getent passwd ${testUser}")
+
+    with subtest("Log in as ${testUser}"):
+        machine.wait_until_tty_matches("1", "login: ")
+        machine.send_chars("${testUser}\n")
+        machine.wait_until_tty_matches("1", "login: ${testUser}")
+        machine.wait_until_succeeds("pgrep login")
+        machine.wait_until_tty_matches("1", "Password: ")
+        machine.send_chars("${testPassword}\n")
+        machine.wait_until_succeeds("pgrep -u ${testUser} bash")
+        machine.send_chars("touch done\n")
+        machine.wait_for_file("/home/${testUser}/done")
+
+    with subtest("Change ${testUser}'s password"):
+        machine.send_chars("passwd\n")
+        machine.wait_until_tty_matches("1", "Current Password: ")
+        machine.send_chars("${testPassword}\n")
+        machine.wait_until_tty_matches("1", "New Password: ")
+        machine.send_chars("${testNewPassword}\n")
+        machine.wait_until_tty_matches("1", "Reenter new Password: ")
+        machine.send_chars("${testNewPassword}\n")
+        machine.wait_until_tty_matches("1", "passwd: password updated successfully")
+
+    with subtest("Log in as ${testUser} with new password in virtual console 2"):
+        machine.send_key("alt-f2")
+        machine.wait_until_succeeds("[ $(fgconsole) = 2 ]")
+        machine.wait_for_unit("getty@tty2.service")
+        machine.wait_until_succeeds("pgrep -f 'agetty.*tty2'")
+
+        machine.wait_until_tty_matches("2", "login: ")
+        machine.send_chars("${testUser}\n")
+        machine.wait_until_tty_matches("2", "login: ${testUser}")
+        machine.wait_until_succeeds("pgrep login")
+        machine.wait_until_tty_matches("2", "Password: ")
+        machine.send_chars("${testNewPassword}\n")
+        machine.wait_until_succeeds("pgrep -u ${testUser} bash")
+        machine.send_chars("touch done2\n")
+        machine.wait_for_file("/home/${testUser}/done2")
   '';
 })