Skip to content
Menu

Linux Storage11 min read

Secure Boot & LUKS Auto-Unlock

Tying UEFI, TPM, and LUKS together

UEFI Secure Boot Recap

Secure Boot is a UEFI feature that enforces signature checks on every boot-time EFI binary. Before executing anything, the firmware verifies the binary's PE/COFF Authenticode signature against a database of trusted keys and hashes stored in NVRAM. Unsigned or unknown binaries simply won't run.

The Key Hierarchy in NVRAM

PK Platform Key root of trust Owned by the platform owner (usually the OEM). Signs updates to KEK.
KEK Key Exchange Keys 1..N certs Typically holds Microsoft's KEK. Authorises updates to db / dbx.
db Signature Database allow list Certs and hashes of binaries allowed to execute (Microsoft UEFI CA, shim, etc.)
dbx Forbidden Database deny list Revocations — hashes/certs that must not run (e.g. BootHole GRUB hashes)

On every stage-transition (firmware → bootloader, bootloader → kernel when using a shim-linked chain), the previous stage checks the next binary's signature against db and ensures it is not in dbx. Failure aborts the boot.

The Linux Signed Boot Chain

Microsoft's UEFI CA is present in almost every OEM's db. Linux distros exploit this via a tiny first-stage bootloader called shim that Microsoft has signed. Shim then gets to decide what it will load — effectively delegating trust from Microsoft to each distro.

  1. UEFI

    Verifies shimx64.efi against db (signed by MS UEFI CA)

  2. shim

    Embeds distro CA; verifies GRUB against it + user-enrolled MOK list

  3. GRUB

    (Signed) verifies kernel & initramfs signatures before handoff

  4. Kernel

    Enforces signed modules (module.sig_enforce=1)

  5. initramfs

    Unlocks root (TPM unseal or passphrase) → switch_root → init

  • shim ships with every major distro (Ubuntu, Debian, Fedora, RHEL, SUSE). It embeds a distro-specific certificate used to verify the next stage.
  • MOK (Machine Owner Key): a user-enrolled certificate held in a UEFI variable. Useful for self-compiled kernels or third-party drivers (NVIDIA, ZFS DKMS). Enrolled via the MokManager UI on the next reboot.
  • GRUB: the signed build of GRUB ships with a configuration that requires --pubkey to verify further artifacts. Distros configure it to verify kernel and initrd.
  • Kernel lockdown (lockdown=integrity or confidentiality) is typically auto-enabled when booted with Secure Boot — blocks kexec of unsigned kernels, restricts /dev/mem, etc.

Measured Boot vs Secure Boot — Orthogonal

Secure Boot — enforcement

"Don't run unsigned code."

  • Firmware refuses to execute binaries missing a trusted signature.
  • Stops the attack at the door.
  • Provides no evidence afterwards of what ran.
  • Can be turned off at the keyboard (physical access).
Measured Boot — recording

"Record what ran, whether trusted or not."

  • Every stage extends a hash of the next into PCRs.
  • Lets a third party (TPM, remote verifier) gate secrets on the result.
  • Provides evidence but does not prevent anything.
  • Cannot be turned off without physically replacing the TPM.

Tip

Both together is the strong configuration: Secure Boot keeps random unsigned binaries out, and measured boot produces a PCR fingerprint of exactly which signed binaries ran — so a TPM-sealed disk key only unlocks on a known, enforced, recorded chain.

Sealing a LUKS Key to PCRs

This is the mechanism that replaces typed passphrases on boot. Storage 06 showed that a LUKS2 volume has up to 32 key slots, each holding an encrypted copy of the master volume key (MK). Any slot's unwrap-key can open the volume. Adding a TPM slot means: one key slot is unlocked by a TPM-sealed secret instead of a user passphrase.

  1. Generate a random 256-bit key K. Add it to a new LUKS2 key slot with cryptsetup luksAddKey.
  2. Seal K with a TPM policy bound to the current values of chosen PCRs (e.g. PCR 7 only).
  3. Store the sealed blob in the LUKS header token area (systemd-cryptenroll does this automatically — the blob lives in the LUKS2 JSON metadata).
  4. On next boot, the initramfs asks the TPM to unseal the blob. If the current PCR values match the sealed policy, K is released, the slot opens, the volume unlocks, root mounts — no passphrase typed.
  5. If anything in the measured chain differs (malicious bootloader, altered kernel, different firmware), PCRs differ, the TPM refuses to release K, and the initramfs falls back to prompting for a passphrase.

Warning

Evil-maid defence: an attacker with brief physical access cannot just swap in a tampered GRUB — it would change PCR 4/7, the TPM would refuse to unseal, and the attack visibly degrades to "please type your passphrase", which the user will notice. This is the whole point of binding to PCRs.

systemd-cryptenroll --tpm2-device

systemd-cryptenroll is the distro-native tool for managing LUKS2 key slots backed by TPM, FIDO2, or PKCS#11 tokens. It handles all the sealing mechanics, stores the blob as a LUKS2 token, and integrates with systemd-cryptsetup at boot.

Basic Enrollment

console
console

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

Options

OptionMeaning
--tpm2-device=autoDiscovers /dev/tpmrm0 automatically. Use an explicit path to pick a specific TPM.
--tpm2-pcrs=LISTPCR selection, e.g. 0+2+7 or 7+11+14. Default varies by distro (often 7).
--tpm2-with-pin=yesAdds a user-typed PIN to the unseal policy. Two-factor: PIN + TPM presence.
--tpm2-public-key=FILESeal against a signed PCR policy — policy authority, not current values. Survives updates signed by the same authority.
--tpm2-signature=FILESignature blob produced for a given set of future PCR values (used with --tpm2-public-key).
--recovery-keyGenerate and print a one-time recovery key simultaneously (store it in a password manager).
--wipe-slot=TYPERemove existing slots of a type (tpm2, recovery, password) before enrolling. Use to re-enroll after an update.

Inspecting and Removing

console
console

# systemd-cryptenroll /dev/nvme0n1p3
SLOT TYPE
   0 password
   1 tpm2
   2 recovery

# systemd-cryptenroll --wipe-slot=tpm2 /dev/nvme0n1p3
Wiped slot 1.
  

/etc/crypttab Entry

console
# <name>    <device>                               <keyfile> <options>
root-crypt  UUID=a1b2c3d4-...                      none      tpm2-device=auto,discard
  

The tpm2-device=auto option tells systemd-cryptsetup to try the TPM token first. Failure falls through to interactive passphrase entry.

PCR Selection — The Critical Trade-off

Which PCRs you bind to determines two things: what kinds of tampering will block unseal, and what routine events will accidentally block unseal. There is no single right answer — the choice depends on the platform's update cadence and the threat model.

SelectionBinds toBreaks onTypical use
7 only Secure Boot policy: PK/KEK/db/dbx + MOK list Secure Boot toggled, dbx update, new MOK enrolled Default. Loose but stable across kernel/bootloader updates (as long as they stay signed by trusted keys)
0+2+7 + firmware code + option ROMs Any BIOS/UEFI update, GPU firmware update Tighter; acceptable if firmware rarely changes
4+7 or 7+12 + bootloader image Every grub-install / update-grub Only if you control bootloader updates
7+8+9 + kernel cmdline + kernel + initrd Every update-initramfs, kernel upgrade, cmdline edit Tight; requires automated re-seal on every update
0..9 (full) Entire legacy boot chain Basically any system update Locked appliances only
7+11 (UKI) SB policy + Unified Kernel Image measurement UKI change — but UKI is a single signed artifact Modern best practice for systemd-boot setups

Note

Default reasoning: PCR 7 alone is the common default because it binds to policy, not artifacts. As long as signed kernels/bootloaders continue to be signed by a key in db, they all match. An attacker who disables Secure Boot, enrolls their own MOK, or rolls back dbx will fail to unseal. This gives "policy integrity" without update pain.

Handling Updates

If the PCR selection includes anything that changes with system updates (firmware, bootloader, kernel, initramfs), the sealed policy will not match current PCRs after the update, and the TPM will refuse to unseal. The machine reboots to a passphrase prompt. You must re-enroll.

console
console

# # After a kernel update that invalidated PCR 9 binding:
# systemd-cryptenroll --wipe-slot=tpm2 \
      --tpm2-device=auto --tpm2-pcrs=7+9 \
      /dev/nvme0n1p3
  

Automating Re-seal

  • Distro hooks: Ubuntu's full-disk encryption, Fedora Silverblue, and systemd-boot UKI setups precompute expected PCR values for the new kernel/initrd and re-seal automatically as part of the update transaction. dracut-install-kernel-style hooks trigger this.
  • Signed PCR policies (--tpm2-public-key): instead of sealing to a concrete PCR value, seal to "any PCR value signed by this public key". The vendor ships a signature alongside each update that authorises the new expected PCRs. No re-enroll needed on the machine. This is how modern "stable auto-unlock across updates" is built.

Unified Kernel Images (UKI)

A UKI is a single PE executable containing the kernel, initramfs, cmdline, microcode, and os-release — all signed as one artifact. UEFI loads it directly (often via systemd-boot); no GRUB, no initramfs generation on the target host.

Why it helps TPM sealing

  • The kernel+initrd are a single signed file measured as a unit.
  • systemd-stub records the UKI hash into PCR 11 before entry.
  • Sealing to 7+11 covers Secure Boot policy and the exact UKI, without the fragility of PCR 8/9 across initramfs regenerations.

Where you see it

  • Fedora CoreOS, RHEL 10 roadmap, Ubuntu Core, NixOS images.
  • Any systemd-boot-based server deployment pushing toward reproducible images.
  • Proxmox guests can use UKIs the same way; they simplify the PCR story inside the VM.

Clevis + Tang (Network-Bound Disk Encryption)

TPM sealing binds unlock to local state. NBDE binds unlock to network presence: the disk only decrypts when the server can reach a particular internal service. Stolen hardware off-site = cannot unlock.

Tang (server)
  • Stateless HTTP server. No database.
  • Advertises ECDH public keys.
  • Performs a blinded ECDH exchange on request.
  • Does not see the secret — cannot, by design, unseal for anyone.
  • Survives restarts; can be replicated; trivial to run (systemd unit, ~50 LoC).
Clevis (client)
  • Binds a LUKS slot to one or more Tang servers ("pins").
  • At boot, performs the matching ECDH step to recover the unwrap key.
  • Pin types: tang, tpm2, sss (Shamir Secret Sharing).
  • Configured via clevis luks bind; auto-unlocks via clevis-luks-askpass in initramfs.
console
console

# clevis luks bind -d /dev/nvme0n1p3 tang \
      '{"url":"http://tang.internal.example"}'
# # M-of-N with Shamir: need 2 of (TPM, tang1, tang2)
# clevis luks bind -d /dev/nvme0n1p3 sss '{
      "t": 2,
      "pins": {
        "tpm2": {"pcr_ids":"7"},
        "tang": [
          {"url":"http://tang1.internal"},
          {"url":"http://tang2.internal"}
        ]
      }
    }'
  

Tip

Defence in depth: Combine TPM + Tang via SSS. Stolen from the rack → no Tang network → cannot unlock. Stolen and the TPM cloned → still needs Tang. Tang compromised → still needs the TPM. This is a common pattern for on-prem server fleets.

Recovery Planning

Warning

Read this before enrolling anything. Every TPM-sealed disk needs a working recovery path. TPM hardware fails, motherboards get replaced, firmware updates invalidate policies, admins lock themselves out.

Recovery Checklist

  • Always keep a passphrase slot. Before enrolling TPM, cryptsetup luksAddKey a strong passphrase. Never let TPM be the only unlock path.
  • Back up the LUKS header (cryptsetup luksHeaderBackup --header-backup-file) to offline storage. A corrupted header bricks the whole volume regardless of slot contents.
  • Generate a recovery key at enrollment (--recovery-key) and store it in a password manager or a paper safe. This is a long random string, not a user-chosen passphrase — resistant to brute-force.
  • Document the PCR policy. Record which PCRs each host is bound to and why. After firmware/kernel changes you want to know what broke without guessing.
  • TPM hardware replacement = total loss of sealed secrets. A new motherboard, a new CPU (for fTPM), or a BIOS "Clear TPM" gives you a new EK, new SRK, new everything rooted in the TPM. Nothing previously sealed is recoverable. The passphrase slot is your only way in.
  • Major firmware updates: expect to re-enroll. Schedule a maintenance window where you can type the passphrase.
  • Test recovery periodically. Boot with the TPM slot wiped and verify the passphrase works. Do this at least once after setup.

Putting It All Together — Secure Server Recipe

The full configuration a modern DevOps/SRE would deploy on a Proxmox host, bare-metal node, or VM with a virtual TPM:

  1. UEFI: Secure Boot enabled with the default distro keys. Optionally enroll a site MOK for signing custom kernel modules (ZFS, OpenVSwitch DKMS).
  2. Boot chain: shim → GRUB (or systemd-boot + UKI) → signed kernel. Kernel lockdown at integrity mode via Secure Boot auto-detection.
  3. LUKS2 root with three slots:
    · slot 0: strong passphrase (emergency / updates)
    · slot 1: TPM-sealed under PCR 7 (7+11 if UKI)
    · slot 2: recovery key printed at install, stored in a password manager
  4. Measured boot records the actual boot path into PCRs. tpm2_eventlog available for incident response.
  5. Boot behaviour: clean boot → TPM unseals silently → root mounts, no console interaction. Tampered chain or post-update PCR change → initramfs falls back to passphrase prompt → admin investigates or re-enrolls.
  6. Optional: Clevis + Tang with SSS threshold "TPM AND tang" for on-prem racks — disks only unlock while plugged into the internal network.
  7. Operational hygiene: document which hosts are enrolled against which PCR selections; automate re-seal in the post-update hook; test the passphrase fallback after every major kernel or firmware bump.

Tip

End state: a server that boots unattended with full-disk encryption, refuses to give up its root disk if its firmware or bootloader is tampered with, and still has a documented path back to recovery if hardware fails or updates invalidate the TPM seal. That is the capstone — LUKS from Storage 06, plus everything on this page, composing into a single operational posture.

Solidnines — solidnines.com