tests: refactor into separate files called via runTest
This commit is contained in:
		
							parent
							
								
									e17ed9d6da
								
							
						
					
					
						commit
						acf7fffcc9
					
				
							
								
								
									
										19
									
								
								flake.nix
								
								
								
								
							
							
						
						
									
										19
									
								
								flake.nix
								
								
								
								
							|  | @ -197,26 +197,17 @@ | |||
|             inherit (config.packages) tool; | ||||
|           }; | ||||
| 
 | ||||
|           checks = | ||||
|             let | ||||
|               nixosLib = import (pkgs.path + "/nixos/lib") { }; | ||||
|               runTest = module: nixosLib.runTest { | ||||
|                 imports = [ module ]; | ||||
|                 hostPkgs = pkgs; | ||||
|               }; | ||||
|             in | ||||
|             { | ||||
|           checks = { | ||||
|             toolClippy = toolCrane.clippy; | ||||
|             stubClippy = stubCrane.clippy; | ||||
|             fatStubClippy = fatStubCrane.clippy; | ||||
|             toolFmt = toolCrane.rustfmt; | ||||
|             stubFmt = stubCrane.rustfmt; | ||||
|             } // (import ./nix/tests/lanzaboote.nix { | ||||
|           } // (import ./nix/tests { | ||||
|             inherit pkgs; | ||||
|               lanzabooteModule = self.nixosModules.lanzaboote; | ||||
|             }) // (import ./nix/tests/stub.nix { | ||||
|               inherit pkgs runTest; | ||||
|               ukiModule = self.nixosModules.uki; | ||||
|             extraBaseModules = { | ||||
|               inherit (self.nixosModules) lanzaboote uki; | ||||
|             }; | ||||
|           }); | ||||
| 
 | ||||
|           devShells.default = pkgs.mkShell { | ||||
|  |  | |||
|  | @ -0,0 +1,35 @@ | |||
| { pkgs, extraBaseModules }: | ||||
| 
 | ||||
| let | ||||
|   runTest = module: pkgs.testers.runNixOSTest { | ||||
|     imports = [ module ]; | ||||
|     globalTimeout = 5 * 60; | ||||
|     extraBaseModules = { | ||||
|       imports = builtins.attrValues extraBaseModules; | ||||
|     }; | ||||
|   }; | ||||
| 
 | ||||
|   # Run the test only on the specified systems. Otherwise build hello to work | ||||
|   # around flake behaviour. | ||||
|   runTestOn = systems: module: | ||||
|     if builtins.elem pkgs.system systems | ||||
|     then runTest module | ||||
|     else pkgs.hello; | ||||
| in | ||||
| { | ||||
|   # Lanzaboote | ||||
|   basic = runTest ./lanzaboote/basic.nix; | ||||
|   systemd-initrd = runTest ./lanzaboote/systemd-initrd.nix; | ||||
|   initrd-secrets = runTest ./lanzaboote/initrd-secrets.nix; | ||||
|   initrd-secrets-update = runTest ./lanzaboote/initrd-secrets-update.nix; | ||||
|   hash-mismatch = runTest ./lanzaboote/hash-mismatch.nix; | ||||
|   specialisation = runTest ./lanzaboote/specialisation.nix; | ||||
|   synthesis = runTestOn [ "x86_64-linux" ] ./lanzaboote/synthesis.nix; | ||||
|   systemd-boot-loader-config = runTest ./lanzaboote/systemd-boot-loader-config.nix; | ||||
|   export-efivars = runTest ./lanzaboote/export-efivars.nix; | ||||
|   export-efivars-tpm = runTest ./lanzaboote/export-efivars-tpm.nix; | ||||
| 
 | ||||
|   # Stub | ||||
|   systemd-stub = runTest ./stub/systemd-stub.nix; | ||||
|   fat-stub = runTest ./stub/fat-stub.nix; | ||||
| } | ||||
|  | @ -1,444 +0,0 @@ | |||
| { pkgs | ||||
| , lanzabooteModule | ||||
| }: | ||||
| 
 | ||||
| let | ||||
|   inherit (pkgs) lib system; | ||||
|   defaultTimeout = 5 * 60; # = 5 minutes | ||||
| 
 | ||||
|   inherit (pkgs.stdenv.hostPlatform) efiArch; | ||||
|   efiArchUppercased = lib.toUpper efiArch; | ||||
| 
 | ||||
|   mkSecureBootTest = { name, machine ? { }, useSecureBoot ? true, useTPM2 ? false, readEfiVariables ? false, testScript }: | ||||
|     let | ||||
|       tpmSocketPath = "/tmp/swtpm-sock"; | ||||
|       tpmDeviceModels = { | ||||
|         x86_64-linux = "tpm-tis"; | ||||
|         aarch64-linux = "tpm-tis-device"; | ||||
|       }; | ||||
|       # Should go to nixpkgs. | ||||
|       efiVariablesHelpers = '' | ||||
|         import struct | ||||
| 
 | ||||
|         SD_LOADER_GUID = "4a67b082-0a4c-41cf-b6c7-440b29bb8c4f" | ||||
|         def read_raw_variable(var: str) -> bytes: | ||||
|             attr_var = machine.succeed(f"cat /sys/firmware/efi/efivars/{var}-{SD_LOADER_GUID}").encode('raw_unicode_escape') | ||||
|             _ = attr_var[:4] # First 4 bytes are attributes according to https://www.kernel.org/doc/html/latest/filesystems/efivarfs.html | ||||
|             value = attr_var[4:] | ||||
|             return value | ||||
|         def read_string_variable(var: str, encoding='utf-16-le') -> str: | ||||
|             return read_raw_variable(var).decode(encoding).rstrip('\x00') | ||||
|         # By default, it will read a 4 byte value, read `struct` docs to change the format. | ||||
|         def assert_variable_uint(var: str, expected: int, format: str = 'I'): | ||||
|             with subtest(f"Is `{var}` set to {expected} (uint)"): | ||||
|               value, = struct.unpack(f'<{format}', read_raw_variable(var)) | ||||
|               assert value == expected, f"Unexpected variable value in `{var}`, expected: `{expected}`, actual: `{value}`" | ||||
|         def assert_variable_string(var: str, expected: str, encoding='utf-16-le'): | ||||
|             with subtest(f"Is `{var}` correctly set"): | ||||
|                 value = read_string_variable(var, encoding) | ||||
|                 assert value == expected, f"Unexpected variable value in `{var}`, expected: `{expected.encode(encoding)!r}`, actual: `{value.encode(encoding)!r}`" | ||||
|         def assert_variable_string_contains(var: str, expected_substring: str): | ||||
|             with subtest(f"Do `{var}` contain expected substrings"): | ||||
|                 value = read_string_variable(var).strip() | ||||
|                 assert expected_substring in value, f"Did not find expected substring in `{var}`, expected substring: `{expected_substring}`, actual value: `{value}`" | ||||
|       ''; | ||||
|       tpm2Initialization = '' | ||||
|         import subprocess | ||||
|         from tempfile import TemporaryDirectory | ||||
| 
 | ||||
|         # From systemd-initrd-luks-tpm2.nix | ||||
|         class Tpm: | ||||
|             def __init__(self): | ||||
|                 self.state_dir = TemporaryDirectory() | ||||
|                 self.start() | ||||
| 
 | ||||
|             def start(self): | ||||
|                 self.proc = subprocess.Popen(["${pkgs.swtpm}/bin/swtpm", | ||||
|                     "socket", | ||||
|                     "--tpmstate", f"dir={self.state_dir.name}", | ||||
|                     "--ctrl", "type=unixio,path=${tpmSocketPath}", | ||||
|                     "--tpm2", | ||||
|                     ]) | ||||
| 
 | ||||
|                 # Check whether starting swtpm failed | ||||
|                 try: | ||||
|                     exit_code = self.proc.wait(timeout=0.2) | ||||
|                     if exit_code is not None and exit_code != 0: | ||||
|                         raise Exception("failed to start swtpm") | ||||
|                 except subprocess.TimeoutExpired: | ||||
|                     pass | ||||
| 
 | ||||
|             """Check whether the swtpm process exited due to an error""" | ||||
|             def check(self): | ||||
|                 exit_code = self.proc.poll() | ||||
|                 if exit_code is not None and exit_code != 0: | ||||
|                   raise Exception("swtpm process died") | ||||
| 
 | ||||
|         tpm = Tpm() | ||||
| 
 | ||||
|         @polling_condition | ||||
|         def swtpm_running(): | ||||
|           tpm.check() | ||||
|       ''; | ||||
|     in | ||||
|     pkgs.nixosTest { | ||||
|       inherit name; | ||||
|       globalTimeout = defaultTimeout; | ||||
| 
 | ||||
|       testScript = '' | ||||
|         ${lib.optionalString useTPM2 tpm2Initialization} | ||||
|         ${lib.optionalString readEfiVariables efiVariablesHelpers} | ||||
|         ${testScript} | ||||
|       ''; | ||||
| 
 | ||||
|       nodes.machine = { pkgs, lib, ... }: { | ||||
|         imports = [ | ||||
|           lanzabooteModule | ||||
|           machine | ||||
|         ]; | ||||
| 
 | ||||
|         virtualisation = { | ||||
|           useBootLoader = true; | ||||
|           useEFIBoot = true; | ||||
| 
 | ||||
|           efi.OVMF = pkgs.OVMFFull.fd; | ||||
| 
 | ||||
|           qemu.options = lib.mkIf useTPM2 [ | ||||
|             "-chardev socket,id=chrtpm,path=${tpmSocketPath}" | ||||
|             "-tpmdev emulator,id=tpm_dev_0,chardev=chrtpm" | ||||
|             "-device ${tpmDeviceModels.${system}},tpmdev=tpm_dev_0" | ||||
|           ]; | ||||
| 
 | ||||
|           inherit useSecureBoot; | ||||
|         }; | ||||
| 
 | ||||
|         boot.initrd.availableKernelModules = lib.mkIf useTPM2 [ "tpm_tis" ]; | ||||
| 
 | ||||
|         boot.loader.efi = { | ||||
|           canTouchEfiVariables = true; | ||||
|         }; | ||||
|         boot.lanzaboote = { | ||||
|           enable = true; | ||||
|           enrollKeys = useSecureBoot; | ||||
|           pkiBundle = ./fixtures/uefi-keys; | ||||
|         }; | ||||
|       }; | ||||
|     }; | ||||
| 
 | ||||
|   # Execute a boot test that has an intentionally broken secure boot | ||||
|   # chain. This test is expected to fail with Secure Boot and should | ||||
|   # succeed without. | ||||
|   # | ||||
|   # Takes a set `path` consisting of a `src` and a `dst` attribute. The file at | ||||
|   # `src` is copied to `dst` inside th VM. Optionally append some random data | ||||
|   # ("crap") to the end of the file at `dst`. This is useful to easily change | ||||
|   # the hash of a file and produce a hash mismatch when booting the stub. | ||||
|   mkHashMismatchTest = { name, appendCrapGlob, useSecureBoot ? true }: mkSecureBootTest { | ||||
|     inherit name; | ||||
|     inherit useSecureBoot; | ||||
| 
 | ||||
|     testScript = '' | ||||
|       machine.start() | ||||
|       machine.succeed("echo some_garbage_to_change_the_hash | tee -a ${appendCrapGlob} > /dev/null") | ||||
|       machine.succeed("sync") | ||||
|       machine.crash() | ||||
|       machine.start() | ||||
|     '' + (if useSecureBoot then '' | ||||
|       machine.wait_for_console_text("hash does not match") | ||||
|     '' else '' | ||||
|       # Just check that the system came up. | ||||
|       print(machine.succeed("bootctl", timeout=120)) | ||||
|     ''); | ||||
|   }; | ||||
| 
 | ||||
|   # The initrd is not directly signed. Its hash is embedded into | ||||
|   # lanzaboote. To make integrity verification fail, we actually have | ||||
|   # to modify the initrd. Appending crap to the end is a harmless way | ||||
|   # that would make the kernel still accept it. | ||||
|   mkModifiedInitrdTest = { name, useSecureBoot }: mkHashMismatchTest { | ||||
|     inherit name useSecureBoot; | ||||
|     appendCrapGlob = "/boot/EFI/nixos/initrd-*.efi"; | ||||
|   }; | ||||
| 
 | ||||
|   mkModifiedKernelTest = { name, useSecureBoot }: mkHashMismatchTest { | ||||
|     inherit name useSecureBoot; | ||||
|     appendCrapGlob = "/boot/EFI/nixos/kernel-*.efi"; | ||||
|   }; | ||||
| 
 | ||||
| in | ||||
| { | ||||
|   # TODO: user mode: OK | ||||
|   # TODO: how to get in: {deployed, audited} mode ? | ||||
|   basic = mkSecureBootTest { | ||||
|     name = "lanzaboote"; | ||||
|     testScript = '' | ||||
|       machine.start() | ||||
|       assert "Secure Boot: enabled (user)" in machine.succeed("bootctl status") | ||||
| 
 | ||||
|       # We want systemd to recognize our PE binaries as true UKIs. systemd has | ||||
|       # become more picky in the past, so make sure. | ||||
|       assert "Kernel Type: uki" in machine.succeed("bootctl kernel-inspect /boot/EFI/Linux/nixos-generation-1-*.efi") | ||||
|     ''; | ||||
|   }; | ||||
| 
 | ||||
|   systemd-initrd = mkSecureBootTest { | ||||
|     name = "lanzaboote-systemd-initrd"; | ||||
|     machine = { ... }: { | ||||
|       boot.initrd.systemd.enable = true; | ||||
|     }; | ||||
|     testScript = '' | ||||
|       machine.start() | ||||
|       assert "Secure Boot: enabled (user)" in machine.succeed("bootctl status") | ||||
|     ''; | ||||
|   }; | ||||
| 
 | ||||
|   # Test that a secret is appended to the initrd during installation. Smilar to | ||||
|   # the initrd-secrets test in Nixpkgs: | ||||
|   # https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/initrd-secrets.nix | ||||
|   initrd-secrets = | ||||
|     let | ||||
|       secret = (pkgs.writeText "oh-so-secure" "uhh-ooh-uhh-security"); | ||||
|     in | ||||
|     mkSecureBootTest { | ||||
|       name = "lanzaboote-initrd-secrets"; | ||||
|       machine = { ... }: { | ||||
|         boot.initrd.secrets = { | ||||
|           "/test" = secret; | ||||
|         }; | ||||
|         boot.initrd.postMountCommands = '' | ||||
|           cp /test /mnt-root/secret-from-initramfs | ||||
|         ''; | ||||
|       }; | ||||
|       testScript = '' | ||||
|         machine.start() | ||||
|         machine.wait_for_unit("multi-user.target") | ||||
| 
 | ||||
|         machine.succeed("cmp ${secret} /secret-from-initramfs") | ||||
|         assert "Secure Boot: enabled (user)" in machine.succeed("bootctl status") | ||||
|       ''; | ||||
|     }; | ||||
| 
 | ||||
|   # Test that the secrets configured to be appended to the initrd get updated | ||||
|   # when installing a new generation even if the initrd itself (i.e. its store | ||||
|   # path) does not change. | ||||
|   # | ||||
|   # An unfortunate result of this NixOS feature is that updating the secrets | ||||
|   # without creating a new initrd might break previous generations. Verify that | ||||
|   # a new initrd (which is supposed to only differ by the secrets) is created | ||||
|   # in this case. | ||||
|   # | ||||
|   # This tests uses a specialisation to imitate a newer generation. This works | ||||
|   # because `lzbt` installs the specialisation of a generation AFTER installing | ||||
|   # the generation itself (thus making the specialisation "newer"). | ||||
|   initrd-secrets-update = | ||||
|     let | ||||
|       originalSecret = (pkgs.writeText "oh-so-secure" "uhh-ooh-uhh-security"); | ||||
|       newSecret = (pkgs.writeText "newly-secure" "so-much-better-now"); | ||||
|     in | ||||
|     mkSecureBootTest { | ||||
|       name = "lanzaboote-initrd-secrets-update"; | ||||
|       machine = { pkgs, lib, ... }: { | ||||
|         boot.initrd.secrets = { | ||||
|           "/test" = lib.mkDefault originalSecret; | ||||
|         }; | ||||
|         boot.initrd.postMountCommands = '' | ||||
|           cp /test /mnt-root/secret-from-initramfs | ||||
|         ''; | ||||
| 
 | ||||
|         specialisation.variant.configuration = { | ||||
|           boot.initrd.secrets = { | ||||
|             "/test" = newSecret; | ||||
|           }; | ||||
|         }; | ||||
|       }; | ||||
|       testScript = '' | ||||
|         machine.start() | ||||
|         machine.wait_for_unit("multi-user.target") | ||||
| 
 | ||||
|         # Assert that only three boot files exists (a single kernel and a two | ||||
|         # initrds). | ||||
|         assert int(machine.succeed("ls -1 /boot/EFI/nixos | wc -l")) == 3 | ||||
| 
 | ||||
|         # It is expected that the initrd contains the original secret. | ||||
|         machine.succeed("cmp ${originalSecret} /secret-from-initramfs") | ||||
| 
 | ||||
|         machine.succeed("bootctl set-default nixos-generation-1-specialisation-variant-\*.efi") | ||||
|         machine.succeed("sync") | ||||
|         machine.crash() | ||||
|         machine.start() | ||||
|         machine.wait_for_unit("multi-user.target") | ||||
|         # It is expected that the initrd of the specialisation contains the new secret. | ||||
|         machine.succeed("cmp ${newSecret} /secret-from-initramfs") | ||||
|       ''; | ||||
|     }; | ||||
| 
 | ||||
|   modified-initrd-doesnt-boot-with-secure-boot = mkModifiedInitrdTest { | ||||
|     name = "modified-initrd-doesnt-boot-with-secure-boot"; | ||||
|     useSecureBoot = true; | ||||
|   }; | ||||
| 
 | ||||
|   modified-initrd-boots-without-secure-boot = mkModifiedInitrdTest { | ||||
|     name = "modified-initrd-boots-without-secure-boot"; | ||||
|     useSecureBoot = false; | ||||
|   }; | ||||
| 
 | ||||
|   modified-kernel-doesnt-boot-with-secure-boot = mkModifiedKernelTest { | ||||
|     name = "modified-kernel-doesnt-boot-with-secure-boot"; | ||||
|     useSecureBoot = true; | ||||
|   }; | ||||
| 
 | ||||
|   modified-kernel-boots-without-secure-boot = mkModifiedKernelTest { | ||||
|     name = "modified-kernel-boots-without-secure-boot"; | ||||
|     useSecureBoot = false; | ||||
|   }; | ||||
| 
 | ||||
|   specialisation-works = mkSecureBootTest { | ||||
|     name = "specialisation-still-boot-under-secureboot"; | ||||
|     machine = { pkgs, ... }: { | ||||
|       specialisation.variant.configuration = { | ||||
|         environment.systemPackages = [ | ||||
|           pkgs.efibootmgr | ||||
|         ]; | ||||
|       }; | ||||
|     }; | ||||
|     testScript = '' | ||||
|       machine.start() | ||||
|       print(machine.succeed("ls -lah /boot/EFI/Linux")) | ||||
|       # TODO: make it more reliable to find this filename, i.e. read it from somewhere? | ||||
|       machine.succeed("bootctl set-default nixos-generation-1-specialisation-variant-\*.efi") | ||||
|       machine.succeed("sync") | ||||
|       machine.fail("efibootmgr") | ||||
|       machine.crash() | ||||
|       machine.start() | ||||
|       print(machine.succeed("bootctl")) | ||||
|       # Only the specialisation contains the efibootmgr binary. | ||||
|       machine.succeed("efibootmgr") | ||||
|     ''; | ||||
|   }; | ||||
| 
 | ||||
|   # We test if we can install Lanzaboote without Bootspec support. | ||||
|   synthesis = | ||||
|     if pkgs.hostPlatform.isAarch64 then | ||||
|     # FIXME: currently broken on aarch64 | ||||
|     #> mkfs.fat 4.2 (2021-01-31) | ||||
|     #> setting up /etc... | ||||
|     #> Enrolling keys to EFI variables...✓ | ||||
|     #> Enrolled keys to the EFI variables! | ||||
|     #> Installing Lanzaboote to "/boot"... | ||||
|     #> No bootable generations found! Aborting to avoid unbootable system. Please check for Lanzaboote updates! | ||||
|     #> [ 2.788390] reboot: Power down | ||||
|       pkgs.hello | ||||
|     else | ||||
|       mkSecureBootTest { | ||||
|         name = "lanzaboote-synthesis"; | ||||
|         machine = { lib, ... }: { | ||||
|           boot.bootspec.enable = lib.mkForce false; | ||||
|         }; | ||||
|         testScript = '' | ||||
|           machine.start() | ||||
|           assert "Secure Boot: enabled (user)" in machine.succeed("bootctl status") | ||||
|         ''; | ||||
|       }; | ||||
| 
 | ||||
|   systemd-boot-loader-config = mkSecureBootTest { | ||||
|     name = "lanzaboote-systemd-boot-loader-config"; | ||||
|     machine = { | ||||
|       boot.loader.timeout = 0; | ||||
|       boot.loader.systemd-boot.consoleMode = "auto"; | ||||
|     }; | ||||
|     testScript = '' | ||||
|       machine.start() | ||||
| 
 | ||||
|       actual_loader_config = machine.succeed("cat /boot/loader/loader.conf").split("\n") | ||||
|       expected_loader_config = ["timeout 0", "console-mode auto"] | ||||
| 
 | ||||
|       assert all(cfg in actual_loader_config for cfg in expected_loader_config), \ | ||||
|         f"Expected: {expected_loader_config} is not included in actual config: '{actual_loader_config}'" | ||||
|     ''; | ||||
|   }; | ||||
| 
 | ||||
|   export-efi-variables = mkSecureBootTest { | ||||
|     name = "lanzaboote-exports-efi-variables"; | ||||
|     machine.environment.systemPackages = [ pkgs.efibootmgr ]; | ||||
|     readEfiVariables = true; | ||||
|     testScript = '' | ||||
|       # We will choose to boot directly on the stub. | ||||
|       # To perform this trick, we will boot first with systemd-boot. | ||||
|       # Then, we will add a new boot entry in EFI with higher priority | ||||
|       # pointing to our stub. | ||||
|       # Finally, we will reboot. | ||||
|       # We will also assert that systemd-boot is not running | ||||
|       # by checking for the sd-boot's specific EFI variables. | ||||
|       machine.start() | ||||
| 
 | ||||
|       # By construction, nixos-generation-1.efi is the stub we are interested in. | ||||
|       # TODO: this should work -- machine.succeed("efibootmgr -d /dev/vda -c -l \\EFI\\Linux\\nixos-generation-1.efi") -- efivars are not persisted | ||||
|       # across reboots atm? | ||||
|       # cheat code no 1 | ||||
|       machine.succeed("cp /boot/EFI/Linux/nixos-generation-1-*.efi /boot/EFI/BOOT/BOOT${efiArchUppercased}.EFI") | ||||
|       machine.succeed("cp /boot/EFI/Linux/nixos-generation-1-*.efi /boot/EFI/systemd/systemd-boot${efiArch}.efi") | ||||
| 
 | ||||
|       # Let's reboot. | ||||
|       machine.succeed("sync") | ||||
|       machine.crash() | ||||
|       machine.start() | ||||
| 
 | ||||
|       # This is the sd-boot EFI variable indicator, we should not have it at this point. | ||||
|       print(machine.execute("bootctl")[1]) # Check if there's incorrect value in the output. | ||||
|       machine.succeed( | ||||
|           "test -e /sys/firmware/efi/efivars/LoaderEntrySelected-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f && false || true" | ||||
|       ) | ||||
| 
 | ||||
|       expected_variables = ["LoaderDevicePartUUID", | ||||
|         "LoaderImageIdentifier", | ||||
|         "LoaderFirmwareInfo", | ||||
|         "LoaderFirmwareType", | ||||
|         "StubInfo", | ||||
|         "StubFeatures" | ||||
|       ] | ||||
| 
 | ||||
|       # Debug all systemd loader specification GUID EFI variables loaded by the current environment. | ||||
|       print(machine.succeed(f"ls /sys/firmware/efi/efivars/*-{SD_LOADER_GUID}")) | ||||
|       with subtest("Check if supported variables are exported"): | ||||
|           for expected_var in expected_variables: | ||||
|               machine.succeed(f"test -e /sys/firmware/efi/efivars/{expected_var}-{SD_LOADER_GUID}") | ||||
| 
 | ||||
|       with subtest("Is `StubInfo` correctly set"): | ||||
|           assert "lanzastub" in read_string_variable("StubInfo"), "Unexpected stub information, provenance is not lanzaboote project!" | ||||
| 
 | ||||
|       assert_variable_string("LoaderImageIdentifier", "\\EFI\\BOOT\\BOOT${efiArchUppercased}.EFI") | ||||
|       # TODO: exploit QEMU test infrastructure to pass the good value all the time. | ||||
|       assert_variable_string("LoaderDevicePartUUID", "1c06f03b-704e-4657-b9cd-681a087a2fdc") | ||||
|       # OVMF tests are using EDK II tree. | ||||
|       assert_variable_string_contains("LoaderFirmwareInfo", "EDK II") | ||||
|       assert_variable_string_contains("LoaderFirmwareType", "UEFI") | ||||
| 
 | ||||
|       with subtest("Is `StubFeatures` non-zero"): | ||||
|           assert struct.unpack('<Q', read_raw_variable("StubFeatures")) != 0 | ||||
|     ''; | ||||
|   }; | ||||
| 
 | ||||
|   tpm2-export-efi-variables = mkSecureBootTest { | ||||
|     name = "lanzaboote-tpm2-exports-efi-variables"; | ||||
|     useTPM2 = true; | ||||
|     readEfiVariables = true; | ||||
|     testScript = '' | ||||
|       machine.start() | ||||
| 
 | ||||
|       # TODO: the other variables are not yet supported. | ||||
|       expected_variables = [ | ||||
|         "StubPcrKernelImage" | ||||
|       ] | ||||
| 
 | ||||
|       # Debug all systemd loader specification GUID EFI variables loaded by the current environment. | ||||
|       print(machine.succeed(f"ls /sys/firmware/efi/efivars/*-{SD_LOADER_GUID}")) | ||||
|       with subtest("Check if supported variables are exported"): | ||||
|           for expected_var in expected_variables: | ||||
|             machine.succeed(f"test -e /sys/firmware/efi/efivars/{expected_var}-{SD_LOADER_GUID}") | ||||
| 
 | ||||
|       # "Static" parts of the UKI is measured in PCR11 | ||||
|       assert_variable_uint("StubPcrKernelImage", 11) | ||||
|     ''; | ||||
|   }; | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,18 @@ | |||
| { | ||||
| 
 | ||||
|   name = "lanzaboote"; | ||||
| 
 | ||||
| 
 | ||||
|   nodes.machine = { | ||||
|     imports = [ ./common/lanzaboote.nix ]; | ||||
|   }; | ||||
| 
 | ||||
|   testScript = '' | ||||
|     machine.start() | ||||
|     assert "Secure Boot: enabled (user)" in machine.succeed("bootctl status") | ||||
| 
 | ||||
|     # We want systemd to recognize our PE binaries as true UKIs. systemd has | ||||
|     # become more picky in the past, so make sure. | ||||
|     assert "Kernel Type: uki" in machine.succeed("bootctl kernel-inspect /boot/EFI/Linux/nixos-generation-1-*.efi") | ||||
|   ''; | ||||
| } | ||||
|  | @ -0,0 +1,25 @@ | |||
| '' | ||||
|   import struct | ||||
| 
 | ||||
|   SD_LOADER_GUID = "4a67b082-0a4c-41cf-b6c7-440b29bb8c4f" | ||||
|   def read_raw_variable(var: str) -> bytes: | ||||
|       attr_var = machine.succeed(f"cat /sys/firmware/efi/efivars/{var}-{SD_LOADER_GUID}").encode('raw_unicode_escape') | ||||
|       _ = attr_var[:4] # First 4 bytes are attributes according to https://www.kernel.org/doc/html/latest/filesystems/efivarfs.html | ||||
|       value = attr_var[4:] | ||||
|       return value | ||||
|   def read_string_variable(var: str, encoding='utf-16-le') -> str: | ||||
|       return read_raw_variable(var).decode(encoding).rstrip('\x00') | ||||
|   # By default, it will read a 4 byte value, read `struct` docs to change the format. | ||||
|   def assert_variable_uint(var: str, expected: int, format: str = 'I'): | ||||
|       with subtest(f"Is `{var}` set to {expected} (uint)"): | ||||
|         value, = struct.unpack(f'<{format}', read_raw_variable(var)) | ||||
|         assert value == expected, f"Unexpected variable value in `{var}`, expected: `{expected}`, actual: `{value}`" | ||||
|   def assert_variable_string(var: str, expected: str, encoding='utf-16-le'): | ||||
|       with subtest(f"Is `{var}` correctly set"): | ||||
|           value = read_string_variable(var, encoding) | ||||
|           assert value == expected, f"Unexpected variable value in `{var}`, expected: `{expected.encode(encoding)!r}`, actual: `{value.encode(encoding)!r}`" | ||||
|   def assert_variable_string_contains(var: str, expected_substring: str): | ||||
|       with subtest(f"Do `{var}` contain expected substrings"): | ||||
|           value = read_string_variable(var).strip() | ||||
|           assert expected_substring in value, f"Did not find expected substring in `{var}`, expected substring: `{expected_substring}`, actual value: `{value}`" | ||||
| '' | ||||
|  | @ -0,0 +1,21 @@ | |||
| { config, pkgs, ... }: { | ||||
| 
 | ||||
|   virtualisation = { | ||||
|     useBootLoader = true; | ||||
|     useEFIBoot = true; | ||||
|     useSecureBoot = true; | ||||
| 
 | ||||
|     efi.OVMF = pkgs.OVMFFull.fd; | ||||
|   }; | ||||
| 
 | ||||
|   boot = { | ||||
|     loader.efi.canTouchEfiVariables = true; | ||||
| 
 | ||||
|     lanzaboote = { | ||||
|       enable = true; | ||||
|       enrollKeys = config.virtualisation.useSecureBoot; | ||||
|       pkiBundle = ../../fixtures/uefi-keys; | ||||
|     }; | ||||
|   }; | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,27 @@ | |||
| { | ||||
|   name = "lanzaboote-export-efivars-tpm"; | ||||
| 
 | ||||
|   nodes.machine = { | ||||
|     imports = [ ./common/lanzaboote.nix ]; | ||||
| 
 | ||||
|     virtualisation.tpm.enable = true; | ||||
|   }; | ||||
| 
 | ||||
|   testScript = (import ./common/efivariables-helper.nix) + '' | ||||
|     machine.start() | ||||
| 
 | ||||
|     # TODO: the other variables are not yet supported. | ||||
|     expected_variables = [ | ||||
|       "StubPcrKernelImage" | ||||
|     ] | ||||
| 
 | ||||
|     # Debug all systemd loader specification GUID EFI variables loaded by the current environment. | ||||
|     print(machine.succeed(f"ls /sys/firmware/efi/efivars/*-{SD_LOADER_GUID}")) | ||||
|     with subtest("Check if supported variables are exported"): | ||||
|         for expected_var in expected_variables: | ||||
|           machine.succeed(f"test -e /sys/firmware/efi/efivars/{expected_var}-{SD_LOADER_GUID}") | ||||
| 
 | ||||
|     # "Static" parts of the UKI is measured in PCR11 | ||||
|     assert_variable_uint("StubPcrKernelImage", 11) | ||||
|   ''; | ||||
| } | ||||
|  | @ -0,0 +1,72 @@ | |||
| { lib, pkgs, ... }: | ||||
| 
 | ||||
| let | ||||
| 
 | ||||
|   inherit (pkgs.stdenv.hostPlatform) efiArch; | ||||
|   efiArchUppercased = lib.toUpper efiArch; | ||||
| 
 | ||||
| in | ||||
| 
 | ||||
| { | ||||
|   name = "lanzaboote-export-efivars"; | ||||
| 
 | ||||
|   nodes.machine = { pkgs, ... }: { | ||||
|     imports = [ ./common/lanzaboote.nix ]; | ||||
|   }; | ||||
| 
 | ||||
|   testScript = (import ./common/efivariables-helper.nix) + '' | ||||
|     # We will choose to boot directly on the stub. | ||||
|     # To perform this trick, we will boot first with systemd-boot. | ||||
|     # Then, we will add a new boot entry in EFI with higher priority | ||||
|     # pointing to our stub. | ||||
|     # Finally, we will reboot. | ||||
|     # We will also assert that systemd-boot is not running | ||||
|     # by checking for the sd-boot's specific EFI variables. | ||||
|     machine.start() | ||||
| 
 | ||||
|     # By construction, nixos-generation-1.efi is the stub we are interested in. | ||||
|     # TODO: this should work -- machine.succeed("efibootmgr -d /dev/vda -c -l \\EFI\\Linux\\nixos-generation-1.efi") -- efivars are not persisted | ||||
|     # across reboots atm? | ||||
|     # cheat code no 1 | ||||
|     machine.succeed("cp /boot/EFI/Linux/nixos-generation-1-*.efi /boot/EFI/BOOT/BOOT${efiArchUppercased}.EFI") | ||||
|     machine.succeed("cp /boot/EFI/Linux/nixos-generation-1-*.efi /boot/EFI/systemd/systemd-boot${efiArch}.efi") | ||||
| 
 | ||||
|     # Let's reboot. | ||||
|     machine.succeed("sync") | ||||
|     machine.crash() | ||||
|     machine.start() | ||||
| 
 | ||||
|     # This is the sd-boot EFI variable indicator, we should not have it at this point. | ||||
|     print(machine.execute("bootctl")[1]) # Check if there's incorrect value in the output. | ||||
|     machine.succeed( | ||||
|         "test -e /sys/firmware/efi/efivars/LoaderEntrySelected-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f && false || true" | ||||
|     ) | ||||
| 
 | ||||
|     expected_variables = ["LoaderDevicePartUUID", | ||||
|       "LoaderImageIdentifier", | ||||
|       "LoaderFirmwareInfo", | ||||
|       "LoaderFirmwareType", | ||||
|       "StubInfo", | ||||
|       "StubFeatures" | ||||
|     ] | ||||
| 
 | ||||
|     # Debug all systemd loader specification GUID EFI variables loaded by the current environment. | ||||
|     print(machine.succeed(f"ls /sys/firmware/efi/efivars/*-{SD_LOADER_GUID}")) | ||||
|     with subtest("Check if supported variables are exported"): | ||||
|         for expected_var in expected_variables: | ||||
|             machine.succeed(f"test -e /sys/firmware/efi/efivars/{expected_var}-{SD_LOADER_GUID}") | ||||
| 
 | ||||
|     with subtest("Is `StubInfo` correctly set"): | ||||
|         assert "lanzastub" in read_string_variable("StubInfo"), "Unexpected stub information, provenance is not lanzaboote project!" | ||||
| 
 | ||||
|     assert_variable_string("LoaderImageIdentifier", "\\EFI\\BOOT\\BOOT${efiArchUppercased}.EFI") | ||||
|     # TODO: exploit QEMU test infrastructure to pass the good value all the time. | ||||
|     assert_variable_string("LoaderDevicePartUUID", "1c06f03b-704e-4657-b9cd-681a087a2fdc") | ||||
|     # OVMF tests are using EDK II tree. | ||||
|     assert_variable_string_contains("LoaderFirmwareInfo", "EDK II") | ||||
|     assert_variable_string_contains("LoaderFirmwareType", "UEFI") | ||||
| 
 | ||||
|     with subtest("Is `StubFeatures` non-zero"): | ||||
|         assert struct.unpack('<Q', read_raw_variable("StubFeatures")) != 0 | ||||
|   ''; | ||||
| } | ||||
|  | @ -0,0 +1,60 @@ | |||
| # Execute a boot test that has an intentionally broken secure boot chain. This | ||||
| # test is expected to fail with Secure Boot and should succeed without. We test | ||||
| # this both for an intentionally broken initrd as well as for a broken kernel. | ||||
| 
 | ||||
| # The initrd is not directly signed. Its hash is embedded into the stub. To | ||||
| # make integrity verification fail, we actually have to modify the initrd. | ||||
| # Appending crap to the end is a harmless way that would make the kernel still | ||||
| # accept it. | ||||
| 
 | ||||
| { | ||||
| 
 | ||||
|   name = "lanzaboote-hash-mismatch"; | ||||
| 
 | ||||
|   nodes = { | ||||
| 
 | ||||
|     brokenInitrd = { lib, ... }: { | ||||
|       imports = [ ./common/lanzaboote.nix ]; | ||||
|       virtualisation.useSecureBoot = lib.mkForce false; | ||||
|     }; | ||||
| 
 | ||||
|     brokenInitrdSecureBoot = { | ||||
|       imports = [ ./common/lanzaboote.nix ]; | ||||
|     }; | ||||
| 
 | ||||
|     brokenKernel = { lib, ... }: { | ||||
|       imports = [ ./common/lanzaboote.nix ]; | ||||
|       virtualisation.useSecureBoot = lib.mkForce false; | ||||
|     }; | ||||
| 
 | ||||
|     brokenKernelSecureBoot = { | ||||
|       imports = [ ./common/lanzaboote.nix ]; | ||||
|     }; | ||||
| 
 | ||||
|   }; | ||||
| 
 | ||||
|   testScript = '' | ||||
|     initrdGlob = "/boot/EFI/nixos/initrd-*.efi" | ||||
|     kernelGlob = "/boot/EFI/nixos/kernel-*.efi" | ||||
| 
 | ||||
|     def prepare(machine, glob): | ||||
|       machine.start() | ||||
|       machine.succeed(f"echo some_garbage_to_change_the_hash | tee -a {glob} > /dev/null") | ||||
|       machine.succeed("sync") | ||||
|       machine.crash() | ||||
|       machine.start() | ||||
| 
 | ||||
|     # Start all VMs simultaneously to save some time | ||||
|     start_all() | ||||
| 
 | ||||
|     prepare(brokenInitrd, initrdGlob) | ||||
|     prepare(brokenInitrdSecureBoot, initrdGlob) | ||||
|     prepare(brokenKernel, kernelGlob) | ||||
|     prepare(brokenKernelSecureBoot, kernelGlob) | ||||
| 
 | ||||
|     brokenInitrd.succeed("bootctl", timeout=120) | ||||
|     brokenInitrdSecureBoot.wait_for_console_text("hash does not match") | ||||
|     brokenKernel.succeed("bootctl", timeout=120) | ||||
|     brokenKernelSecureBoot.wait_for_console_text("hash does not match") | ||||
|   ''; | ||||
| } | ||||
|  | @ -0,0 +1,65 @@ | |||
| # Test that the secrets configured to be appended to the initrd get updated | ||||
| # when installing a new generation even if the initrd itself (i.e. its store | ||||
| # path) does not change. | ||||
| # | ||||
| # An unfortunate result of this NixOS feature is that updating the secrets | ||||
| # without creating a new initrd might break previous generations. Verify that | ||||
| # a new initrd (which is supposed to only differ by the secrets) is created | ||||
| # in this case. | ||||
| # | ||||
| # This tests uses a specialisation to imitate a newer generation. This works | ||||
| # because `lzbt` installs the specialisation of a generation AFTER installing | ||||
| # the generation itself (thus making the specialisation "newer"). | ||||
| 
 | ||||
| { pkgs, ... }: | ||||
| 
 | ||||
| let | ||||
| 
 | ||||
|   originalSecret = pkgs.writeText "oh-so-secure" "uhh-ooh-uhh-security"; | ||||
|   newSecret = pkgs.writeText "newly-secure" "so-much-better-now"; | ||||
| 
 | ||||
| in | ||||
| 
 | ||||
| { | ||||
|   name = "lanzaboote-initrd-secrets-update"; | ||||
| 
 | ||||
|   nodes.machine = { lib, ... }: { | ||||
|     imports = [ ./common/lanzaboote.nix ]; | ||||
| 
 | ||||
|     boot.initrd = { | ||||
|       secrets = { | ||||
|         "/test" = lib.mkDefault (toString originalSecret); | ||||
|       }; | ||||
|       postMountCommands = '' | ||||
|         cp /test /mnt-root/secret-from-initramfs | ||||
|       ''; | ||||
|     }; | ||||
| 
 | ||||
|     specialisation.variant.configuration = { | ||||
|       boot.initrd.secrets = { | ||||
|         "/test" = toString newSecret; | ||||
|       }; | ||||
|     }; | ||||
|   }; | ||||
| 
 | ||||
|   testScript = '' | ||||
|     machine.start() | ||||
|     machine.wait_for_unit("multi-user.target") | ||||
| 
 | ||||
|     # Assert that only three boot files exists (a single kernel and a two | ||||
|     # initrds). | ||||
|     assert int(machine.succeed("ls -1 /boot/EFI/nixos | wc -l")) == 3 | ||||
| 
 | ||||
|     # It is expected that the initrd contains the original secret. | ||||
|     machine.succeed("cmp ${originalSecret} /secret-from-initramfs") | ||||
| 
 | ||||
|     machine.succeed("bootctl set-default nixos-generation-1-specialisation-variant-\*.efi") | ||||
|     machine.succeed("sync") | ||||
|     machine.crash() | ||||
|     machine.start() | ||||
|     machine.wait_for_unit("multi-user.target") | ||||
|     # It is expected that the initrd of the specialisation contains the new secret. | ||||
|     machine.succeed("cmp ${newSecret} /secret-from-initramfs") | ||||
|   ''; | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,38 @@ | |||
| # Test that a secret is appended to the initrd during installation. Smilar to | ||||
| # the initrd-secrets test in Nixpkgs: | ||||
| # https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/initrd-secrets.nix | ||||
| 
 | ||||
| { pkgs, ... }: | ||||
| 
 | ||||
| let | ||||
| 
 | ||||
|   secret = (pkgs.writeText "oh-so-secure" "uhh-ooh-uhh-security"); | ||||
| 
 | ||||
| in | ||||
| 
 | ||||
| { | ||||
| 
 | ||||
|   name = "lanzaboote-initrd-secrets"; | ||||
| 
 | ||||
|   nodes.machine = { | ||||
|     imports = [ ./common/lanzaboote.nix ]; | ||||
| 
 | ||||
|     boot.initrd = { | ||||
|       secrets = { | ||||
|         "/test" = toString secret; | ||||
|       }; | ||||
|       postMountCommands = '' | ||||
|         cp /test /mnt-root/secret-from-initramfs | ||||
|       ''; | ||||
|     }; | ||||
|   }; | ||||
| 
 | ||||
|   testScript = '' | ||||
|     machine.start() | ||||
|     machine.wait_for_unit("multi-user.target") | ||||
| 
 | ||||
|     machine.succeed("cmp ${secret} /secret-from-initramfs") | ||||
|     assert "Secure Boot: enabled (user)" in machine.succeed("bootctl status") | ||||
|   ''; | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,27 @@ | |||
| { | ||||
|   name = "lanzaboote-specialisation"; | ||||
| 
 | ||||
|   nodes.machine = { pkgs, ... }: { | ||||
|     imports = [ ./common/lanzaboote.nix ]; | ||||
| 
 | ||||
|     specialisation.variant.configuration = { | ||||
|       environment.systemPackages = [ | ||||
|         pkgs.efibootmgr | ||||
|       ]; | ||||
|     }; | ||||
|   }; | ||||
| 
 | ||||
|   testScript = '' | ||||
|     machine.start() | ||||
|     print(machine.succeed("ls -lah /boot/EFI/Linux")) | ||||
|     # TODO: make it more reliable to find this filename, i.e. read it from somewhere? | ||||
|     machine.succeed("bootctl set-default nixos-generation-1-specialisation-variant-\*.efi") | ||||
|     machine.succeed("sync") | ||||
|     machine.fail("efibootmgr") | ||||
|     machine.crash() | ||||
|     machine.start() | ||||
|     print(machine.succeed("bootctl")) | ||||
|     # Only the specialisation contains the efibootmgr binary. | ||||
|     machine.succeed("efibootmgr") | ||||
|   ''; | ||||
| } | ||||
|  | @ -0,0 +1,27 @@ | |||
| # Test installing Lanzaboote without Bootspec support. | ||||
| 
 | ||||
| # FIXME: currently broken on aarch64 | ||||
| #> mkfs.fat 4.2 (2021-01-31) | ||||
| #> setting up /etc... | ||||
| #> Enrolling keys to EFI variables...✓ | ||||
| #> Enrolled keys to the EFI variables! | ||||
| #> Installing Lanzaboote to "/boot"... | ||||
| #> No bootable generations found! Aborting to avoid unbootable system. Please check for Lanzaboote updates! | ||||
| #> [ 2.788390] reboot: Power down | ||||
| 
 | ||||
| { | ||||
| 
 | ||||
|   name = "lanzaboote-synthesis"; | ||||
| 
 | ||||
|   nodes.machine = { lib, ... }: { | ||||
|     imports = [ ./common/lanzaboote.nix ]; | ||||
| 
 | ||||
|     boot.bootspec.enable = lib.mkForce false; | ||||
|   }; | ||||
| 
 | ||||
|   testScript = '' | ||||
|     machine.start() | ||||
|     assert "Secure Boot: enabled (user)" in machine.succeed("bootctl status") | ||||
|   ''; | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,22 @@ | |||
| { | ||||
| 
 | ||||
|   name = "lanzaboote-systemd-boot-loader-config"; | ||||
| 
 | ||||
|   nodes.machine = { | ||||
|     imports = [ ./common/lanzaboote.nix ]; | ||||
| 
 | ||||
|     boot.loader.timeout = 0; | ||||
|     boot.loader.systemd-boot.consoleMode = "auto"; | ||||
|   }; | ||||
| 
 | ||||
|   testScript = '' | ||||
|     machine.start() | ||||
| 
 | ||||
|     actual_loader_config = machine.succeed("cat /boot/loader/loader.conf").split("\n") | ||||
|     expected_loader_config = ["timeout 0", "console-mode auto"] | ||||
| 
 | ||||
|     assert all(cfg in actual_loader_config for cfg in expected_loader_config), \ | ||||
|       f"Expected: {expected_loader_config} is not included in actual config: '{actual_loader_config}'" | ||||
|   ''; | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,14 @@ | |||
| { | ||||
|   name = "lanzaboote-systemd-initrd"; | ||||
| 
 | ||||
|   nodes.machine = { ... }: { | ||||
|     imports = [ ./common/lanzaboote.nix ]; | ||||
| 
 | ||||
|     boot.initrd.systemd.enable = true; | ||||
|   }; | ||||
| 
 | ||||
|   testScript = '' | ||||
|     machine.start() | ||||
|     assert "Secure Boot: enabled (user)" in machine.succeed("bootctl status") | ||||
|   ''; | ||||
| } | ||||
|  | @ -1,49 +0,0 @@ | |||
| { pkgs, runTest, ukiModule }: | ||||
| 
 | ||||
| let | ||||
|   defaultTimeout = 5 * 60; # = 5 minutes | ||||
|   common = _: { | ||||
|     imports = [ ukiModule ]; | ||||
| 
 | ||||
|     virtualisation = { | ||||
|       useBootLoader = true; | ||||
|       useEFIBoot = true; | ||||
|     }; | ||||
| 
 | ||||
|     boot.loader.uki.enable = true; | ||||
|     boot.loader.efi = { | ||||
|       canTouchEfiVariables = true; | ||||
|     }; | ||||
|   }; | ||||
| in | ||||
| { | ||||
|   # This test serves as a baseline to make sure that the custom boot installer | ||||
|   # script defined in the ukiModule works with the upstream systemd-stub. When | ||||
|   # this test fails something is very wrong. | ||||
|   systemd-stub = runTest { | ||||
|     name = "systemd-stub"; | ||||
|     globalTimeout = defaultTimeout; | ||||
| 
 | ||||
|     nodes.machine = _: { | ||||
|       imports = [ common ]; | ||||
|       boot.loader.uki.stub = "${pkgs.systemd}/lib/systemd/boot/efi/linux${pkgs.hostPlatform.efiArch}.efi.stub"; | ||||
|     }; | ||||
|     testScript = '' | ||||
|       machine.start() | ||||
|       print(machine.succeed("bootctl status")) | ||||
|     ''; | ||||
|   }; | ||||
| 
 | ||||
|   fatStub = runTest { | ||||
|     name = "fat-stub"; | ||||
|     globalTimeout = defaultTimeout; | ||||
| 
 | ||||
|     nodes.machine = _: { | ||||
|       imports = [ common ]; | ||||
|     }; | ||||
|     testScript = '' | ||||
|       machine.start() | ||||
|       print(machine.succeed("bootctl status")) | ||||
|     ''; | ||||
|   }; | ||||
| } | ||||
|  | @ -0,0 +1,13 @@ | |||
| { | ||||
| 
 | ||||
|   virtualisation = { | ||||
|     useBootLoader = true; | ||||
|     useEFIBoot = true; | ||||
|   }; | ||||
| 
 | ||||
|   boot.loader.uki.enable = true; | ||||
|   boot.loader.efi = { | ||||
|     canTouchEfiVariables = true; | ||||
|   }; | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,14 @@ | |||
| { | ||||
| 
 | ||||
|   name = "fat-stub"; | ||||
| 
 | ||||
|   nodes.machine = _: { | ||||
|     imports = [ ./common.nix ]; | ||||
|   }; | ||||
| 
 | ||||
|   testScript = '' | ||||
|     machine.start() | ||||
|     print(machine.succeed("bootctl status")) | ||||
|   ''; | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,20 @@ | |||
| # This test serves as a baseline to make sure that the custom boot installer | ||||
| # script defined in the ukiModule works with the upstream systemd-stub. When | ||||
| # this test fails something is very wrong. | ||||
| 
 | ||||
| { | ||||
| 
 | ||||
|   name = "systemd-stub"; | ||||
| 
 | ||||
|   nodes.machine = { pkgs, ... }: { | ||||
|     imports = [ ./common.nix ]; | ||||
|     boot.loader.uki.stub = "${pkgs.systemd}/lib/systemd/boot/efi/linux${pkgs.hostPlatform.efiArch}.efi.stub"; | ||||
|   }; | ||||
| 
 | ||||
|   testScript = '' | ||||
|     machine.start() | ||||
|     print(machine.succeed("bootctl status")) | ||||
|   ''; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
		Loading…
	
		Reference in New Issue
	
	 nikstur
						nikstur