Ubuntu Secure Boot, take 2

Deprecated — do not follow the PCR recommendation below. The approach in this article works in the narrow sense that it boots, but the set of PCRs it tells you to bind is wrong, and I’m no longer running it.

  • PCR 11 and 12 do nothing here. Both are measured by systemd-stub, so they are only populated when you boot a UKI. This article describes a GRUB setup with a separate kernel and initrd, where both registers read zero — as the article itself notices further down, while still binding them in the command above.
  • PCR 7 is the wrong anchor. Binding disk encryption to the Secure Boot policy register is now advised against upstream: the ArchWiki says so outright, systemd v258 stopped defaulting to it, and Canonical’s own secboot calls the PCR 7 and PCR 4 profiles “inherently fragile”. An unattended dbx revocation pushed by fwupd is enough to lock you out of your own disk.
  • The “super-secure” 0+2+4+7+9+11+12 set is a lockout recipe. PCR 0 changes on any BIOS update, PCR 2 on any GPU, NIC or Thunderbolt change, PCR 4 on every bootloader or shim update. Those are routine events, not attacks.

That leaves PCR 9 doing all the actual work — and the EDIT below, where the setup stopped unlocking after a kernel upgrade, is PCR 9 behaving exactly as designed.

There is a deeper problem than the register list, though. systemd-cryptenroll works by measurement: a tampered initrd still runs, and the TPM merely declines to unseal afterwards. What protects you at that point is you recognising the fallback prompt and refusing to type your passphrase into it — a judgement call, under time pressure, in front of a password box that looks entirely normal. A signed boot chain works by prevention: shim refuses the image, the boot stops, and there is no judgement call to get wrong.

What I run instead: ubuntu-systemd-boot-mok-shim — a signed shim → systemd-boot → UKI chain, which is the first article’s approach done properly. It uses no TPM at all.

Where the TPM half is going: having both — a signed chain and a TPM-checked measurement — is the right end state, and I’m still researching it. The shape it needs is a signed PCR policy (ukify --pcr-private-key plus systemd-cryptenroll --tpm2-public-key, binding PCR 11 only), which survives UKI rebuilds without re-enrollment and never touches PCR 7, 4 or 2. The working notes, including a register-by-register breakdown and a fact-check of the PCR 7 brittleness claims, are in ideas/tpm2-auto-unlock.md. It is not solidified, and I’d rather say so than publish another recipe I have to retract.

The rest of this article is still accurate and still useful: the crypttab wiring, the initramfs-tools-versus-dracut trap, and how to recover once you’ve locked yourself out.

The first Ubuntu Secure Boot article didn’t really suggested a viable solution for fully secure boot, but there is a surprisingly easy way to do this: by using systemd-cryptenroll. The idea is to use the TPM feature called TPM Platform Configuration Registers (PCRs). PCRs are TPM registers that accumulate hashes (essentially checksums) of boot components measured during the boot process—such as firmware code (PCR 0), boot loader code (PCR 4), Secure Boot policy and certificates (PCR 7), kernel/initrd (PCR 9 or 11), or shim policy (PCR 14). systemd-cryptenroll does not alter PCR values or EFI variables; it only binds to expected PCR hash states for unlocking.

systemd-cryptenroll takes the values of PCRs and uses them to create a LUKS unlock key. This method is therefore way less invasive than the UKI method since you don’t modify any keys in the TPM chip; this method for example doesn’t prevent Windows from booting in dual-boot setup.

This setup uses two unlock keys in LUKS:

  • A traditional password entered via keyboard. You’ll use this as a fallback if you manage to lock yourself out.
  • A TPM2 key derived from a signature of kernel and initrd, protected by a PIN.
    • The PIN can be simple since it resists dictionary attacks: TPM2 locks if invalid PIN is entered three times.
    • If “evil maid” spoofs kernel/initrd, the signature will change which will make the key invalid.

When all is good and the kernel/initrd signatures are valid, Plymouth will ask for a LUKS TPM2 PIN code. Once it does, you can be sure that the kernel/initrd hasn’t been tampered with.

If the signature changes, the key is invalid and Plymouth will fall back to the password key. Once that happens:

  • Either you updated kernel/initrd (via update-initramfs) and you forgot about this, OR
  • You are a victim of the evil maid attack. Do NOT type in any passwords and do not trust your OS; wipe the disks clean.

EDIT: what really happened is that Plymouth/Dracut continued to ask for PIN after kernel upgrade, but the PIN was unable to unlock the disk and the boot failed.

Prerequisites

This can be set up on an already installed Linux. You’ll need:

  • Secure Boot enabled in BIOS
  • EFI partition since you must boot Linux in UEFI mode
  • /boot may be unencrypted
  • / encrypted via LUKS

We will use the systemd-cryptenroll tool. We will NOT use the Clevis tool which does the same thing but in a more complicated way.

Setting up

First, edit your crypttab and add the option tpm2-device=auto:

dm_crypt-0 /dev/XYZ none luks,discard,tpm2-device=auto

This option is what tells Plymouth/systemd-cryptsetup to use the TPM2 metadata from the LUKS2 header and to wait for the TPM2 device to show up.

To install tpm2 support to Plymouth, you must install sudo apt install tpm2-tools otherwise your system won’t be able to unlock LUKS and won’t boot.

You’ll need to rebuild initrd, in order for this change to take effect: sudo update-initramfs -u -k all. Maybe reboot, to check that LUKS+Plymouth still works.

If update-initramfs fails with cryptsetup: WARNING: nvme0n1p3_crypt: ignoring unknown option 'tpm2-device', scroll down for more info.

Next, figure out the device which hosts the LUKS. It won’t most probably be a mapper device; instead it will be /dev/XYZ. Run lsblk to find out the device easily.

$ sudo systemd-cryptenroll /dev/XYZ --tpm2-device=auto --tpm2-pcrs=7+9+11+12 --tpm2-with-pin=true

This will add a new TPM2 unlock key to your LUKS. It will now be used by default by Plymouth. Reboot and test out: you should be asked for a PIN instead.

Testing tampering

Simply rebuild initrd via update-initramfs: this should invalidate the TPM2 LUKS key. Now when you reboot, you’ll be prompted for a password - a clear sign of tampering.

PCR

This section is the part that is wrong — see the deprecation notice at the top. It is left unedited below.

The PCR 7 guards against tampering with the Secure Boot state (PK/KEK/DB keys, enabled/disabled) and should always be enabled. The PCR 9, 11 and 12 guard against changes in kernel, initrd and/or kernel args and should be present at all times too.

If you want to be super-secure, use 0+2+4+7+9+11+12: 0 guards against BIOS change itself etc.

Ideally you want to use PCR 8 as well which guards against changes in “Boot loader policy / GRUB commands / measured boot loader extensions”, but for some reason adding this will always invalidate the TPM2 key, forcing Plymouth to always fall back and prompt for password.

To see which PCRs are active:

systemd-analyze pcrs

On my system, I can see the keys ‘11’ and ‘12’ unpopulated (zeroed out), so there’s no point in including those. The most important key is 9 - kernel initrd, since that’s the single unprotected thing in Ubuntu’s Secure Boot security chain.

After you update BIOS or kernel or rebuild initrd

Run the following command first, to remove any existing TPM2 LUKS:

$ systemd-cryptenroll /dev/XYZ --wipe-slot=tpm2

Then, simply run the setting-up command again.

After you lock yourself out

If you type in PIN incorrectly and TPM enters “dictionary attack lockout mode”, you can’t simply fall back and type in the password.

One way is to boot off an USB stick and remove the TPM2 LUKS key. Use lsblk to discover the device hosting LUKS; then run

$ systemd-cryptenroll /dev/XYZ --wipe-slot=tpm2

This removes the TPM2 key from LUKS, leaving only the password key. Reboot - you should now be asked for the password.

Another way is to boot off an USB stick and run sudo tpm2_dictionarylockout --clear-lockout.

Unfortunately adding the rd.luks.options=timeout=0 kernel parameter to GRUB boot entry doesn’t help: looks to be ignored and Dracut still asks for PIN. No other rd.* from dracut man 7 seem to help either. Keep a fallback USB stick around, just in case.

update-initramfs fails with cryptsetup: WARNING: nvme0n1p3_crypt: ignoring unknown option 'tpm2-device'

This happens when you’re using initramfs-tools to generate initramfs (verify: check that initramfs-tools package is installed). Fresh installs of Ubuntu 25.10 moved to dracut but Ubuntu 25.10 upgraded from earlier stayed with initramfs-tools. More info in the tpm2-luks-systemd blogpost.

You’ll need to migrate to dracut in order to use tpm2.

More Info

Written on January 14, 2026