User:Krin/Secure Boot, full disk encryption, and TPM2 unlocking install
Introduction
This installation is based on my installing Arch on an Asus ROG Strix G G531 I got second hand and a desktop computer made of second hand parts that had to share space with Windows. The primary goals were to replace Windows 10 Pro with Bitlocker encryption and maintain the convenience and security balance of that original operating system.
My requirements boiled down to:
- A fully encrypted disk
- Full Secure Boot validation of all UEFI components.
- Use of the TPM to facilitate unlocking of the disk.
- Hibernate/Suspend working.
My primary use case here is to make it difficult enough for a baggage handler with sticky hands to get at my data that they just wipe the drive. A state level actor with the expertise and equipment to hot swap my ram in my hotel room is not something that I'm defending this particular laptop against. This is more for gaming and web browsing and uploading photos while traveling.
I'll continue to update these notes as I develop further solutions.
Disclaimers
This article is primarily for the author's own notes. It is assumed that the reader is familiar with the Installation guide and will refer to it often. Reading linked articles is recommended.
Dual Booting Modifications
For computers that will have to dual boot with windows, the easiest solution is to use two different physical drives and utilize your motherboard's UEFI boot menu to select which one to boot. This guide will follow that assumption. You can also attempt to have everything live on one EFI partition. Windows's 100 MiB EFI partition is especially problematic to this approach for secure boot, as Unified Kernel Images can approach 80 MiB and Windows 10 regularly uses up to 30 MiB. An alternative is to copy the Microsoft bootloader into your Linux EFI bootloader and then prioritize the Linux EFI disk over your Windows disk. You will want to copy the directory WINDOWS_EFI_PARITION/EFI/Microsoft
to the same location on your Linux EFI partition. Anytime Microsoft updates the bootloader, you will need to copy the Microsoft EFI folder again.
There are two changes that will be made and will be noted in the relevant sections:
- Do not clear your Secure Boot keys. Merely disable Secure Boot for the installation.
- Use
shim
to boot systemd-bootloader - Load your db.cer file into the MOKManager application.
All remaining steps should remain the same, except your system will still be dependent on the factory provided Microsoft Certificates. But you can boot Windows 10 in Secure Boot, which will be important for Windows 11.
Initial setup
Pre-installation
If you will only boot linux, reset your Secure Boot settings in BIOS to enable setup mode. Usually this means you set Secure Boot to Enabled and then select the option to wipe out the keys.
If you will be dual booting Windows, disable secure boot.
Follow the Installation_guide#Pre-installation up to Paritioning the Disks.
Disk preparation
If you have data on your disks you want to overwrite, utilize Dm-crypt/Drive_preparation#dm-crypt_wipe_on_an_empty_disk_or_partition to wipe it. If this disk was previously encrypted, utilize Dm-crypt/Drive_preparation#Wipe_LUKS_header instead for a much faster disposal. Finally, use Dm-crypt/Encrypting_an_entire_system#LVM_on_LUKS to parition your drive with two differences.
First, the partition layout will be similar to here. You may wish to break out the /home directory into it's own partition. The primary requirement is that only your EFI partition be unencrypted.
Partiton Layout
┌────────────────────┬─────────────────────────┬──────────────────────────┬───────────────────────────┐ │ │ │ │ │ │ EFI Boot Partition │ Logical Volume 1 │ Logical Volume 2 │ Logical Volume 3 │ │ /efi │ [Swap] │ / │ /home │ │ /dev/nvme0n1p1 │ /dev/CryptRootVG/swap │ /dev/CryptRootVG/root │ /dev/CryptRootVG/home │ │ 512 MB │ 16GB (Or RAM Size) │ 50 GB │ Remaining Space │ │ │ │ │ │ │ ├─────────────────────────┴──────────────────────────┴───────────────────────────┤ │ │ LUKS and LVM Physical Volume │ │ │ /dev/nvme0n1p2 │ │ │ │ │ │ │ │ │ │ │ │ │ └────────────────────┴────────────────────────────────────────────────────────────────────────────────┘
Second, if your hard drive is an SSD, use the following command to open your LUKS volume.
# cryptsetup open --perf-no_read_workqueue --perf-no_write_workqueue --persistent /dev/nvme0n1p2 cryptroot
After completing your LUKS on LVM setup, format your EFI partition per EFI system partition.
# mkfs.fat -F32 /dev/nvme0n1p1
Mount this partition at /mnt/efi
. It is very important to leave /boot
on your encrypted root partition.
Installation
Continue with Installation_guide#Installation. Be sure to flesh out your pacstrap(8) with the following list of packages:
We are going to leave off the installation guide at the initramfs section, so go ahead and reset your root password.
# passwd
Bootloader
First, install systemd-boot
by following Systemd-boot#Installing_the_EFI_boot_manager. Next, install dracut-hook-uefiAUR.
Ensure you drop in a systemd-boot hook to update your bootctl any time it's updated.
/etc/pacman.d/hooks/998-systemd-boot.hook
[Trigger] Type = Package Operation = Install Operation = Upgrade Target = systemd [Action] Description = Updating systemd-boot When = PostTransaction Exec = /usr/bin/bootctl update
Obtain the UUID of your LUKS drive via the following command:
blkid -s UUID -o value /dev/nvme0n1p2
Create the following files with the listed content. Be sure to replace UUIDs with what you actually need. See dracut.conf(5) for more information on what these commands do.
This is just an example command line, you may need to rework this based on your partitions and UUIDs.
/etc/dracut.conf.d/cmdline.conf
kernel_cmdline="rd.luks.uuid=luks-17eb0a1e-0e1a-4d06-9bf0-a4f8c5eebb02 rd.lvm.lv=CryptRoot/root rd.lvm.lv=CryptRoot/swap resume=/dev/mapper/CryptRoot-swap root=/dev/mapper/CryptRoot-root rootfstype=ext4 rootflags=rw,relatime"
/etc/dracut.conf.d/flags.conf
compress="zstd" hostonly="no"
Run the following command to generate your EFI stub kernels.
dracut --uefi
/lib/modules
and add it as the value of the --kver
argument to all dracut commands. For example, dracut --uefi --kver 5.14.16-zen1-1-zen
.Secure boot
Dual Booting Windows
Review Unified Extensible Firmware Interface/Secure Boot#shim to get a rough understanding of how it's going to work. Install shim-signedAUR. Perform the following commands to place files where both the UEFI boot entry and shim will load them.
# mv /efi/EFI/BOOT/BOOTx64.EFI /efi/EFI/BOOT/grubx64.efi # cp /usr/share/shim-signed/shimx64.efi /efi/EFI/BOOT/BOOTx64.EFI # cp /usr/share/shim-signed/mmx64.efi /efi/EFI/BOOT/
Install sbctl. Use the following command to check that secure boot is in the right status. Your output should be similar.
# sbctl status Installed: ✘ Sbctl is not installed Setup Mode: ✔ Disabled Secure Boot: ✘ Disabled
# sbctl create-keys Created Owner UUID a2ee3d08-725a-408b-9d95-28026a44611c Creating secure boot keys...✔ Secure boot keys created! # sbctl verify Verifying file database and EFI images in /efi... ✘ /efi/EFI/BOOT/BOOTx64.EFI is not signed ✘ /efi/EFI/BOOT/grubx64.efi is not signed ✘ /efi/EFI/BOOT/mmx64.efi is not signed ✘ /efi/EFI/Linux/linux-5.14.8-arch1-1-df10c5e79c5444b78ef1b154eef3ca32-rolling.efi is not signed ✘ /efi/EFI/systemd/systemd-bootx64.efi is not signed
Note that shim and MOKManager (mmx) is included in this list, even though we will not be signing it.
# sbctl sign -s /efi/EFI/BOOT/grubx64.efi ✔ Signed /efi/EFI/BOOT/grubx64.efi # sbctl sign -s /efi/EFI/systemd/systemd-bootx64.efi ✔ Signed /efi/EFI/systemd/systemd-bootx64.efi # sbctl sign -s /efi/EFI/Linux/linux-5.14.8-arch1-1-df10c5e79c5444b78ef1b154eef3ca32-rolling.efi ✔ Signed /efi/EFI/Linux/linux-5.14.8-arch1-1-df10c5e79c5444b78ef1b154eef3ca32-rolling.efi # sbctl verify Verifying file database and EFI images in /efi... ✔� /efi/EFI/BOOT/grubx64.efi is signed ✔ /efi/EFI/Linux/linux-5.14.8-arch1-1-df10c5e79c5444b78ef1b154eef3ca32-rolling.efi is signed ✔ /efi/EFI/systemd/systemd-bootx64.efi is signed ✘ /efi/EFI/BOOT/BOOTx64.EFI is not signed ✘ /efi/EFI/BOOT/mmx64.efi is not signed
You can validate that BOOTx64.EFI
and mmx64.efi
have signatures with sbverify(1) from the sbsigntools package.
Next, we need to create the DER format certificate to load into MokManager.
# openssl x509 -outform DER -in /usr/share/secureboot/keys/db/db.pem -out /efi/signing.cer
Use efibootmgr (efibootmgr(8)) to adjust your boot entries. Primarily, you will want to remove the Linux Boot Manager
entry.
# efibootmgr BootCurrent: 0004 Timeout: 1 seconds BootOrder: 0001,0000,0002,0003,0004 Boot0000* Windows Boot Manager Boot0001* Linux Boot Manager Boot0002* UEFI: Samsung Flash Drive FIT 1100 Boot0003* UEFI: Samsung Flash Drive FIT 1100, Partition 2 Boot0004* UEFI OS
In this example, we see that 0000
is the Windows boot manager. 0001
is the Linux Boot Manager and also the first in line to boot. 0001
needs to be deleted and optionally the boot order modified so 0004
is the default boot entry.
# efibootmgr --bootnum 0001 --delete-bootnum BootCurrent: 0004 Timeout: 1 seconds BootOrder: 0000,0002,0003,0004 Boot0000* Windows Boot Manager Boot0002* UEFI: Samsung Flash Drive FIT 1100 Boot0003* UEFI: Samsung Flash Drive FIT 1100, Partition 2 Boot0004* UEFI OS # efibootmgr --bootorder 0004,0000,0002,0003 BootCurrent: 0004 Timeout: 1 seconds BootOrder: 0004,0000,0002,0003 Boot0000* Windows Boot Manager Boot0002* UEFI: Samsung Flash Drive FIT 1100 Boot0003* UEFI: Samsung Flash Drive FIT 1100, Partition 2 Boot0004* UEFI OS
Finally, we need to let dracut know to sign it's unified kernel images when it creates them. Create the following file:
/etc/dracut.conf.d/secureboot.conf
uefi_secureboot_cert="/usr/share/secureboot/keys/db/db.pem" uefi_secureboot_key="/usr/share/secureboot/keys/db/db.key"
Now reboot. You should get an error from your motherboard's UEFI BIOS that secure boot checks failed. Your computer should restart directly into MokManager, if you rearranged the boot order. If not, you will need to select UEFI OS
from your UEFI boot menu. Navigate to the signing.cer
file we placed on the EFI partition and enroll the key. Select reboot, and the system should boot directly into linux with no problem.
Booting only Linux
Install sbctl. At this point you will likely need to reboot and adjust your secure boot settings. You want secure boot in setup mode.
$ sbctl status Installed: X Sbctl is not installed Setup Mode: X Enabled Secure Boot: X Disabled
Run the following commands as root. See sbctl(8) for more information on their function. Of particular note is the -s
flag which indicates sbctl
should remember the file for future verification and signing.
Note your Linux EFI binary name may be slightly different.
# sbctl create-keys Created Owner UUID a2ee3d08-725a-408b-9d95-28026a44611c Creating secure boot keys...✔ Secure boot keys created! # sbctl verify Verifying file database and EFI images in /efi... ✘ /efi/EFI/BOOT/BOOTx64.EFI is not signed ✘ /efi/EFI/Linux/linux-5.14.8-arch1-1-8ac4653b867643879835f2c25c0d0cd7-rolling.efi is not signed ✘ /efi/EFI/systemd/systemd-bootx64.efi is not signed # sbctl sign -s /efi/EFI/BOOT/BOOTx64.EFI ✔ Signed /efi/EFI/BOOT/BOOTx64.EFI # sbctl sign -s /efi/EFI/Linux/linux-5.14.8-arch1-1-8ac4653b867643879835f2c25c0d0cd7-rolling.efi ✔ Signed /efi/EFI/Linux/linux-5.14.8-arch1-1-8ac4653b867643879835f2c25c0d0cd7-rolling.efi # sbctl sign -s /efi/EFI/systemd/systemd-bootx64.efi ✔ Signed /efi/EFI/systemd/systemd-bootx64.efi
Finally, we need to let dracut know to sign it's unified kernel images when it creates them. Create the following file:
/etc/dracut.conf.d/secureboot.conf
uefi_secureboot_cert="/usr/share/secureboot/keys/db/db.pem" uefi_secureboot_key="/usr/share/secureboot/keys/db/db.key"
Finally, enroll the Secure Boot keys.
# sbctl enroll-keys Enrolling keys to EFI variables...✔ Enrolled keys to the EFI variables!
Verify that secure boot now has an owner GUID and setup mode is disabled.
# sbctl status Installed: ✔ Sbctl is installed Owner GUID: a2ee3d08-725a-408b-9d95-28026a44611c Setup Mode: ✔ Disabled Secure Boot: ✘ Disabled
Now reboot and enable secure boot in your firmware.
Enroll LUKS key in TPM
We are fortunate that in our shift to dracut includes utilizing systemd and sd-encrypt, as we can use systemd's native support for enrolling LUKS keys. A review of Trusted Platform Module#Using TPM 2.0 is recommended but not very clear.
Avoid dracut bugs
Subtitle: The Case of the Missing d
Two bugs, one in systemd 249.4-1-arch [1] and one in dracut 055 [2][3] will combine to prevent your system from being able to boot. The dracut issue causes the tpm2-tss module to not be included in your image. The systemd issue causes systemd to go into an emergency when it can't locate the TPM2 libraries, instead of falling back to asking for your passcode.
The systemd bug isn't a problem so long as you always have TPM2 libraries present.
To resolve this, first check your version of dracut.
$ dracut --version dracut 055
Since the next release of dracut should have this fixed, we can simply fix the script locally until the next release.
Find the following lines in /usr/lib/dracut/modules.d/91tpm2-tss/module-setup.sh
:
# Module dependency requirements. depends() { # This module has external dependency on other module(s). echo systemd-sysusers systemd-udev # Return 0 to include the dependent module(s) in the initramfs. return 0 }
Change the dependency systemd-udev
to systemd-udevd
.
Continue with the remaining steps in this section.
Enrollment
First, be sure to install tpm2-tools. Alternatively ibm-tssAUR can be used per Trusted Platform Module, however this guide is not tested against those.
systemd-cryptenroll
that TPM 2.0 devices now exist on the machine.There are several decisions to be made here. The table at Trusted Platform Module#Accessing PCR registers is accurate as of October 2021. Essentially, once you seal your LUKS key, if any of the registers you've sealed against change, then you will be asked for your LUKS passphrase. This is equivalent to when Windows 10 makes you enter your BitLocker recovery key after a hardware change or changing BIOS settings.
Your mileage may vary, based on UEFI vendors. For my laptop, I went with 0+1+2+3+4+5+7+8 and have been able to invalidate my TPM. Unfortunately, if you change your system BACK the volume will unlock. I'm investigating how to disable that.
Other options on systemd-cryptenroll(1) will reveal that several hardware tokens can also be enrolled to allow unlocking the root LUKS volume automatically, but only if a key is present. Further testing is requried.
To enroll your LUKS volume, simply run the following command as root
# systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=0+1+2+3+4+5+7+8 /dev/nvme0n1p2
To remove your key, run this command:
# systemd-cryptenroll --wipe-slot=tpm2 /dev/nvme0n1p2
You will need to add a kernel argument like below to enable the TPM2 bits. If following this guide, this should be located in /etc/dracut.conf.d/cmdline.conf
rd.luks.options={UUID}=tpm2-device=auto
Ensure that dracut builds in the TPM2 libraries. Create the following file.
/etc/dracut.conf.d/tpm2-tss.conf
add_dracutmodules+=" tpm2-tss "
Finally, rebuild your unified kernel image.
# dracut --force --uefi
A useful script to have handy for when you change UEFI settings to reenroll your LUKS key under the different PCR values is as follows:
~/bin/luks_reenroll_tpm
sudo systemd-cryptenroll --wipe-slot=tpm2 /dev/disk/by-uuid/{UUID_HERE} systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=0+1+2+3+4+5+7+8 /dev/disk/by-uuid/{UUID_HERE}
Wrapping up
Set your UEFI password.
Change your LUKS passphrase from something easily memorable to a good long recovery key.
Keep both of these pieces of info somewhere safe. This varies depending on your threat model. If we're just talking about Stolen Laptops and Nosy Maids, a hard copy in your safe should be sufficient. If it's the NSA, I'm not sure this setup is appropriate for that model.
This is an excellent base to handle stolen laptop and even some lower grade Evil Maids. From here, you should head into Security. Consider learning a framework to help you manage your system state such as Ansible or headless Puppet.
Justifications
LVM on LUKS vs other dm-crypt approaches
LVM on LUKS was selected becasue it is simply the easiest to manage while conferring both encrypted swap and disk. The other approaches required much more tooling and manual fussing to utilize. This selection also gives room change freely between busybox
and systemd
initrds.
Using dracut
dracut just makes it easier to build full unified kernels that systemd-boot(7) can launch as EFI binaries. There is also available tooling in the AUR for automating the creation of these binaries. Finally, dracut
will sign the binaries it creates automatically when configured to do so.
Unified kernel images
While UKI's could be problematic on other distributions, they fit right in with an Arch Linux install. The automation is there through pacman hooks and sbctl to sign our UKIs as they're created. Furthermore, you can only sign and create a new signed UKI while booted into a properly signed UKI. It creates a chain of trust, which means it's important to never disable Secure Boot once you've set it up.
The UKI encompasses all of the following items inside the signature, which means you can't change any of them without invalidating the signature.
- Kernel arguments
- Initrd/Initramfs
- Kernel
In particular, all other flavors of secureboot do not sign or validate the initramfs. This is a big attack area and can be relatively trivialized that a semi-decent pawn shop owner with Google could likely find an image that would grant him access and make the TPM none the wiser the system has changed. One can prevent this with encrypted /boot, but that would limit you to GRUB2 and LUKS version 1 devices only. This also doesn't include the work to keep GRUB2's files signed with a PGP key by root.
LUKS enrolled in TPM
The goal here is to only require the passphrase when something spooky happens in your boot path.
UEFI settings change? Request password.
Some weird swap of some module of UEFI code that lives on your motherboard somewhere? Request password.
Unified Kernel Image changes? Don't even boot.
Drive in another computer? Request password.
The only time the drive should unlock is when it's in the right computer with the right configuration and the right signed unified kernel image with the right kernel arguments. If your Xorg server just auto logs in and opens a terminal well...don't do that.
Miscellaneous discussions
Filesystem Selection
Most examples on Arch utilize ext4
, but other file systems such as brtfs
can work equally well. As for performance or issues with being inside LVM, this question in the brtfs FAQ implies that there is some performance loss but no other issues.
LUKS TPM enrollment and managing luks keys
Note that using systemd-cryptenroll(1) will add a key to your LUKS volume. It's not clear how exactly the option --wipe-slot=tpm2
is able to identify which slot it needs to wipe, but it does. This even works if your TPM has changed.
You can see the effects by comparing the output of cryptsetup luksDump /dev/nvme0n1p2
before and after you enroll or wipe a TPM based key. You can identify what slot your own passphrase is in with cryptsetup --verbose open --test-passphrase /dev/nvme0n1p2
and entering your passphrase. The output will inform you which key slot was unlocked.
Finally, your passphrase may be changed with cryptsetup luksChangeKey /dev/nvme0n1p2 -S 0
.
Planned TODOs
A panic command that can dump the TPM and shutdown the computer (without requiring the time of entering a password in sudo) forcing an adversary to resort to rubber-hose cryptanalysis to get to the encrypted data.