Fedora Rawhide and Systemd/UKI - A status Update
The current Fedora Rawhide (development for Fedora 41 as of 5/25/2024) offers some significant additions to Trusted Booting. Most notably it adds an optional plugin to rpm that automatically adds Fedora signatures to all files for IMA measurement and appraisal. This is a huge improvement not only for supporting local IMA appraisal, but also for supporting the remote verification of TPM based attestation. Now IMA measurement with the ima-sig template will include a file's hash and a Fedora signature on that hash in each measurement event. The verifier can easily verify that all measured files are authentic Fedora files, by verifying their signatures with the Fedora public certificate. These signatures are updated automatically with dnf updates and upgrades.
Taken together with the previously available Trusted Boot enhancements, and some additional optional packages, a Fedora system now can have:
Secure boot of a signed UKI kernel+initrd+cmdline
no more appraisal gap in the initrd
enhanced measurements in PCRs 11 and 15
Simplified Management of all secure boot keys
Measurement, appraisal, and attestation of all files signed by Fedora
Use of the TPM to unlock all encrypted partitions at boot
TPM provisioning for backup and restore of all stored keys across TPM failure
Comments on Booting
The new UKI kernel cannot be booted from Grub.
It can be booted from:
sdboot (This was how the version on Fedora 39 was booted)
UEFI Boot Menu
shim
Booting from sdboot is convenient, as it has shim support for Fedora signed uki, and it has an attractive menu at boot. It is, however the hardest to install, as shown in the Fedora 39 instructions.
Booting from the UEFI boot menu is a little less friendly - you have to remember which key to press to get the boot menu, and have to time it well at boot time. In addition, the UEFI does not support Fedora's signature keys. If you want to sign your own UKI, you have to add the keys to the UEFI db.
Fedora 40 boots the UKI directly from the shim. The shim has been extended to look at its UEFI boot variable, where the target UKI file path has been appended. Fedora 40 added a utility "kernel-bootcfg" as a friendly front end to the underlying efibootmgr. Booting from the shim is convenient, as it supports Fedora's keys, and it is already installed. As with booting directly from the UEFI menu, it is tricky to get the UEFI boot menu if you want to boot something other than the default.
Comments on Keyrings and Key Management
Secureboot, LUKS partition encryption, and IMA appraisal depend on a number of keys. These keys are stored and managed in a number of locations, and can be used in only certain ways, which can be a bit confusing. Keys can be stored in:
UEFI secure boot storage hierarchy of PK, KEK, and DB
MOK keys, maintained by the boot SHIM
Keys built into the kernel
Keys loaded into the kernel from the initrd
Keys in the TPM
LUKS keys stored in a partition header
In Linux keys are loaded onto keyrings for use. These keyrings include:
.platform (UEFI DB keys)
.machine (MOK keys loaded by the SHIM, including IMA-CA keys)
.ima (IMA appraisal public key certificates, which must be signed by IMA-CA key)
With secureboot enabled, UEFI will not load the first efi executable unless it is signed by a key whose public key certificate is in the UEFI DB. Most commercial systems ship with Microsoft and OEM keys in the DB. Linux distributions did not want users to have to add keys to the DB, so they created a shim that is signed by Microsoft, and which maintains a MOK list of keys needed to boot the Linux distribution. As the kernel boots, it loads the UEFI keys into the .platform keyring, and the MOK keys into the .machine keyring. Keys used by IMA to appraise the signatures on files are loaded onto the .ima keyring by code in the initrd. These IMA keys cannot be loaded unless they are signed by a CA key which is already loaded on the .machine keyring. With secureboot enabled, if you want to load a new IMA policy file (and we do), it must also be signed by a key on the .ima keyring.
So to summarize, we have to boot the SHIM, to be able to load an IMA-CA key, so that we can load an IMA appraisal key on the .ima keyring. Since the SHIM is signed by Microsoft, we don't have to add any keys to the DB, although for tightest security we may want to remove unneeded keys from the DB. This is strictly optional, but will be shown in the following detailed installation instructions.
As an example, here is the .platform keyring (basically the UEFI DB) for my desktop:
root@fedora:~# keyctl show %:.platform
Keyring
974482125 ---lswrv 0 0 keyring: .platform
906701518 ---lswrv 0 0 \_ asymmetric: MSI SHIP DB: ebc30d5be5f35f8041c1c2d9e613eee2
373368820 ---lswrv 0 0 \_ asymmetric: Red Hat, Inc.: fedoraca: b280c7ae6b884e0f4d2a0d8724c25eaf6c65c326
27530299 ---lswrv 0 0 \_ asymmetric: Microsoft Windows Production PCA 2011: a92902398e16c49778cd90f99e4f9ae17c55af53
349870059 ---lswrv 0 0 \_ asymmetric: Microsoft Corporation UEFI CA 2011: 13adbf4309bd82709c8cd54f316ed522988a1bd4
Here are the keyrings that result from installing Fedora Rawhide according to the following instructions:
root@fedora:~# keyctl show %:.platform
Keyring
39127028 ---lswrv 0 0 keyring: .platform
568656572 ---lswrv 0 0 \_ asymmetric: Database Key: 4ad183226cb3782590ba640dcc64b71d
848605927 ---lswrv 0 0 \_ asymmetric: Red Hat, Inc.: fedoraca: b280c7ae6b884e0f4d2a0d8724c25eaf6c65c326
root@fedora:~# keyctl show %:.machine
Keyring
979512042 ---lswrv 0 0 keyring: .machine
208043151 ---lswrv 0 0 \_ asymmetric: Fedora IMA CA: a8a00c31663f853f9c6ff2564872e378af026b28
256133155 ---lswrv 0 0 \_ asymmetric: IMA-CA: IMA/EVM certificate signing key: 053d15b3dc9cd8037c279f312c15d1898907ddfa
root@fedora:~# keyctl show %:.ima
Keyring
103441207 ---lswrv 0 0 keyring: .ima
481320317 ---lswrv 0 0 \_ asymmetric: Fedora kernel signing key: bdd97e5f021e87305c0853e144d02b0c516919d4
6610314 --als--v 0 0 \_ asymmetric: fedora: dave signing key: 7acd5d5e0e6765c2aa7aea8e4c4af1e1174f5c3b
155080207 --als--v 0 0 \_ asymmetric: Fedora 42 IMA Code-signing cert: a1a5c4c8d90554e0ce5c07c9e127f20362f02aa4
576236972 --als--v 0 0 \_ asymmetric: Fedora 41 IMA Code-signing cert: 158befb98fc2ee070833d1a2a46669e7876d7435
114564040 --als--v 0 0 \_ asymmetric: Fedora 40 IMA Code-signing cert: 2defa2e1d528db308d3e1ca28274aa40a3204a9e
30695816 --als--v 0 0 \_ asymmetric: Fedora 39 IMA Code-signing cert: 155266a4a3ea7bdddc9e38ddb192c2d2388b603e
Note that the .platform keyring (from UEFI) no longer has any Microsoft keys, but does have a Red Hat key, and a local key.
The .platform keyring (from the SHIM's MOK) has Fedora CA and local CA keys.
The .ima keyring has several Fedora appraisal keys, and one local (dave) signing key, so that I can sign and load a new IMA policy. (more about that later).
Comments on Verification of the Event Log:
Most notably this Rawhide adds Fedora signatures to all files installed from an RPM. For attestation this is huge. Even if you don't want to enable IMA apprasial, simply turning on IMA measurement with the ima-sig template causes every event log entry to contain a files hash and Fedora signature. If the event log is sent to a remote verifier, all it needs to verify the files as authentic, untampered Fedora files is to verify the signatures with the Fedora signature public key certificate. The following instructions show how to enable this attestation with the kernel command line options "ima_template=ima-sig" and "ima_policy=tcb".
Fedora 40 has also significantly extended the measurement system. Most importantly it has added the measurement of the root partition ID and volume key to PCR-15. Because the event log does not contain the secret volume key, there is no way for the verifier to verify the digest for this event - the verifier can only tell if it has changed or not. In addition, the verifier needs the digest that was extended for this event, as it cannot calculate it from the event content. In earlier versions of systemd, the events were logged in journald, and did not contain the digests.
Fortunately systemd version 255 in Fedora 40 adds an explicit event log in the file /run/log/systemd/tpm2-measure.log. This file is in json format, and is easily converted to CEL format for verification. With this new log, it is possible to verify the overall events for PCR-15, although it is still not possible to verify the event contents for the root/volume key event.
Note that there is currently (as of 5/27/2024) still a bug in the selinux policy that blocks the creation of the tpm2-measure.log. The cel_utils that are installed are able to extract the systemd events from either this log, or from systgemd's journal.
Detailed Installation Instructions
These instructions are for installation in a virt-manager VM running on a current Fedora. You should be able to install it on other systems or even on bare metal, but that has not yet been tested.
In the instructions, a '$' prompt indicates the command is run as a user. If
the prompt is '#', it is run as root.
I. Install the base Fedora Rawhide:
a. Download Fedora Rawhide installation image. I used:
Fedora-Everything-netinst-x86_64-Rawhide-20240525.n.0.iso
from https://openqa.fedoraproject.org/nightlies.html
b. Create the VM with virt-manager. To create a VM:
- 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 40GB), 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",
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.
c. Install Fedora in the VM:
- The system will boot to the GUI installer. Run the installer as normal, except,
in the "Installation Summary" screen:
- Click "Installation Destination" select "Custom", and "Done"
- In the "Manual Partitioning" screen":
- click on "Encrypt my data"
- change the "Btrfs" default to "Standard Partition",
- click "Click here to create them automatically"
- 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", "C Development Tools", "Development Tools" 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. Install rpm-plugin-ima and add Fedora signatures to all files
First install the plugin and reinstall all packages to apply the Fedora signatures:
# dnf install rpm-plugin-ima openssl
# dnf reinstall --skip-unavailable $(rpm -qa)
fedora-gpg-keys-41-0.2.noarch installed the IMA code signing keys and the IMA-CA key:
$ ls /etc/keys/ima/
$ ls /usr/share/ima/ca.der
We will use mokutil to load the IMA-CA key.
# mokutil --import /usr/share/ima/ca.der
mokutil will ask for a temporary password which you will need to add the key when you reboot.
Now we need to add our own local IMA-CA and signing keys, so that we can sign a new policy:
$ git clone https://github.com/linux-integrity/ima-evm-utils.git
$ cd ima-evm-utils/examples
$ ./ima-gen-local-ca.sh
$ ./ima-genkey.sh
$ sudo mokutil --import ima-local-ca.x509
$ sudo cp x509_ima.der /etc/keys/ima
reboot to add the keys to MOK
When you reboot, the shim will invoke the MOK key manager.
Follow the prompts to add the CA key to the MOK database,
entering the temporary password you used above.
After the reboot, next edit
/lib/dracut/modules.d/97masterkey/module-setup.sh
/lib/dracut/modules.d/98integrity/module-setup.sh
In each file change check() to return 0 instead of 255.
Rebuild the initramfs.
sudo dracut --kver $(uname -r) --force --add integrity
reboot
Use keyctl show to check the keyrings:
# keyctl show %:.platform
# keyctl show %:.machine
# keyctl show %:.ima
III. Sign sdboot, and the traditional kernel and turn on Secure Boot:
- reboot and press ESC repeatedly to enter UEFI setup mode, 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.
a. Log in the the booted VM and Install the needed tools:
- $ sudo dnf install efitools keyutils mokutil pesign sbsigntools kernel-devel-$(uname -r) golang asciidoc systemd-ukify
- 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 with sbctl sign --save <filename>
d. Check that everything is signed:
# sbctl verify
Verifying file database and EFI images in /boot/efi...
✓ /boot/efi/EFI/fedora/grubx64.efi is signed
✓ /boot/efi/EFI/fedora/mmx64.efi is signed
✓ /boot/efi/EFI/fedora/shim.efi is signed
✓ /boot/efi/EFI/fedora/shimx64.efi is signed
✓ /boot/efi/EFI/BOOT/BOOTX64.EFI is signed
✓ /boot/efi/EFI/BOOT/fbx64.efi is signed
e. reboot
IV. 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 --tpm2-seal-key-handle=0x81000001
d. add ",tpm2-device=auto" to the end of the line in /etc/crypttab
e. Rebuild initrd
# dracut -f --add integrity
f. reboot
This time, the encryption keys will be obtained from the TPM automatically!
V. Build UKI Image and sign it (as root):
a. Create uki image with ukify - modify the paths and cmdline for your system.
Mine looked like:
# /usr/lib/systemd/ukify build \
--linux=/boot/vmlinuz-6.9.0-64.fc41.x86_64 \
--initrd=/boot/initramfs-6.9.0-64.fc41.x86_64.img \
--cmdline='root=UUID=c4e3e1e6-cc2c-44c3-8231-f36ad9227e1a ro rd.luks.uuid=luks-eb5f0672-332f-450f-a9b1-89670d54d931 rhgb ima_template=ima-sig ima_policy=tcb'
(You can get the current cmdline from /proc/cmdline, remove the first boot parameter,
and add the ima parameters at the end).
b. This will create an unsigned UKI file. Sign it with:
# cp vmlinuz-6.9.0-64.fc41.x86_64.unsigned.efi vmlinuz-6.9.0-64.fc41.x86_64.signed.efi
# sbctl sign vmlinuz-6.9.0-64.fc41.x86_64.signed.efi
✓ Signed /root/vmlinuz-6.9.0-64.fc41.x86_64.signed.efi
# mkdir /boot/efi/EFI/Linux
# cp vmlinuz-6.9.0-64.fc41.x86_64.signed.efi /boot/efi/EFI/Linux
c. enroll the new boot image
# dnf install python3-virt-firmware
# kernel-bootcfg --add-uki /boot/efi/EFI/Linux/vmlinuz-6.9.0-64.fc41.x86_64.signed.efi --boot-order 0 --title "Fedora Rawhide UKI"
d. reboot
(The new UKI boot option will be the default at the top of the UEFI boot menu.)
VI Set new IMA policy, and reenroll LUKS
a. As root, cd to the ima-evm-utils/examples directory (where the local IMA keys were generated)
# cat /sys/kernel/security/ima/policy | grep -v MAY_READ > ima-policy
# cp ima-policy /etc/sysconfig
# evmctl ima_sign -k privkey_ima.pem /etc/sysconfig/ima-policy
b. reenroll the LUKS key
# systemd-cryptenroll /dev/vda3 --tpm2-device=auto --tpm2-seal-key-handle=0x81000001
# reboot
VI. install cel_utils and run verify
WHEW!
Now that everything is installed and configured, you can check things out:
Check out the keyrings with
# keyctl show %:.platform
# keyctl show %:.machine
# keyctl show %:.ima
Test the attestation verification with:
$ git clone https://github.com/safforddr/cel_util.git
$ sudo dnf install systemd-devel tpm2-tss-devel
$ cd cel_util
$ make
$ sudo ./verify
This should yield the following attestation verification summary:
Some things to note in the summary:
IMA events are ongoing, and getting the PCR values from the TPM and getting teh event log from the kernel are not synchronized. In the example verifier, the PCR values are retrieved first, and then the IMA event log. This way there may be events at the end of the event log that were not yet extended into PCR-10 when the PCR value was read. The verifier checks after each event in the log to see if the PCR-10 matches. In this example case the result "MATCHED EARLIER" indicates that there were events in the log after the PCR-10 value matched. This is normal.
"verfified by ima-sig" indicates that the signatures in the ima-sig template have successfully been verified against one of the Fedora signing key certificates or the local IMA certificate.
The early IMA events are not verified by ima-sig, as they were in the initramfs, which does not have signed files. The entire UKI file is signed to protect against changes.