This guide allows you to use the TPM on your computer to decrypt your LUKS encrypted volumes. If you are worried about a cold boot attack on your hardware please DO NOT use this guide with your root volume!
Verify that you have a TPM in your computer:
# systemd-cryptenroll --tpm2-device=list
PATH DEVICE DRIVER
/dev/tpmrm0 MSFT0101:00 tpm_crb
Note: If you have more than one TPM in your computer you will need to change --tpm2-device=auto
to the exact TPM you want to use: --tpm2-device=/dev/tpmrm0
and rd.luks.options=tpm2-device=/dev/tpmrm0
in GRUB_CMDLINE_LINUX
.
Verify that you are booted into SecureBoot.
# mokutil --sb-state
SecureBoot disabled
If you see that it is disabled you will need to enable it in the BIOS. You should enable SecureBoot before you start.
Note: Enabling SecureBoot will cause third party kernel modules (such as NVIDIA drivers) to fail to load. You can work around this by using something like this to automatically sign the drivers built by akmod.
If you use the RPM Fusion repo for NVIDIA drivers you can use their support for automatically signing locally built kmod with a self generated key.
# blkid -t TYPE=crypto_LUKS
/dev/nvme0n1p3: UUID="0818cd36-a007-11ec-aaab-7c10c93c41b1" TYPE="crypto_LUKS" PARTUUID="c362bcd2-87"
/dev/nvme1n1p1: UUID="15bc3342-a007-11ec-a502-7c10c93c41b1" TYPE="crypto_LUKS" PARTUUID="e8ead241-02"
# systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=0+2+4+7+8+9 /dev/nvme0n1p3
# systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=0+2+4+7+8+9 /dev/nvme1n1p1
This will ask for your volume's passphrase. If you'd like to automate this in a script you may set the PASSWORD
environment variable to your LUKS passphrase.
When setting --tpm2-pcrs=0+2+4+7+8+9
the following items are these are validated at boot time:
0: System firmware executable
2: Kernel
4: Bootloader
7: Secure boot state
8: Cmdline
9: Initrd
PCR 0,2,4,7,8,9 verifies the firmware, kernel, bootloader, secure boot state, cmdline and initrd before releasing the decryption key. If you are using PCR 2 and multiple kernels you will need to enroll a key for each kernel. If you have updated the firmware, kernel, or bootloader, and cmdline then auto volume decryption on your next reboot will fail. As long as you have a password set on your LUKS volumes you will be prompted to have to enter it to decrypt them and you will need to wipe the old key and enroll a new key if anything changes.
systemd-cryptenroll /dev/nvme0n1p3 --wipe-slot=tpm2 --tpm2-device=auto --tpm2-pcrs=0,2,4,7,8,9
systemd-cryptenroll /dev/nvme1n1p1 --wipe-slot=tpm2 --tpm2-device=auto --tpm2-pcrs=0,2,4,7,8,9
Add tpm2-device=auto,discard
to the end of each LUKS device line in /etc/crypttab
# cat /etc/crypttab
luks-014aa5a6-a007-11ec-a054-7c10c93c41b1 UUID=0818cd36-a007-11ec-aaab-7c10c93c41b1 - tpm2-device=auto,discard
luks-0e9e99f6-a007-11ec-8130-7c10c93c41b1 UUID=15bc3342-a007-11ec-a502-7c10c93c41b1 - tpm2-device=auto,discard
Edit /etc/default/grub
and add rd.luks.options=tpm2-device=auto
to GRUB_CMDLINE_LINUX
.
GRUB_CMDLINE_LINUX="rd.driver.blacklist=nouveau modprobe.blacklist=nouveau nvidia-drm.modeset=1 rd.luks.uuid=luks-014aa5a6-a007-11ec-a054-7c10c93c41b1 rd.luks.uuid=luks-0e9e99f6-a007-11ec-8130-7c10c93c41b1 rd.luks.options=tpm2-device=auto rhgb quiet rd.driver.blacklist=nouveau"
If you have a safe place to store a recovery key you can generate and add one for each LUKS volume. It will show the recovery key phrase on screen and generate a QR code you may scan off screen.
systemd-cryptenroll --recovery-key /dev/nvme0n1p3
systemd-cryptenroll --recovery-key /dev/nvme1n1p1
Verify that you have the TPM added to the encrypted volumes:
# systemd-cryptenroll /dev/nvme0n1p3
SLOT TYPE
0 password
1 tpm2
2 recovery
# systemd-cryptenroll /dev/nvme1n1p1
SLOT TYPE
0 password
1 tpm2
2 recovery
and now you can reboot and your TPM should unlock your encrypted drives!
Sources:
- https://gist.github.com/chrisx8/cda23e2d1fa3dcda0d739bc74f600175
- https://bugzilla.redhat.com/show_bug.cgi?id=1976462
- https://www.freedesktop.org/software/systemd/man/systemd-cryptenroll.html
- https://wiki.archlinux.org/title/Trusted_Platform_Module#systemd-cryptenroll
- https://man7.org/linux/man-pages/man5/dracut.conf.5.html
- https://0pointer.net/blog/unlocking-luks2-volumes-with-tpm2-fido2-pkcs11-security-hardware-on-systemd-248.html
- https://github.com/larsks/akmod-sign-modules
- https://rpmfusion.org/Howto/Secure%20Boot
PCR8 and non-ui reboots or update-installations
During the investigation why the system doesn't unlock when I reboot via ssh without any login, I played around with
grub-setenv list
andsystemd-analyze pcrs
. PCR8 does not only hash the cmdline like explained in ArchWiki but also the commands executed in grub (see grub docs). PCR8 is indeed the only register which changes when addinginit=/bin/bash
to the cmdline.grubenv
PCR8 does not contain the cmdline only but every command which is executed by grub. This register changes it's content everytime when the grub-env vars
boot_success
and/orboot_indeterminate
are changing because it changes the way grub executes the commands. And becauseboot_success
is only set when a user-session is running for two minutes (systemctl status --user grub-boot-success.{service,timer}
) or when rebooting via the gdm-ui (0001-Fedora-Set-grub-boot-flags-on-shutdown-reboot.patch) a reboot via ssh will not setboot_success
. This also explains why two reboots are necessary after an update to fix the unlock because this also plays withboot_indeterminate
(systemctl cat grub-boot-indeterminate.service
).TL;DR;
I disabled the grub-scripts which are responsible for the autohide and timeout-changes and set a countdown timeout of 1 second:
PCR9
PCR9 should also be added to the list. The initrd is not measured in PCR8, so an attacker could manipulate it, e.g. with a simple initrd which only prints the luks headers on the screen or uploads it somewhere.
envfile and
boot_indeterminate
PCR9 not only get changed when the initrd changes but when any file changes which is read by grub, booting when executing updates asks for the password because of the change in
grubenv
. To prevent this, executesystemctl disable grub-boot-indeterminate.service
.cmdline
The additinal parameter in the cmdline is not necessary.