Debian · Secure Boot · TPM · LUKS2

Pure Debian Secure Boot, TPM, and Full-Disk Encryption Guide

A Debian-only playbook for full-disk encryption with TPM-backed automatic unlock, Secure Boot, stable PCR policy, Clevis, and a recovery path that does not prompt every normal boot.

Target: HP EliteBook 645 G10-class laptop Storage: NVMe, UEFI, Secure Boot Primary unlock method: Clevis TPM2 Updated: 2026-05-20
Contents
  1. Executive summary
  2. BIOS and firmware baseline
  3. Debian installation approach
  4. Secure Boot on Debian
  5. MOK enrollment for DKMS module signing
  6. What is a UKI, and why would I care?
  7. Choosing Clevis or systemd-cryptenroll
  8. Finding the correct LUKS partition
  9. Primary method: Debian LUKS2 + Clevis TPM2
  10. Optional method: Debian LUKS2 + systemd-cryptenroll
  11. Correct crypttab syntax
  12. PCR guidance and recommended policies
  13. Optional: IMA file integrity measurements
  14. Verification checklist
  15. Troubleshooting recovery prompts
  16. Updates and rebinding workflow
  17. Potential future work: PCR 9 maintenance automation
  18. Sources
  19. Kernel update automation toolset

1. Executive summary

Recommended Debian path: install Debian with standard encrypted LVM/LUKS2, enable Secure Boot with Debian’s signed shim/GRUB, then bind the LUKS container to TPM2 using Clevis with a stable PCR policy.

The desired behavior is:

  • Debian boots in UEFI mode.
  • Secure Boot remains enabled.
  • The internal NVMe drive is encrypted at rest using LUKS2.
  • The TPM releases the disk unlock secret automatically on normal boots.
  • The system asks for the LUKS passphrase only when TPM policy fails, the TPM is cleared, or the boot trust state changes.
  • At least one human-enterable passphrase remains enrolled for recovery.

This guide makes Clevis the primary method because it is a common Debian-friendly way to bind a LUKS volume to TPM2. It also includes an optional systemd-cryptenroll path for systems where you prefer native systemd LUKS2 token handling.

Key correction: PCR policy is chosen in the Clevis or systemd enrollment command. Do not put fake syntax such as tpm2 pcrs=0,2,4,7 into /etc/crypttab.

2. BIOS and firmware baseline

Enter HP BIOS setup with Esc then F10. Exact menu names vary by firmware revision, but the target configuration is straightforward.

AreaSettingRecommended stateReason
Boot mode UEFI / Legacy / CSM UEFI only; Legacy/CSM disabled Secure Boot requires UEFI. Avoid mixed boot paths.
Secure Boot Secure Boot Enabled for final configuration Debian can boot through signed shim/GRUB when the signed packages are installed.
Secure Boot Microsoft UEFI CA key Enabled Debian’s shim is signed through the Microsoft UEFI CA trust path.
Secure Boot Custom keys / clear keys Do not import or clear unless intentionally managing your own chain Changing Secure Boot keys changes PCR state and may force recovery or TPM rebind.
TPM TPM device / TPM state Available and enabled Required for TPM-backed automatic unlock.
TPM Clear TPM Do not clear Clearing the TPM invalidates sealed unlock material.
Storage RAID / RST / special controller modes Use standard NVMe/AHCI-style exposure where applicable Debian install and initramfs unlock are simplest when storage is exposed normally.
Boot order Internal Debian boot entry First Changing boot paths can change TPM measurements.
Vendor features Absolute / persistence / preboot network boot Disable unless intentionally needed Vendor preboot features can alter measured boot behavior.
After the TPM binding is created, avoid casually changing Secure Boot state, Secure Boot keys, boot order, TPM state, or firmware settings. Those changes are supposed to trigger recovery or require re-binding.

3. Debian installation approach

Recommended installer path

  1. Boot the Debian installer in UEFI mode.
  2. If the installer has trouble with Secure Boot, temporarily disable Secure Boot for installation only.
  3. Use guided encrypted LVM, or manually create a LUKS2 encrypted container with LVM inside.
  4. Create an EFI System Partition mounted at /boot/efi.
  5. Use a separate unencrypted /boot unless you are intentionally building a more advanced encrypted-boot layout.
  6. Create a strong LUKS passphrase. Keep it as the recovery path.
  7. Complete installation and boot into Debian.

Typical layout

LayerExampleRole
EFI System Partition /dev/nvme0n1p1, FAT32, mounted at /boot/efi UEFI firmware loads shim/GRUB from here.
Boot partition /dev/nvme0n1p2, ext4, mounted at /boot Stores kernels, initramfs, and GRUB files.
LUKS2 container /dev/nvme0n1p3, crypto_LUKS The encrypted parent partition to bind to TPM.
LVM inside LUKS /dev/mapper/vg-root, /dev/mapper/vg-swap Root filesystem and swap inside the unlocked container.

Confirm LUKS2 after first boot

lsblk -o NAME,PATH,TYPE,FSTYPE,FSVER,SIZE,MOUNTPOINTS,UUID
export LUKSDEV=/dev/nvme0n1p3   # adjust to your crypto_LUKS partition
sudo cryptsetup luksDump "$LUKSDEV" | head -40

You want to see:

LUKS header information
Version:        2

4. Secure Boot on Debian

Debian Secure Boot uses signed boot components. On amd64, install the signed shim and GRUB packages before enabling Secure Boot permanently.

Install signed boot packages

sudo apt update
sudo apt install shim-signed grub-efi-amd64-signed mokutil

Refresh GRUB and confirm boot path

sudo update-grub
sudo grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=debian --recheck
Some Debian installs already have the correct GRUB EFI installation. If grub-install reports that Secure Boot forbids installing unsigned binaries, confirm that the signed packages are installed and that the boot entry points through shim.

Verify Secure Boot state

mokutil --sb-state

Expected final state:

SecureBoot enabled

Boot entry inspection

Install efibootmgr if you want to inspect or reorder UEFI entries:

sudo apt install efibootmgr
sudo efibootmgr -v

Look for a stable Debian entry pointing through shim, commonly under an EFI path such as:

\EFI\debian\shimx64.efi

Avoid alternating between fallback paths such as \EFI\BOOT\BOOTX64.EFI and the Debian shim path.

5. MOK enrollment for DKMS module signing

Use case: Secure Boot can reject locally built DKMS modules even when the modules are signed, unless the signing certificate is enrolled into the Machine Owner Key database. This commonly affects VirtualBox modules such as vboxdrv, vboxnetflt, and vboxnetadp.

A typical failure looks like this:

modprobe: ERROR: could not insert 'vboxdrv': Key was rejected by service
Loading of module with unavailable key is rejected

If modinfo shows a signer such as DKMS module signing key, but modprobe still fails with Key was rejected by service, the module was signed but the signing key is not yet trusted by Secure Boot.

sudo modinfo vboxdrv | grep -Ei 'filename|signer|sig_key|sig_hashalgo'
sudo modprobe vboxdrv
sudo dmesg | tail -80 | grep -Ei 'vbox|virtualbox|module|key|lockdown|secure'

Confirm the Debian shim boot path

MOK enrollment is a shim workflow. Confirm that the active boot entry goes through shimx64.efi, not directly through grubx64.efi.

sudo efibootmgr -v

Expected path:

\EFI\debian\shimx64.efi

Find and test the DKMS signing certificate

On Debian DKMS systems, the public certificate is commonly stored at /var/lib/dkms/mok.pub.

sudo find /var/lib/dkms /etc/dkms /usr/lib/dkms -type f \
  \( -iname '*.der' -o -iname '*.pub' -o -iname '*.pem' -o -iname '*.crt' \) \
  -print

sudo mokutil --test-key /var/lib/dkms/mok.pub

If the key is not enrolled, import it:

sudo mokutil --import /var/lib/dkms/mok.pub

Create the one-time MOK enrollment password when prompted. Then reboot and complete the blue MOK Manager flow:

Enroll MOK
Continue
Yes
enter the one-time password
Reboot

Verify enrollment after reboot

mokutil --sb-state
sudo mokutil --test-key /var/lib/dkms/mok.pub
mokutil --list-enrolled | grep -i -A3 -B3 'DKMS\|module'

A successful enrollment should show the DKMS module signing certificate. Then retry the module load:

sudo modprobe vboxdrv
sudo modprobe vboxnetflt
sudo modprobe vboxnetadp

lsmod | grep '^vbox'
sudo dmesg | tail -80 | grep -Ei 'vbox|virtualbox|module|key|lockdown|secure'

For the observed VirtualBox case, successful output included:

vboxdrv: Successfully loaded version 7.2.8_Debian
VBoxNetFlt: Successfully started.
VBoxNetAdp: Successfully started.

If the one-time MOK enrollment password is lost

The password created with mokutil --import is only for the pending MOK Manager enrollment. It is not the LUKS passphrase, not the user password, and not needed after enrollment succeeds.

  • If the password is lost before completing enrollment, boot normally, cancel or ignore the MOK Manager prompt, then cancel the pending import and start again.
  • If supported by your mokutil version, cancel the pending import with:
    sudo mokutil --revoke-import
  • Then re-import the certificate and choose a new one-time password:
    sudo mokutil --import /var/lib/dkms/mok.pub
    sudo reboot
  • If the key is already enrolled, the old one-time password no longer matters.

Check pending and enrolled state with:

mokutil --list-new
sudo mokutil --test-key /var/lib/dkms/mok.pub
mokutil --list-enrolled | grep -i -A3 -B3 'DKMS\|module'

Removing and re-enrolling a DKMS MOK key

If the DKMS signing key changes, is replaced, or you want to remove and re-enroll it, schedule deletion:

sudo mokutil --delete /var/lib/dkms/mok.pub
sudo reboot

Complete the MOK Manager removal flow at boot. Then verify the old key is no longer enrolled:

sudo mokutil --test-key /var/lib/dkms/mok.pub

Re-enroll the current DKMS key:

sudo mokutil --import /var/lib/dkms/mok.pub
sudo reboot

After enrollment, rebuild or reload the affected modules if needed:

sudo dkms autoinstall
sudo modprobe vboxdrv

Interaction with strict PCR policy

MOK enrollment is a measured-boot change. If the Clevis policy includes PCRs affected by Secure Boot, shim, MOK, or boot artifacts, MOK enrollment may cause TPM auto-unlock to fail until the new state is approved.

If a MOK operation causes a recovery prompt, unlock with the human LUKS passphrase, complete the module-signing work, confirm the module loads, then rebind Clevis to the new approved PCR state.

export DEV=/dev/nvme0n1p3
STRICT_POLICY='{"pcr_bank":"sha256","pcr_ids":"0,1,2,4,5,7,8,9"}'

sudo tpm2_pcrread sha256:0,1,2,4,5,7,8,9
sudo clevis luks bind -y -d "$DEV" tpm2 "$STRICT_POLICY"
sudo clevis luks list -d "$DEV"

Do not run update-initramfs after rebinding to a PCR 9 policy unless you intend to repeat the approval process.

6. What is a UKI, and why would I care?

UKI means Unified Kernel Image. Instead of booting several separate files such as a kernel, initramfs, kernel command line, and boot metadata, a UKI packages the bootable operating-system payload into a single signed EFI executable.

In a conventional Debian GRUB boot, the firmware starts shim, shim starts GRUB, and GRUB loads the kernel and initramfs as separate files:

Firmware → shim → GRUB → kernel + initramfs + kernel command line

In a UKI-oriented design, the firmware or boot manager loads a single signed EFI binary that contains the kernel, initramfs, command line, and related metadata:

Firmware → shim/systemd-boot/boot manager → signed UKI

The practical appeal is that the thing being booted is easier to sign, measure, and reason about. Instead of asking, “Did GRUB load the exact kernel, exact initramfs, and exact command line I expected?”, the system can ask, “Is this signed UKI one of the approved boot artifacts?”

Why UKIs matter for Secure Boot and TPM unlock

Secure Boot verifies signatures before code executes. TPM measured boot records what happened into PCRs. Full-disk auto-unlock uses those measurements to decide whether the TPM should release the disk unlock secret. A UKI can make that chain cleaner because the kernel, initramfs, and command line become one signed, measurable boot object.

Conventional GRUB-style boot UKI-oriented boot
Kernel and initramfs are separate files. Kernel and initramfs are bundled into one EFI executable.
GRUB reads files and constructs the boot handoff. The boot payload is prebuilt and signed as a unit.
PCRs can be affected by GRUB behavior, file reads, command-line changes, and initramfs changes. Measurements can be centered around the signed UKI and systemd-stub measurements.
Flexible and familiar. More controlled and more appliance-like.
Good default for most Debian laptops. Attractive for stricter measured-boot designs.

Why someone might head toward UKIs

  • Cleaner Secure Boot story: the bootable kernel artifact can be signed as one object instead of relying on a bootloader to assemble several moving parts.
  • Cleaner TPM measurement story: UKI-aware boot flows can use systemd measurements such as PCR 11 more intentionally.
  • Reduced GRUB complexity: GRUB is powerful because it can read many filesystems and interpret configuration, but that flexibility also increases the amount of boot behavior to reason about.
  • Better fit for measured-boot policy: if the boot artifact is built, signed, and measured as a unit, it can be easier to decide which boot states should unlock the disk.
  • Better fit for future systemd tooling: tools such as systemd-stub, systemd-measure, and related PCR-policy workflows are designed around UKI-style boot artifacts.

Why this guide does not start with UKIs

UKIs are attractive, but they add build and lifecycle complexity. A normal Debian laptop already has a well-understood path:

Debian installer → LUKS2 encrypted root → signed shim/GRUB → Clevis TPM2 unlock

That path is easier to install, easier to recover, and easier to document. For a daily-driver laptop, it is better to first get stable TPM auto-unlock working with conventional Debian boot components. UKIs can be a later hardening project once the basic Secure Boot, LUKS2, TPM, and recovery workflow is proven.

When UKIs become worth considering

Consider UKIs when… Stay with conventional Debian GRUB when…
You want stricter measured boot and are willing to manage signed boot artifacts. You want the most conventional Debian laptop setup.
You want to bind disk unlock to UKI/systemd-stub measurements such as PCR 11. You want TPM unlock with minimal re-enrollment pain.
You are building an appliance-like or high-assurance endpoint. You are building a general-purpose workstation.
You are comfortable rebuilding and signing boot artifacts after kernel/initramfs changes. You prefer Debian package updates and GRUB behavior to remain mostly stock.
You want to explore systemd-measure, systemd-stub, or PCR policy signing. You want the guide’s recommended Clevis PCR 7 baseline.
Practical recommendation: do not start with UKIs for this build. Use conventional Debian Secure Boot plus LUKS2 plus Clevis TPM2 first. Once that is stable, a UKI-based design can be evaluated as a separate hardening phase.

How UKIs affect the Clevis vs. systemd-cryptenroll choice

Clevis is the practical default for a conventional Debian encrypted-root laptop. If you later move toward UKIs, systemd-cryptenroll becomes more interesting because it fits naturally with the broader systemd measured-boot ecosystem.

Conventional Debian laptop:
  Prefer Clevis + TPM2 + PCR 7

Conventional Debian laptop, slightly stronger:
  Clevis + TPM2 + PCR 4,7

UKI / systemd-stub / measured-boot project:
  Consider systemd-cryptenroll, systemd-measure, PCR 11, and signed PCR policies

In short: Clevis is the practical Debian default today; UKI is the future-looking measured-boot path.

7. Choosing Clevis or systemd-cryptenroll

Debian can support TPM-backed LUKS auto-unlock through more than one stack. The two practical choices are Clevis and systemd-cryptenroll. Both can bind a LUKS2 encrypted root volume to the TPM, but they fit different operational styles.

Default recommendation for this guide: use Clevis first on a conventional Debian encrypted-root laptop. Use systemd-cryptenroll when you specifically want the systemd-native LUKS2 token model and have confirmed your Debian initramfs and crypttab path unlock cleanly with it.

Decision summary

Use case Prefer Why
Conventional Debian encrypted LVM/LUKS laptop Clevis Clevis has explicit Debian-friendly initramfs integration through clevis-initramfs and is easy to inspect with clevis luks list.
Lowest-friction TPM auto-unlock baseline Clevis Start with PCR 7 only. It binds unlock to Secure Boot policy without dragging in frequently changing measurements such as initramfs or IMA.
You want a systemd-native LUKS2 token workflow systemd-cryptenroll The TPM2 token is managed directly by systemd tooling and stored in the LUKS2 header. This is cleaner if you are standardizing around systemd cryptsetup behavior.
You want a clean 7+14 PCR expression systemd-cryptenroll systemd-cryptenroll --tpm2-pcrs=7+14 directly expresses Secure Boot policy plus shim/MOK state.
You may later want Tang/network-bound unlock Clevis Clevis is designed around “pins,” including TPM2 and Tang. It is the more natural choice if network-bound disk encryption may be added later.
You are experimenting with UKI, systemd-stub, systemd-measure, or pcrlock systemd-cryptenroll Those designs fit better with systemd’s measured-boot ecosystem than with a simple Clevis setup.
You want the easiest troubleshooting path on Debian Clevis The moving parts are explicit: clevis, clevis-luks, clevis-tpm2, clevis-initramfs, the LUKS token, and initramfs.

Clevis PCR-count limitation observed on this build

Observed behavior: on this Debian/Clevis/tpm2-tools stack, TPM2 policy creation succeeded with 8 PCRs but failed with 9 or more PCRs, producing errors such as couldnot hash pcr values, Could not build PCR policy, and Unable to run tpm2_createpolicy.

Treat this as a practical Clevis/toolchain limit unless your installed version proves otherwise. This is not the same as “too many LUKS keyslots.” The failure occurs while Clevis is building the TPM2 PCR policy, before a new LUKS binding is created.

The strictest confirmed conventional-GRUB Clevis profile in this guide is:

{"pcr_bank":"sha256","pcr_ids":"0,1,2,4,5,7,8,9"}

If you need more than eight PCRs, or if you want signed PCR policies or UKI-oriented measured boot, evaluate systemd-cryptenroll instead of forcing Clevis to build an oversized policy.

Practical recommendation

For a daily-driver Debian laptop, use this progression:

  1. Start with Clevis + TPM2 + PCR 7. This is the most practical baseline for avoiding a disk prompt on every normal boot.
  2. After multiple successful cold boots, warm reboots, and ordinary package updates, consider Clevis + TPM2 + PCR 4,7 if you want stronger boot-path binding.
  3. Use systemd-cryptenroll + PCR 7+14 instead if you want the systemd-native model or are building toward a UKI/systemd-measured-boot design.

Recommended starting commands

Clevis baseline

sudo apt update
sudo apt install clevis clevis-luks clevis-tpm2 clevis-initramfs tpm2-tools

export LUKSDEV=/dev/nvme0n1p3   # adjust to the crypto_LUKS partition

sudo clevis luks bind -y -d "$LUKSDEV" tpm2 \
  '{"pcr_bank":"sha256","pcr_ids":"7"}'

sudo update-initramfs -u -k all
sudo clevis luks list -d "$LUKSDEV"

Clevis moderate-aggressive option

sudo clevis luks bind -y -d "$LUKSDEV" tpm2 \
  '{"pcr_bank":"sha256","pcr_ids":"4,7"}'

sudo update-initramfs -u -k all
sudo clevis luks list -d "$LUKSDEV"

systemd-cryptenroll baseline

sudo apt update
sudo apt install cryptsetup-bin systemd-cryptsetup tpm2-tools

export LUKSDEV=/dev/nvme0n1p3   # adjust to the crypto_LUKS partition

sudo systemd-cryptenroll "$LUKSDEV" \
  --tpm2-device=auto \
  --tpm2-pcrs=7+14

sudo cp /etc/crypttab /etc/crypttab.bak.$(date +%F-%H%M%S)
sudo nano /etc/crypttab
# Add tpm2-device=auto to the root crypt line, preserving existing options.

sudo update-initramfs -u -k all

crypttab differences

Method crypttab line Where PCRs are configured
Clevis cryptroot UUID=<luks-uuid> none luks,discard In the Clevis bind command: clevis luks bind ... '{"pcr_ids":"7"}'
systemd-cryptenroll cryptroot UUID=<luks-uuid> none luks,discard,tpm2-device=auto In the systemd enrollment command: systemd-cryptenroll --tpm2-pcrs=7+14
Do not put PCRs directly into crypttab. This is invalid:
cryptroot UUID=<luks-uuid> none tpm2 pcrs=0,2,4,7
PCRs belong in the Clevis bind command or the systemd-cryptenroll command.

When to avoid each option

Avoid When Use instead
Clevis You are intentionally building a systemd-stub, UKI, systemd-measure, or pcrlock-based boot chain. systemd-cryptenroll
systemd-cryptenroll Your Debian initramfs does not unlock reliably with tpm2-device=auto, or you want the most explicit Debian package path. Clevis
Aggressive PCRs PCR 0, 2, 8, 9, or 10 change across ordinary reboots or normal updates. Clevis PCR 7, Clevis PCR 4,7, or systemd PCR 7+14.

Bottom line

For this Debian guide, the mainline path should be:

Primary:  Clevis + TPM2 + PCR 7
Upgrade:  Clevis + TPM2 + PCR 4,7 after stability testing
Optional: systemd-cryptenroll + PCR 7+14
Avoid:    PCR 0,2,8,9,10 for the daily-driver baseline

8. Finding the correct LUKS partition

The correct target for cryptsetup luksDump, clevis luks bind, or systemd-cryptenroll is the parent partition with FSTYPE=crypto_LUKS.

lsblk -o NAME,PATH,TYPE,FSTYPE,FSVER,SIZE,MOUNTPOINTS,UUID

Typical layout:

nvme0n1
├─nvme0n1p1  /dev/nvme0n1p1  part  vfat        /boot/efi
├─nvme0n1p2  /dev/nvme0n1p2  part  ext4        /boot
└─nvme0n1p3  /dev/nvme0n1p3  part  crypto_LUKS
  └─cryptroot               crypt LVM2_member
    ├─vg-root               lvm   ext4        /
    └─vg-swap               lvm   swap        [SWAP]

Correct device in this example:

export LUKSDEV=/dev/nvme0n1p3

Confirm with luksDump

sudo cryptsetup luksDump "$LUKSDEV" | head -40

A correct result begins with:

LUKS header information
Version:        2

Cross-check with crypttab

cat /etc/crypttab

Example:

cryptroot UUID=aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee none luks,discard

Resolve the UUID:

findfs UUID=aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee

Do not use these as LUKSDEV

Wrong targetWhy
/dev/nvme0n1p1EFI System Partition, usually /boot/efi.
/dev/nvme0n1p2Usually unencrypted /boot.
/dev/mapper/vg-rootThe root filesystem inside the already-unlocked container.
/dev/mapper/vg-swapSwap inside the already-unlocked container.
/A mount point, not a block device with a LUKS header.

9. Primary method: Debian LUKS2 + Clevis TPM2

Cross-platform context: RHEL-style Clevis guides use the same core model—TPM2 pin, PCR policy, LUKS binding, recovery passphrase, and initramfs integration—but RHEL rebuilds the initramfs with dracut. This Debian guide uses update-initramfs instead.

Clevis binds a LUKS slot to a TPM2 policy and allows the initramfs to unlock the encrypted root automatically when the measured boot state matches.

Install packages

sudo apt update
sudo apt install clevis clevis-luks clevis-tpm2 clevis-initramfs tpm2-tools cryptsetup-bin mokutil
If you see 'tpm2' is not a valid pin!, the package clevis-tpm2 is missing or not installed correctly.

Verify the TPM and the Clevis TPM2 pin

Do not rely on a hard-coded Clevis plugin directory. Debian package layouts can vary. The useful checks are: the TPM device exists, the clevis-tpm2 package is installed, and Clevis can actually encrypt a test value using the TPM2 pin.

ls -l /dev/tpmrm0 /dev/tpm0 2>/dev/null
dpkg -L clevis-tpm2 | grep -Ei 'tpm2|pin|clevis' || true
echo test | clevis encrypt tpm2 '{}'

Prefer /dev/tpmrm0 when present; it is the TPM resource manager device. Some systems may also expose /dev/tpm0. If the final command outputs a JSON/JWE-looking blob rather than 'tpm2' is not a valid pin!, the Clevis TPM2 pin is available.

Set your device path before running any command below — and again if you open a new terminal. Replace /dev/nvme0n1p3 with the path to your actual crypto_LUKS partition (find it with lsblk -o NAME,FSTYPE):
export LUKSDEV=/dev/nvme0n1p3
All commands in this section and the next use $LUKSDEV.

Bind LUKS to TPM2

Start with PCR 7. This is the stable baseline: it binds unlock to Secure Boot policy without dragging in firmware/device/initramfs measurements that are likely to change during ordinary maintenance.

export LUKSDEV=/dev/nvme0n1p3   # adjust this to the crypto_LUKS partition

sudo clevis luks bind -y -d "$LUKSDEV" tpm2 \
  '{"pcr_bank":"sha256","pcr_ids":"7"}'

sudo update-initramfs -u -k all
sudo clevis luks list -d "$LUKSDEV"

Moderately aggressive Clevis policy

After several normal boots and at least one ordinary update cycle, consider PCR 4,7. PCR 4 adds bootloader/boot-path sensitivity.

sudo clevis luks bind -y -d "$LUKSDEV" tpm2 \
  '{"pcr_bank":"sha256","pcr_ids":"4,7"}'

sudo update-initramfs -u -k all
sudo clevis luks list -d "$LUKSDEV"
Avoid starting with {"pcr_ids":"0,2,4,7"}. PCR 0 and PCR 2 can change after BIOS updates, dock/device changes, firmware settings changes, option ROM behavior changes, or other platform-level events.

Strict 8-PCR Clevis profile

For a strict conventional Debian GRUB profile that includes PCR 9 and stays within the observed 8-PCR Clevis limit, use:

sudo clevis luks bind -y -d "$LUKSDEV" tpm2 \
  '{"pcr_bank":"sha256","pcr_ids":"0,1,2,4,5,7,8,9"}'

This intentionally makes firmware, platform configuration, option ROM/device behavior, boot path, boot configuration, Secure Boot policy, GRUB command line, and kernel/initramfs/GRUB-read file changes approval events.

Removing or replacing a Clevis binding

List bindings first:

sudo clevis luks list -d "$LUKSDEV"

Remove a binding by slot number only after confirming you still have a working passphrase:

sudo clevis luks unbind -d "$LUKSDEV" -s <slot-number>
sudo update-initramfs -u -k all

10. Optional method: Debian LUKS2 + systemd-cryptenroll

On modern Debian with systemd, systemd-cryptenroll can store TPM2 token metadata directly in the LUKS2 header. This is a clean alternative to Clevis when your initramfs and crypttab stack support it.

Install tools

sudo apt update
sudo apt install cryptsetup-bin systemd-cryptsetup tpm2-tools mokutil

Enroll TPM2

Recommended systemd baseline:

export LUKSDEV=/dev/nvme0n1p3   # adjust this

sudo systemd-cryptenroll "$LUKSDEV" \
  --tpm2-device=auto \
  --tpm2-pcrs=7+14

If re-sealing after a known trust-chain change:

sudo systemd-cryptenroll "$LUKSDEV" \
  --wipe-slot=tpm2 \
  --tpm2-device=auto \
  --tpm2-pcrs=7+14

Edit crypttab

Add tpm2-device=auto to the existing root crypt line. Preserve existing options.

sudo cp /etc/crypttab /etc/crypttab.bak.$(date +%F-%H%M%S)
sudo nano /etc/crypttab

Correct examples:

cryptroot UUID=<luks-uuid> none luks,tpm2-device=auto
cryptroot UUID=<luks-uuid> none luks,discard,tpm2-device=auto

Then rebuild initramfs:

sudo update-initramfs -u -k all
sudo reboot
If this path does not unlock automatically on your Debian release/initramfs stack, use the Clevis method. Clevis is the primary path in this guide because it is explicit and Debian-friendly.

11. Correct crypttab syntax

/etc/crypttab tells the initramfs and early userspace which encrypted devices exist and how they should be unlocked. It has four fields:

<name> <source-device> <key-file> <options>
Field Example Meaning
<name> cryptroot The mapper name that will appear under /dev/mapper/.
<source-device> UUID=<luks-uuid> The encrypted parent partition, usually the partition with FSTYPE=crypto_LUKS.
<key-file> none Use interactive/token-based unlock rather than a static key file.
<options> luks,discard Comma-separated options controlling how the encrypted volume is opened.

Correct for Clevis

Clevis normally does not require PCR values in crypttab. Keep the line ordinary and let clevis-initramfs handle unlock from the LUKS token:

cryptroot UUID=<luks-uuid> none luks

Or, if you intentionally want discard/TRIM passthrough:

cryptroot UUID=<luks-uuid> none luks,discard

Correct for systemd-cryptenroll

For the systemd-cryptenroll path, add tpm2-device=auto to the existing root crypt line. Preserve existing options unless you are intentionally changing them.

cryptroot UUID=<luks-uuid> none luks,tpm2-device=auto

Or, with discard/TRIM passthrough:

cryptroot UUID=<luks-uuid> none luks,discard,tpm2-device=auto

What does discard mean?

The discard option allows discard/TRIM commands to pass through the encrypted mapping to the underlying SSD or NVMe drive. In practical terms, it lets the storage device learn which encrypted blocks are no longer in use so the drive can manage flash cleanup, wear leveling, and long-term write performance more effectively.

Without discard, deleted files are still deleted from the filesystem, but the SSD may not be told promptly that those lower-level blocks are free. The system remains encrypted either way. The difference is mostly storage-maintenance behavior and information leakage, not whether files are deleted at the filesystem level.

Why include discard?

  • Better SSD/NVMe maintenance: the drive can reclaim unused flash blocks more efficiently.
  • Potentially better sustained write performance: especially on systems with lots of file churn or limited free space.
  • Common on laptop SSD installs: many users accept the privacy tradeoff for normal workstation use.
  • Useful after large deletes or package churn: the storage device can eventually reuse freed space more efficiently.

Why avoid discard?

  • It leaks allocation patterns: an observer with raw access to the encrypted disk may be able to tell which encrypted sectors are currently unused, and may infer rough filesystem usage patterns.
  • It weakens “everything looks like random occupied ciphertext” behavior: encryption still protects file contents, but the drive may reveal which regions are unused.
  • It is not required for correctness: the encrypted system will boot and function without it.
  • High-assurance threat models may reject it: especially if disk image analysis, plausible deniability, or minimizing metadata leakage matters.

Continuous discard vs. periodic TRIM

There are two common ways to give an SSD discard information:

Approach How Tradeoff
Continuous discard Add discard to /etc/crypttab. Simple and automatic, but leaks block-freeing behavior continuously.
Periodic TRIM Leave discard out and use fstrim.timer, if supported through the stack. Less continuous leakage, but still reveals freed regions when TRIM runs.

Periodic TRIM is often the nicer compromise for privacy-conscious systems, but it still requires the encrypted mapping to permit discard when the TRIM operation runs. If discard is blocked at the dm-crypt layer, fstrim cannot pass those commands all the way to the device.

Practical recommendation for this guide

Threat model Recommended crypttab option Reason
Normal laptop / workstation luks,discard Reasonable SSD maintenance and performance tradeoff.
Privacy-sensitive but practical luks, then evaluate periodic TRIM deliberately Reduces continuous leakage of block-freeing behavior.
High-assurance / minimize metadata leakage luks Avoids exposing free-space patterns through discard.
For this HP EliteBook Debian build: using discard is reasonable for a normal daily-driver laptop with an internal NVMe SSD. If the goal is maximum privacy against raw-disk analysis, omit discard.

Check whether discard is active

After boot, inspect the active encrypted mapping. Replace cryptroot with the mapper name used on your system:

sudo cryptsetup status cryptroot

If discard is active, the status output normally includes:

flags: discards

You can also check whether the block stack advertises discard capability:

lsblk -D

Invalid

cryptroot UUID=<luks-uuid> none tpm2 pcrs=0,2,4,7

That line is neither a shell command nor valid crypttab syntax. PCRs belong in the binding/enrollment command:

# Clevis
sudo clevis luks bind -y -d "$LUKSDEV" tpm2 '{"pcr_bank":"sha256","pcr_ids":"7"}'

# systemd
sudo systemd-cryptenroll "$LUKSDEV" --tpm2-device=auto --tpm2-pcrs=7+14

12. PCR guidance and recommended policies

Platform Configuration Registers, or PCRs, are TPM measurement buckets. A TPM-sealed unlock secret is released only when the current measured state satisfies the policy created at bind/enroll time.

PCR Typical meaning Disk unlock recommendation Operational risk
0Firmware/BIOS codeAvoid initiallyBIOS updates can break unlock.
1Firmware/platform configurationOnly after testingBIOS setting changes can break unlock.
2Option ROMs / device firmwareAvoid on laptopsDocks/devices/firmware behavior can be noisy.
4Bootloader / boot manager pathGood moderate-aggressive additionBootloader updates or path changes can break unlock.
5Boot config / partition stateGood only after layout is stablePartition or boot config changes can break unlock.
7Secure Boot policy and key databasesBest Clevis baselineSecure Boot state/key changes break unlock.
8GRUB commands / kernel command line on many GRUB systemsAvoid unless measured and stableKernel command-line or GRUB state changes can break unlock.
9Initrd/initramfs measurementsAvoid for low-maintenance systemsKernel/initramfs updates can break unlock.
10IMA runtime file measurementsAvoid for disk unlockCan vary during normal boot/runtime.
11systemd-stub / UKI boot phasesUseful for UKI designsNot the default GRUB baseline.
14shim/MOK stateUseful with systemd enrollmentMOK enroll/remove changes break unlock.

Recommended policies

GoalClevissystemd-cryptenrollNotes
Stable convenience {"pcr_bank":"sha256","pcr_ids":"7"} 7 Closest to BitLocker-like no-prompt behavior.
Recommended Debian baseline {"pcr_bank":"sha256","pcr_ids":"7"} 7+14 Clevis starts with Secure Boot policy; systemd can also bind shim/MOK state.
Moderately aggressive {"pcr_bank":"sha256","pcr_ids":"4,7"} 4+5+7+14 Better boot-path binding, more update sensitivity.
Firmware-config locked Consider only after testing 1+4+5+7+14 Can be appropriate when BIOS settings rarely change.
Lab / attestation experiment Not recommended for daily use Include 8/9/10 only intentionally High chance of recurring recovery prompts.

Do PCRs need to match elsewhere in the guide?

Yes, the PCRs shown in the guidance must match the binding/enrollment you actually create. But they do not belong in /etc/crypttab.

Clevis bind command       = where Clevis PCR policy is chosen
systemd-cryptenroll      = where systemd PCR policy is chosen
/etc/crypttab            = tells initramfs what encrypted volume exists and how to try unlocking it

Measure stability before getting aggressive

sudo systemd-analyze pcrs | tee ~/pcrs-boot1.txt
sudo reboot
sudo systemd-analyze pcrs | tee ~/pcrs-boot2.txt
diff -u ~/pcrs-boot1.txt ~/pcrs-boot2.txt

Do not bind disk unlock to PCRs that change across ordinary reboots.

13. Optional: IMA file integrity measurements

IMA means Integrity Measurement Architecture. It is a Linux subsystem that can measure files as they are accessed and extend those measurements into the TPM, commonly PCR 10.

What IMA is useful for

  • Runtime file-integrity evidence.
  • Attestation experiments.
  • High-assurance integrity monitoring.
  • Detecting changes to measured binaries, libraries, modules, or policy-selected files.

Why IMA is risky for auto-unlock

PCR 10 can change during otherwise normal boots depending on IMA policy and measurement order. If the disk unlock policy depends on PCR 10, the TPM may refuse to unlock and ask for the LUKS passphrase.

Recommendation: do not bind disk unlock to PCR 10 on a daily-driver laptop. Enable IMA only after TPM auto-unlock is stable, and treat IMA as a separate attestation project.

Check whether IMA is active

cat /proc/cmdline | tr ' ' '\n' | grep -Ei 'ima|integrity|evm' || true
ls -l /sys/kernel/security/ima 2>/dev/null || true
sudo wc -l /sys/kernel/security/ima/ascii_runtime_measurements 2>/dev/null || true

Compare IMA measurements across boots

sudo cp /sys/kernel/security/ima/ascii_runtime_measurements /root/ima-boot1.txt 2>/dev/null || true

# Reboot, then:
sudo cp /sys/kernel/security/ima/ascii_runtime_measurements /root/ima-boot2.txt 2>/dev/null || true
sudo diff -u /root/ima-boot1.txt /root/ima-boot2.txt | less

14. Verification checklist

Baseline system checks

sudo mokutil --sb-state
sudo systemd-analyze has-tpm2
lsblk -o NAME,PATH,TYPE,FSTYPE,FSVER,SIZE,MOUNTPOINTS,UUID

LUKS header and crypttab

sudo cryptsetup luksDump "$LUKSDEV" | sed -n '1,220p'
cat /etc/crypttab

Recovery passphrase backup

TPM auto-unlock must not be your only recovery path. Clevis adds an unlock method; it does not replace the need for a human-enterable LUKS passphrase. Keep at least one passphrase slot active and store the passphrase offline.

sudo cryptsetup luksDump "$LUKSDEV"
sudo cryptsetup open --test-passphrase "$LUKSDEV"

Do not remove ordinary passphrase keyslots while cleaning up Clevis slots. If TPM unsealing fails after a BIOS update, Secure Boot key change, kernel/initramfs update, or PCR policy change, the human passphrase is what gets you back into the system.

Clevis path

sudo clevis luks list -d "$LUKSDEV"
dpkg -l | grep -E 'clevis|tpm2'
journalctl -b -g 'clevis|tpm|crypt|luks' --no-pager

systemd-cryptenroll path

sudo cryptsetup luksDump "$LUKSDEV" | grep -Ei 'systemd|tpm|token|luks2' -A3 -B3
journalctl -b -g 'tpm|crypt|luks|systemd-cryptsetup' --no-pager

Boot tests

  1. Cold boot once. Confirm no disk prompt.
  2. Cold boot a second time. Confirm no disk prompt.
  3. Warm reboot. Confirm no disk prompt.
  4. Install normal package updates. Reboot. Confirm no disk prompt.
  5. Record recovery passphrase location and confirm it is available offline.

15. Troubleshooting recovery prompts

Handling TPM seal failures

A TPM seal failure means the TPM refused to release the Clevis-sealed unlock key because the current PCR values do not match the policy used when the binding was created. This is expected after certain intentional changes, especially when using PCR 9 to lock kernel/initramfs/boot-file changes.

  1. Boot manually using the human LUKS passphrase.
  2. Confirm the change was expected: firmware update, Secure Boot change, kernel/initramfs change, GRUB change, or PCR-policy experiment.
  3. List current bindings and identify the stale slot.
  4. Bind a new Clevis slot to the current approved PCR state.
  5. Remove the old Clevis slot only after the new one is present and tested.
  6. For PCR 9 policies, do not rebuild initramfs again after rebinding unless you intend to repeat the approval process.
export LUKSDEV=/dev/nvme0n1p3   # adjust

sudo clevis luks list -d "$LUKSDEV"
sudo tpm2_pcrread sha256:0,1,2,4,5,7,8,9,11,14

# Example strict PCR 9 re-approval:
sudo clevis luks bind -y -d "$LUKSDEV" tpm2 \
  '{"pcr_bank":"sha256","pcr_ids":"0,1,2,4,5,7,8,9"}'

sudo clevis luks list -d "$LUKSDEV"

# After testing the new slot, remove the old stale Clevis slot:
sudo clevis luks unbind -d "$LUKSDEV" -s <old-slot>
Do not remove the human passphrase slot. TPM seal failures are recoverable only if you still have a working passphrase or another recovery method.

'tpm2' is not a valid pin!

Install the missing Clevis TPM2 plugin:

sudo apt update
sudo apt install clevis clevis-luks clevis-tpm2 clevis-initramfs tpm2-tools

No key available with this passphrase

This usually means you are testing the wrong LUKS partition or the secret is not valid for that LUKS header. Re-identify the parent crypto_LUKS partition and verify with:

lsblk -o NAME,PATH,TYPE,FSTYPE,FSVER,SIZE,MOUNTPOINTS,UUID
sudo cryptsetup luksDump "$LUKSDEV" | head -40
sudo cryptsetup open --test-passphrase "$LUKSDEV"

Clevis binding exists but boot still prompts

  • Confirm clevis-initramfs is installed.
  • Confirm the TPM2 binding exists with clevis luks list.
  • Rebuild initramfs.
  • Use PCR 7 only until the system is stable.
  • Confirm Secure Boot state has not changed.
  • Confirm the system always boots the same Debian shim path.
sudo clevis luks list -d "$LUKSDEV"
sudo update-initramfs -u -k all
sudo mokutil --sb-state
sudo efibootmgr -v

systemd enrollment exists but boot still prompts

  • Confirm tpm2-device=auto is in /etc/crypttab.
  • Confirm the LUKS header has a systemd TPM2 token.
  • Rebuild initramfs.
  • Use 7+14 or 7 before trying more aggressive PCRs.
cat /etc/crypttab
sudo cryptsetup luksDump "$LUKSDEV" | sed -n '1,240p'
sudo update-initramfs -u -k all

PCRs changing across normal boots

sudo systemd-analyze pcrs | tee ~/pcrs-boot1.txt
sudo reboot
sudo systemd-analyze pcrs | tee ~/pcrs-boot2.txt
diff -u ~/pcrs-boot1.txt ~/pcrs-boot2.txt

If PCR 8, 9, or 10 changes across normal reboots, do not bind disk unlock to those PCRs. Use PCR 7 for Clevis, or PCR 7+14 for systemd, until the platform is stable.

16. Updates and rebinding workflow

Purpose: this workflow is for strict Clevis policies that include PCR 9. It treats kernel, initramfs, GRUB-read boot files, and related early-boot changes as approval events. The goal is to make those changes fail closed until they are deliberately approved.

If your Clevis policy includes PCR 9, an intentional kernel or initramfs change should be handled as a two-boot approval process. The short form is:

update initramfs
bind current/maintenance state
reboot
rebind if necessary to the new PCR 9 state
reboot again to verify
clean up unused Clevis slots

The reason for the two-step behavior is that PCR 9 reflects the measured boot path. After update-initramfs, the next boot may produce a new PCR 9 value. If the first binding does not survive that transition, the system should still be recoverable through the human LUKS passphrase or a temporary fallback slot.

Recommended manual workflow

  1. Confirm the current device and slots. Identify the LUKS container and list existing Clevis bindings.
  2. Rebuild initramfs. This intentionally changes boot artifacts and may change PCR 9.
  3. Add a Clevis binding for the current state. This gives you a known binding before the first reboot. If using a temporary fallback, add that here too.
  4. Reboot. If TPM unlock fails, enter the human LUKS passphrase. That is expected when PCR 9 changed.
  5. After boot, test which slots work. If the strict slot fails under the new PCR state, rebind Clevis to the current approved state.
  6. Reboot again. Confirm the newly approved strict binding unlocks automatically.
  7. Clean up old or non-functioning Clevis slots. Remove stale slots only after confirming at least one strict slot works and the human passphrase remains valid.

Command sequence

export DEV=/dev/nvme0n1p3   # adjust to the crypto_LUKS partition

sudo clevis luks list -d "$DEV"
sudo cryptsetup open --test-passphrase "$DEV"

# Intentional early-boot artifact update.
sudo update-initramfs -u -k all
sudo update-grub

# Strict PCR 9 policy used by this guide.
STRICT_POLICY='{"pcr_bank":"sha256","pcr_ids":"0,1,2,4,5,7,8,9"}'

# Bind to the current state before reboot.
sudo clevis luks bind -y -d "$DEV" tpm2 "$STRICT_POLICY"

sudo clevis luks list -d "$DEV"

# Reboot into the new boot state.
sudo reboot

After reboot, if the system unlocked automatically, verify the slots. If it required the LUKS passphrase, boot normally and then rebind to the current approved PCR 9 state:

export DEV=/dev/nvme0n1p3
STRICT_POLICY='{"pcr_bank":"sha256","pcr_ids":"0,1,2,4,5,7,8,9"}'

sudo tpm2_pcrread sha256:0,1,2,4,5,7,8,9
sudo clevis luks list -d "$DEV"

for slot in $(sudo clevis luks list -d "$DEV" | awk -F: '/tpm2/ {print $1}'); do
  sudo clevis luks pass -d "$DEV" -s "$slot" >/dev/null 2>&1 \
    && echo "slot $slot works" \
    || echo "slot $slot fails"
done

# If the strict slot does not work under the new PCR state, approve/rebind.
sudo clevis luks bind -y -d "$DEV" tpm2 "$STRICT_POLICY"

sudo clevis luks list -d "$DEV"

# Do not run update-initramfs again after this rebind unless you intend to repeat approval.
sudo reboot

Useful shortcut: use the new working slot to remove the old stale slot

In modest cases, after running update-initramfs and binding a new Clevis slot, the new slot may already work under the current measured state. In that case, the new slot can be used as the authorization secret to remove the old stale slot in one operation:

sudo clevis luks pass -d "$DEV" -s <new-working-slot> | \
sudo clevis luks unbind -d "$DEV" -s <old-stale-slot>

This has two advantages: it confirms the new slot is valid, and it removes the old slot immediately. It will fail if the running initramfs or measured boot state is not compatible with the new binding, which is expected for stricter PCR 9 transitions.

Cleaning up old slots after the verification reboot

After the second reboot verifies that the new strict binding works, remove old non-functioning Clevis slots. If one slot works and another is stale, the working slot can authorize removal of the stale slot.

export DEV=/dev/nvme0n1p3

sudo clevis luks list -d "$DEV"

# Example: slot 2 works and slot 1 is stale.
sudo clevis luks pass -d "$DEV" -s 2 | \
sudo clevis luks unbind -d "$DEV" -s 1

sudo clevis luks list -d "$DEV"
sudo cryptsetup luksDump "$DEV" | sed -n '/Keyslots:/,/Tokens:/p'
Do not remove the human LUKS passphrase slot. Before cleanup, always verify that a human-enterable passphrase still works:
sudo cryptsetup open --test-passphrase "$DEV"

When to use a temporary fallback slot

During experimentation, it may be useful to add a temporary less-restrictive PCR 7 slot before rebooting. This is a convenience bypass, not a strict PCR 9 policy. Remove it after the strict binding is verified.

# Temporary fallback before planned reboot.
sudo clevis luks bind -y -d "$DEV" tpm2 \
  '{"pcr_bank":"sha256","pcr_ids":"7"}'

# Later, after the strict slot has been verified, remove the temporary fallback.
sudo clevis luks unbind -d "$DEV" -s <temporary-fallback-slot>

Operational rules

  • If PCR 9 is in the policy, kernel/initramfs/boot-file changes are approval events.
  • Run update-initramfs before approving the new PCR 9 state, not after.
  • After rebinding to the new PCR 9 state, do not run update-initramfs again unless you intend to repeat the approval process.
  • Keep one known-good human passphrase slot at all times.
  • Clean up stale Clevis slots only after verifying the new strict binding works.

17. Potential future work: PCR 9 maintenance automation

Design goal: automate preparation and cleanup around strict PCR 9 maintenance without automatically approving a new boot state. If the system could silently approve a new kernel/initramfs state by itself, PCR 9 would no longer provide the explicit approval gate desired by this guide.

When PCR 9 is included in the Clevis policy, kernel, initramfs, microcode, GRUB, or early-boot file changes should intentionally cause TPM unseal failure until the new state is approved. A useful future enhancement is a two-phase workflow.

  1. Pre-reboot: add a temporary less-restrictive Clevis TPM2 slot, such as PCR 7, so the planned maintenance reboot can complete without entering the LUKS passphrase.
  2. Post-reboot: after booting into the new kernel/initramfs state, deliberately approve the new state, create a new strict PCR 9 binding, and remove the temporary fallback slot.

Security model

Normal strict state:
  Clevis slot: PCR 0,1,2,4,5,7,8,9
  Human LUKS passphrase retained for recovery

During planned maintenance:
  Temporary fallback slot: PCR 7
  Strict old slot: likely stale after kernel/initramfs update

After post-reboot approval:
  New strict slot: PCR 0,1,2,4,5,7,8,9
  Temporary fallback slot removed
  Human LUKS passphrase retained

Two-phase maintenance helper: kernel-update-tpm.sh

This concept is now implemented as kernel-update-tpm.sh. The script runs a three-phase workflow across two reboots:

  • Phase 1 (run before the update): creates the temporary loose PCR-7 slot, removes the now-stale strict slot, runs the kernel update, and reboots.
  • Phase 2 (first post-update boot, automatic): confirms the new kernel is running, re-enrolls the strict slot against the new PCR values, and reboots to verify.
  • Phase 3 (second post-update boot, automatic): verifies the strict slot unseals after a full TPM measurement cycle, removes the temporary slot, and cleans up.

A sentinel file at /var/lib/kernel-update-tpm/state and a systemd oneshot unit carry state across reboots. The unit disables itself as its first action each phase — a failure leaves the system booted and waiting rather than looping. Download the script and see secureboot-manual.html for the full installation and operations guide.

APT hook concept

APT can call a hook before package installation. The hook can scan the transaction for boot-sensitive packages and run the pre-reboot phase automatically. This is useful for kernel, initramfs, GRUB, shim, firmware, microcode, Clevis, cryptsetup, or TPM tooling updates.

Important: the APT hook should prepare the temporary fallback. It should not approve the new strict PCR 9 state. Approval happens only after reboot, when the TPM PCRs reflect the new boot artifacts.

APT hook script

Download kernel-update-tpm-apt-hook. The hook reads the APT transaction's package list from stdin (Version 3 protocol), matches it against a pattern covering kernel images, initramfs-tools, GRUB, shim, firmware, Clevis, cryptsetup, and TPM packages, and calls kernel-update-tpm.sh --hook-mode if any match. Exiting non-zero causes APT to abort the transaction before any packages are installed.

APT configuration snippet

Download 90-kernel-update-tpm.conf and install it to /etc/apt/apt.conf.d/. It registers the hook and requests Version 3 protocol:

DPkg::Pre-Install-Pkgs {
  "/usr/local/sbin/kernel-update-tpm-apt-hook";
};

DPkg::Tools::Options::/usr/local/sbin/kernel-update-tpm-apt-hook::Version "3";

Operational considerations

  • The temporary PCR 7 slot is intentionally weaker than the strict policy. It is only present during a two-reboot maintenance window and is removed automatically by Phase 3.
  • The LUKS device path and PCR policy are auto-detected from the running system. If auto-detection fails, pass them explicitly: sudo kernel-update-tpm.sh /dev/nvme0n1p3 0,1,2,4,5,7,8,9.
  • Test on a non-critical install first, or ensure a human passphrase slot is available for recovery before running for the first time.
  • If moving to UKI, signed PCR policies, or pcrlock-style designs, revisit systemd-cryptenroll and systemd-measure as alternatives to Clevis.

18. Sources

19. Kernel update automation toolset

Section 17 of this guide described the PCR 9 maintenance problem and sketched a two-phase automation concept. That concept has since been fully implemented as a production-ready toolset. This section summarises what was built and where to find it.

What the future-work section anticipated — and what changed

The Section 17 design proposed a pre-reboot / post-reboot helper with a manual approval gate. The delivered toolset extends that design in three ways. First, the approval gate is automated: Phase 2 runs via a systemd oneshot unit after the first reboot and binds the strict policy to the new measured state without operator input, matching the BitLocker-style model where the TPM re-seals automatically after a trusted update. Second, LUKS device and PCR policy are auto-detected from the running system rather than hardcoded. Third, an APT hook fires before package installation, so the slot transition happens before any postinst script calls update-initramfs or update-grub.

Boot-loop safety

The Section 17 caveats noted that automated approval must not create a retry loop. The toolset addresses this with three guards: the systemd unit disables itself as its first action in each phase, so any failure leaves the system booted and waiting rather than rebooting again; Phase 2 aborts if the kernel version is unchanged (update did not take effect); and both Phase 2 and 3 abort if the sentinel is older than 30 minutes (unexpected delay between phases). Reboot is always the last action of a successful phase, never a response to failure.

Deferred reboot warning — why it can appear on an unrelated command

When the APT hook prepares the slot transition, it does not reboot the machine — an unattended reboot must be a human decision. Instead, Phase 1 writes a one-shot APT Post-Invoke drop-in at /etc/apt/apt.conf.d/99-kernel-update-tpm-reboot that prints a REBOOT REQUIRED banner and flags the standard Debian /run/reboot-required mechanism (so the login MOTD and needrestart also surface it).

Expected quirk: that banner fires on the next apt command of any kind — not the transaction that created it. APT parses /etc/apt/apt.conf.d/ exactly once, at process startup, and the hook runs later in the same run, so the drop-in it writes is not part of the already-parsed config. It first takes effect on the following apt invocation. The classic symptom is an unrelated command such as sudo apt remove keepass2 suddenly printing a kernel reboot warning. This is inherent to how APT hooks work — the toolset cannot make it fire “now,” so instead it makes a late firing harmless and truthful.

Two safeguards keep the banner honest so it can never become a stale false alarm:

  • Self-gating (the banner checks reality before printing). The drop-in deletes itself first (true one-shot), then runs test -f /var/lib/kernel-update-tpm/state || exit 0. If the sentinel is gone, the three-phase transition already completed and there is nothing to warn about, so it exits silently. The banner appears only while a transition is genuinely pending.
  • Cleanup at completion. Phase 3 and the restore-from-temp path delete the drop-in outright (alongside removing the sentinel and the systemd unit), so a finished run normally leaves nothing behind to fire at all. The self-gate is the belt-and-suspenders backstop for the window before that cleanup runs.

Practical takeaway for students: if you see the REBOOT REQUIRED — TPM re-enrollment is NOT finished banner attached to a package command that had nothing to do with the kernel, it means an earlier boot-sensitive update staged a transition you have not rebooted to complete. Confirm the real state rather than trusting the message's timing: cat /var/lib/kernel-update-tpm/state (present = transition still pending) and sudo clevis luks list -d <LUKS-device> (a full pcr_ids":"0,1,2,4,5,7,8,9" means the strict slot is already back in place).

Toolset files

FileRole
kernel-update-tpm.sh Main three-phase script. Handles preflight, slot transition, apt upgrade (manual mode), initramfs rebuild, systemd unit install, and re-enrollment across two reboots.
kernel-update-tpm-apt-hook APT Pre-Install-Pkgs hook. Detects boot-sensitive packages (kernel, initramfs, grub, shim, microcode, clevis, tpm2, systemd) and triggers Phase 1 before installation begins.
90-kernel-update-tpm.conf APT configuration registering the hook with DPkg::Pre-Install-Pkgs at Version 3.
update-clevis-pcrs.sh Lower-level helper. Re-enrolls a single LUKS slot against current PCR values. Use only outside of an active kernel-update-tpm.sh sequence.

Companion documents

DocumentContents
secureboot-guide.html Conceptual guide covering the full PCR policy design, Clevis slot management, the three-phase kernel update workflow, and the update-clevis-pcrs.sh lower-level helper. Read this to understand the mechanism before relying on the automation.
secureboot-manual.html Installation, testing, and operations manual for the toolset. Covers installation with correct ownership and permissions, five verification checks before first use, annotated expected output for both manual and hook-triggered runs, sentinel file anatomy, and six specific recovery procedures keyed to exact failure messages.

Relationship to this lab

The toolset does not replace the concepts in this lab — it implements them. Sections 12, 15, 16, and 17 of this guide provide the foundation needed to understand what the automation is doing and to recover from any failure it cannot handle automatically. A practitioner operating the automated toolset should be comfortable with the manual procedure before depending on the automation in a production context.

Test on a non-critical install first. The Section 17 caveat remains applicable: run the toolset on a disposable or test system before trusting it on a primary laptop. Ensure at least one human LUKS passphrase is enrolled and accessible before any automated run begins.

Downloadable Scripts