LUKS TPM2 Unlock with systemd-cryptenroll and Secure Boot

AI-generated

I use full disk encryption on my Linux laptops, but typing the LUKS passphrase on every boot gets annoying. TPM2 unlock with systemd-cryptenroll is a good middle ground, as long as the TPM policy is tied to Secure Boot and I keep a normal recovery passphrase.

On my Arch laptop I use systemd-boot and a Unified Kernel Image. This works especially well with the Secure Boot setup from my Arch Linux Secure Boot with systemd-boot, sbctl and a Unified Kernel Image post. The important part is that TPM unlock is not just a replacement for disk encryption. It only unlocks the disk automatically when the machine booted in an expected state.

What TPM2 binding actually protects

A TPM stores measurements from the boot process in Platform Configuration Registers, usually called PCRs. systemd-cryptenroll can seal a LUKS key to selected PCR values.

At boot time, systemd checks whether the PCR values still match the enrolled policy. If they match, it gets the key from the TPM and opens the encrypted LUKS volume. If they do not match, the TPM refuses access and I need my normal LUKS recovery passphrase.

This is useful if somebody modifies the EFI bootloader, kernel image, Secure Boot state, or some firmware settings. It does not protect against somebody who already has access to my unlocked running system, of course.

TPM2 auto unlock without Secure Boot is convenient, but it is weaker. An attacker may be able to boot a modified kernel that still satisfies a too loose TPM policy.

For my setup I bind to PCR 7. PCR 7 reflects the Secure Boot policy state and is the usual practical choice for Linux Secure Boot setups. With a signed UKI, changing the kernel command line or replacing the kernel image also requires a valid signature.

Check the existing LUKS setup

Before enrolling anything, I make sure I can unlock the volume with my existing passphrase. I also keep a recovery passphrase in a password manager and, for important machines, offline as well.

First I check the LUKS device and its current keyslots:

$ sudo cryptsetup luksDump /dev/nvme0n1p3
LUKS header information
Version:        2
...
Keyslots:
  0: luks2
    Key:        512 bits
...
Tokens:

The encrypted partition will differ on your machine. Do not blindly use /dev/nvme0n1p3, especially on systems with multiple disks.

systemd-cryptenroll requires LUKS2. Most current installations already use it, but an old LUKS1 installation needs migration before TPM enrollment is possible.

I also check whether the TPM device exists:

$ ls -l /dev/tpmrm0
crw-rw---- 1 tss tss 10, 224 Nov 14 10:22 /dev/tpmrm0

On Arch, the required userspace tooling comes from systemd. On Gentoo, the systemd tools need to be installed and the kernel needs TPM support enabled.

$ systemd-cryptenroll --version
systemd 258

Enroll a TPM2 slot with Secure Boot PCR 7

I enroll the TPM key into a new LUKS token and leave the existing passphrase untouched:

$ sudo systemd-cryptenroll \
  --tpm2-device=auto \
  --tpm2-pcrs=7 \
  /dev/nvme0n1p3
🔐 Please enter current passphrase for disk /dev/nvme0n1p3:
New TPM2 token enrolled as key slot 1.

The current LUKS passphrase is needed to add the TPM-protected key. The command does not remove or replace the existing slot.

Afterwards, I inspect the result:

$ sudo systemd-cryptenroll /dev/nvme0n1p3
SLOT TYPE
   0 passphrase
   1 tpm2

For the initramfs to use the token, the root volume entry in /etc/crypttab needs the tpm2-device=auto option:

cryptroot UUID=11111111-2222-3333-4444-555555555555 none tpm2-device=auto

Then I rebuild my initramfs or UKI. On an Arch system using mkinitcpio this is usually:

$ sudo mkinitcpio -P

If I use a UKI, I make sure the regenerated image is signed again. Unsigned kernel updates are one of the most common reasons why TPM unlock suddenly stops working with Secure Boot enabled.

Do not add TPM auto unlock and immediately remove the passphrase. A TPM token is a convenience keyslot, not your only recovery plan.

Adding a TPM PIN

A TPM PIN adds another factor. The machine still needs to be in the expected measured boot state, but somebody with physical access also needs the PIN to release the secret.

I normally use a PIN on a laptop which leaves my home. For a Proxmox machine in my homelab, automatic boot after a power failure is more important, so I usually do not use one there.

To enroll a PIN, I add another TPM token:

$ sudo systemd-cryptenroll \
  --tpm2-device=auto \
  --tpm2-pcrs=7 \
  --tpm2-with-pin=yes \
  /dev/nvme0n1p3
🔐 Please enter current passphrase for disk /dev/nvme0n1p3:
🔐 Please enter TPM2 PIN:
🔐 Please confirm TPM2 PIN:
New TPM2 token enrolled as key slot 2.

The matching crypttab option is:

cryptroot UUID=11111111-2222-3333-4444-555555555555 none tpm2-device=auto,tpm2-pin=yes

Using a PIN means I still get a boot prompt, but I do not need to enter the long disk passphrase. The PIN is not stored as plain text on the disk.

Test recovery before a firmware update

Firmware updates, BIOS resets, Secure Boot key changes, and TPM clears can change PCR values. This is expected. The important thing is being able to recover without panic.

Before any firmware update, I do one simple test: reboot and deliberately enter the wrong TPM PIN, or temporarily boot with a changed Secure Boot state if I have physical console access. The automatic unlock should fail and systemd should ask for the normal LUKS passphrase.

After entering the recovery passphrase, the system must boot normally.

I also keep a bootable USB installer around. My Windows 11 USB Installer from Linux: Ventoy and Secure Boot setup is for Windows media, but Ventoy is also handy when I need a Linux rescue ISO.

If a firmware update changed the TPM state permanently, I boot with the recovery passphrase, verify Secure Boot is enabled again, and enroll a fresh token. I remove old TPM tokens only after the new one works:

$ sudo systemd-cryptenroll --wipe-slot=tpm2 /dev/nvme0n1p3

Be careful with this command. It removes all TPM2 token slots, not the normal passphrase slot. I always check systemd-cryptenroll /dev/nvme0n1p3 first.

TPM2 unlock is quite nice once it is set up correctly. Just keep the recovery passphrase, test it occasionally, and treat firmware updates as something that can require it.