Linux UEFI Secure Boot with systemd-boot
Understand UEFI Secure Boot in Linux. Learn how it works and follow this step-by-step guide to easily configure Secure Boot using systemd-boot.
What is UEFI
UEFI stands for Unified Extended Firmware Interface. It is a sofware extension that runs with BIOS (shipped into the motherboard) and brings modern features to the boot process.
Unlike BIOS, UEFI has the ability to read and load operating systems from modern block devices like SSD (Solid State Drive), partitions like LVM and RAID or block devices formatted with the modern GUID Partition Table (GPT).
Where are UEFI settings stored
UEFI settings are stored inside the NVRAM (Non-volatile Random Access Memory), which is a dedicated chip on the motherboard (often the same chip that contains the BIOS/UEFI firmware).
UEFI uses settings stored inside the NVRAM to locate bootloaders and operating systems binaries or to know the devices ordering to use during boot. These information are stored inside the NVRAM from the Linux operating system during boot utilities installation (first/second stage bootloaders) and can be inspected or edited with tools like efibootmgr. Linux exposes UEFI settings through the following pseudo filesystem: /sys/firmware/efi/efivars.
How UEFI loads bootloaders binaries
UEFI loads bootloaders and operating systems binaries only from disks using the GPT (GUID Partiton Table), from a special partition called the ESP (EFI System Partition). The ESP partition should be formatted in FATx (16, 32, vFAT) and is usually mounted at /boot/efi.
UEFI locates bootloaders binaries by using information stored inside the NVRAM by installed bootloaders. If not found or the NVRAM empty, it looks for the standardized fallback bootloader binary location inside the ESP partition at: <ESP>/EFI/BOOT/BOOTX64.efi.
What is UEFI Secure Boot
UEFI Secure Boot is a security mechanism used in UEFI boot mode to ensure that binaries loaded during boot (bootloader, kernel, etc) are the same as those provided by their original publishers. If an attacker modifies the bootloader binary, the system will not boot.
How UEFI Secure Boot works
UEFI Secure Boot uses a chain of trust to verify the binaries used during the boot. The first binary in that chain of trust is shim. Shim binary is signed by Microsoft and its signature can be verified by the BIOS/UEFI firmwares embedded inside the motherboards of well known computer manufacturers.
Once the shim binary is verified and loaded, shim verifies its trusted stores of allowed/denied public key certificates and binaries hashes to know if the bootloader can securely be loaded or not.
Where should shim binary be placed for secure boot
Shim binary should be located in operating systems specific folders inside the ESP partition. For instance, the shim binary for a Linux Debian operating system should be located at: <ESP>/EFI/Debian/shimx64.efi.
The shim binary could also be placed at the UEFI boot fallback binary path: <ESP>/EFI/BOOT/BOOTX64.efi. This ensures that the shim binary is loaded even if the NVRAM is empty.
How shim locates, verifies and loads bootloaders
Shim looks for the bootloader inside its current directory. The name of the file it looks for is grubx64.efi. This is not necessarily the binary of the GRUB bootloader as its name could tell. It could be any bootloader of your choice.
Shim verifies the integrity of bootloaders by using public key certificates and binary hashes enrolled inside its trusted stores. Bootloaders inside Debian packages repositories are signed by Debian, and the required public key certificates to verify their signatures are enrolled either inside the shim executable directly or available as UEFI NVRAM variables.
UEFI NVRAM variables used by secure boot as trusted stores
Here are UEFI NVRAM variables used by secure boot to find trusted certificates that should be used to verify bootloaders / kernel binaries signatures and their blacklist equivalent, that are used to deny execution of vulnerable bootloaders / kernels:
-
db- database of trusted Public Key Certificates or raw binary hashes. Managed by the BIOS firmware. Certificates and hashes inside this database are added by the motherboard manufacturer. Binaries signed with public keys in this database or whose hashes are present in this database (even when unsigned), will be allowed to execute. The equivalent blacklist database that is used to deny execution of binaries even when allowed by other databases isdbX. -
MokList- Machine Owner Key allowlist database. Populated by shim through the Linux distribution or the user (through the mokutil tool). Contains certificates for distributions provided or vendor specific bootloaders and also authorized binaries hashes. Also contains certificates to verify users custom kernel / kernel modules signatures. The equivalent blacklist database that is used to deny execution of binaries even when allowed by other databases isMokListX. -
vendor_authorized- trusted certificates embedded inside the shim executable by the Linux distribution maintainer. -
shim_signature- EFI binaries files inside the same directory as the shim binary whose names start withshim_signature_*. Contains additional sections allowing operating systems to ship extra certificates without modifying firmware variables.
Once the bootloader binary is verified, it is loaded and then able to load operating systems binaries.
How systemd-boot verifies the integrity of Linux
Systemd-boot delegates the verification of the Linux operating system integrity to the UEFI boot service. The UEFI boot service then uses the motherboard native firmware for the checkup. The firmware evaluates the kernel signature againts it trusted stores (db and dbx) and shim's MOK database.
Using systemd-boot as bootloader
Systemd-boot installation
To configure systemd-boot as the default bootlader:
bootctl install [--esp-path=PATH --boot-path=PATH]
This command will install systemd-boot bootloader inside the ESP partition at:
<ESP>/EFI/systemd/systemd-bootx64.efior<ESP>/EFI/systemd/systemd-bootx64.efi.signed- if the distribution automatically handles secure boot)
A copy of the systemd-boot binary file is also made into the standardized bootloader fallback path at: <ESP>/EFI/BOOT/BOOTX64.efi. That latter path will be used to load the bootloader in case information about the ESP partition (UUID for instance) and the path to the bootloader binary are not found inside the NVRAM.
The command will also deploy necessary configuration files allowing menu and bootloader entries configuration:
<ESP>/loader/entries/*.conf- menu entries configuration files, one per menu<ESP>/loader.conf- main configuration file where we define the default boot menu and settings like temporary menu editing, menu display timeouts, etc.
For more about systemd-boot run:
man 7 systemd-boot
Systemd-boot configuration files
Sample default settings configuration (/boot/efi/loader.conf):
timeout 3
default debian.conf
editor no
console-mode max
For details about all the settings you can put inside that file run:
man 5 loader.conf
Sample menu entry configuration (/boot/efi/loader/entries/*.conf):
title Arch Linux
linux /vmlinuz-linux
initrd /intel-ucode.img
initrd /initramfs-linux.img
options root=PARTUUID=YOUR-PARTITION-UUID-HERE rw
For details about all the settings you can put inside that file have a look at systemd-boot menu entry settings.
Interesting commands for managing systemd-boot
- Get details about the system boot mode (secure boot or not) and bootloader files used to boot the system:
bootctl status
- Reboot the system and use a specific menu entry to boot:
systemctl reboot --boot-loader-entry=...
- Reboot the system to display systemd-boot menu:
systemctl reboot --boot-loader-menu=...
Want to report a mistake or ask questions ? Feel free to email me at gmkziz@hackerstack.org. I will be glad to answer.
If you like my articles, consider registering to my newsletter in order to receive the latest posts as soon as they are available.
Take care, keep learning and see you in the next post 🚀