User:Krin/Secure Boot, full disk encryption, and TPM2 unlocking install

From ArchWiki

This article is not officially supported.

The Arch Linux community does not offer support for the information contained in this page; for installation procedures, the Installation guide is the only officially supported document. The content below is mainly maintained by User:Krin, who last reviewed it on 1 October 2021, and it may be out of date or inaccurate.

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

Warning: If you use BitLocker on your Windows drive, ensure you have your recovery key handy. The installation process will change your TPM measurements causing Windows to require you enter the key manually the next time you boot it.

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.

Tip: Using a memorable LUKS passphrase until you've completed the install process can be helpful. It will take several reboots to finish setup.

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
Warning: If the archiso kernel version (or type) differs from what is installed in your chroot, find the appropriate name in /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

Note: Credit to this blog post by lunaryorn for the inspiration.

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

Warning: Yes, this will cause a system to automatically unlock it's encrypted root volume without any interaction from a human. Yes, this exposes it to any sufficently advanced Evil Maid attacks. If sealed against the appropriate registers (especially register 8 with the kernel arguments), the TPM measurements should force systemd to ask for your LUKS passphrase in almost any attempt to access the encrypted volume from outside your normal boot process.

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.

Warning: These steps are only necessary for dracut 055

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.

Note: A reboot may be required to convince 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.