diff --git a/nix/tests/lanzaboote.nix b/nix/tests/lanzaboote.nix index cc79538..8be761a 100644 --- a/nix/tests/lanzaboote.nix +++ b/nix/tests/lanzaboote.nix @@ -145,6 +145,52 @@ in ''; }; + # 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. Lanzaboote + # has no control over that. + # + # 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 two boot files exists (a single kernel and a single + # initrd). If there are two initrds, the test would not be able to test + # updating the secret of an already existing initrd. + assert int(machine.succeed("ls -1 /boot/EFI/nixos | wc -l")) == 2 + + # It is expected that the initrd 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; diff --git a/rust/tool/src/install.rs b/rust/tool/src/install.rs index cc7fffb..4be05fe 100644 --- a/rust/tool/src/install.rs +++ b/rust/tool/src/install.rs @@ -61,16 +61,22 @@ impl Installer { .map(GenerationLink::from_path) .collect::>>()?; + // Sort the links by version. The links need to always be sorted to ensure the secrets of + // the latest generation are appended to the initrd when multiple generations point to the + // same initrd. + links.sort_by_key(|l| l.version); + // A configuration limit of 0 means there is no limit. if self.configuration_limit > 0 { - // Sort the links by version. - links.sort_by_key(|l| l.version); - - // Only install the number of generations configured. + // Only install the number of generations configured. Reverse the list to only take the + // latest generations and then, after taking them, reverse the list again so that the + // generations are installed from oldest to newest, i.e. from smallest to largest + // generation version. links = links .into_iter() .rev() .take(self.configuration_limit) + .rev() .collect() }; self.install_generations_from_links(&links)?;