CLEVIS-ENCRYPT-TPM(1)

πŸ“¦ version 24, in fedora 44

✨ clevis-encrypt-tpm1 ✨

okay bestie, lemme put you on real quick πŸ’– this is the manual page for clevis-encrypt-tpm1, and it's all about sealing your secrets to a TPM 1.2 chip. strap in, it's giving very secure, very demure πŸŽ€


πŸ¦‹ NAME πŸ¦‹

clevis-encrypt-tpm1 β€” encrypts using a TPM 1.2 chip binding policy 🌸


πŸŽ€ SYNOPSIS πŸŽ€

clevis encrypt tpm1 CONFIG < PT > JWE

πŸ‘‘ OVERVIEW πŸ‘‘

so basically, the clevis encrypt tpm1 command encrypts stuff using a Trusted Platform Module 1.2 (TPM 1.2) chip. it might work with TPM 1.1 too, but ngl nobody tested that, so who knows πŸ’€. also important: the tpm1 pin does not support TPM 2.0 and higher β€” for those you gotta use the tpm2 pin instead, periodt.

the only argument is the JSON configuration object. that's it. that's the whole vibe check. ✨

when you're using the tpm1 pin, here's the tea: we create a brand new, cryptographically-strong, random key πŸ”₯. that key gets encrypted using the TPM 1.2 chip. then later, at decryption time, that key gets decrypted again using the TPM 1.2 chip. cute lil round trip, iconic behavior honestly.

$ clevis encrypt tpm1 '{}' < PT > JWE

to decrypt the data, you literally just hand it the ciphertext (JWE):

$ clevis decrypt < JWE > PT

and no cap, like the other pins, no configuration is used for decryption β€” this is because clevis stores the sealed encryption key inside the JWE itself, so clevis can just fetch that key outta there and unseal it using the TPM 1.2 chip. the JWE is carrying its own little key around, main character energy πŸ”‘πŸ’…


🌈 sealing to PCR state 🌈

the pin also supports sealing data to a Platform Configuration Registers (PCR) state πŸ‘€. what that means: the data can only be unsealed if the PCRs hash values match the values that were used when sealing. no match, no unseal, sorry babe πŸ™ˆ.

for example, to seal the data to the PCR with indexes 0, 4 and 7, you'd do:

$ clevis encrypt tpm1 '{"pcr_ids":"0,4,7"}' < PT > JWE

okay so here's why this is so cool: the BIOS, boot loader, Grub and the Linux kernel incrementally add hashes of the various system states to those registers πŸ“. the added hashes represent the state of different components, like:

  • the BIOS (PCR 0 and PCR 1)
  • option ROMs (PCR 2 and PCR 3)
  • the boot loader (PCR 4)
  • EFI Secure Boot state (PCR 0 and PCR 7)
  • etc. ✨

and the requirement for exact matching of values is what guarantees the TPM 1.2 chip only unseals the data when the system state measured by the selected registers has not changed. it's like a fingerprint for your boot state, fr fr 🫢

although the usage of Platform Configuration Registers is standardized, the BIOS implementation might differ slightly β€” so always test how the particular register value changes when the system is updated, most notably when the Grub boot loaded or the Linux kernel is updated ⚠️. you can check the PCR values with:

$ cat /sys/class/tpm/tpm0/pcrs

πŸ’œ CONFIG πŸ’œ

this command uses the following configuration properties:

  • pcr_ids (string) β€” comma separated list of PCR used for policy. if it's not present, no policy is used 🌸

πŸ’… LIMITATIONS πŸ’…

okay so this part matters, bestie, listen up πŸ‘‚

to avoid prompting for a password during unlocking, the encryption and decryption processes require that the well-known Storage Root Key (SRK) be configured when taking ownership of the TPM 1.2 chip. so you must have either run the tpm_takeownership command

$ tpm_takeownership --srk-well-known

during setup, or executed the tpm_changeownerauth command

$ tpm_changeownerauth --srk --set-well-known

to configure it. and btw β€” a well-known key is not the same as an empty key. do not confuse them, i'm begging πŸ™

Important ⚠️ if you have changed the SRK to a well-known key, remember to run the update-initramfs command (on Debian-like systems) text $ update-initramfs -u or the dracut command (on Fedora-like systems) text $ dracut -f afterward, to recreate the initramfs image, because /var/lib/tpm is included in the image 🧚✨. this applies to initramfs-tools and Dracut in host-only mode. in Dracut's default mode, /var/lib/tpm is already configured to allow access to the TPM 1.2 chip using a well-known SRK, so you're already slaying over there πŸ’–

πŸ” UNLOCKING WITH A SEPARATELY-ENCRYPTED /var VOLUME WITH TPM1 PIN πŸ”

okay so real talk bestie, this part is the ✨advanced✨ level and it's giving "read it twice" energy 😭

Because TPM1 PIN relies on the tcsd daemon from the Trousers project to access the TPM 1.2 chip, that daemon has to start early in the boot process so your root filesystem can be unlocked automatically. And here's the kicker: the /var/lib/tpm directory holds runtime data for tcsd, so it has to already be available before the daemon starts. 🫠

Clevis bakes a minimal copy of the /var files it needs into the initramfs image it prepares, so if everything's configured right, tcsd should be able to start during the initrd bootup phase. 🌸 But once you switch over to the real root (/) filesystem, the System Manager bootup phase kicks in and /var gets mounted from the actual target. And at that point, Clevis literally cannot unlock it β€” because tcsd would need /var to unlock /var. πŸ˜­πŸ’€ So /var has to already be unlocked by then. The instructions for initramfs-tools and Dracut are below, keep scrolling.

Now, if your /var volume is part of the main LVM volume group (same one as the root / filesystem) and it's protected by the same LUKS volume β€” no special config, you're living your best life, nothing to do. πŸ’… But if /var is encrypted separately (i.e., it uses a different LUKS volume, regardless of whether it has the same password), follow the instructions below to get that automatic Clevis unlocking going. πŸ‘‘


πŸŽ€ initramfs-tools Initrd Bootup

initramfs-tools unlocks the root and swap filesystems by copying the corresponding option lines from /etc/crypttab into the initramfs. So to make sure your /var volume options also get included, add the initramfs option on Debian-like systems to the relevant line in /etc/crypttab, like this:

/etc/crypttab.

...
luks-aa0ce19c-cde9-44a2-adbd-4afb1845a959 UUID=aa0ce19c-cde9-44a2-adbd-4afb1845a959 none discard,initramfs
...

That line corresponds to the crypto_LUKS volume that your /var volume uses, and you can see that with the lsblk -fp command:

LVM on LUKS.

...
└─/dev/vda3                                               crypto_LUKS 2              aa0ce19c-cde9-44a2-adbd-4afb1845a959
  └─/dev/mapper/luks-aa0ce19c-cde9-44a2-adbd-4afb1845a959 LVM2_member LVM2 001       lgk4ap-Fo39-PemI-eqKn-fxW2-e3Zt-CPGIv2
    └─/dev/mapper/separate-var                            xfs                        767b750e-bba7-4ea7-b2b8-b1e6a2e22e43    753,3M    22% /var

The example above uses an LVM-on-LUKS encryption scheme, but the same thing applies to LUKS-on-LVM β€” just check the crypto_LUKS volume UUID. πŸ¦‹

LUKS on LVM.

...
└─/dev/vda3                                                 LVM2_member LVM2 001       lgk4ap-Fo39-PemI-eqKn-fxW2-e3Zt-CPGIv2
  └─/dev/mapper/separate-var                                crypto_LUKS 2              aa0ce19c-cde9-44a2-adbd-4afb1845a959
    └─/dev/mapper/luks-aa0ce19c-cde9-44a2-adbd-4afb1845a959 xfs                        767b750e-bba7-4ea7-b2b8-b1e6a2e22e43    781,5M    19% /var
Important πŸ’– After modifying /etc/crypttab, you must run update-initramfs -u (on Debian-like systems).

πŸ¦‹ Dracut Initrd Bootup

Dracut automatically unlocks the root and swap filesystems. Your operating system installer makes sure the kernel command line (in /etc/default/grub) contains the necessary parameters for Dracut and Systemd. And Dracut considers both the kernel command line and the lines copied from /etc/crypttab when unlocking. πŸ‘€

By default, the root and swap lines from /etc/crypttab get copied into the initramfs. To make sure the /var volume is also unlocked, you must make sure its options are included and referenced by the kernel command line (as described below).

Caution ⚠️ Changing the following options can render the system unbootable, potentially requiring a rescue DVD and expert knowledge to recover. Make a full backup before proceeding!

For recovery, you may find these commands helpful: 🫢

  • cryptsetup open /dev/<device> <mapped-device>
  • mount /dev/mapper/<mapped-device> /<target>
  • lvm vgscan
  • lvm lvdisplay -o lv_full_name,lv_dm_path

To ensure that the /var options are included, add either the x-initrd.attach option to the corresponding line in /etc/crypttab (to unlock the /var volume) or the x-initrd.mount option to the corresponding line in /etc/fstab (to unlock and mount the /var volume). Using both is equivalent to x-initrd.mount. ✨

/etc/crypttab.

...
luks-aa0ce19c-cde9-44a2-adbd-4afb1845a959 UUID=aa0ce19c-cde9-44a2-adbd-4afb1845a959 none discard,x-initrd.attach
...

/etc/fstab.

...
UUID=767b750e-bba7-4ea7-b2b8-b1e6a2e22e43  /var  xfs  defaults,x-systemd.device-timeout=0,x-initrd.mount 0 0
...

Refer to the initramfs-tools section for instructions on finding the correct /etc/crypttab line with lsblk -fp. The /etc/fstab entry is matched by the UUID of the filesystem (see the line with /var in the lsblk -fp output). πŸ’…

Important πŸŽ€ After changing /etc/crypttab and/or /etc/fstab, run dracut -f.
Note 🌸 If you use x-initrd.mount, the volume is mounted during the initrd bootup phase. However, this is not strictly necessary. Systemd's startup order ensures that /var is mounted before tcsd starts in the System Manager bootup phase, so using x-initrd.attach alone is sufficient.

Next, make sure the volumes are found and unlocked. Two kernel command line parameters in /etc/default/grub affect this:

  • rd.luks.uuid – Either remove all values or add the UUID of the crypto_LUKS volume (optionally prefixed by luks-). If this option is present (it can appear multiple times), only the specified volumes are initialized from /etc/crypttab. If it is missing, all lines from /etc/crypttab are considered.
  • rd.lvm.lv – Either remove all values or add the full LVM volume name for /var. If this option is present (it can appear multiple times), only the listed logical volumes are initialized. If it is missing, Dracut automatically detects LVM volumes during boot.
Note πŸ’œ The rd.lvm.lv option matters only in the LUKS-on-LVM case, because the crypto_LUKS volume is accessible only after the LVM logical volume is activated. If rd.lvm.lv is missing, Dracut will detect LVM volumes automatically. If it is present, make sure to include the /var full volume name.

For more information, see manual pages of dracut.cmdline and systemd-cryptsetup-generator. πŸ“–

Note πŸ“ Dracut internally uses the same Systemd options, so the same logic applies even if Systemd is not present in the Dracut initrd environment.

To find the correct rd.lvm.lv value, run:

$ lvs -o lv_full_name,lv_dm_path

This shows the logical volume's full name and Device Mapper path, which also appears in the lsblk -fp output. For example, if it shows separate/var (see example below), the rd.lvm.lv value would be rd.lvm.lv=separate/var: ⭐

LV                   DMPath
...
separate/var         /dev/mapper/separate-var
...

Example of a kernel command line in /etc/default/grub with all options present:

/etc/default/grub.

GRUB_CMDLINE_LINUX="rd.lvm.lv=fedora/root rd.luks.uuid=luks-21a9c1b8-c202-4985-809a-aba2d6fdab01 rd.lvm.lv=separate/var rd.luks.uuid=luks-aa0ce19c-cde9-44a2-adbd-4afb1845a959 quiet"

Example of a kernel command line in /etc/default/grub when relying on the configuration copied from /etc/crypttab and Dracut's automatic LVM detection:

/etc/default/grub.

GRUB_CMDLINE_LINUX="quiet"
Important πŸ”₯ After changing the kernel command line, update the Grub configuration with update-grub2 (on Debian-like systems) or grub2-mkconfig -o /etc/grub2.cfg (on Fedora-like systems).

πŸ•΅οΈ THREAT MODEL πŸ•΅οΈ

The Clevis security model relies in the fact that an attacker will not be able to access both the encrypted data and the decryption key. That's the whole vibe check. πŸ’…

For most Clevis pins, the decryption key is not locally stored, so the decryption policy is only satisfied if the decryption key can be remotely accessed. It could for example be stored in a remote server or in a hardware authentication device that has to be plugged into the machine. πŸ”Œ

The tpm1 pin is different in this regard, since a key is wrapped by a TPM 1.2 chip that is always present in the machine. This does not mean that there are not use cases for this pin, but it is important to understand the fact that an attacker that has access to both the encrypted data and the local TPM 1.2 chip will be able to decrypt the data. 😭 No cap β€” that's the tradeoff, know it going in.

The use of specific Platform Configuration Registers along with Secure Boot limits the attack surface because an attacker must reproduce the exact register values that are present during sealing before the TPM 1.2 chip will allow the encryption key to be unsealed. Careful selection of registers ensures that any change to the boot process will prevent the TPM 1.2 from unsealing the encryption key. πŸ‘‘ Iconic behaviour honestly.


πŸ‘€ SEE ALSO πŸ‘€


aaand that's the whole page bestie, you made it to the end, I'm so proud of you 😭🫢 this was CLEVIS-ENCRYPT-TPM(1) β€” the source is the Clevis manual, dated 10/16/2026. go forth and encrypt responsibly, mwah πŸ’–βœ¨πŸŽ€

πŸ“¦ where this page comes from