Skip to content
Menu

Linux Storage10 min read

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

SlotCredentialPurpose
0Strong passphrase (human-memorable)Recovery. Always present. Written down in a safe / password manager.
1TPM2-sealed key, bound to PCRs 0/2/4/7Automatic unlock on this machine, this firmware, this bootloader.
2FIDO2 (YubiKey)Portable unlock — works on the same user's other machines if they carry the key.
3Clevis / Tang pinAuto-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.

console
console

$ 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.

console
console

# 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.

console
console

# 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.

console
console

$ 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.

console
console

# 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.

CommandWhat it does
systemd-cryptenroll --password /dev/sdXEnroll a passphrase
systemd-cryptenroll --recovery-key /dev/sdXGenerate and enroll a printable recovery key
systemd-cryptenroll --tpm2-device=auto /dev/sdXEnroll TPM2-sealed key
systemd-cryptenroll --fido2-device=auto /dev/sdXEnroll FIDO2 token
systemd-cryptenroll --pkcs11-token-uri=auto /dev/sdXEnroll a PKCS#11 smartcard
systemd-cryptenroll --wipe-slot=tpm2 /dev/sdXRemove 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

bash
target-name  source-device  key-file  options
FieldPurposeCommon 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

OptionMeaning
luksLUKS format (autodetected; default on modern systems)
luks1 / luks2Force a specific LUKS version
tpm2-device=autoUnlock via TPM2 token in the header
fido2-device=autoUnlock via FIDO2 token (requires user presence — not for headless auto-unlock)
discardEnable TRIM pass-through (--allow-discards)
timeout=30Seconds to wait for passphrase before failing
headlessFail rather than prompt; for scripted/automated unlock via tokens or keyfiles
x-initrd.attachAttach early, in initramfs (for root and anything needed before switch-root)
noautoDon't attach at boot (manual systemctl start)
nofailBoot continues even if this device fails to unlock
keyfile-timeout=10Wait N seconds for keyfile (on removable media) before falling back to passphrase prompt
keyfile-size=NRead only the first N bytes of the keyfile

A realistic crypttab

console
console

# /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. nofail ensures boot continues if the data disk is missing.
  • cryptbak — backup disk. noauto + headless means it's not touched at boot; unlock manually when needed. headless prevents 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:

  1. LUKS open

    cryptsetup@cryptroot

  2. LVM activate

    LVs appear on cryptdevice

  3. FS mount

    /etc/fstab entries

  4. local-fs.target

    System continues

console
console

# 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

  1. Firmware / bootloader loads kernel + initramfs from /boot (typically unencrypted, or encrypted in GRUB2's LUKS1-only path)
  2. Kernel unpacks initramfs into RAM, runs its init
  3. Initramfs asks for passphrase (or uses TPM / keyfile / dropbear-ssh) → unlocks LUKS root
  4. Activates LVM LVs on the cryptdevice
  5. Mounts real root read-only
  6. switch_root — real /sbin/init (systemd) takes over
Debian / Ubuntu

Uses initramfs-tools. The cryptroot hook scans /etc/crypttab at build time and embeds the relevant entries.

  • Modern systemd-based initramfs respects x-initrd.attach in crypttab options
  • Regenerate after any crypttab change: update-initramfs -u -k all
  • Config: /etc/cryptsetup-initramfs/conf-hook
RHEL / Fedora

Uses dracut. Modular; the crypt module adds LUKS support, tpm2-tss adds TPM unlock, clevis adds NBDE, etc.

  • Regenerate after crypttab change: dracut -f (or dracut --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).

console
console

# 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

  1. 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
  2. Permissions: chmod 0400 and owned by root. No group or world access. Parent directory also chmod 0700.
  3. Location: on the encrypted root disk, not on /boot or an unencrypted partition. This chains trust: root must be unlocked (by passphrase / TPM) before the data disk's keyfile is readable.
  4. Never keep keyfiles on the same block device they unlock. Always a separate device (root → data pattern).
  5. 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. luksDump shows 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:

WhereHow to enable
Ad-hoc opencryptsetup 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 sideMount 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

Solidnines — solidnines.com