Enroll TPM2, FIDO2, and Recovery Keys on LUKS2 with systemd-cryptenroll

Use systemd-cryptenroll on LUKS2 volumes to add TPM2, FIDO2, PKCS#11, and recovery-key unlock methods, store token metadata in the LUKS2 JSON area, and wire boot unlock through crypttab.

Published

Updated

Read time 10 min read

Reviewed byDeepak Prasad

Enroll TPM2 and FIDO2 tokens on a LUKS2 volume with systemd-cryptenroll

systemd-cryptenroll adds hardware and recovery unlock paths to an existing LUKS2 volume:

  • Writes credentials to LUKS2 key slots
  • Records how to use them in the header token area (JSON metadata)
  • Lets systemd-cryptsetup in the initramfs consume those tokens at boot

Unlock options covered:

  • TPM2 seal
  • FIDO2 security key (hmac-secret)
  • PKCS#11 smartcard
  • Printed recovery key

Examples use /dev/sdc1 from encrypt a partition with LUKS. Recovery-key enrollment output comes from a disposable loop lab on this host; TPM2 and FIDO2 commands match physical hardware when devices are present.

Related guides:

Tested on: Ubuntu 26.04 LTS (Resolute Raccoon); kernel 7.0.0-27-generic; systemd 259.5; cryptsetup 2.8.4.

IMPORTANT
This article covers enrolling unlock methods on an existing LUKS2 data volume (for example /dev/sdc1). It does not replace your distribution installer for full-disk encryption at OS install time, and it does not configure Clevis/Tang network unlock—that is a separate workflow. Enrollment changes the LUKS header; take a header backup before wiping slots or rotating tokens.

Quick reference

Goal Command
List candidate block devices systemd-cryptenroll --list-devices
Enroll recovery key sudo systemd-cryptenroll --recovery-key /dev/sdc1
Enroll TPM2 with Secure Boot PCR sudo systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=7 /dev/sdc1
Enroll TPM2 without PCR binding sudo systemd-cryptenroll --tpm2-device=auto /dev/sdc1
Enroll FIDO2 HMAC token sudo systemd-cryptenroll --fido2-device=auto /dev/sdc1
List PKCS#11 tokens systemd-cryptenroll --pkcs11-token-uri=list
Inspect LUKS2 tokens sudo cryptsetup luksDump /dev/sdc1
Export one token as JSON sudo cryptsetup token export --token-id 0 /dev/sdc1
Wipe a key slot sudo systemd-cryptenroll --wipe-slot=2 /dev/sdc1

Replace /dev/sdc1 with your LUKS2 partition.

  • You need an existing passphrase or --unlock-key-file to authorize enrollment

Some flags vary by systemd version. This guide was tested against Ubuntu 26.04 (systemd 259). On older Ubuntu or Debian releases, check systemd-cryptenroll --help before using newer options:

Option Added in systemd
--unlock-key-file 252
--unlock-fido2-device 253
--unlock-tpm2-device 256
--fido2-parameters-in-header 257

What you need

  • LUKS2 volume (not LUKS1) — confirm with cryptsetup luksDump
  • Root or sudo
  • An existing unlock secret (passphrase or key file) for every enrollment except --wipe-slot
  • For TPM2: TPM 2.0 chip or firmware vTPM (/dev/tpmrm0 or /dev/tpm0)
  • For FIDO2: security key with hmac-secret support
  • For boot unlock: matching /etc/crypttab option and regenerated initramfs (update-initramfs -u -k all on Ubuntu/Debian when multiple kernels are installed)
  • LUKS header backup before wiping slots

On Fedora and other distributions, install systemd and cryptsetup locally—the enrollment commands are the same when versions are new enough.


LUKS2 tokens and the JSON metadata area

LUKS2 stores two related concepts in the header:

Area What it holds
Key slots Passphrases, recovery keys, or key material wrapped by hardware policies
Tokens (JSON) Metadata telling cryptsetup / systemd-cryptsetup how to use a slot (TPM2 PCR policy, FIDO2 credential, Clevis pin, systemd-recovery, and so on)

cryptsetup luksDump prints a human summary under Tokens:. Export the full JSON for one token:

bash
sudo cryptsetup token export --token-id 0 /dev/sdc1

After recovery-key enrollment on a lab loop volume, token 0 looked like:

text
{"type":"systemd-recovery","keyslots":["1"]}

Confirm the volume is LUKS2 before enrolling:

bash
sudo cryptsetup luksDump /dev/sdc1 | head -5

Sample output:

text
LUKS header information
Version:       	2
Epoch:         	3
Metadata area: 	16384 [bytes]
Keyslots area: 	16744448 [bytes]

LUKS1 volumes have no Tokens: section — systemd-cryptenroll refuses them.


Hardware and environment requirements

Method Needs Typical use
Recovery key CPU only Offline backup when a token is lost
TPM2 TPM 2.0 chip or firmware vTPM Laptop or server that should unlock when PCR state matches
FIDO2 Token with hmac-secret (most YubiKeys, many FIDO2 keys) USB/NFC key you touch at boot
PKCS#11 Smartcard or token with RSA/EC key Enterprise smartcard login

On this lab VM, list TPM2 candidates:

bash
systemd-cryptenroll --tpm2-device=list

Sample output:

text
No suitable TPM2 devices found.

List FIDO2 devices:

bash
systemd-cryptenroll --fido2-device=list

Sample output:

text
No FIDO2 devices found.

List PKCS#11 tokens:

bash
systemd-cryptenroll --pkcs11-token-uri=list

Sample output:

text
No suitable PKCS#11 tokens found.

Check for TPM device nodes:

bash
ls /dev/tpm* 2>/dev/null || echo "No /dev/tpm* nodes"

Sample output:

text
No /dev/tpm* nodes
IMPORTANT
TPM2 and FIDO2 steps below match official systemd-cryptenroll(1) behavior on physical hardware. You cannot complete TPM2 or FIDO2 enrollment in a plain VM unless the hypervisor exposes a virtual TPM and you attach a FIDO2 key to the guest.

Install and list devices

systemd-cryptenroll ships with systemd on Ubuntu 26.04 (package systemd).

List block devices that look like LUKS2 candidates:

bash
systemd-cryptenroll --list-devices

Sample output (trimmed):

text
/dev/sdc1
/dev/disk/by-uuid/e2e306a9-d94e-45d9-b209-4c9975fcede5
...

/dev/sdc1 is the lab secondary disk. The list also includes symlinks under /dev/disk/by-*.

If you omit the device argument and are not wiping slots, the tool may default to the block device backing /var (often the root filesystem) — always pass the LUKS partition explicitly.


Enroll a recovery key

Recovery keys are long, random, hyphen-separated words:

  • Occupy a normal LUKS key slot
  • Register a systemd-recovery token so administrators know which slot is the recovery path

You must prove an existing key:

  • Interactive (preferred): run without --unlock-key-file and type the passphrase when prompted
  • Automation: --unlock-key-file only when the unlock secret is already stored as a key file
  • Do not type real passphrases into shell commands — history, scrollback, and /tmp handling can leak them

Enroll interactively on your LUKS partition:

bash
sudo systemd-cryptenroll --recovery-key /dev/sdc1

Or authorize with an existing key file:

bash
chmod 600 /path/to/existing-unlock.key
sudo systemd-cryptenroll --recovery-key --unlock-key-file=/path/to/existing-unlock.key /dev/sdc1

On a disposable loop LUKS2 volume (same command shape as /dev/sdc1), enrollment printed:

text
A secret recovery key has been generated for this volume:

    cnfnjjdb-dgtrvjbu-fivfvvkb-lddibdcr-irijjtkb-ufrkvbdd-ckcjkfhe-uillgvvt

Please save this secret recovery key at a secure location. It may be used to
regain access to the volume if the other configured access credentials have
been lost or forgotten. The recovery key may be entered in place of a password
whenever authentication is requested.
New recovery key enrolled as key slot 1.

Confirm the new slot and systemd-recovery token:

bash
sudo cryptsetup luksDump /dev/sdc1 | sed -n '/Keyslots:/,/Digests:/p'

Sample output after recovery enrollment:

text
Keyslots:
  0: luks2
	...
  1: luks2
	...
	PBKDF:      pbkdf2
	...
Tokens:
  0: systemd-recovery
	Keyslot:    1

Test the recovery key without opening the mapper (replace with your generated key):

bash
printf '%s' 'cnfnjjdb-dgtrvjbu-fivfvvkb-lddibdcr-irijjtkb-ufrkvbdd-ckcjkfhe-uillgvvt' | \
  sudo cryptsetup open --test-passphrase /dev/sdc1

Silent exit code 0 means the recovery key matches the enrolled slot.

WARNING
Store recovery keys on paper or in a sealed envelope offline. Anyone with the recovery key can unlock the volume. Pair recovery keys with TPM2 or FIDO2 enrollment so day-to-day unlock does not depend on the recovery secret.

Enroll TPM2

When a TPM2 device is present, list it first:

bash
systemd-cryptenroll --tpm2-device=list

Sample output on hardware with a TPM:

text
/path/to/tpm2 (driver: …)

Enroll with automatic device selection. To bind unlock to boot state, pass PCR values — PCR 7 is a common starting point because it reflects Secure Boot policy:

bash
sudo systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=7 /dev/sdc1

TPM2 enrollment without PCR binding:

  • sudo systemd-cryptenroll --tpm2-device=auto /dev/sdc1 — omit --tpm2-pcrs
  • TPM can unlock whenever the TPM seed matches
  • Weaker assurance about firmware, kernel, or bootloader integrity

PCR binding cautions:

  • PCR 7 — common starting point (Secure Boot policy)
  • PCR 11 — mainly useful with UKI/systemd-stub boot paths
  • Avoid binding to many firmware PCRs (0, 2, …) unless you have tested kernel, firmware, Secure Boot, and bootloader updates
Option Purpose
--tpm2-pcrs=7 Bind to Secure Boot policy (common starting point)
--tpm2-pcrs=7+11 Add PCR 11 for systemd-stub/UKI-style boot flows
--tpm2-with-pin=BOOL Require TPM PIN in addition to PCR match
--tpm2-public-key= Bind to signed PCR policies from your vendor

After enrollment:

  • luksDump shows a systemd-tpm2 token linked to the new key slot
  • Wire crypttab with tpm2-device=auto
  • Test systemd-cryptsetup attach before reboot
  • Regenerate initramfs on Ubuntu/Debian: sudo update-initramfs -u -k all (or -u alone if only one kernel is installed)

VM testing:

  • Enable a virtual TPM in VMware, Hyper-V, or libvirt
  • Plain VirtualBox without vTPM keeps returning “No suitable TPM2 devices found”

Enroll FIDO2

FIDO2 enrollment needs a security key that implements the hmac-secret extension. List devices:

bash
systemd-cryptenroll --fido2-device=list

Plug in the key, then enroll:

bash
sudo systemd-cryptenroll --fido2-device=auto /dev/sdc1

Useful flags:

Flag Effect
--fido2-with-client-pin=yes Require PIN on each unlock
--fido2-with-user-presence=yes Require touch (default on most keys)
--fido2-parameters-in-header=yes Store credential parameters in LUKS2 (needed for some boot paths; systemd 257+)

After FIDO2 enrollment:

  • Add crypttab with fido2-device=auto
  • Test systemd-cryptsetup attach before reboot
  • Ensure initramfs includes FIDO2 support (cryptsetup-initramfs and a recent systemd on Ubuntu)

Enroll PKCS#11 (smartcard)

List smartcard or token URIs:

bash
systemd-cryptenroll --pkcs11-token-uri=list

Enroll with a specific URI or auto:

bash
sudo systemd-cryptenroll --pkcs11-token-uri=auto /dev/sdc1

PKCS#11 cautions:

  • Cannot add another PKCS#11 key when the volume is already PKCS#11-only for unlock (see systemd-cryptenroll(1))
  • Keep a passphrase, recovery key, FIDO2, or TPM2 slot available for rotation
  • Wire crypttab with pkcs11-uri=auto and test attach before reboot

Wipe or rotate enrollments

Remove a specific key slot (for example after losing a FIDO2 key):

bash
sudo systemd-cryptenroll --wipe-slot=2 /dev/sdc1

Before wiping a slot:

  • Confirm another working slot remains (cryptsetup luksDump)
  • Keep a LUKS header backup
  • You can combine --wipe-slot with a new enrollment in one invocation

To change passphrases without hardware tokens, use cryptsetup luksChangeKey or see change LUKS keys and cipher.


Boot integration with crypttab

Enrollment writes token metadata to the LUKS2 header. Boot unlock still needs crypttab to name the mechanism:

  • Plain luks — passphrase or recovery key prompt only (not unattended hardware unlock)
  • tpm2-device=auto — TPM2 token unlock
  • fido2-device=auto — FIDO2 token unlock
  • pkcs11-uri=auto — PKCS#11 token unlock

Read the LUKS UUID for crypttab:

bash
sudo cryptsetup luksUUID /dev/sdc1

Sample output on the lab disk:

text
e2e306a9-d94e-45d9-b209-4c9975fcede5

Add one line to /etc/crypttab based on what you enrolled:

text
# TPM2 unlock
secure_data UUID=e2e306a9-d94e-45d9-b209-4c9975fcede5 none luks,tpm2-device=auto

# FIDO2 unlock
secure_data UUID=e2e306a9-d94e-45d9-b209-4c9975fcede5 none luks,fido2-device=auto

# PKCS#11 unlock
secure_data UUID=e2e306a9-d94e-45d9-b209-4c9975fcede5 none luks,pkcs11-uri=auto

# Recovery key or passphrase prompt
secure_data UUID=e2e306a9-d94e-45d9-b209-4c9975fcede5 none luks

Recovery key at boot:

  • Not unattended unlock
  • High-entropy passphrase you type at the normal prompt when hardware unlock is unavailable

Boot flow:

  1. Enroll TPM2, FIDO2, PKCS#11, or a recovery key on /dev/sdc1
  2. Add the matching crypttab line (mount LUKS at boot for fstab and generator behavior)
  3. Test attach before rebooting — official crypttab examples use this pattern

Test TPM2 attach (requires TPM enrollment on the volume):

bash
sudo systemd-cryptsetup attach secure_data /dev/sdc1 none tpm2-device=auto
sudo systemd-cryptsetup detach secure_data

Test FIDO2 attach (key plugged in):

bash
sudo systemd-cryptsetup attach secure_data /dev/sdc1 none fido2-device=auto
sudo systemd-cryptsetup detach secure_data

Test PKCS#11 attach (smartcard present):

bash
sudo systemd-cryptsetup attach secure_data /dev/sdc1 none pkcs11-uri=auto
sudo systemd-cryptsetup detach secure_data
  1. Regenerate initramfs on Ubuntu/Debian:
bash
sudo update-initramfs -u -k all

For a single currently running kernel, sudo update-initramfs -u is usually enough.

  1. On boot, systemd-cryptsetup@secure_data.service uses crypttab options to try token unlock before prompting

Fleet alternative:


Troubleshooting

Symptom Likely cause Fix
No suitable TPM2 devices found No TPM driver node (/dev/tpmrm0, /dev/tpm0) Enable TPM in firmware or add a hypervisor vTPM; until then use --recovery-key or FIDO2
No FIDO2 devices found Key unplugged or missing hmac-secret support Plug in a compatible FIDO2 key or enroll a recovery key first
Device is not a valid LUKS device or LUKS1 Volume is LUKS1 or not LUKS at all Recreate or convert to LUKS2 — systemd-cryptenroll refuses LUKS1
Enrollment succeeds but boot still prompts for password crypttab missing token option, stale initramfs, or root unlock not wired into early boot Add tpm2-device=auto, fido2-device=auto, or pkcs11-uri=auto to crypttab; run sudo update-initramfs -u -k all; for root volumes, confirm your distro uses crypttab/initramfs hooks or kernel rd.luks.* options correctly
systemd-cryptsetup attach fails after enrollment Wrong device, volume not enrolled for that token type, or hardware absent Match /dev/sdc1 and mapper name; confirm luksDump shows the token; plug in FIDO2/smartcard or enable TPM
Unlock works until kernel update (TPM2) PCR binding too strict or wrong PCR set Re-enroll with tested PCR set; start with PCR 7 only before adding 11
No key available with this passphrase during enrollment Wrong unlock secret for an active slot Use --unlock-key-file with the exact passphrase bytes or key file that unlocks the volume

References


Summary

  • Adds TPM2, FIDO2, PKCS#11, and recovery-key unlock paths to LUKS2 volumes and stores token metadata in the header JSON area
  • Recovery keys work on any machine; TPM2 and FIDO2 need real hardware (or vTPM for TPM2 in VMs)
  • After enrollment, add the matching crypttab option (tpm2-device=auto, fido2-device=auto, or pkcs11-uri=auto), test with systemd-cryptsetup attach, regenerate initramfs, and keep a recovery key or passphrase slot as a backstop

Frequently Asked Questions

1. Does systemd-cryptenroll work on LUKS1 volumes?

No. Enrollment writes token metadata into the LUKS2 JSON token area. Convert or recreate the volume as LUKS2 first (cryptsetup default on current Ubuntu).

2. Can I enroll TPM2 on a VM without a virtual TPM?

No. TPM2 enrollment needs a reachable TPM2 device (/dev/tpmrm0 or /dev/tpm0). Many default VMs expose no TPM; enable a vTPM in the hypervisor or use FIDO2 or a recovery key instead.

3. What is a systemd recovery key for LUKS?

systemd-cryptenroll --recovery-key generates a high-entropy passphrase stored in a dedicated key slot with a systemd-recovery token. Save it offline; it unlocks the volume anywhere a normal passphrase works.

4. How does boot unlock differ from crypttab keyfiles?

Enrollment stores token metadata in the LUKS2 header, but crypttab must name the unlock mechanism (tpm2-device=auto, fido2-device=auto, or pkcs11-uri=auto). Recovery keys and passphrases use a plain luks line and prompt at boot. See the mount guide for fstab wiring.

5. Does a recovery key unlock the disk automatically at boot?

No. A recovery key is a high-entropy passphrase you type at the normal unlock prompt. TPM2 can provide unattended unlock when PCR policy and initramfs support are correct. FIDO2 and PKCS#11 can provide token-based boot unlock, but may still require touch, PIN, or smartcard interaction depending on enrollment options.

6. What happens if I enroll TPM2 without --tpm2-pcrs?

Enrollment succeeds but is not bound to PCR boot state—the TPM can unlock whenever the TPM seed matches, with weaker assurance about firmware, kernel, or bootloader integrity. Use --tpm2-pcrs=7 as a common Secure Boot starting point when you want policy binding.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive …