Full Disk Encryption (encryption /boot), Grub, Issue with initrd options failing to embed modules, keyfile, and header file

It appears that the modules specified in both boot.initrd.availableKernelModules and boot.initrd.luks.cryptoModules do not get embedded into the initrd file as per what is stated in the documentation. Same with keyfiles and header files using the boot.initrd.secrets option.

This issue is similar to another issue that I had with options used with grub, so I have put the whole module file here (minus some of my colourful annotations). You can see my jerry rigging to correct for the how the nix build system handles the extraGrubInstallArgs options… that being the nix build system appears to ignore said options. It seems like the option defaults are overwriting the options I have selected in many instances, but not every instance.

I have been spelunking in the source code, but I am not overly familiar with perl (despite its apparent readability), and I am new to the nix expression language. So if you decide to help me. Please take my understanding level into consideration.

This is the script that creates my boot module using heredoc. Please assume the parameters are all set correctly. I have exhaustively confirmed this. I know this is a weird way to do it. But as I am new to nix, I decided to manage the nix dot file creation in shell instead as it’s actually what I am most comfortable with and requires the minimal amount of change of my workflow.

cat << EOF > /mnt/etc/nixos/modules/bootloader_GrUB.nix
{ config, lib, pkgs, ... }:

{
  environment.systemPackages = with pkgs; [ efibootmgr ];
  boot = {
    loader = {
      efi = {
        canTouchEfiVariables = false;
        efiSysMountPoint = "$ESP_MountPoint";
      };
      systemd-boot = {
        enable = false;
      };
      timeout = 5;

      grub = let
        grub = pkgs.grub2_efi;
        bootid = "$host_name-GrUB";
        modules = "part_gpt luks2 cryptodisk gcry_rijndael gcry_whirlpool gcry_serpent pbkdf2 sleep fat lvm btrfs file true echo test probe search_label search search_fs_uuid search_fs_file";
        inherit (lib) escapeShellArg;
        stub = "\${config.boot.loader.efi.efiSysMountPoint}/EFI/BOOT/BOOTX64.EFI";
      in {
        enable = true;
        device = "nodev";
        fsIdentifier = "uuid";
        efiSupport = true;
        efiInstallAsRemovable = true;
        enableCryptodisk = true;
        gfxmodeEfi = "1920x1080x32,1920x1080x24,1024x768x32,1024x768x24,auto";
        extraEntries = ''
          menuentry "Reboot" {
            reboot
          }
          menuentry "Poweroff" {
            halt
          }
        '';
        memtest86 = {
          enable = true;
        };
        extraGrubInstallArgs = [
          "--bootloader-id=\${bootid}"
          "--modules=\${modules}"
        ];
        extraInstallCommands = ''

          grub_tmp_dir=\$(mktemp --directory grub.conf.XXXXXXXX)

          trap 'rm --recursive --force -- "\$grub_tmp_dir"' EXIT

          cat << 'EOS' > "\$grub_tmp_dir/grub.cfg"

set crypto_uuid=$rootfs_UUID
cryptomount -u \$crypto_uuid
#set root=(crypto0)/(lvm/$root_VolumeGroup_Name-$rootfs_LogicalVolume_Name)/@
set root=(lvm/$root_VolumeGroup_Name-$rootfs_LogicalVolume_Name)/@
set prefix=\$root/boot/grub
EOS
          mkdir --parents \${escapeShellArg (builtins.dirOf stub)}

          \${grub}/bin/grub-mkimage \\
            --prefix '(lvm/$root_VolumeGroup_Name-$rootfs_LogicalVolume_Name)/@/boot/grub' \\
            --format \${grub.grubTarget} \\
            --config \$grub_tmp_dir/grub.cfg \\
            --output \${escapeShellArg stub} \\
            \${modules}

# Turns out the shell interpreter for nix via this option is not happy with 
# 'if statements', 'setting variables' or 'using loops* (kinda... loops sometimes 
# work, and I gave up figuring out the limits)'. Fucked if I know why there's 
# restrictions 'echo $SHELL' here justs gives a bash shell.. Soo Xargs is the 
# only option I could think of here... -_-
# NOTE possible bug on build when zero results for xargs needs testing to confirm. Doesn't cause issues.
          efibootmgr | \\
          grep --extended-regexp "NixOS|$host_name|\${bootid}" | \\
          sed --regexp-extended 's/^Boot([0-9]{4})([[:alnum:]]*).*/\1\2/' | \\
          xargs --replace {} efibootmgr --quiet --delete-bootnum --bootnum {}

          efibootmgr \\
            --quiet \\
            --create \\
            --part 1 \\
            --disk "$( \
              udevadm info --query=property --property=DEVLINKS --name="$Target_Device" | \
              grep --only-matching --extended-regexp --max-count 1 '/dev/disk/by-id/[^[:space:]]+' | \
              head --lines 1 \
            )" \\
            --label "\${bootid}" \\
            --loader EFI/boot/bootx64.efi
        '';
      };
    };
    initrd = {
      luks = {
        cryptoModules = [ "aes" "aes_generic" "blowfish" "twofish" "serpent" "cbc" "xts" "lrw" "sha1" "sha256" "sha512" "wp512" "af_alg" "algif_skcipher" ];
        devices = {
          "$crypt_vol_name" = {
            device = "/dev/disk/by-uuid/$rootfs_UUID";
            preLVM = true;
            keyFile = "/$Master_Keyfile_Name";
            header = "/$Master_Header_Name";
            fallbackToPassword = true;
            allowDiscards = true;
            bypassWorkqueues = true;
          };
        };
      };
      secrets = {
        "/$Master_Keyfile_Name" = /etc/secrets/initrd/$Master_Keyfile_Name;
        "/$Master_Header_Name" = /etc/secrets/initrd/$Master_Header_Name;
      };
    };
  };

  # Testing for integration
  boot.initrd.services.lvm.enable = true;
  boot.initrd.supportedFilesystems = [
    "btrfs"
    "fat"
    "ext4"
  ];
  boot.initrd.availableKernelModules = [
    "wp512"
  ];
  #boot.initrd.preLVMCommands =
  #boot.initrd.prepend = [];
}
EOF

As you can see at the end. I am trying to use the whirlpool hasing algorithm denoted by “wp512” which I have confirmed exists in the kernel modules for the nix store for this kernel.

When I reach ‘Stage 1’ of the initramfs boot process I get:


Notice the missing keyfile, the ignoring of the seperate header file and just using the attached one, and the lack of whirlpool support.

This is becoming a repeat issue for me. A lot of the nix options appear to not work at all, or the annotations on their use is wrong.

I have also tried unpacking the initrd file to inspect if the files that should be embedded are infact there. But all I see in the microcode for my CPU.

Have I mess up? If so, how? because I just don’t see it, and I am lost as to why this doesn’t work.