Installing Fedora 39 with the New Linux Boot (sdboot and UKI)
In October 2022, Lennart Poettering posted a blog entry
(https://0pointer.net/blog/brave-new-trusted-boot-world.html) outlining
a new more secure Linux boot design that filled in the troubling gaps
in the prior secure and measured boot systems. Since this change involved
the bootloader, systemd, the kernel and the initrd, it was difficult to
implement on an existing system. Since then we have been waiting for a
distribution to offer this in an easy to install fashion. Originally
slated for Fedora 38, it is now largely available in Fedora 39,
albeit with some limitations and some bugs. In Fedora 39, the new boot
is implemented in two parts: the replacement of Grub with Systemd boot (sdboot),
and an optional RPM package containing UKI versions of the kernel and initrd.
This document gives a step by step set of instructions on how to install Fedora 39
with the new boot system in a guest VM on top of a Fedora 38 host. It should work
on any host with a reasonably current virt-manager.
Limitations:
The eventual goal is for these features to be built in and transparently installed.
While Fedora 39 makes impressive strides in that direction, there are still some
limitations and bugs to work around.
The biggest current limitation is that sdboot and the UKI kernels are not
signed by Fedora, so you either have to turnoff secure boot (which rather defeats
the security improvements) or you have to sign the files locally. With the new
sbctl (secure boot control) tool, managing the secure boot keys and signing files
is really easy, and this approach is used and fully documented in these instructions.
The second limitation is the the Fedora UKI package is intended only for virtual
machine guests, to simplify its implementation. Since we have to sign the UKI
locally anyway, we can at the same time locally generate the UKI, so these instructions
should work on any system, not just VMs.
There are two minor bugs in the Fedora installer related to sdboot. Fortunately
these have trivial work arounds, which are included in the following instructions.
In the instructions, a '$' primpt indicates the command is run as a user. If
the prompt is '#', it is run as root.
I. Install the base Fedora 39 with sdboot:
a. Download Fedora 39 "Everything" net installer ISO image from:
https://download.fedoraproject.org/pub/fedora/linux/releases/39/Everything/x86_64/iso/Fedora-Everything-netinst-x86_64-39-1.5.iso
b. Create the VM with virt-manager. To create a VM:
[ - BUGFIX: If the host is fedora 38 first fix the default guest firmware:
# sed -i 's/ovmf/ovmf-4m/g' \
/usr/share/qemu/firmware/30-edk2-ovmf-x64-sb-enrolled.json ]
- Start virt-manager as a user:
$ virt-manager
- Click the icon to create a new VM.
- Select "local install media" (which should be the default), then click "forward".
- Browse to find and select the Everything iso you downloaded, and click "forward".
- Set CPU parameters (I used 4096 RAM, 4 threads), and click "forward".
- Set the disk size (I set 100GB), then click "forward".
- In the "Create a new virtual machine" window, select
"Customize configuration before install", then click "Finish".
- In the Installation window, first
- set "Firmware" to "UEFI x86_64:/usr/share/edk2/ovmf/OVMF_CODE_4M.secboot.qcow2",
[ "UEFI x86_64:/usr/share/edk2/ovmf-4m/OVMF_CODE.secboot.fd" on fedora 38 ]
and click "Apply".
- Click the "Add Hardware" button.
- In the "Add New Virtual Hardware" window, click on "TPM", then click "Finish"
- Back in the Installation window, click on the "Begin Installation" button.
(Be ready to press ESC repeatedly to enter UEFI setup mode, before the ISO boots.)
c. Install Fedora in the VM:
- On first boot of vm, press ESC repeatedly to enter UEFI setup, and force
secure boot setup mode:
- Use arrows to select "Device Manager" and press Enter.
- Select "Secure Boot Configuration" and press Enter.
- Select "Reset Secure Boot Keys", and respond yes to confirm.
- Press F10 to save, and press ESC to return to the top menu.
- Select "Continue" to boot to the Grub boot menu.
- BUGFIX: Once the VM has booted to the Grub menu, select the "install" entry
and edit it by pressing 'e', and add "inst.sdboot" to installer's kernel
command line. Press control-x to continue boot to the installer.
(Eventually this will be an option in the installer GUI.)
- The system will boot to the GUI installer. Run the installer as normal, except,
in the "Installation Summary" screen:
- BUGFIX: Click "Installation Destination" select "Custom", and "Done"
- In the "Manual Partitioning" screen":
- change the "Btrfs" default to "Standard Partition",
- click "Click here to create them automatically"
- select the / partition and click "Encrypt", and "Update Settings"
(This should be /dev/vda3.)
- select the /home partion and click "Encrypt" and "Update Settings"
(This should be /dev/vda4.)
- click "Done"
- enter your desired LUKS disk encryption key twice, and press "Save Passphrase"
(Remember the LUKS password - we will eventually seal one in the TPM.)
- click "Accept changes"
- Click "Software Selection"
- click "Fedora Workstation" and click "Done"
- Click "User Creation"
- set Name, username, and password as normal
- click "Advanced" and set the "Add user to the following groups" contents to
"wheel,tss"
click "Save Changes"
- click "Done"
- Click "Begin Installation"
- When installation is complete click "Reboot System" to reboot the VM.
(Note you will be asked for the LUKS key during boot. We will fix this later.)
II. Sign sdboot, and the traditional kernel and turn on Secure Boot:
a. Log in the the booted VM and Install the needed tools:
- $ sudo dnf install efitools keyutils mokutil openssl pesign sbsigntools \
kernel-devel-$(uname -r) golang asciidoc systemd-ukify
$ sudo dnf group install "C Development Tools and Libraries"
- build and install sbctl
$ git clone https://github.com/foxboron/sbctl.git
$ cd sbctl
$ make
$ sudo make install
b. Create local UEFI keys (stored in /usr/share/secureboot):
# mkdir /usr/share/secureboot /usr/share/secureboot/keys
# sbctl create-keys -d /usr/share/secureboot -e /usr/share/secureboot/keys
# sbctl enroll-keys --tpm-eventlog
c. Sign all the files
run "sbctl verify" which will list all files that need to be signed.
Sign all the files. For my system this looked like:
# sbctl sign --save /boot/efi/0042ed5443b743ee8c525d58fd4bf98f/0-rescue/linux
# sbctl sign --save /boot/efi/0042ed5443b743ee8c525d58fd4bf98f/6.5.5-300.fc39.x86_64/linux
# sbctl sign --save /boot/efi/EFI/BOOT/BOOTX64.EFI
# sbctl sign --save /boot/efi/EFI/systemd/systemd-bootx64.efi is signed
d. Check that everything is signed:
# sbctl verify
Verifying file database and EFI images in /boot/efi...
✓ /boot/efi/0042ed5443b743ee8c525d58fd4bf98f/0-rescue/linux is signed
✓ /boot/efi/0042ed5443b743ee8c525d58fd4bf98f/6.5.5-300.fc39.x86_64/linux is signed
✓ /boot/efi/EFI/BOOT/BOOTX64.EFI is signed
✓ /boot/efi/EFI/systemd/systemd-bootx64.efi is signed
e. reboot
III. Provision TPM and enroll LUKS key:
a. check that secure boot is running correctly:
# sbctl status
Installed: ✓ sbctl is installed
Owner GUID: e3593590-f537-4f3f-b6e0-c37a10f2837f
Setup Mode: ✓ Disabled
Secure Boot: ✓ Enabled
Vendor Keys: tpm-eventlog
b. Set TPM owner passsword, and create a persistent SRK at 0x81000001'
(Since the user is in tss group, we do these as user.)
Replace <my_owner_password> with yours, and remember it.
$ tpm2_changeauth -c owner <my_owner_password>
$ echo "SRK" | tpm2_createprimary -c primary.ctx -P <my_owner_password> -u -
$ tpm2_evictcontrol -C o -c primary.ctx 0x81000001 -P <my_owner_password>
c. As root, enroll the LUKS key in the TPM:
# systemd-cryptenroll /dev/vda3 --tpm2-device=auto
# systemd-cryptenroll /dev/vda4 --tpm2-device=auto
c. add ",tpm2-device=auto" to the end of the lines in /etc/crypttab
d. Rebuild initrd
# dracut -f
e. reboot
This time, the encryption keys will be obtained from the TPM automatically!
IV. Build UKI Image and sign it (as root):
a. Relabel root partition as Linux Root, type 23. E.G. if root is partition 3:
# fdisk /dev/vda
t
3
23
w
b. Create uki image with ukify - modify the paths and cmdline for your system.
Mine looked like:
# /usr/lib/systemd/ukify build \
--linux=/boot/efi/0042ed5443b743ee8c525d58fd4bf98f/6.5.5-300.fc39.x86_64/linux \
--initrd=/boot/efi/0042ed5443b743ee8c525d58fd4bf98f/6.5.5-300.fc39.x86_64/initrd \
--cmdline='rhgb rd.luks.uuid=luks-ec89f6ec-d686-44c3-a40b-e76814fa65f6 rd.auto=1 root=UUID=8873d206-b20a-4957-8000-aeec6bfb7a60 systemd.machine_id=0042ed5443b743ee8c525d58fd4bf98f'
c. This will create the default linux.unsigned.uki file. Sign it with:
# cp linux.unsigned.efi linux.signed.efi
# sbctl sign linux.signed.efi
# cp linux.signed.efi /boot/efi/EFI/Linux
d. reboot
(The new UKI boot option will be the default at the top of the boot menu.)
You now have a Fedora system running the new Linux boot, including
secure boot verification of sdboot, boot from a signed UKI image, and verification
of the kernel and initrd, and a fully measured boot.
At this point you can try out building the UKI with signed PCR policies
(see the man pages for ukify and systemd-crptenroll for details.)
You can also make all TPM secrets recoverable with the TPM_KEYS package
(https://github.com/safforddr/tpm_keys), and use the TPM to HMAC all shadow passwords, with the crypt_tpmhmac package (https://github.com/safforddr/crypt_tpmhmac).