#Measured boot with systemd

I travel a lot. (Well, at least in the pre-COVID era, I did.) This means I drag my Framework 13 (and formerly my Thinkpad X1 Carbon gen 6) to many places, and often leave it unattended in hotel rooms. While my data is all encrypted-at-rest, getting access to the data when the machine is first powered on necessarily involves running some code that is neither encrypted nor authenticated: namely, the bootloader and enough of the operating system kernel to decrypt and mount volumes.

Furthermore, this process requires me to type in a passphrase every time I boot the machine, which exposes the passphrase to anyone observing surreptitiously, and is in general an annoyance.

To increase the complexity of someone instrumenting my laptop without my knowledge and in doing so gaining access to secrets as I use the compromised machine unawares, I decided I needed to explore the use of trusted computing technologies to protect my installation from tampering.

Measured boot

There are a number of technologies that can reduce (but of course not eliminate) the attack surface of a machine. For this purpose, I am limiting my evaluation of technologies to those that protect the boot sequence from most kinds of tampering. Other measures are required for runtime protection against zero-day remote exploits, physical attacks that employ USB DMA, tire irons, etc., as well as some advanced kinds of physical tampering.

It's a wrench!

My adversary is someone who intends to tamper with my machine without my knowledge, with the purposes of either making off with some information or instrumenting the machine such that information encrypted-at-rest is revealed to the attacker out-of-band. There's not a lot of value to me in tamper prevention: once someone's screwed with my machine, I am unlikely to trust it again regardless. My main goal is tamper detection. I thus settled on measured boot, which measures the sequence of executable code launched during the boot process in a way that is highly resistant to forgery: at the end of this process, this measurement can be used to verify that the machine has not been tampered with, as well as to unlock disk encryption keys that can be used to mount volumes without requiring the user enter a passphrase.

Juniper has a short page outlining the differences between measured boot and secure boot.

Requirements

I used to maintain a set of scripts for implementing measured boot on Debian, but it looks like the systemd folks have finally lapped me and put together a much more flexible and complete solution using systemd-cryptenroll. The basic steps for doing this are outlined on a Arch wiki. I used dracut.

NB: You are well-advised to avoid taking bits and pieces of the process from from ChatGPT or other sources, because some steps you might find (like creating a public/private key pair in /etc/systemd) will confuse systemd and cause basic measured boot to fail. So, for instance, to rebuild initrd I need to do dpkg-reconfigure linux-image-<version> rather than the dracut -f I see recommended in some places because the latter fails with an obscure error that I haven't had time to track down.

My requirements haven't changed much from when I was maintaining my own scripts, except for now accepting the use of the systemd-boot boot loader:

As a result, I settled on PCRs 0, 2, 4, 11, 12, and 13 to cover all executable code prior to mounting the root partition:

There may be redundancy in the above set of PCRs (what various components of systemd are measuring into each PCR seems to be covered in detail here), but it's basically free to include stable values, so I'm okay with that.

Here are the pros and cons so far of my experience relative to my own scripts. The pros:

The cons:

References