Booting Arch Linux Like It's 2012; Secure Boot and TPM

I recently got a new job and with that came a new Laptop: a Dell XPS 13 Plus 9320. The first thing I did before installing the OS, is something that many Linux users will be familiar with: I disabled secure boot. This has been a tried-and-true ritual of mine. But as with all rituals, there comes a time when we need to change them. At the same time, I wanted to modernise other parts of my booting process, while still keeping it reasonably secure. To understand why I did this, I will start by giving a short overview of how I do it “currently” and then show what I did to improve the situation.

Normally, I encrypt my main root partition with LUKS while my boot partition stays unencrypted. I do this because encrypting the boot partition has downsides: it only supports LUKS 1 which would mean entering 2 passphrases. Not encrypting the boot partition means being open to evil maid attacks. But even encrypting the boot partition does not save one from this vector, because the ESP stays unencrypted. Secure boot solves this problem: it only allows booting operating systems that were signed and makes sure that the “boot chain” is secure. This does not prevent all attacks, but provides a reasonable hurdle for most use cases.

Configuring secure boot enables another convenience feature: unlocking the root partition with a key from the TPM rather than a passphrase. To do this, we need to configure the following components:

  • systemd-boot
  • mkinitcpio
  • secure boot
  • systemd-cryptenroll

systemd-boot

NB: This part is not strictly necessary, I think it would theoretically be possible to configure GRUB to do the same thing, but it “fits” well into the rest of the setup.

sudo bootctl install

After installing it, we need to add some configuration. First, we edit the file /boot/loader/loader.conf.

timeout 0
console-mode max
default arch.conf
editor no

Then we create the default boot entry (arch.conf).

title   Arch Linux
linux   /vmlinuz-linux
initrd  /intel-ucode.img
initrd  /initramfs-linux.img
options root=/dev/mapper/root rw loglevel=3 quiet splash

To update our Bootloader every time the kernel or systemd is updated, we have 2 options. The first option is using systemd-boot-update.service. This updates the bootloader on the next reboot. This option won’t work for our setup, since we need to generate and sign the boot loader files before we reboot. To solve this issue, I installed systemd-boot-pacman-hook (AUR). There are other solutions for this problem, but this seemed the simplest. It also works well with the workflow of sbctl that I will go into later in this article.

mkinitcpio

After installing the bootloader, we need to make sure that our disk can be decrypted. We do this by configuring the initramfs. I will be using the default tool under Arch Linux (mkinitcpio). If you’re using dracut the setup should be similar, but I have not tested it.

There are 2 ways of decrypting a drive with mkinitcpio: the encrypt or the sd-encrypt hook. The sd-encrypt hook uses systemd, and it is the one I will be using. To configure this hook, first we need to configure /etc/crypttab.initramfs. This file gets added to the initramfs and contains the devices that should be decrypted by systemd-cryptsetup-generator:

root	UUID=<label>	none	tmp2-device=auto

The last column specifies that we want this drive to be automatically unlocked using a key stored in the TPM. If you want to use a passphrase, omit the last two columns.

We also need to set the correct hooks. To do that, we edit /etc/mkinitcpio.conf and change the HOOKS variable to the following:

HOOKS=(base systemd autodetect modconf kms keyboard sd-vconsole block sd-encrypt filesystems fsck)

Secure boot

This is the most finicky party of the setup, and the exact way of doing this depends on your hardware manufacturer/UEFI implementation. To use secure boot, there are two strategies: using your own keys, or using a signed bootloader. Most distros take the second route. I will be implementing the first one.

Note: Doing this can brick your device. Please make sure you understand what you are doing and don’t blindly follow the instructions in this article.

It’s a good idea to back up the variables, in case something goes wrong. These can then be restored later, if your UEFI supports it.

for var in PK KEK db dbx ; do efi-readvar -v $var -o old_${var}.esl ; done

After that, we need to put the UEFI in “Setup Mode”. This mode is activated when the Platform Key (PK) is deleted. After deleting the key and rebooting, we can generate our own keys with sbctl.

sudo sbctl create-keys

After that, we can enroll these keys into the UEFI. The -m also enrolls Microsofts keys. This is necessary on many motherboards, since they use these keys to sign their firmware. Only leave this option out if you know what you are doing.

sudo sbctl enroll-keys -m

After that, we can sign our kernel image and boot loader.

sudo sbctl sign -s /boot/vmlinuz-linux
sudo sbctl sign -s /boot/EFI/systemd/systemd-bootx64.efi

Since I use fwupd I signed that one as well.

sudo sbctl sign -s /boot/EFI/Arch/fwupdx64.efi

After they are added once, the files will be automatically resigned every time the kernel or systemd are updated. This is done via a pacman hook. For that reason, we installed systemd-boot-pacman-hook earlier: this automatically regenerates systemd-bootx64.efi when systemd-boot is updated. After that, it will be signed by the pacman hook of sbctl.

systemd-cryptenroll

Before we generate the key, we need to think about when we want systemd to use the TPM. This is configured using so-called Platform Configuration Registers (PCRs). One of these is the secure boot state (7). This means systemd will only unlock the drive if secure boot is enabled and no secure boot errors have been reported. This is the default setting. Some guides on the internet also use PCR 0 (firmware). To me personally, this does not make much sense. I’m not an expert, but I would think that we have to inherently trust the firmware anyway, since a malicious firmware could theoretically also falsely report PCR 0. If anyone has a good answer to this, feel free to contact me.

In the meantime, I’ve decided to not use PCR 0 since it would be quite annoying (rebinding every time the firmware is updated).

With that discussion out of the way, we can generate our key.

sudo systemd-cryptenroll --tpm2-device=auto /dev/sdX

If you’re paranoid, you can add a PIN as well with --tpm2-with-pin=true.

It’s also sensible to generate a recovery key and store it somewhere safe. This can be used when systemd can not decrypt the drive from the TPM.

sudo systemd-cryptenroll --recovery /dev/sdX

I’m very happy with this setup. This problem has been in the back of my mind for some years now, and I’m glad I finally tried to solve it. I will probably be using this setup on all of my future Linux machines.


Articles from blogs I read - Generated by openring