Change:
I have a FDE with luks. Grub prompts me for my passphrase, reads /boot, and then linux boots. To type my passphrase only once, my luks partition can also be opened by a keyfile: This keyfile is optional. I accidentally wiped /boot and had to recreate a keyfile. For some reason I put the new keyfile at a different path, but then I could not switch to the new generation, because grub could only be installed if the keyfile of old generations was available as well. I was forced to copy the keyfile at the old location as well to switch. In this case the two files are interchangeable, to it does not harm, but in some other situation, if a secret is irrevocably lost, there should be a way to ignore the missing secret, even if this makes generations unbootable. Create Switch to a generation with Move to Switch to a generation with Switching fails because it requires both and . Please run and paste the result. system: host os: multi-user?: sandbox: version: channels(root): channels(symphorien): nixpkgs: Related to
garbage collecting the previous generations is a workaround iirc
Well in my case I couldn't beause it also affected the generation I booted in, and one cannot remote the currently booted generation.
I marked this as stale due to inactivity. &rarr;
I ran into the same issue today while installing my nixos system and only having temporarily ` set. I am not sure why the script is not properly regenerated and impure.
This seems to fix the issue for me, allowing nixos-rebuild. Though there appears to still be some other (unidentified) cruft left over. sudo mount -o remount,rw /nix/store sudo find /nix/store -type l -name "append-initrd-secrets" -exec mv {} {}.bak ; Source:
Follow-up: It appears that I am able to clear out the cruft by deleting previous generations.
This issue has been mentioned on NixOS Discourse. There might be relevant details there:
I think there is a bug here. TLDR; The secrets are only named after the initrd, so if the initrd is unchanged, as install- runs, it overwrites the secrets in turn with the secrets in the system corresponding oldest matching initrd name. Profiles are in a loop here: This appears to add generations in the most-recent-first order. Then is responsible for creating a secrets for the profile, which it does by renaming onto . However, this path is only a function of the initrd name: Which is the base name of the : Consequently, if the initrd is unchanged between generations, it looks to me as though the secrets will end up using the oldest version of the secrets available for that initrd. Indeed if I look through my grub menu, that's what I see. When I change the secrets, I get a new , but not a new .
I've posted a potential fix for the above described issue at
does not fully fix this issue: renaming a secret prevents running nixos-rebuild as long as there are still generations that reference it. However if the secret is lost then there is no way we can reconstruct a sensible initird, so I think the workaround of ing the lost file is good enough.

Discussion:
# Include second initrd with secrets if (-e -x "$path/append-initrd-secrets") { <del> my $initrdName = basename($initrd); my $initrdSecretsPath = "$bootPath/kernels/$initrdName-secrets"; </del> <ins> # Name the initrd secrets after the system from which they're derived. my $systemName = basename(Cwd::abs_path("$path")); my $initrdSecretsPath = "$bootPath/kernels/$systemName-secrets"; </ins> mkpath(dirname($initrdSecretsPath), 0, 0755); my $oldUmask = umask;

if (-e $initrdSecretsPathTemp && ! -z _) { rename $initrdSecretsPathTemp, $initrdSecretsPath or die "failed to move initrd secrets into place: $!n"; $copied{$initrdSecretsPath} = 1; <del> $initrd .= " " . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/kernels/$initrdName-secrets"; </del> <ins> $initrd .= " " . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/kernels/$systemName-secrets"; </ins> } else { unlink $initrdSecretsPathTemp; rmdir dirname($initrdSecretsPathTemp);

${lib.optionalString cfg.useBootLoader '' <del> # 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" </del> <ins> 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 </ins> NIX_EFI_VARS=$(readlink -f "''${NIX_EFI_VARS:-${cfg.efiVars}}")

''; }; <ins> 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. ''; }; </ins> virtualisation.emptyDiskImages = mkOption { type = types.listOf types.ints.positive;

# * 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`. <ins> # * The disk.img is recreated each time the VM is booted unless #   virtualisation.persistBootDevice is set. </ins> # If `useBootLoader`, GRUB goes to the second disk, see # note [Disk layout with `useBootLoader`].

initrd-network-ssh = handleTest ./initrd-network-ssh {}; initrdNetwork = handleTest ./initrd-network.nix {}; initrd-secrets = handleTest ./initrd-secrets.nix {}; <ins> initrd-secrets-changing = handleTest ./initrd-secrets-changing.nix {}; </ins> input-remapper = handleTest ./input-remapper.nix {}; inspircd = handleTest ./inspircd.nix {}; installer = handleTest ./installer.nix {};

<ins> { system ? builtins.currentSystem , config ? {} , pkgs ? import ../.. { inherit system config; } , lib ? pkgs.lib , testing ? import ../lib/testing-python.nix { inherit system pkgs; } }: let secret1InStore = pkgs.writeText "topsecret" "iamasecret1"; secret2InStore = pkgs.writeText "topsecret" "iamasecret2"; in testing.makeTest { name = "initrd-secrets-changing"; nodes.machine = { ... }: { virtualisation.useBootLoader = true; virtualisation.persistBootDevice = true; boot.loader.grub.device = "/dev/vda"; boot.initrd.secrets = { "/test" = secret1InStore; "/run/keys/test" = secret1InStore; }; boot.initrd.postMountCommands = "cp /test /mnt-root/secret-from-initramfs"; specialisation.secrets2System.configuration = { boot.initrd.secrets = lib.mkForce { "/test" = secret2InStore; "/run/keys/test" = secret2InStore; }; }; }; testScript = '' start_all() machine.wait_for_unit("multi-user.target") print(machine.succeed("cat /run/keys/test")) machine.succeed( "cmp ${secret1InStore} /secret-from-initramfs", "cmp ${secret1InStore} /run/keys/test", ) # Select the second boot entry corresponding to the specialisation secrets2System. machine.succeed("grub-reboot 1") machine.shutdown() with subtest("Check that the specialisation's secrets are distinct despite identical kernels"): machine.wait_for_unit("multi-user.target") print(machine.succeed("cat /run/keys/test")) machine.succeed( "cmp ${secret2InStore} /secret-from-initramfs", "cmp ${secret2InStore} /run/keys/test", ) machine.shutdown() ''; } </ins>