LUKS Keys, crypttab & Auto-Unlock
Multiple unlock methods and integrating with systemd
The Multi-Slot Unlock Model
LUKS's defining feature for operators is that a single master key can be unlocked by multiple independent credentials. Each key slot holds a copy of the master key encrypted under a different slot key (derived from a passphrase, a keyfile, a TPM-sealed secret, a FIDO2 response, etc.). Any slot's credential opens the device — they are not a chain, they are alternatives.
This is the foundation for realistic unlock policies:
A Realistic Slot Layout
| Slot | Credential | Purpose |
|---|---|---|
| 0 | Strong passphrase (human-memorable) | Recovery. Always present. Written down in a safe / password manager. |
| 1 | TPM2-sealed key, bound to PCRs 0/2/4/7 | Automatic unlock on this machine, this firmware, this bootloader. |
| 2 | FIDO2 (YubiKey) | Portable unlock — works on the same user's other machines if they carry the key. |
| 3 | Clevis / Tang pin | Auto-unlock only when attached to the trusted network (server in a datacenter). |
Warning
Always keep a passphrase slot as recovery. TPM hardware can fail. Motherboards get replaced. Firmware updates change PCR values and invalidate TPM-sealed keys. FIDO2 keys get lost. Tang servers die. If you lose every automated unlock slot and have no passphrase, the data is gone. Document the recovery slot's credential and store it offline.
Unlock Methods
Passphrase
The simplest credential: a string typed at a prompt. Goes through the KDF (Argon2id on LUKS2) to derive the slot key. Typically occupies slot 0.
$ cryptsetup luksAddKey /dev/sdX
Enter any existing passphrase: ****
Enter new passphrase for key slot: ****
Verify passphrase: ****
Keyfile
A keyfile is just bytes — any file. cryptsetup hashes the content (with the configured KDF) to derive the slot key. The file can be 4 KiB of random data or a sentence in a text file; cryptographic strength depends on the content's entropy.
# Generate a strong keyfile
$ dd if=/dev/urandom of=/root/keys/data.key bs=512 count=8
$ chmod 0400 /root/keys/data.key
# Enroll it to a LUKS slot
$ cryptsetup luksAddKey /dev/sdX /root/keys/data.key
Enter any existing passphrase: ****
TPM 2.0 (LUKS2 token)
The systemd-cryptenroll tool seals a random key to the TPM, bound to a set of PCR values (firmware, bootloader, kernel measurements). The sealed blob is stored as a LUKS2 token in the header, referencing a key slot. At boot, systemd asks the TPM to unseal — the TPM only releases the key if the PCRs still match.
# Bind to default PCRs (7 = Secure Boot state, usually)
$ systemd-cryptenroll --tpm2-device=auto /dev/sdX
# Bind to multiple PCRs — firmware (0), bootloader (2), kernel (4), Secure Boot (7)
$ systemd-cryptenroll \
--tpm2-device=auto \
--tpm2-pcrs=0+2+4+7 \
/dev/sdX
Details of PCR selection, sealing, and Measured Boot live in Storage 07 — TPM & Secure Boot.
FIDO2 security key
A YubiKey or similar. The unlock requires user presence (touch the key). Useful for portable unlock that doesn't depend on a single TPM.
$ systemd-cryptenroll --fido2-device=auto /dev/sdX
Requested to enroll FIDO2 security token...
Please touch security token: ...
Tang / Clevis (Network-Bound Disk Encryption)
NBDE binds unlock to reachability of a trusted network service. Tang is a lightweight HTTP server that holds asymmetric keys; Clevis is the client that talks to Tang. The disk unlocks automatically only when the machine is on a network where Tang is reachable. Remove the network → no unlock → disk stays encrypted at rest.
# Simple single-Tang binding
$ clevis luks bind -d /dev/sdX tang '{"url":"http://tang.example.com"}'
# Shamir Secret Sharing: require 2 of 3 pins
$ clevis luks bind -d /dev/sdX sss '{
"t": 2,
"pins": {
"tang": [
{"url": "http://tang1.dc.example"},
{"url": "http://tang2.dc.example"}
],
"tpm2": {"pcr_ids": "7"}
}
}'
Use cases: servers in a trusted datacenter that should auto-recover from power cycles without operator intervention, but should not be trivially readable if someone steals the disk and takes it home.
systemd-cryptenroll — the unified tool
On modern systems, systemd-cryptenroll is the preferred single entry point for managing LUKS2 tokens. It speaks all the protocols above.
| Command | What it does |
|---|---|
systemd-cryptenroll --password /dev/sdX | Enroll a passphrase |
systemd-cryptenroll --recovery-key /dev/sdX | Generate and enroll a printable recovery key |
systemd-cryptenroll --tpm2-device=auto /dev/sdX | Enroll TPM2-sealed key |
systemd-cryptenroll --fido2-device=auto /dev/sdX | Enroll FIDO2 token |
systemd-cryptenroll --pkcs11-token-uri=auto /dev/sdX | Enroll a PKCS#11 smartcard |
systemd-cryptenroll --wipe-slot=tpm2 /dev/sdX | Remove all TPM2 enrollments |
systemd-cryptenroll /dev/sdX (no action) | List enrolled tokens/slots |
/etc/crypttab Format
One line per encrypted device. Fields separated by whitespace.
Syntax
target-name source-device key-file options| Field | Purpose | Common values |
|---|---|---|
target-name |
Name of the resulting device-mapper node | cryptroot, cryptdata — appears as /dev/mapper/<name> |
source-device |
The backing encrypted block device | UUID=… (most robust), PARTUUID=…, PARTLABEL=…, or /dev/sdX |
key-file |
How to get the unlock credential | none or - (prompt), absolute path to a keyfile, or irrelevant when a token option is set |
options |
Comma-separated flags | See below |
Common options
| Option | Meaning |
|---|---|
luks | LUKS format (autodetected; default on modern systems) |
luks1 / luks2 | Force a specific LUKS version |
tpm2-device=auto | Unlock via TPM2 token in the header |
fido2-device=auto | Unlock via FIDO2 token (requires user presence — not for headless auto-unlock) |
discard | Enable TRIM pass-through (--allow-discards) |
timeout=30 | Seconds to wait for passphrase before failing |
headless | Fail rather than prompt; for scripted/automated unlock via tokens or keyfiles |
x-initrd.attach | Attach early, in initramfs (for root and anything needed before switch-root) |
noauto | Don't attach at boot (manual systemctl start) |
nofail | Boot continues even if this device fails to unlock |
keyfile-timeout=10 | Wait N seconds for keyfile (on removable media) before falling back to passphrase prompt |
keyfile-size=N | Read only the first N bytes of the keyfile |
A realistic crypttab
# /etc/crypttab
# name source key-file options
cryptroot UUID=3e2d6bc8-8d1c... none luks,discard,tpm2-device=auto,x-initrd.attach
cryptdata UUID=def01234-5678... /root/data.key luks,discard,nofail
cryptbak UUID=ghi56789-abcd... none luks,headless,nofail,noauto
- cryptroot — root disk. Unlocks via TPM2 in initramfs; falls back to passphrase if the TPM refuses (PCR mismatch).
- cryptdata — data disk. Keyfile lives on the encrypted root, so it's only readable after root is unlocked. Chain of trust: passphrase / TPM → root → data keyfile.
nofailensures boot continues if the data disk is missing. - cryptbak — backup disk.
noauto+headlessmeans it's not touched at boot; unlock manually when needed.headlessprevents any prompt dialog if run in a non-interactive context.
systemd Integration
At boot, systemd-cryptsetup-generator reads /etc/crypttab and generates a systemd-cryptsetup@<target>.service unit for each entry. These units are wired into the boot ordering so that:
LUKS open
cryptsetup@cryptroot
LVM activate
LVs appear on cryptdevice
FS mount
/etc/fstab entries
local-fs.target
System continues
# See the generated units
$ systemctl list-units 'systemd-cryptsetup*'
UNIT LOAD ACTIVE SUB DESCRIPTION
systemd-cryptsetup@cryptroot.service loaded active exited Cryptography Setup for cryptroot
systemd-cryptsetup@cryptdata.service loaded active exited Cryptography Setup for cryptdata
# Inspect one
$ systemctl cat systemd-cryptsetup@cryptdata.service
Initramfs Unlock (Root Disk)
The root filesystem is encrypted, but the kernel needs something to boot before it can unlock root. That something is the initramfs: a small userspace packed alongside the kernel in /boot, loaded into RAM, run before the real root is mounted.
Boot Sequence with Encrypted Root
- Firmware / bootloader loads kernel + initramfs from
/boot(typically unencrypted, or encrypted in GRUB2's LUKS1-only path) - Kernel unpacks initramfs into RAM, runs its
init - Initramfs asks for passphrase (or uses TPM / keyfile / dropbear-ssh) → unlocks LUKS root
- Activates LVM LVs on the cryptdevice
- Mounts real root read-only
switch_root— real/sbin/init(systemd) takes over
Uses initramfs-tools. The cryptroot hook scans /etc/crypttab at build time and embeds the relevant entries.
- Modern systemd-based initramfs respects
x-initrd.attachin crypttab options - Regenerate after any crypttab change:
update-initramfs -u -k all - Config:
/etc/cryptsetup-initramfs/conf-hook
Uses dracut. Modular; the crypt module adds LUKS support, tpm2-tss adds TPM unlock, clevis adds NBDE, etc.
- Regenerate after crypttab change:
dracut -f(ordracut --regenerate-all -f) - Config:
/etc/dracut.conf.d/*.conf rd.luks.*kernel command-line params override crypttab
Warning
Gotcha: keyboard layout in initramfs. The initramfs typically ships with a US QWERTY layout and doesn't know about your configured XKB keymap. A passphrase containing non-US-accessible characters (accented letters, symbols in different positions on AZERTY / QWERTZ) will be typed differently at the initramfs prompt than after boot. Test unlock by rebooting before you rely on it. Consider a passphrase that's identical on any layout.
Headless Remote Unlock
Servers in colocation / remote datacenters with no console need a way to supply the passphrase at boot. Three common approaches:
dropbear-initramfs (SSH in the initramfs)
Embeds a minimal SSH server in the initramfs. On boot, the system acquires an IP (via DHCP) and waits for an SSH connection. The operator SSHes in and types the passphrase (or runs cryptroot-unlock).
# Debian/Ubuntu
$ apt install dropbear-initramfs
# Place your ops key (different from the one on the booted system)
$ echo "ssh-ed25519 AAAAC3Nza... ops@laptop" \
> /etc/dropbear/initramfs/authorized_keys
$ chmod 0600 /etc/dropbear/initramfs/authorized_keys
# Configure network in initramfs (DHCP by default, or static)
$ echo 'IP=dhcp' >> /etc/initramfs-tools/initramfs.conf
# Rebuild
$ update-initramfs -u -k all
# Later, from the ops laptop
$ ssh -i ~/.ssh/ops_initramfs root@server.example.com
To unlock root partition, and maybe others like swap, run `cryptroot-unlock`
# cryptroot-unlock
Please unlock disk cryptroot: ****
cryptsetup: cryptroot set up successfully
tinyssh-initramfs
Smaller alternative to dropbear, Ed25519-only. Same idea, lighter binary footprint.
Tang / Clevis NBDE (auto-unlock)
If the server only needs to auto-unlock when it's on the trusted datacenter network, NBDE removes the need for any human intervention on reboot. Power cycle → boots → reaches Tang → unlocks. If the disk is stolen and taken off-network, it stays encrypted.
Security boundary: you're trusting the network as the policy. An attacker with access to the Tang server, or the ability to connect the stolen disk to the datacenter network, can unlock. Useful in physical-theft threat models, not in insider ones.
Keyfile Best Practices
Rules
- High-entropy content. Generate with
/dev/urandom, not a text file you typed.bash dd if=/dev/urandom of=/root/keys/data.key bs=512 count=8 # 4 KiB random - Permissions:
chmod 0400and owned by root. No group or world access. Parent directory alsochmod 0700. - Location: on the encrypted root disk, not on
/bootor an unencrypted partition. This chains trust: root must be unlocked (by passphrase / TPM) before the data disk's keyfile is readable. - Never keep keyfiles on the same block device they unlock. Always a separate device (root → data pattern).
- Backup the keyfile when you back up the LUKS header. Losing either means losing that unlock path.
Recovery Planning Checklist
Per-volume, once and annually
- Header backup off-device.
cryptsetup luksHeaderBackup --header-backup-file hdr.bin /dev/sdX. Store on a USB stick and on another server. Repeat after every slot change. - One passphrase slot. Memorable or in a password manager. This is the "if everything else fails" path.
- Document each slot's purpose.
luksDumpshows slot numbers but not what credential is in each. Keep a README: "slot 0 = recovery passphrase in 1Password; slot 1 = TPM2; slot 2 = Alice's YubiKey; slot 3 = keyfile at /root/keys/data.key". - Test recovery annually. Boot from rescue media (Ubuntu live USB, SystemRescue), restore the header backup to a scratch device, unlock with the recovery passphrase. If this fails, you discover it now, not during an emergency.
- Firmware-update drill. On systems using TPM-sealed unlock, any UEFI/firmware update can invalidate the sealed PCRs. Either re-enroll before the update or know how to fall back to passphrase.
--allow-discards Details
See dm-crypt & LUKS for the full trade-off discussion. Quick reference:
| Where | How to enable |
|---|---|
| Ad-hoc open | cryptsetup open --allow-discards /dev/sdX name |
| Persistent (crypttab) | Add discard to the options field |
| Persistent (LUKS2 header flag) | cryptsetup --allow-discards --persistent refresh /dev/sdX |
| Filesystem side | Mount with discard (ext4, xfs, btrfs), or use fstrim.timer for periodic batch TRIM |
Tip
Recommended: enable discard in crypttab and rely on fstrim.timer (weekly batch TRIM) rather than continuous discard mount option. Batch TRIM is easier on SSD controllers and still gives most of the benefit. The LUKS-level --allow-discards is what permits the TRIM to reach the SSD at all.
See Also
- dm-crypt & LUKS — the layer beneath everything on this page.
- Storage 04 — Device Mapper — dm-crypt is a DM target; understanding the DM framework explains how the crypto device appears in
/dev/mapper. - Storage 07 — TPM & Secure Boot — PCR selection, sealing, Measured Boot, and the full story of TPM-based unlock.