‹ back home

A simple boot setup with SecureBoot

2021-06-11 #boot #kiss #linux #sbctl #uefi

I use a pretty simple setup for booting my systems.

That’s the basic boot process. Note that the bootloader is a single binary, and the bundle is another single binary. There are no moving parts, just both binaries are protecting from tampering by being signed. There are no extra modules, configuration files, or alike.

The OS itself has some extra steps before i see an actual desktop.

For the remainder of this artile, it is assumed that the EFI ESP partition is mounted in /efi. There is no /boot partition.

systemd-boot

(reminder: gummiboot is basically the same tool for non-systemd setups)

First of all, copy the bootloader into the EFI partition and register it with the UEFI firmware. Both things can be done with one command:

bootctl install

On startup, systemd-boot will automatically look for signed bundles in EFI/Linux. Since I usually have my primary bundle and a backup one, I manually configure it to prefer the main one:

# The bundle is located in /efi/EFI/Linux/arch.efi:
bootctl set-default arch.efi

systemd-boot requires no additional configuration.

I don’t configure a menu with a timeout since I want the system to boot directly without any prompt. In case I need to boot from the recovery one, the menu can be triggered by spamming Space during start-up.

Note that the menu will only allow picking another stub to boot from, but overriding the cmdline is not possible since it’s embedded in the bundle. This prevents random strangers from executing arbitrary commands.

sbctl

sbctl is a tool to manage SecureBoot keys and generate signed bundles with the initrd, cmdline and kernel. Keeping all items in a single signed bundle is important. As a counter-example: if the cmdline is just a file in the same partition, it can be altered, which would allow easily executing unsigned code.

The UEFI firmware first needs to be configured to use a key that will be generated and managed by sbctl.

Prepare the firmware

Set up the OS

sudo sbctl create-keys
sudo sbctl enroll-key

Generate the bundles

sbctl will read the cmdline from /etc/kernel/cmdline, so make sure to put your current cmdline there. This can be done with:

cat /proc/cmdline | sudo tee /etc/kernel/cmdline

My partitions has the right Partition Type set, so systemd knows what partition to pick up based on its id. This follows the Discoverable Partitions Specification. The Partition Type can be safely updated for partitions, even if currently mounted (since this updates the GPT, not the partition itself).

Then, generate the sbctl bundle:

sbctl bundle --sign /boot/EFI/Linux/arch.efi

The bundle will use the existing initrd and kernel. So each time mkinitcpio generates a new initrd, the bundle needs to be rebuilt. Fortunately, sbctl includes pacman hooks to re-build the bundle when you update the kernel or the initrd.

The file /etc/kernel/cmdline is read on each re-generation, so don’t delete it. It’s also the file you want to update when you need to make changes to your cmdline.

Finally, you should also sign the bootloader itself:

# This is the entry explicitly configured in the UEFI settings.
# The entry for it MAY be deleted during firmware updates.
sbctl sign --save /efi/EFI/systemd/systemd-bootx64.efi

# This is default fallback in case there are no entries in the UEFI.
# If you dualboot, some aggressive OSs may overwrite it:
sbctl sign --save /efi/EFI/BOOT/BOOTX64.EFI

Turning on SecureBoot

Reboot and cleanup

You should be able to reboot with SecureBoot turned on. The amount of moving parts in the whole boot process are extremely few, and it’s also very low-maintenance.

I’d recommend also removing all other unnecessary boot entries from the firmware using:

sudo efibootmgr --delete-bootnum  --bootnum 00XX

If you’re unsure which one you should leave, delete all of them and re-run bootctl install afterwards.

Breakage recovery

This setup is unlikely to break by itself – but as with any Arch setup, it may break due to the user fiddling with things (e.g.: a broken cmdline).

I have a small tool that creates the above-mentioned backup bundle after every successful boot. If I break the main bundle and it doesn’t boot, then that image is kept around, and I can reboot off it.

As mentioned above, tapping Space during startup shows the boot entry menu.

Credit

sb-backup relies on go-uefi and sbctl, so thanks to the author for doing all the hard lifting.

Update 1

It seems that using SecureBoot with a discrete GPU can be problematic in some cases. Since the GPU’s firmware is not signed, it may fail to load in some cases due initialisation order, and may result in a bricked motherboard.

I’d recommend you do some serious research before trying this if you have a discrete GPU. See this reddit thread for more details.

— § —