Introduction to LUKS
Linux Unified Key Setup (LUKS) is a disk encryption specification that encrypts block devices, such as disk drives and removable storage media. LUKS offers Full Disk Encryption (FDE) and selective partition-based encryption.
The system prompts you for a passphrase every time you boot the computer to unlock the encrypted disk. LUKS encrypted volumes can be automatically unlocked (without the need to provide a passphrase at boot), using a Trusted Platform Module 2.0 (TPM 2.0) policy
Introduction to TPM2.0
The Trusted Platform Module 2.0 (TPM) is a hardware-based system security feature that securely stores passwords, certificates, and encryption keys to authenticate the platform. It is embedded in the server motherboard.
TPM supports auto unlocking of the encrypted disk during system startup without requiring any user intervention.
Setup Information
This was performed on vanilla Ubuntu 24.04 Partition based encryption. Ubuntu was installed on a VM which was created using multipass on top of Hyper-V, with TPM and secure boot enabled for the VM.
The steps should work on a bare-metal node as well.
root@honeyglaze:~# uname -r
6.8.0-60-generic
root@honeyglaze:~# cat /etc/os-release
PRETTY_NAME="Ubuntu 24.04.2 LTS"
NAME="Ubuntu"
VERSION_ID="24.04"
VERSION="24.04.2 LTS (Noble Numbat)"
VERSION_CODENAME=noble
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=noble
LOGO=ubuntu-logo
# Validating TPM device
root@honeyglaze:~# ls -l /dev/tpm*
crw-rw---- 1 tss root 10, 224 May 22 05:49 /dev/tpm0
crw-rw---- 1 tss tss 253, 65536 May 22 05:49 /dev/tpmrm0
# Validating secure boot
root@honeyglaze:~# mokutil --sb-state
SecureBoot enabled
# cryptsetup version
root@honeyglaze:~# cryptsetup --version
cryptsetup 2.7.0 flags: UDEV BLKID KEYRING FIPS KERNEL_CAPI HW_OPAL
root@honeyglaze:~# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 10G 0 disk #### We will be working on /dev/sda disk
sdb 8:16 0 20G 0 disk
├─sdb1 8:17 0 19G 0 part /
├─sdb14 8:30 0 4M 0 part
├─sdb15 8:31 0 106M 0 part /boot/efi
└─sdb16 259:0 0 913M 0 part /boot
sr0 11:0 1 54K 0 rom
Encrypting the disk
- Use the
cryptsetup luksFormat
command to encrypt the disk. Provide a passphrase for encrypting the partition.
root@honeyglaze:~# cryptsetup luksFormat /dev/sda
WARNING!
========
This will overwrite data on /dev/sda irrevocably.
Are you sure? (Type 'yes' in capital letters): YES
Enter passphrase for /dev/sda:
Verify passphrase:
- Create a logical device-mapper that can be mounted to the encrypted partition. Provide the same passphrase given in the previous step to unlock the encrypted drive.
root@honeyglaze:~# cryptsetup luksOpen /dev/sda cryptpart
Enter passphrase for /dev/sda:
- Format the new partition with the ext4 file system.
root@honeyglaze:~# mkfs.ext4 /dev/mapper/cryptpart
mke2fs 1.47.0 (5-Feb-2023)
Creating filesystem with 2617344 4k blocks and 655360 inodes
Filesystem UUID: 73c76f23-804d-431d-84be-4767a6dc81c7
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632
Allocating group tables: done
Writing inode tables: done
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done
- Mount the new file system.
root@honeyglaze:~# mkdir -p /mnt/cryptpart
root@honeyglaze:~# mount /dev/mapper/cryptpart /mnt/cryptpart/
root@honeyglaze:~# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 10G 0 disk
└─cryptpart 252:0 0 10G 0 crypt /mnt/cryptpart
sdb 8:16 0 20G 0 disk
├─sdb1 8:17 0 19G 0 part /
├─sdb14 8:30 0 4M 0 part
├─sdb15 8:31 0 106M 0 part /boot/efi
└─sdb16 259:0 0 913M 0 part /boot
sr0 11:0 1 54K 0 rom
- Adding a recovery key.
# Generate a key
root@honeyglaze:~# openssl rand -base64 32 | cut -c1-32 | tee keyfile.txt
UXItVWYLicvja4u4PdYtMZ/iPwcufRGz
# Use luksAddKey to add the generate key, enter the passphrase (given in step 1) when prompted.
root@honeyglaze:~# cryptsetup luksAddKey /dev/sda keyfile.txt
Enter any existing passphrase:
- Validate the key slots.
# Key slot 0 and 1 are for the primary passphrase and the recovery key
root@honeyglaze:~# cryptsetup luksDump /dev/sda
LUKS header information
Version: 2
Epoch: 4
Metadata area: 16384 [bytes]
Keyslots area: 16744448 [bytes]
UUID: 608d9da9-a3fa-4a4e-8923-c3e4d95247a3
Label: (no label)
Subsystem: (no subsystem)
Flags: (no flags)
Data segments:
0: crypt
offset: 16777216 [bytes]
length: (whole device)
cipher: aes-xts-plain64
sector: 4096 [bytes]
Keyslots:
0: luks2
Key: 512 bits
Priority: normal
Cipher: aes-xts-plain64
Cipher key: 512 bits
PBKDF: argon2id
Time cost: 6
Memory: 1048576
Threads: 2
Salt: c4 ec 58 59 3a 34 48 bb 01 44 94 6f 7f 41 4d d9
a8 74 ba a6 1a 57 60 86 f1 19 69 e5 c2 d6 e7 30
AF stripes: 4000
AF hash: sha256
Area offset:32768 [bytes]
Area length:258048 [bytes]
Digest ID: 0
1: luks2
Key: 512 bits
Priority: normal
Cipher: aes-xts-plain64
Cipher key: 512 bits
PBKDF: argon2id
Time cost: 5
Memory: 1048576
Threads: 2
Salt: 3e fb 84 89 4a c2 53 bb 4e d8 0f d1 aa a3 f1 81
e1 31 8a af 67 69 b8 96 c1 da ba a1 d7 db 2f 72
AF stripes: 4000
AF hash: sha256
Area offset:290816 [bytes]
Area length:258048 [bytes]
Digest ID: 0
Tokens:
Digests:
0: pbkdf2
Hash: sha256
Iterations: 246375
Salt: 2a 25 1d 0e b9 33 2b 8b b6 8f 28 fc 71 4e 51 08
78 ba d8 36 45 21 13 b4 6e 66 0a a5 85 f1 a9 eb
Digest: 2a d1 7b 8a e2 b2 3d f1 17 29 7a 16 54 1e 3a 67
a8 f8 f0 f7 79 c1 a3 06 2b bd bf 09 9d 3b 36 4d
Decrypt a LUKS volume with Clevis and TPM
Clevis is a pluggable framework for automated decryption. It can be used to provide automated decryption of data or even automated unlocking of LUKS volumes. I went with clevis since in case of passphrase in TPM chip is corrupted/unreachable, clevis automatically prompts for passphrase/recovery key without requiring intervention to update /etc/crypttab
to drop TPM device.
- Install the required packages.
apt-get install dracut-core clevis clevis-tpm2 clevis-luks clevis-dracut tss2
- Bind LUKS encryption key/password to TPM2.0. Enter the passphrase when prompted.
# "-s 2" specifies the key slot. 0 and 1 slots are already in use.
root@honeyglaze:~# clevis luks bind -d /dev/sdb tpm2 '{"pcr_bank":"sha256","pcr_ids":"7"}'
Enter existing LUKS password:
- Validate the binding.
root@honeyglaze:~# clevis luks list -d /dev/sda
2: tpm2 '{"hash":"sha256","key":"ecc","pcr_bank":"sha256","pcr_ids":"7"}'
root@honeyglaze:~# systemd-cryptenroll /dev/sda
SLOT TYPE
0 password # passphrase
1 password # recovery key
2 other # clevis binding
- Let's try to unlock the drive and see whether we are getting the prompt to enter the password or not.
# First we need to unmount the disk
root@honeyglaze:~# umount /mnt/cryptpart
root@honeyglaze:~# cryptsetup luksClose cryptpart
root@honeyglaze:~# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 10G 0 disk
sdb 8:16 0 20G 0 disk
├─sdb1 8:17 0 19G 0 part /
├─sdb14 8:30 0 4M 0 part
├─sdb15 8:31 0 106M 0 part /boot/efi
└─sdb16 259:0 0 913M 0 part /boot
sr0 11:0 1 54K 0 rom
# unlock command should not give a prompt to enter the passphrase
root@honeyglaze:~# clevis luks unlock -d /dev/sda -n cryptpart
root@honeyglaze:~# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 10G 0 disk
└─cryptpart 252:0 0 10G 0 crypt
sdb 8:16 0 20G 0 disk
├─sdb1 8:17 0 19G 0 part /
├─sdb14 8:30 0 4M 0 part
├─sdb15 8:31 0 106M 0 part /boot/efi
└─sdb16 259:0 0 913M 0 part /boot
sr0 11:0 1 54K 0 rom
Enable automatic decryption during boot
- Add an entry to
/etc/crypttab
for decrypting the volume at boot time.
# Use blkid to get the UUID of the encrypted drive
root@honeyglaze:~# blkid -t TYPE=crypto_LUKS
/dev/sda: UUID="608d9da9-a3fa-4a4e-8923-c3e4d95247a3" TYPE="crypto_LUKS"
root@honeyglaze:~# cat /etc/crypttab
# <target name> <source device> <key file> <options>
cryptpart UUID=608d9da9-a3fa-4a4e-8923-c3e4d95247a3 none luks,discard,nofail
- Rebuild the initramfs and reboot.
root@honeyglaze:~# dracut -f
......
dracut[I]: *** Creating image file '/boot/initrd.img-6.8.0-60-generic' ***
dracut[I]: Using auto-determined compression method 'pigz'
dracut[I]: *** Creating initramfs image file '/boot/initrd.img-6.8.0-60-generic' done ***
root@honeyglaze:~# reboot
- Validate boot logs.
root@honeyglaze:~# journalctl -b > boot.log
root@honeyglaze:~# cat boot.log | grep tpm
May 22 08:10:24 localhost kernel: tpm_crb VTPM0101:00: [Firmware Bug]: Bad ACPI memory layout
May 22 08:10:24 localhost kernel: tpm_crb VTPM0101:00: [Firmware Bug]: Bad ACPI memory layout
May 22 08:10:26 honeyglaze systemd[1]: systemd-tpm2-setup-early.service - TPM2 SRK Setup (Early) was skipped because of an unmet condition check (ConditionSecurity=measured-uki).
May 22 08:10:26 honeyglaze systemd[1]: systemd-tpm2-setup.service - TPM2 SRK Setup was skipped because of an unmet condition check (ConditionSecurity=measured-uki).
May 22 08:10:29 honeyglaze systemd[1]: systemd-tpm2-setup-early.service - TPM2 SRK Setup (Early) was skipped because of an unmet condition check (ConditionSecurity=measured-uki).
May 22 08:10:29 honeyglaze systemd[1]: systemd-tpm2-setup.service - TPM2 SRK Setup was skipped because of an unmet condition check (ConditionSecurity=measured-uki).
May 22 08:10:32 honeyglaze systemd[1]: tpm-udev.path - Handle dynamically added tpm devices was skipped because of an unmet condition check (ConditionVirtualization=container).
root@honeyglaze:~# cat boot.log | grep clevis
May 22 08:10:24 localhost systemd[1]: Started clevis-luks-askpass.path - Forward Password Requests to Clevis Directory Watch.
May 22 08:10:26 localhost systemd[1]: clevis-luks-askpass.path: Deactivated successfully.
May 22 08:10:26 localhost systemd[1]: Stopped clevis-luks-askpass.path - Forward Password Requests to Clevis Directory Watch.
May 22 08:10:24 localhost systemd[1]: Started clevis-luks-askpass.path - Forward Password Requests to Clevis Directory Watch.
May 22 08:10:26 localhost systemd[1]: clevis-luks-askpass.path: Deactivated successfully.
May 22 08:10:26 localhost systemd[1]: Stopped clevis-luks-askpass.path - Forward Password Requests to Clevis Directory Watch.
May 22 08:10:26 honeyglaze systemd[1]: Started clevis-luks-askpass.path - Forward Password Requests to Clevis Directory Watch.
May 22 08:10:29 honeyglaze systemd[1]: Started clevis-luks-askpass.service - Forward Password Requests to Clevis.
May 22 08:10:29 honeyglaze clevis-luks-askpass[809]: Unlocked /dev/disk/by-uuid/608d9da9-a3fa-4a4e-8923-c3e4d95247a3 (UUID=608d9da9-a3fa-4a4e-8923-c3e4d95247a3) successfully
May 22 08:10:36 honeyglaze systemd[1]: clevis-luks-askpass.service: Deactivated successfully.
May 22 08:10:36 honeyglaze systemd[1]: clevis-luks-askpass.service: Consumed 1.283s CPU time.
Bonus Info
- TPM is a hardware chip that can securely store keys and perform cryptographic operations.
-
pcr_id
refers to the Platform Configuration Register (PCR) index that is used by the TPM (Trusted Platform Module) to store measurements of system state during the boot process. These registers in TPM (numbered 0–23 for TPM 2.0) that store hashes of system components during boot. These values change if the boot environment changes, which makes them great for detecting tampering. - When you bind a LUK S volume to TPM2 using Clevis , you specify one or more
pcr_ids
—for example, withclevis luks bind -d /dev/sdX tpm2 '{"pcr_ids":"0,7"}'
. This command seals the decryption key to the current values of PCRs 0 and 7, which represent specific measurements of the system state (like firmware, bootloader, and kernel). If these PCR values change—due to modifications in boot parameters, kernel, initramfs, or the bootloader—the TPM will not unseal the key, and the system will prompt for the LUKS passphrase. This mechanism ensures that the disk can only be automatically decrypted in a known, trusted, and unmodified boot environment. - Execute
sudo tpm2_pcrread sha256:0,1,2,3,4,5,6,7
to display the hashes. Reboot the validate whether these hash values are changing.
Top comments (0)