1. Executive summary
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.
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.
| Area | Setting | Recommended state | Reason |
|---|---|---|---|
| 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. |
3. Debian installation approach
Recommended installer path
- Boot the Debian installer in UEFI mode.
- If the installer has trouble with Secure Boot, temporarily disable Secure Boot for installation only.
- Use guided encrypted LVM, or manually create a LUKS2 encrypted container with LVM inside.
- Create an EFI System Partition mounted at
/boot/efi. - Use a separate unencrypted
/bootunless you are intentionally building a more advanced encrypted-boot layout. - Create a strong LUKS passphrase. Keep it as the recovery path.
- Complete installation and boot into Debian.
Typical layout
| Layer | Example | Role |
|---|---|---|
| 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
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
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
mokutilversion, 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
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. |
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.
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
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:
- Start with Clevis + TPM2 + PCR 7. This is the most practical baseline for avoiding a disk prompt on every normal boot.
- After multiple successful cold boots, warm reboots, and ordinary package updates, consider Clevis + TPM2 + PCR 4,7 if you want stronger boot-path binding.
- 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
|
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 target | Why |
|---|---|
/dev/nvme0n1p1 | EFI System Partition, usually /boot/efi. |
/dev/nvme0n1p2 | Usually unencrypted /boot. |
/dev/mapper/vg-root | The root filesystem inside the already-unlocked container. |
/dev/mapper/vg-swap | Swap inside the already-unlocked container. |
/ | A mount point, not a block device with a LUKS header. |
9. Primary method: Debian LUKS2 + Clevis TPM2
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
'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.
/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"
{"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
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. |
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 |
|---|---|---|---|
0 | Firmware/BIOS code | Avoid initially | BIOS updates can break unlock. |
1 | Firmware/platform configuration | Only after testing | BIOS setting changes can break unlock. |
2 | Option ROMs / device firmware | Avoid on laptops | Docks/devices/firmware behavior can be noisy. |
4 | Bootloader / boot manager path | Good moderate-aggressive addition | Bootloader updates or path changes can break unlock. |
5 | Boot config / partition state | Good only after layout is stable | Partition or boot config changes can break unlock. |
7 | Secure Boot policy and key databases | Best Clevis baseline | Secure Boot state/key changes break unlock. |
8 | GRUB commands / kernel command line on many GRUB systems | Avoid unless measured and stable | Kernel command-line or GRUB state changes can break unlock. |
9 | Initrd/initramfs measurements | Avoid for low-maintenance systems | Kernel/initramfs updates can break unlock. |
10 | IMA runtime file measurements | Avoid for disk unlock | Can vary during normal boot/runtime. |
11 | systemd-stub / UKI boot phases | Useful for UKI designs | Not the default GRUB baseline. |
14 | shim/MOK state | Useful with systemd enrollment | MOK enroll/remove changes break unlock. |
Recommended policies
| Goal | Clevis | systemd-cryptenroll | Notes |
|---|---|---|---|
| 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.
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
- Cold boot once. Confirm no disk prompt.
- Cold boot a second time. Confirm no disk prompt.
- Warm reboot. Confirm no disk prompt.
- Install normal package updates. Reboot. Confirm no disk prompt.
- 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.
- Boot manually using the human LUKS passphrase.
- Confirm the change was expected: firmware update, Secure Boot change, kernel/initramfs change, GRUB change, or PCR-policy experiment.
- List current bindings and identify the stale slot.
- Bind a new Clevis slot to the current approved PCR state.
- Remove the old Clevis slot only after the new one is present and tested.
- 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>
'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-initramfsis 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=autois in/etc/crypttab. - Confirm the LUKS header has a systemd TPM2 token.
- Rebuild initramfs.
- Use
7+14or7before 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
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
- Confirm the current device and slots. Identify the LUKS container and list existing Clevis bindings.
- Rebuild initramfs. This intentionally changes boot artifacts and may change PCR 9.
- 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.
- Reboot. If TPM unlock fails, enter the human LUKS passphrase. That is expected when PCR 9 changed.
- After boot, test which slots work. If the strict slot fails under the new PCR state, rebind Clevis to the current approved state.
- Reboot again. Confirm the newly approved strict binding unlocks automatically.
- 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'
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-initramfsbefore approving the new PCR 9 state, not after. - After rebinding to the new PCR 9 state, do not run
update-initramfsagain 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
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.
- 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.
- 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.
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-cryptenrollandsystemd-measureas 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).
/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
| File | Role |
|---|---|
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
| Document | Contents |
|---|---|
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.