From 76c7b656bfa9b20a4172f7901285560db4c2c695 Mon Sep 17 00:00:00 2001 From: Raito Bezarius Date: Tue, 20 Dec 2022 21:18:10 +0100 Subject: [PATCH 1/5] nixos/qemu-vm: refactor bootDisk generation using make-disk-image --- nixos/modules/virtualisation/qemu-vm.nix | 294 +++++++++++------------ 1 file changed, 144 insertions(+), 150 deletions(-) diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix index 1619c0d9410f..2aa8a36a1cfc 100644 --- a/nixos/modules/virtualisation/qemu-vm.nix +++ b/nixos/modules/virtualisation/qemu-vm.nix @@ -55,6 +55,11 @@ let }; + selectPartitionTableLayout = { useEFIBoot, useDefaultFilesystems }: + if useDefaultFilesystems then + if useEFIBoot then "efi" else "legacy" + else "none"; + driveCmdline = idx: { file, driveExtraOpts, deviceExtraOpts, ... }: let drvId = "drive${toString idx}"; @@ -98,7 +103,6 @@ let addDeviceNames = imap1 (idx: drive: drive // { device = driveDeviceName idx; }); - # Shell script to start the VM. startVM = '' @@ -111,8 +115,23 @@ let NIX_DISK_IMAGE=$(readlink -f "''${NIX_DISK_IMAGE:-${toString config.virtualisation.diskImage}}") || test -z "$NIX_DISK_IMAGE" if test -n "$NIX_DISK_IMAGE" && ! test -e "$NIX_DISK_IMAGE"; then - ${qemu}/bin/qemu-img create -f qcow2 "$NIX_DISK_IMAGE" \ - ${toString config.virtualisation.diskSize}M + echo "Disk image do not exist, creating the virtualisation disk image..." + # If we are using a bootloader and default filesystems layout. + # We have to reuse the system image layout as a backing image format (CoW) + # So we can write on the top of it. + + # If we are not using the default FS layout, potentially, we are interested into + # performing operations in postDeviceCommands or at early boot on the raw device. + # We can still boot through QEMU direct kernel boot feature. + + # CoW prevent size to be attributed to an image. + # FIXME: raise this issue to upstream. + ${qemu}/bin/qemu-img create \ + ${concatStringsSep " \\\n" ([ "-f qcow2" ] + ++ optional (cfg.useBootLoader && cfg.useDefaultFilesystems) "-F qcow2 -b ${systemImage}/nixos.qcow2" + ++ optional (!(cfg.useBootLoader && cfg.useDefaultFilesystems)) "-o size=${toString config.virtualisation.diskSize}M" + ++ [ "$NIX_DISK_IMAGE" ])} + echo "Virtualisation disk image created." fi # Create a directory for storing temporary data of the running VM. @@ -158,13 +177,13 @@ let ${qemu}/bin/qemu-img create -f qcow2 -F qcow2 -b ${bootDisk}/disk.img "$TMPDIR/disk.img" fi - NIX_EFI_VARS=$(readlink -f "''${NIX_EFI_VARS:-${cfg.efiVars}}") + NIX_EFI_VARS=$(readlink -f "''${NIX_EFI_VARS:-${config.system.name}-efi-vars.fd}") ${lib.optionalString cfg.useEFIBoot '' # VM needs writable EFI vars if ! test -e "$NIX_EFI_VARS"; then - cp ${bootDisk}/efi-vars.fd "$NIX_EFI_VARS" + cp ${systemImage}/efi-vars.fd "$NIX_EFI_VARS" chmod 0644 "$NIX_EFI_VARS" fi ''} @@ -200,95 +219,29 @@ let regInfo = pkgs.closureInfo { rootPaths = config.virtualisation.additionalPaths; }; - - # Generate a hard disk image containing a /boot partition and GRUB - # in the MBR. Used when the `useBootLoader' option is set. - # Uses `runInLinuxVM` to create the image in a throwaway VM. - # See note [Disk layout with `useBootLoader`]. - # FIXME: use nixos/lib/make-disk-image.nix. - bootDisk = - pkgs.vmTools.runInLinuxVM ( - pkgs.runCommand "nixos-boot-disk" - { preVM = - '' - mkdir $out - diskImage=$out/disk.img - ${qemu}/bin/qemu-img create -f qcow2 $diskImage "120M" - ${lib.optionalString cfg.useEFIBoot '' - efiVars=$out/efi-vars.fd - cp ${cfg.efi.variables} $efiVars - chmod 0644 $efiVars - ''} - ''; - buildInputs = [ pkgs.util-linux ]; - QEMU_OPTS = "-nographic -serial stdio -monitor none" - + lib.optionalString cfg.useEFIBoot ( - " -drive if=pflash,format=raw,unit=0,readonly=on,file=${cfg.efi.firmware}" - + " -drive if=pflash,format=raw,unit=1,file=$efiVars"); - } - '' - # Create a /boot EFI partition with 120M and arbitrary but fixed GUIDs for reproducibility - ${pkgs.gptfdisk}/bin/sgdisk \ - --set-alignment=1 --new=1:34:2047 --change-name=1:BIOSBootPartition --typecode=1:ef02 \ - --set-alignment=512 --largest-new=2 --change-name=2:EFISystem --typecode=2:ef00 \ - --attributes=1:set:1 \ - --attributes=2:set:2 \ - --disk-guid=97FD5997-D90B-4AA3-8D16-C1723AEA73C1 \ - --partition-guid=1:1C06F03B-704E-4657-B9CD-681A087A2FDC \ - --partition-guid=2:970C694F-AFD0-4B99-B750-CDB7A329AB6F \ - --hybrid 2 \ - --recompute-chs /dev/vda - - ${optionalString (config.boot.loader.grub.device != "/dev/vda") - # In this throwaway VM, we only have the /dev/vda disk, but the - # actual VM described by `config` (used by `switch-to-configuration` - # below) may set `boot.loader.grub.device` to a different device - # that's nonexistent in the throwaway VM. - # Create a symlink for that device, so that the `grub-install` - # by `switch-to-configuration` will hit /dev/vda anyway. - '' - ln -s /dev/vda ${config.boot.loader.grub.device} - '' - } - - ${pkgs.dosfstools}/bin/mkfs.fat -F16 /dev/vda2 - export MTOOLS_SKIP_CHECK=1 - ${pkgs.mtools}/bin/mlabel -i /dev/vda2 ::boot - - # Mount /boot; load necessary modules first. - ${pkgs.kmod}/bin/insmod ${pkgs.linux}/lib/modules/*/kernel/fs/nls/nls_cp437.ko.xz || true - ${pkgs.kmod}/bin/insmod ${pkgs.linux}/lib/modules/*/kernel/fs/nls/nls_iso8859-1.ko.xz || true - ${pkgs.kmod}/bin/insmod ${pkgs.linux}/lib/modules/*/kernel/fs/fat/fat.ko.xz || true - ${pkgs.kmod}/bin/insmod ${pkgs.linux}/lib/modules/*/kernel/fs/fat/vfat.ko.xz || true - ${pkgs.kmod}/bin/insmod ${pkgs.linux}/lib/modules/*/kernel/fs/efivarfs/efivarfs.ko.xz || true - mkdir /boot - mount /dev/vda2 /boot - - ${optionalString config.boot.loader.efi.canTouchEfiVariables '' - mount -t efivarfs efivarfs /sys/firmware/efi/efivars - ''} - - # This is needed for GRUB 0.97, which doesn't know about virtio devices. - mkdir /boot/grub - echo '(hd0) /dev/vda' > /boot/grub/device.map - - # This is needed for systemd-boot to find ESP, and udev is not available here to create this - mkdir -p /dev/block - ln -s /dev/vda2 /dev/block/254:2 - - # Set up system profile (normally done by nixos-rebuild / nix-env --set) - mkdir -p /nix/var/nix/profiles - ln -s ${config.system.build.toplevel} /nix/var/nix/profiles/system-1-link - ln -s /nix/var/nix/profiles/system-1-link /nix/var/nix/profiles/system - - # Install bootloader - touch /etc/NIXOS - export NIXOS_INSTALL_BOOTLOADER=1 - ${config.system.build.toplevel}/bin/switch-to-configuration boot - - umount /boot - '' # */ - ); + # System image is akin to a complete NixOS install with + # a boot partition and root partition. + systemImage = import ../../lib/make-disk-image.nix { + inherit pkgs config lib; + additionalPaths = [ regInfo ]; + format = "qcow2"; + onlyNixStore = false; + partitionTableType = selectPartitionTableLayout { inherit (cfg) useDefaultFilesystems useEFIBoot; }; + # Bootloader should be installed on the system image only if we are booting through bootloaders. + # Though, if a user is not using our default filesystems, it is possible to not have any ESP + # or a strange partition table that's incompatible with GRUB configuration. + # As a consequence, this may lead to disk image creation failures. + # To avoid this, we prefer to let the user find out about how to install the bootloader on its ESP/disk. + # Usually, this can be through building your own disk image. + # TODO: If a user is interested into a more fine grained heuristic for `installBootLoader` + # by examining the actual contents of `cfg.fileSystems`, please send a PR. + installBootLoader = cfg.useBootLoader && cfg.useDefaultFilesystems; + touchEFIVars = cfg.useEFIBoot; + diskSize = "auto"; + additionalSpace = "0M"; + copyChannel = false; + OVMF = cfg.efi.OVMF; + }; storeImage = import ../../lib/make-disk-image.nix { inherit pkgs config lib; @@ -297,17 +250,42 @@ let onlyNixStore = true; partitionTableType = "none"; installBootLoader = false; + touchEFIVars = false; diskSize = "auto"; additionalSpace = "0M"; copyChannel = false; }; + bootConfiguration = + if cfg.useDefaultFilesystems + then + if cfg.useBootLoader + then + if cfg.useEFIBoot then "efi_bootloading_with_default_fs" + else "legacy_bootloading_with_default_fs" + else + "direct_boot_with_default_fs" + else + "custom"; + suggestedRootDevice = { + "efi_bootloading_with_default_fs" = "${cfg.bootLoaderDevice}2"; + "legacy_bootloading_with_default_fs" = "${cfg.bootLoaderDevice}1"; + "direct_boot_with_default_fs" = cfg.bootLoaderDevice; + # This will enforce a NixOS module type checking error + # to ask explicitly the user to set a rootDevice. + # As it will look like `rootDevice = lib.mkDefault null;` after + # all "computations". + "custom" = null; + }.${bootConfiguration}; in { imports = [ ../profiles/qemu-guest.nix (mkRenamedOptionModule [ "virtualisation" "pathsInNixDB" ] [ "virtualisation" "additionalPaths" ]) + (mkRemovedOptionModule [ "virtualisation" "bootDevice" ] "This option was renamed to `virtualisation.rootDevice`, as it was incorrectly named and misleading. Take the time to review what you want to do and look at the new options like `virtualisation.{bootLoaderDevice, bootPartition}`, open an issue in case of issues.") + (mkRemovedOptionModule [ "virtualisation" "efiVars" ] "This option was removed, it is possible to provide a template UEFI variable with `virtualisation.efi.variables` ; if this option is important to you, open an issue") + (mkRemovedOptionModule [ "virtualisation" "persistBootDevice" ] "Boot device is always persisted if you use a bootloader through the root disk image ; if this does not work for your usecase, please examine carefully what `virtualisation.{bootLoaderDevice, rootDevice, bootPartition}` options offers you and open an issue explaining your need.`") ]; options = { @@ -362,13 +340,47 @@ in ''; }; - virtualisation.bootDevice = + virtualisation.bootLoaderDevice = mkOption { type = types.path; + default = lookupDriveDeviceName "root" cfg.qemu.drives; + defaultText = literalExpression ''lookupDriveDeviceName "root" cfg.qemu.drives''; example = "/dev/vda"; description = lib.mdDoc '' - The disk to be used for the root filesystem. + The disk to be used for the boot filesystem. + By default, it is the same disk as the root filesystem. + ''; + }; + + virtualisation.bootPartition = + mkOption { + type = types.nullOr types.path; + default = if cfg.useEFIBoot then "${cfg.bootLoaderDevice}1" else null; + defaultText = literalExpression ''if cfg.useEFIBoot then "''${cfg.bootLoaderDevice}1" else null''; + example = "/dev/vda1"; + description = + lib.mdDoc '' + The boot partition to be used to mount /boot filesystem. + In legacy boots, this should be null. + By default, in EFI boot, it is the first partition of the boot device. + ''; + }; + + virtualisation.rootDevice = + mkOption { + type = types.nullOr types.path; + example = "/dev/vda2"; + description = + lib.mdDoc '' + The disk or partition to be used for the root filesystem. + By default (read the source code for more details): + + - under EFI with a bootloader: 2nd partition of the boot disk + - in legacy boot with a bootloader: 1st partition of the boot disk + - in direct boot (i.e. without a bootloader): whole disk + + In case you are not using a default boot device or a default filesystem, you have to set explicitly your root device. ''; }; @@ -749,10 +761,22 @@ in }; virtualisation.efi = { + OVMF = mkOption { + type = types.package; + default = (pkgs.OVMF.override { + secureBoot = cfg.useSecureBoot; + }).fd; + defaultText = ''(pkgs.OVMF.override { + secureBoot = cfg.useSecureBoot; + }).fd''; + description = + lib.mdDoc "OVMF firmware package, defaults to OVMF configured with secure boot if needed."; + }; + firmware = mkOption { type = types.path; - default = pkgs.OVMF.firmware; - defaultText = literalExpression "pkgs.OVMF.firmware"; + default = cfg.efi.OVMF.firmware; + defaultText = literalExpression "cfg.efi.OVMF.firmware"; description = lib.mdDoc '' Firmware binary for EFI implementation, defaults to OVMF. @@ -761,8 +785,8 @@ in variables = mkOption { type = types.path; - default = pkgs.OVMF.variables; - defaultText = literalExpression "pkgs.OVMF.variables"; + default = cfg.efi.OVMF.variables; + defaultText = literalExpression "cfg.efi.OVMF.variables"; description = lib.mdDoc '' Platform-specific flash binary for EFI variables, implementation-dependent to the EFI firmware. @@ -786,18 +810,17 @@ in ''; }; - virtualisation.efiVars = + virtualisation.useSecureBoot = mkOption { - type = types.str; - default = "./${config.system.name}-efi-vars.fd"; - defaultText = literalExpression ''"./''${config.system.name}-efi-vars.fd"''; + type = types.bool; + default = false; description = lib.mdDoc '' - Path to nvram image containing UEFI variables. The will be created - on startup if it does not exist. + Enable Secure Boot support in the EFI firmware. ''; }; + virtualisation.bios = mkOption { type = types.nullOr types.package; @@ -853,30 +876,13 @@ in ${opt.writableStore} = false; ''; - # Note [Disk layout with `useBootLoader`] - # - # If `useBootLoader = true`, we configure 2 drives: - # `/dev/?da` for the root disk, and `/dev/?db` for the boot disk - # which has the `/boot` partition and the boot loader. - # Concretely: - # - # * The second drive's image `disk.img` is created in `bootDisk = ...` - # using a throwaway VM. Note that there the disk is always `/dev/vda`, - # even though in the final VM it will be at `/dev/*b`. - # * The disks are attached in `virtualisation.qemu.drives`. - # Their order makes them appear as devices `a`, `b`, etc. - # * `fileSystems."/boot"` is adjusted to be on device `b`. - # * The disk.img is recreated each time the VM is booted unless - # virtualisation.persistBootDevice is set. - - # If `useBootLoader`, GRUB goes to the second disk, see - # note [Disk layout with `useBootLoader`]. - boot.loader.grub.device = mkVMOverride ( - if cfg.useBootLoader - then driveDeviceName 2 # second disk - else cfg.bootDevice - ); + # In UEFI boot, we use a EFI-only partition table layout, thus GRUB will fail when trying to install + # legacy and UEFI. In order to avoid this, we have to put "nodev" to force UEFI-only installs. + # Otherwise, we set the proper bootloader device for this. + # FIXME: make a sense of this mess wrt to multiple ESP present in the system, probably use boot.efiSysMountpoint? + boot.loader.grub.device = mkVMOverride (if cfg.useEFIBoot then "nodev" else cfg.bootLoaderDevice); boot.loader.grub.gfxmodeBios = with cfg.resolution; "${toString x}x${toString y}"; + virtualisation.rootDevice = mkDefault suggestedRootDevice; boot.initrd.kernelModules = optionals (cfg.useNixStoreImage && !cfg.writableStore) [ "erofs" ]; @@ -890,10 +896,10 @@ in '' # If the disk image appears to be empty, run mke2fs to # initialise. - FSTYPE=$(blkid -o value -s TYPE ${cfg.bootDevice} || true) - PARTTYPE=$(blkid -o value -s PTTYPE ${cfg.bootDevice} || true) + FSTYPE=$(blkid -o value -s TYPE ${cfg.rootDevice} || true) + PARTTYPE=$(blkid -o value -s PTTYPE ${cfg.rootDevice} || true) if test -z "$FSTYPE" -a -z "$PARTTYPE"; then - mke2fs -t ext4 ${cfg.bootDevice} + mke2fs -t ext4 ${cfg.rootDevice} fi ''; @@ -939,8 +945,6 @@ in optional cfg.writableStore "overlay" ++ optional (cfg.qemu.diskInterface == "scsi") "sym53c8xx"; - virtualisation.bootDevice = mkDefault (driveDeviceName 1); - virtualisation.additionalPaths = [ config.system.build.toplevel ]; virtualisation.sharedDirectories = { @@ -997,7 +1001,7 @@ in ]) (mkIf cfg.useEFIBoot [ "-drive if=pflash,format=raw,unit=0,readonly=on,file=${cfg.efi.firmware}" - "-drive if=pflash,format=raw,unit=1,file=$NIX_EFI_VARS" + "-drive if=pflash,format=raw,unit=1,readonly=off,file=$NIX_EFI_VARS" ]) (mkIf (cfg.bios != null) [ "-bios ${cfg.bios}/bios.bin" @@ -1013,23 +1017,14 @@ in file = ''"$NIX_DISK_IMAGE"''; driveExtraOpts.cache = "writeback"; driveExtraOpts.werror = "report"; + deviceExtraOpts.bootindex = "1"; }]) (mkIf cfg.useNixStoreImage [{ name = "nix-store"; file = ''"$TMPDIR"/store.img''; - deviceExtraOpts.bootindex = if cfg.useBootLoader then "3" else "2"; + deviceExtraOpts.bootindex = "2"; driveExtraOpts.format = if cfg.writableStore then "qcow2" else "raw"; }]) - (mkIf cfg.useBootLoader [ - # The order of this list determines the device names, see - # note [Disk layout with `useBootLoader`]. - { - name = "boot"; - file = ''"$TMPDIR"/disk.img''; - driveExtraOpts.media = "disk"; - deviceExtraOpts.bootindex = "1"; - } - ]) (imap0 (idx: _: { file = "$(pwd)/empty${toString idx}.qcow2"; driveExtraOpts.werror = "report"; @@ -1065,7 +1060,7 @@ in device = "tmpfs"; fsType = "tmpfs"; } else { - device = cfg.bootDevice; + device = cfg.rootDevice; fsType = "ext4"; autoFormat = true; }); @@ -1086,9 +1081,8 @@ in options = [ "mode=0755" ]; neededForBoot = true; }; - # see note [Disk layout with `useBootLoader`] - "/boot" = lib.mkIf cfg.useBootLoader { - device = "${lookupDriveDeviceName "boot" cfg.qemu.drives}2"; # 2 for e.g. `vdb2`, as created in `bootDisk` + "/boot" = lib.mkIf (cfg.useBootLoader && cfg.bootPartition != null) { + device = cfg.bootPartition; # 1 for e.g. `vda1`, as created in `systemImage` fsType = "vfat"; noCheck = true; # fsck fails on a r/o filesystem }; From 58f4c3944db804bd28d35ceb4687961683052a91 Mon Sep 17 00:00:00 2001 From: Raito Bezarius Date: Sun, 16 Oct 2022 00:18:03 +0200 Subject: [PATCH 2/5] =?UTF-8?q?nixos/tests(treewide):=20vdb=20=E2=86=92=20?= =?UTF-8?q?vda,=20vdc=20=E2=86=92=20vdb,=20vdd=20=E2=86=92=20vdc,=20?= =?UTF-8?q?=E2=80=A6=20/=20bootDevice=20=E2=86=92=20rootDevice?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nixos/tests/hibernate.nix | 2 +- nixos/tests/initrd-luks-empty-passphrase.nix | 10 +++++----- nixos/tests/installer.nix | 3 ++- nixos/tests/luks.nix | 8 ++++---- nixos/tests/lvm2/systemd-stage-1.nix | 8 ++++---- nixos/tests/non-default-filesystems.nix | 3 ++- nixos/tests/swap-file-btrfs.nix | 2 +- nixos/tests/swap-partition.nix | 2 +- nixos/tests/systemd-initrd-btrfs-raid.nix | 8 ++++---- nixos/tests/systemd-initrd-luks-fido2.nix | 8 ++++---- nixos/tests/systemd-initrd-luks-keyfile.nix | 6 +++--- nixos/tests/systemd-initrd-luks-password.nix | 12 ++++++------ nixos/tests/systemd-initrd-luks-tpm2.nix | 8 ++++---- nixos/tests/systemd-initrd-swraid.nix | 6 +++--- nixos/tests/zfs.nix | 12 ++++++------ 15 files changed, 50 insertions(+), 48 deletions(-) diff --git a/nixos/tests/hibernate.nix b/nixos/tests/hibernate.nix index cb75322ca5f9..4d0b53e95b32 100644 --- a/nixos/tests/hibernate.nix +++ b/nixos/tests/hibernate.nix @@ -63,7 +63,7 @@ in makeTest { # Small root disk for installer 512 ]; - virtualisation.bootDevice = "/dev/vdb"; + virtualisation.rootDevice = "/dev/vdb"; }; }; diff --git a/nixos/tests/initrd-luks-empty-passphrase.nix b/nixos/tests/initrd-luks-empty-passphrase.nix index 41765a395ec6..d2805f2f1734 100644 --- a/nixos/tests/initrd-luks-empty-passphrase.nix +++ b/nixos/tests/initrd-luks-empty-passphrase.nix @@ -30,26 +30,26 @@ in { specialisation.boot-luks-wrong-keyfile.configuration = { boot.initrd.luks.devices = lib.mkVMOverride { cryptroot = { - device = "/dev/vdc"; + device = "/dev/vdb"; keyFile = "/etc/cryptroot.key"; tryEmptyPassphrase = true; fallbackToPassword = !systemdStage1; }; }; - virtualisation.bootDevice = "/dev/mapper/cryptroot"; + virtualisation.rootDevice = "/dev/mapper/cryptroot"; boot.initrd.secrets."/etc/cryptroot.key" = keyfile; }; specialisation.boot-luks-missing-keyfile.configuration = { boot.initrd.luks.devices = lib.mkVMOverride { cryptroot = { - device = "/dev/vdc"; + device = "/dev/vdb"; keyFile = "/etc/cryptroot.key"; tryEmptyPassphrase = true; fallbackToPassword = !systemdStage1; }; }; - virtualisation.bootDevice = "/dev/mapper/cryptroot"; + virtualisation.rootDevice = "/dev/mapper/cryptroot"; }; }; @@ -76,7 +76,7 @@ in { # Create encrypted volume machine.wait_for_unit("multi-user.target") - machine.succeed("echo "" | cryptsetup luksFormat /dev/vdc --batch-mode") + machine.succeed("echo "" | cryptsetup luksFormat /dev/vdb --batch-mode") machine.succeed("bootctl set-default nixos-generation-1-specialisation-boot-luks-wrong-keyfile.conf") machine.succeed("sync") machine.crash() diff --git a/nixos/tests/installer.nix b/nixos/tests/installer.nix index d441765fe194..f385a99ce652 100644 --- a/nixos/tests/installer.nix +++ b/nixos/tests/installer.nix @@ -316,8 +316,9 @@ let # installer. This ensures the target disk (/dev/vda) is # the same during and after installation. virtualisation.emptyDiskImages = [ 512 ]; - virtualisation.bootDevice = + virtualisation.rootDevice = if grubVersion == 1 then "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_drive2" else "/dev/vdb"; + virtualisation.bootLoaderDevice = "/dev/vda"; virtualisation.qemu.diskInterface = if grubVersion == 1 then "scsi" else "virtio"; diff --git a/nixos/tests/luks.nix b/nixos/tests/luks.nix index 82f5095cb260..c2b95c6a95fb 100644 --- a/nixos/tests/luks.nix +++ b/nixos/tests/luks.nix @@ -18,10 +18,10 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: { boot-luks.configuration = { boot.initrd.luks.devices = lib.mkVMOverride { # We have two disks and only type one password - key reuse is in place - cryptroot.device = "/dev/vdc"; - cryptroot2.device = "/dev/vdd"; + cryptroot.device = "/dev/vdb"; + cryptroot2.device = "/dev/vdc"; }; - virtualisation.bootDevice = "/dev/mapper/cryptroot"; + virtualisation.rootDevice = "/dev/mapper/cryptroot"; }; boot-luks-custom-keymap.configuration = lib.mkMerge [ boot-luks.configuration @@ -37,8 +37,8 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: { testScript = '' # Create encrypted volume machine.wait_for_unit("multi-user.target") + machine.succeed("echo -n supersecret | cryptsetup luksFormat -q --iter-time=1 /dev/vdb -") machine.succeed("echo -n supersecret | cryptsetup luksFormat -q --iter-time=1 /dev/vdc -") - machine.succeed("echo -n supersecret | cryptsetup luksFormat -q --iter-time=1 /dev/vdd -") # Boot from the encrypted disk machine.succeed("bootctl set-default nixos-generation-1-specialisation-boot-luks.conf") diff --git a/nixos/tests/lvm2/systemd-stage-1.nix b/nixos/tests/lvm2/systemd-stage-1.nix index 617ba77b1796..b711cd22d7f4 100644 --- a/nixos/tests/lvm2/systemd-stage-1.nix +++ b/nixos/tests/lvm2/systemd-stage-1.nix @@ -1,18 +1,18 @@ { kernelPackages ? null, flavour }: let preparationCode = { raid = '' - machine.succeed("vgcreate test_vg /dev/vdc /dev/vdd") + machine.succeed("vgcreate test_vg /dev/vdb /dev/vdc") machine.succeed("lvcreate -L 512M --type raid0 test_vg -n test_lv") ''; thinpool = '' - machine.succeed("vgcreate test_vg /dev/vdc") + machine.succeed("vgcreate test_vg /dev/vdb") machine.succeed("lvcreate -L 512M -T test_vg/test_thin_pool") machine.succeed("lvcreate -n test_lv -V 16G --thinpool test_thin_pool test_vg") ''; vdo = '' - machine.succeed("vgcreate test_vg /dev/vdc") + machine.succeed("vgcreate test_vg /dev/vdb") machine.succeed("lvcreate --type vdo -n test_lv -L 6G -V 12G test_vg/vdo_pool_lv") ''; }.${flavour}; @@ -79,7 +79,7 @@ in import ../make-test-python.nix ({ pkgs, ... }: { kernelPackages = lib.mkIf (kernelPackages != null) kernelPackages; }; - specialisation.boot-lvm.configuration.virtualisation.bootDevice = "/dev/test_vg/test_lv"; + specialisation.boot-lvm.configuration.virtualisation.rootDevice = "/dev/test_vg/test_lv"; }; testScript = '' diff --git a/nixos/tests/non-default-filesystems.nix b/nixos/tests/non-default-filesystems.nix index 7fa75aaad724..d4e8bfbc65e9 100644 --- a/nixos/tests/non-default-filesystems.nix +++ b/nixos/tests/non-default-filesystems.nix @@ -5,9 +5,10 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: nodes.machine = { config, pkgs, lib, ... }: let - disk = config.virtualisation.bootDevice; + disk = config.virtualisation.rootDevice; in { + virtualisation.rootDevice = "/dev/vda"; virtualisation.useDefaultFilesystems = false; boot.initrd.availableKernelModules = [ "btrfs" ]; diff --git a/nixos/tests/swap-file-btrfs.nix b/nixos/tests/swap-file-btrfs.nix index 4f73942b5f32..d9fcd2be1160 100644 --- a/nixos/tests/swap-file-btrfs.nix +++ b/nixos/tests/swap-file-btrfs.nix @@ -9,7 +9,7 @@ import ./make-test-python.nix ({ lib, ... }: { virtualisation.useDefaultFilesystems = false; - virtualisation.bootDevice = "/dev/vda"; + virtualisation.rootDevice = "/dev/vda"; boot.initrd.postDeviceCommands = '' ${pkgs.btrfs-progs}/bin/mkfs.btrfs --label root /dev/vda diff --git a/nixos/tests/swap-partition.nix b/nixos/tests/swap-partition.nix index 2279630b57b8..ddcaeb95453e 100644 --- a/nixos/tests/swap-partition.nix +++ b/nixos/tests/swap-partition.nix @@ -7,7 +7,7 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: { virtualisation.useDefaultFilesystems = false; - virtualisation.bootDevice = "/dev/vda1"; + virtualisation.rootDevice = "/dev/vda1"; boot.initrd.postDeviceCommands = '' if ! test -b /dev/vda1; then diff --git a/nixos/tests/systemd-initrd-btrfs-raid.nix b/nixos/tests/systemd-initrd-btrfs-raid.nix index 40fd2d4dc611..c9cdf0060b1b 100644 --- a/nixos/tests/systemd-initrd-btrfs-raid.nix +++ b/nixos/tests/systemd-initrd-btrfs-raid.nix @@ -21,14 +21,14 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: { fileSystems = lib.mkVMOverride { "/".fsType = lib.mkForce "btrfs"; }; - virtualisation.bootDevice = "/dev/vdc"; + virtualisation.rootDevice = "/dev/vdb"; }; }; testScript = '' # Create RAID - machine.succeed("mkfs.btrfs -d raid0 /dev/vdc /dev/vdd") - machine.succeed("mkdir -p /mnt && mount /dev/vdc /mnt && echo hello > /mnt/test && umount /mnt") + machine.succeed("mkfs.btrfs -d raid0 /dev/vdb /dev/vdc") + machine.succeed("mkdir -p /mnt && mount /dev/vdb /mnt && echo hello > /mnt/test && umount /mnt") # Boot from the RAID machine.succeed("bootctl set-default nixos-generation-1-specialisation-boot-btrfs-raid.conf") @@ -38,7 +38,7 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: { # Ensure we have successfully booted from the RAID assert "(initrd)" in machine.succeed("systemd-analyze") # booted with systemd in stage 1 - assert "/dev/vdc on / type btrfs" in machine.succeed("mount") + assert "/dev/vdb on / type btrfs" in machine.succeed("mount") assert "hello" in machine.succeed("cat /test") assert "Total devices 2" in machine.succeed("btrfs filesystem show") ''; diff --git a/nixos/tests/systemd-initrd-luks-fido2.nix b/nixos/tests/systemd-initrd-luks-fido2.nix index 133e552a3dc9..e80d95f79c7e 100644 --- a/nixos/tests/systemd-initrd-luks-fido2.nix +++ b/nixos/tests/systemd-initrd-luks-fido2.nix @@ -19,19 +19,19 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: { specialisation.boot-luks.configuration = { boot.initrd.luks.devices = lib.mkVMOverride { cryptroot = { - device = "/dev/vdc"; + device = "/dev/vdb"; crypttabExtraOpts = [ "fido2-device=auto" ]; }; }; - virtualisation.bootDevice = "/dev/mapper/cryptroot"; + virtualisation.rootDevice = "/dev/mapper/cryptroot"; }; }; testScript = '' # Create encrypted volume machine.wait_for_unit("multi-user.target") - machine.succeed("echo -n supersecret | cryptsetup luksFormat -q --iter-time=1 /dev/vdc -") - machine.succeed("PASSWORD=supersecret SYSTEMD_LOG_LEVEL=debug systemd-cryptenroll --fido2-device=auto /dev/vdc |& systemd-cat") + machine.succeed("echo -n supersecret | cryptsetup luksFormat -q --iter-time=1 /dev/vdb -") + machine.succeed("PASSWORD=supersecret SYSTEMD_LOG_LEVEL=debug systemd-cryptenroll --fido2-device=auto /dev/vdb |& systemd-cat") # Boot from the encrypted disk machine.succeed("bootctl set-default nixos-generation-1-specialisation-boot-luks.conf") diff --git a/nixos/tests/systemd-initrd-luks-keyfile.nix b/nixos/tests/systemd-initrd-luks-keyfile.nix index 25c0c5bd866d..257243d92a1d 100644 --- a/nixos/tests/systemd-initrd-luks-keyfile.nix +++ b/nixos/tests/systemd-initrd-luks-keyfile.nix @@ -27,11 +27,11 @@ in { specialisation.boot-luks.configuration = { boot.initrd.luks.devices = lib.mkVMOverride { cryptroot = { - device = "/dev/vdc"; + device = "/dev/vdb"; keyFile = "/etc/cryptroot.key"; }; }; - virtualisation.bootDevice = "/dev/mapper/cryptroot"; + virtualisation.rootDevice = "/dev/mapper/cryptroot"; boot.initrd.secrets."/etc/cryptroot.key" = keyfile; }; }; @@ -39,7 +39,7 @@ in { testScript = '' # Create encrypted volume machine.wait_for_unit("multi-user.target") - machine.succeed("cryptsetup luksFormat -q --iter-time=1 -d ${keyfile} /dev/vdc") + machine.succeed("cryptsetup luksFormat -q --iter-time=1 -d ${keyfile} /dev/vdb") # Boot from the encrypted disk machine.succeed("bootctl set-default nixos-generation-1-specialisation-boot-luks.conf") diff --git a/nixos/tests/systemd-initrd-luks-password.nix b/nixos/tests/systemd-initrd-luks-password.nix index 55d0b4324b40..2dd3f304e82a 100644 --- a/nixos/tests/systemd-initrd-luks-password.nix +++ b/nixos/tests/systemd-initrd-luks-password.nix @@ -19,10 +19,10 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: { specialisation.boot-luks.configuration = { boot.initrd.luks.devices = lib.mkVMOverride { # We have two disks and only type one password - key reuse is in place - cryptroot.device = "/dev/vdc"; - cryptroot2.device = "/dev/vdd"; + cryptroot.device = "/dev/vdb"; + cryptroot2.device = "/dev/vdc"; }; - virtualisation.bootDevice = "/dev/mapper/cryptroot"; + virtualisation.rootDevice = "/dev/mapper/cryptroot"; # test mounting device unlocked in initrd after switching root virtualisation.fileSystems."/cryptroot2".device = "/dev/mapper/cryptroot2"; }; @@ -31,9 +31,9 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: { testScript = '' # Create encrypted volume machine.wait_for_unit("multi-user.target") + machine.succeed("echo -n supersecret | cryptsetup luksFormat -q --iter-time=1 /dev/vdb -") machine.succeed("echo -n supersecret | cryptsetup luksFormat -q --iter-time=1 /dev/vdc -") - machine.succeed("echo -n supersecret | cryptsetup luksFormat -q --iter-time=1 /dev/vdd -") - machine.succeed("echo -n supersecret | cryptsetup luksOpen -q /dev/vdd cryptroot2") + machine.succeed("echo -n supersecret | cryptsetup luksOpen -q /dev/vdc cryptroot2") machine.succeed("mkfs.ext4 /dev/mapper/cryptroot2") # Boot from the encrypted disk @@ -47,7 +47,7 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: { machine.send_console("supersecret\n") machine.wait_for_unit("multi-user.target") - assert "/dev/mapper/cryptroot on / type ext4" in machine.succeed("mount") + assert "/dev/mapper/cryptroot on / type ext4" in machine.succeed("mount"), "/dev/mapper/cryptroot do not appear in mountpoints list" assert "/dev/mapper/cryptroot2 on /cryptroot2 type ext4" in machine.succeed("mount") ''; }) diff --git a/nixos/tests/systemd-initrd-luks-tpm2.nix b/nixos/tests/systemd-initrd-luks-tpm2.nix index 085088d2ee25..734ef38579f0 100644 --- a/nixos/tests/systemd-initrd-luks-tpm2.nix +++ b/nixos/tests/systemd-initrd-luks-tpm2.nix @@ -21,11 +21,11 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: { specialisation.boot-luks.configuration = { boot.initrd.luks.devices = lib.mkVMOverride { cryptroot = { - device = "/dev/vdc"; + device = "/dev/vdb"; crypttabExtraOpts = [ "tpm2-device=auto" ]; }; }; - virtualisation.bootDevice = "/dev/mapper/cryptroot"; + virtualisation.rootDevice = "/dev/mapper/cryptroot"; }; }; @@ -55,8 +55,8 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: { # Create encrypted volume machine.wait_for_unit("multi-user.target") - machine.succeed("echo -n supersecret | cryptsetup luksFormat -q --iter-time=1 /dev/vdc -") - machine.succeed("PASSWORD=supersecret SYSTEMD_LOG_LEVEL=debug systemd-cryptenroll --tpm2-pcrs= --tpm2-device=auto /dev/vdc |& systemd-cat") + machine.succeed("echo -n supersecret | cryptsetup luksFormat -q --iter-time=1 /dev/vdb -") + machine.succeed("PASSWORD=supersecret SYSTEMD_LOG_LEVEL=debug systemd-cryptenroll --tpm2-pcrs= --tpm2-device=auto /dev/vdb |& systemd-cat") # Boot from the encrypted disk machine.succeed("bootctl set-default nixos-generation-1-specialisation-boot-luks.conf") diff --git a/nixos/tests/systemd-initrd-swraid.nix b/nixos/tests/systemd-initrd-swraid.nix index 28a0fb3192ae..d201ba99a204 100644 --- a/nixos/tests/systemd-initrd-swraid.nix +++ b/nixos/tests/systemd-initrd-swraid.nix @@ -20,18 +20,18 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: { services.swraid = { enable = true; mdadmConf = '' - ARRAY /dev/md0 devices=/dev/vdc,/dev/vdd + ARRAY /dev/md0 devices=/dev/vdb,/dev/vdc ''; }; kernelModules = [ "raid0" ]; }; - specialisation.boot-swraid.configuration.virtualisation.bootDevice = "/dev/disk/by-label/testraid"; + specialisation.boot-swraid.configuration.virtualisation.rootDevice = "/dev/disk/by-label/testraid"; }; testScript = '' # Create RAID - machine.succeed("mdadm --create --force /dev/md0 -n 2 --level=raid0 /dev/vdc /dev/vdd") + machine.succeed("mdadm --create --force /dev/md0 -n 2 --level=raid0 /dev/vdb /dev/vdc") machine.succeed("mkfs.ext4 -L testraid /dev/md0") machine.succeed("mkdir -p /mnt && mount /dev/md0 /mnt && echo hello > /mnt/test && umount /mnt") diff --git a/nixos/tests/zfs.nix b/nixos/tests/zfs.nix index bcb9d9bcfd60..e86c53befd28 100644 --- a/nixos/tests/zfs.nix +++ b/nixos/tests/zfs.nix @@ -98,15 +98,15 @@ let machine.wait_for_unit("multi-user.target") machine.succeed( "zpool status", + "parted --script /dev/vdb mklabel msdos", + "parted --script /dev/vdb -- mkpart primary 1024M -1s", "parted --script /dev/vdc mklabel msdos", "parted --script /dev/vdc -- mkpart primary 1024M -1s", - "parted --script /dev/vdd mklabel msdos", - "parted --script /dev/vdd -- mkpart primary 1024M -1s", ) with subtest("sharesmb works"): machine.succeed( - "zpool create rpool /dev/vdc1", + "zpool create rpool /dev/vdb1", "zfs create -o mountpoint=legacy rpool/root", # shared datasets cannot have legacy mountpoint "zfs create rpool/shared_smb", @@ -126,8 +126,8 @@ let with subtest("encryption works"): machine.succeed( 'echo password | zpool create -O mountpoint=legacy ' - + "-O encryption=aes-256-gcm -O keyformat=passphrase automatic /dev/vdc1", - "zpool create -O mountpoint=legacy manual /dev/vdd1", + + "-O encryption=aes-256-gcm -O keyformat=passphrase automatic /dev/vdb1", + "zpool create -O mountpoint=legacy manual /dev/vdc1", "echo otherpass | zfs create " + "-o encryption=aes-256-gcm -o keyformat=passphrase manual/encrypted", "bootctl set-default nixos-generation-1-specialisation-encryption.conf", @@ -153,7 +153,7 @@ let machine.succeed( "rm /etc/hostid", "zgenhostid deadcafe", - "zpool create forcepool /dev/vdc1 -O mountpoint=legacy", + "zpool create forcepool /dev/vdb1 -O mountpoint=legacy", "bootctl set-default nixos-generation-1-specialisation-forcepool.conf", "rm /etc/hostid", "sync", From e3a41f3fec8ddfc9e20df2e10f49c464525defa3 Mon Sep 17 00:00:00 2001 From: Raito Bezarius Date: Sun, 26 Mar 2023 00:44:41 +0100 Subject: [PATCH 3/5] nixos/tests/bootspec: assert for boot.json rather than bootspec/boot.json --- nixos/tests/bootspec.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nixos/tests/bootspec.nix b/nixos/tests/bootspec.nix index 077dff918e0d..f388a15ffa2d 100644 --- a/nixos/tests/bootspec.nix +++ b/nixos/tests/bootspec.nix @@ -108,9 +108,9 @@ in machine.start() machine.wait_for_unit("multi-user.target") - machine.succeed("test -e /run/current-system/bootspec/boot.json") + machine.succeed("test -e /run/current-system/boot.json") - bootspec = json.loads(machine.succeed("jq -r '.v1' /run/current-system/bootspec/boot.json")) + bootspec = json.loads(machine.succeed("jq -r '.v1' /run/current-system/boot.json")) assert all(key in bootspec for key in ('initrd', 'initrdSecrets')), "Bootspec should contain initrd or initrdSecrets field when initrd is enabled" ''; From 614b83a3285ca44650473e73f9777d7c41fe88a1 Mon Sep 17 00:00:00 2001 From: Raito Bezarius Date: Wed, 12 Apr 2023 19:55:43 +0200 Subject: [PATCH 4/5] nixos/virtualisation/qemu-vm: remove persistBootDevice option This option has been introduced in 678eed323ffd90117472cd432ebe85dddaff07f1 without realizing there was this PR inflight, unfortunately, it collide with what this PR does and make it irrelevant. Therefore, I remove it here. --- nixos/modules/virtualisation/qemu-vm.nix | 19 +------------------ nixos/tests/initrd-secrets-changing.nix | 1 - 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix index 2aa8a36a1cfc..0a682fff4103 100644 --- a/nixos/modules/virtualisation/qemu-vm.nix +++ b/nixos/modules/virtualisation/qemu-vm.nix @@ -171,12 +171,6 @@ let ${lib.optionalString cfg.useBootLoader '' - if ${if !cfg.persistBootDevice then "true" else "! test -e $TMPDIR/disk.img"}; then - # Create a writable copy/snapshot of the boot disk. - # A writable boot disk can be booted from automatically. - ${qemu}/bin/qemu-img create -f qcow2 -F qcow2 -b ${bootDisk}/disk.img "$TMPDIR/disk.img" - fi - NIX_EFI_VARS=$(readlink -f "''${NIX_EFI_VARS:-${config.system.name}-efi-vars.fd}") ${lib.optionalString cfg.useEFIBoot @@ -285,7 +279,7 @@ in (mkRenamedOptionModule [ "virtualisation" "pathsInNixDB" ] [ "virtualisation" "additionalPaths" ]) (mkRemovedOptionModule [ "virtualisation" "bootDevice" ] "This option was renamed to `virtualisation.rootDevice`, as it was incorrectly named and misleading. Take the time to review what you want to do and look at the new options like `virtualisation.{bootLoaderDevice, bootPartition}`, open an issue in case of issues.") (mkRemovedOptionModule [ "virtualisation" "efiVars" ] "This option was removed, it is possible to provide a template UEFI variable with `virtualisation.efi.variables` ; if this option is important to you, open an issue") - (mkRemovedOptionModule [ "virtualisation" "persistBootDevice" ] "Boot device is always persisted if you use a bootloader through the root disk image ; if this does not work for your usecase, please examine carefully what `virtualisation.{bootLoaderDevice, rootDevice, bootPartition}` options offers you and open an issue explaining your need.`") + (mkRemovedOptionModule [ "virtualisation" "persistBootDevice" ] "Boot device is always persisted if you use a bootloader through the root disk image ; if this does not work for your usecase, please examine carefully what `virtualisation.{bootDevice, rootDevice, bootPartition}` options offer you and open an issue explaining your need.`") ]; options = { @@ -384,17 +378,6 @@ in ''; }; - virtualisation.persistBootDevice = - mkOption { - type = types.bool; - default = false; - description = - lib.mdDoc '' - If useBootLoader is specified, whether to recreate the boot device - on each instantiaton or allow it to persist. - ''; - }; - virtualisation.emptyDiskImages = mkOption { type = types.listOf types.ints.positive; diff --git a/nixos/tests/initrd-secrets-changing.nix b/nixos/tests/initrd-secrets-changing.nix index 775c69d0142d..d6f9ef9ced83 100644 --- a/nixos/tests/initrd-secrets-changing.nix +++ b/nixos/tests/initrd-secrets-changing.nix @@ -15,7 +15,6 @@ testing.makeTest { nodes.machine = { ... }: { virtualisation.useBootLoader = true; - virtualisation.persistBootDevice = true; boot.loader.grub.device = "/dev/vda"; From 76f1b633ba8023ea789347a52dcf1e6c60ec5369 Mon Sep 17 00:00:00 2001 From: Raito Bezarius Date: Wed, 12 Apr 2023 20:00:44 +0200 Subject: [PATCH 5/5] CODEOWNERS: add raitobezarius to qemu-vm.nix I do a lot of work on QEMU VM and make-disk-image and I was bitten by an unnotified change recently, so I want to chime in the future changes of this file. --- .github/CODEOWNERS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 96bdccccf517..f3d4781dc531 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -90,6 +90,9 @@ # NixOS integration test driver /nixos/lib/test-driver @tfc +# NixOS QEMU virtualisation +/nixos/virtualisation/qemu-vm.nix @raitobezarius + # Systemd /nixos/modules/system/boot/systemd.nix @NixOS/systemd /nixos/modules/system/boot/systemd @NixOS/systemd