2024-07-05 | Fedora 40 and signed UKIs
Warning: This post is intended for advanced Linux users.
About Secure Boot
Secure Boot is an UEFI feature which allows only signed EFI executables to be loaded. By default most computers have pre-loaded Microsoft's keys. Of course, Windows boot facilities are signed by these.
Some Linux distributions ship with a preloader called shim for licensing reasons.
But the shim solution only allows to load these distributions without messing with secure boot. But what if you encrypt your data. They may be safe from being accessed, but it's still possible to mess with your kernel or initramfs, usually stored on unencrypted partition.
To solve this, we can pack kernel, initramfs and kernel cmdline parameters into a single EFI executable called Unified Kernel Image (UKI) and sign it with our keys. If we replace the Microsoft keys in firmware with our owns, we can be sure (ofc. unless some bug or backdoor occures) that only executables signed by our keys can be loaded.
I tried this on Arch Linuxbtw which is fairly trivial.
But I also wanted to try this on Fedora, a distribution which isn't that DIY friendly as Arch Linuxbtw is. So here it is.
For this, we are going to use
- sbctl – program for managing secure boot keys – systemd-boot modern and simple UEFI bootloader with UKI autodetection
For existing installations
If you already do have a Fedora installation, you can just skip the installation, do firmware setup and then skip to Setting up secure boot
You may need to perform some adjustments. For example partitioning, in case your EFI partition is too small
(tip: /boot
partition is unnecessary in this setup).
Getting Fedora
This article uses Fedora 40 Workstation. These procedures may be outdated in next releases.
First of all, download and write the Fedora Workstation image. You can obtain it and read how to verify and write it here.
Installation
Here, I will be showing how to install Fedora using typical methods and then modifying the installed system, so most of the typical installation applies.
Firmware setup
First of all, we need to configure secure boot to be ready for custom keys. Entering firmare setup is usually done by pressing specific key, often shown on POST screen.
On systemd Linux system you can try systemctl reboot --firmware-setup
Then, you need to switch secure boot to Setup Mode. Sadly, this process varies across firmware setups and often isn't even marked as such. I recommend consulting a documentation or searching online.
- For my ASUS AM5 mainboard firmware setup, in
Security → Secure Boot
settings, you need to switch the mode toCustom
mode and erase default keys. - On my ThinkPad T470p it's under
Security → Secure Boot
Partitioning
- Select
Full disk summary and bootloader…
in bottom left corner - Select your target disk
- Press
Do not install bootloader
to remove theBoot
check
Now create an EFI partition. There the signed bootloader and UKIs are going to be stored.
- Select Blivet GUI and click
Done
- Create a new partition
- Filesystem: EFI System Partition
- Mountpoint:
/boot/efi
- Size: 1 GiB
For root partition, I recommend using LUKS2 to encrypt the partition as secure boot would otherwise be pointless. Fedora by default uses BtrFS. The default layout looks like:
- Subvolume
root
mounted to/
- Subvolume
home
mounted to/home
Setting up secure boot
Open a console and enter /mnt/sysroot
$ sudo chroot /mnt/sysroot
Then install sbctl, tools for signing the UKIs with Dracut, and setup the keys.
# dnf copr enable chenxiaolong/sbctl
# dnf install sbctl sbsigntools
# ln -s /dev/null /etc/kernel/install.d/91-sbctl.install
# sbctl create-keys
Created Owner UUID cd977b6b-34f6-4a2a-aba2-50500da4efce
Creating secure boot keys...✓
Secure boot keys created!
Bootloader and UKIs
First, let's uninstall GRUB as we are going to use systemd-boot. And remove kernel and initramfs (and related stuff) as we are going to build our own UKIs.
# dnf --setopt=protected_packages= rm grub\*
# kernel-install remove $(ls /usr/lib/modules)
# rm -rf /boot/{efi/EFI,grub2,loader,vmlinuz*,initramfs*,symvers*,config*,System*,.vmlinuz*}
Now let's install and sign systemd-boot
# dnf in systemd-boot
# sbctl sign -s /usr/lib/systemd/boot/efi/systemd-bootx64.efi
-o /usr/lib/systemd/boot/efi/systemd-bootx64.efi.signed
# bootctl install
Setting up UKI building
Now configure ``kernel-install` not to install kernel or care about that and leave it for Dracut. Then we update and configure Dracut to build UKIs.
# dnf up dracut
# echo "layout=other" >> /etc/kernel/install.conf
# cat << EOF > /etc/dracut.conf.d/20-uki.conf
uefi=yes
uefi_secureboot_cert=/usr/share/secureboot/keys/db/db.pem
uefi_secureboot_key=/usr/share/secureboot/keys/db/db.ke
dracut_rescue_image=no
EOF
Kernel cmdline
Now it's time to set kernel cmdline parameters. These are by default stored in /etc/kernel/cmdline
- Read
/etc/kernel/cmdline
. If it contains newlines, replace them with spaces. - Edit
/etc/dracut.conf.d/10-cmdline.conf
- Paste the parameters inside
kernel_cmdline+=" HERE "
(including the spaces)
Fwupd and reinstalling kernel
Now let's configure fwupd, utility for updating firmware. It updates UEFI firmware by running an EFI executable which performs the update. There is already a signed one, not sure with which keys, but anyways, we are going to sign it with our owns.
We are also going to configure fwupd so it works with out custom keys + systemd-boot configuration.
- Edit
/etc/fwupd/fwupd.conf
. - If not exists, create section
[uefi_capsule]
- These, set
EnableGrubChainLoad=false
- Also set
DisableShimForSecureBoot=false
# sbctl sign -s /usr/libexec/fwupd/efi/fwupdx64.efi.signed
# dnf rei kernel-core
Optionally, you can also edit /boot/efi/loader/loader.conf
to eg. add bootmenu timeout.
Now you can exit
the chroot and reboot your computer.
Enrolling keys
In order to firmware to check the executables, we need to enroll the keys to the firmware. Open a console in installed system after setting it up and run following command:
$ sudo sbctl enroll-keys
In the key enrollment process, sbctl can present you an error message that with custom keys only, OptionROMs may not load. You can use one of these arguments to deal with it:
- Parameter
-m
: Loading Microsoft keys. This should just work, but you give away
the control over what can be loaded over to 3rd party (Microsoft). - Parameter
-t
: Read checksums of OptionROMs from TPM eventlog and loads them
to DB database. Might not be available on all computers. Experimental. - Parameter
--yes-this-might-brick-my-machine
: This won't do anything, so OptionROMs
may not load. Be aware it may make hard to fix your computer
(eg. GPU's OptionROM, can't access firmware setup).
On my desktop computer, when trying this with Arch Linuxbtw I went with the TPM eventlog option and everything worked fine. On my laptop as no OptionROMs are used, it didn't ask me at all.
And we are done!