Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save dylanjan313/4293ab5a0105e3f7272a8a1a357fee4d to your computer and use it in GitHub Desktop.

Select an option

Save dylanjan313/4293ab5a0105e3f7272a8a1a357fee4d to your computer and use it in GitHub Desktop.

Fedora 42: Verified and Measured Boot with UKI, Full Disk Encryption and TPM-based unlocking

This guide adapts an existing full-disk-encrypted (FDE) Fedora system to use TPM-backed decryption in a secure and reliable way, by leveraging Secure Boot and UKI. It also replaces GRUB with systemd-boot.

Features are:

  • Kernel and initramfs updates do not break the TPM-backed decryption.
  • Tampering with the boot environment (kernel, initrd or cmdline) requires disabling Secure Boot
  • Disabling Secure Boot requires the recovery key (TPM PCR7)
  • Sharing a computer with non-admin or untrusted user(s) does not require sharing the recovery key anymore. If a TPM PIN is set, it can safely be shared with these users, they still cannot tamper with the OS.
  • The setup is not vulnerable to root partition swaping attacks (by measuring boot phases into PCR11)
  • By not setting a TPM PIN, a system can be (re)booted without requiring physical presence to enter the recovery key

Recommendations:

  • Do not use discrete TPM, they can be attacked by bus sniffing, prefer firmware TPM instead.
  • Do not use CPUs with unfixed vulnerabilities (see TPM-fail and AMD faulTPM).
  • Use systemd-homed to have system encryption (TPM backed) different from user's home encryption
  • Make sure the recovery key is strong. Create a recovery key with systemd-cryptenroll --recovery-key /dev/... and remove the weak password with systemd-cryptenroll --wipe-slot password /dev/...

This guide was tested with Fedora Server 42 freshly installed using the Anaconda installer, by choosing auto-partitioning and disk encryption features.

Boot with Secure Boot in "Setup Mode"

Usually, this is accomplished by clearing the Secure Boot keys from the UEFI firmware setup. Check the motherboard's manual for more details.

Disabled the boot environment shell

No shell should be provided under any circumstance until the OS is fully booted and ensures access control, especially from the pre-boot phase, where the TPM can release the key.

Add the rd.shell=0 and rd.emergency=reboot options to the /etc/kernel/cmdline file.

Install the systemd-boot, systemd-ukify and sbsigntools:

sudo dnf install -y systemd-boot-unsigned systemd-ukify sbsigntools efitools
  • systemd-boot-unsigned: the (unsigned) systemd bootloader.
  • systemd-ukify: generates the UKI with support for measured boot phase and TPM signed policies.
  • sbsigntools: provides sbsign, used by ukify to sign the UKI.

Write the configuration for systemd-ukify

cat <<EOF | sudo tee /etc/kernel/uki.conf
[UKI]
OSRelease=@/etc/os-release
PCRBanks=sha256

[PCRSignature:initrd]
Phases=enter-initrd
PCRPrivateKey=/etc/systemd/tpm2-pcr-private-key-initrd.pem
PCRPublicKey=/etc/systemd/tpm2-pcr-public-key-initrd.pem
EOF

Generate the key to sign TPM policies bound to the "enter-initrd" phase:

sudo ukify genkey --config=/etc/kernel/uki.conf

Configure kernel-install to use dracut to generate the initrd, and ukify to generate the UKI.

cat <<EOF | sudo tee /etc/kernel/install.conf
BOOT_ROOT=/boot/efi
layout=uki
uki_generator=ukify
initrd_generator=dracut
EOF

Set Secure Boot

sudo dnf copr enable chenxiaolong/sbctl -y
sudo dnf install sbctl -y
sudo sbctl create-keys

Depending on the hardware, run one of these: See https://github.com/Foxboron/sbctl/wiki/FAQ#option-rom

sudo sbctl enroll-keys
sudo sbctl enroll-keys --tpm-eventlog
sudo sbctl enroll-keys --microsoft
sudo sbctl enroll-keys --yes-this-might-brick-my-machine

Clean up the /boot and /boot/efi partitions

Uninstall GRUB and shim:

sudo rm /etc/dnf/protected.d/{grub*,shim*}
sudo dnf remove -y grubby grub2\* memtest86\* shim

Remove previously kernel and initramfs installed in the boot partition. Remove old GRUB configuration files. Disable creation of the rescue initramfs.

sudo rm -rf /boot/grub2
sudo rm -rf /boot/loader
sudo rm -rf /boot/EFI
sudo rm /boot/vmlinuz-*
sudo rm /boot/initramfs-*
sudo rm /boot/efi/EFI/fedora/grub.cfg{,.rpmsave}
sudo touch /etc/kernel/install.d/51-dracut-rescue.install

Install and sign systemd-boot in the ESP

Install systemd-boot in the EFI partition, and sign the two files. The files are saved in sbctl database so they can be signed later with the "sign-all" action.

sudo bootctl install
sudo sbctl sign -s /boot/efi/EFI/systemd/systemd-bootx64.efi
sudo sbctl sign -s /boot/efi/EFI/BOOT/BOOTX64.EFI

Note: Updating systemd-boot-unsigned won't update the files installed in the EFI partition. Run sudo bootctl install and sudo sbctl sign-all to update systemd-boot. This is not automated yet (may be related to bootupd).

Build the UKI

Generate an UKI for each kernel installed in the system:

sudo kernel-install add-all

Optional: Verify if systemd-boot and all UKI are signed:

sudo sbctl verify 2>/dev/null

Reboot

Check if systemd-boot ("Linux Boot Manager") is set as the first boot entry in the UEFI firmware using efibootmgr.

An example of output is:

admin@localhost:~$ sudo efibootmgr 
BootCurrent: 0002
Timeout: 0 seconds
BootOrder: 0002,0003,0000,0001
Boot0000* BootManagerMenuApp	FvVol(7cb8bdc9-f8eb-4f34-aaea-3ee4af6516a1)/FvFile(eec25bdc-67f2-4d95-b1d5-f81b2039d11d)
Boot0001* EFI Firmware Setup	FvVol(7cb8bdc9-f8eb-4f34-aaea-3ee4af6516a1)/FvFile(462caa21-7614-4503-836e-8ab6f4662331)
Boot0002* Linux Boot Manager	HD(1,GPT,e7cc2c52-cabb-427b-98bd-397e797ec2cb,0x800,0x12c000)/\EFI\systemd\systemd-bootx64.efi
Boot0003* UEFI Misc Device	PciRoot(0x0)/Pci(0x2,0x3)/Pci(0x0,0x0){auto_created_boot_option}

Boot entry 0002 is the first.

By rebooting, the prompt for the LUKS recovery key should appear. This happens because it is not enrolled in the TPM yet. The GRUB menu did not appear, the system was booted through systemd-boot and the UKI.

Provide the recovery key and authenticate to the system.

The output of sudo bootctl status should tell that the system was booted through the UKI.

The output of sudo sbctl status should confirm secure boot is enabled and the setup mode is disabled.

This reboot was required before enrolling the key in TPM because the previous PCR7 value is not valid anymore, after the custom Secure Boot key was enrolled.

Enroll the LUKS key in TPM

Without TPM PIN (recommended for unattended devices):

sudo systemd-cryptenroll --tpm2-device=auto /dev/sdaXXX

With a TPM PIN (recommended for user devices):

sudo systemd-cryptenroll --tpm2-device=auto --tpm2-with-pin yes /dev/sdaXXX

For information, systemd-cryptenroll uses the current PCR7 value by default. It also finds TPM signed policy so there is no need to explicitly require PCR11.

Reboot

The system will be without asking the LUKS recovery key, but may ask the TPM PIN if set.

@gitnohubz
Copy link

@dylanjan313 hi man , did you see the new profiling thing in ukify with systemd257 , a single uki would have multiple cmdline etc... systemd boot would detect those profiles , could you add that to that guide i.e making normal profile and a rescue one etc.. this profile thing would solve ESP storage space problem in some extent , & can you add more rich options in ukify , i cant see any guide making use of options like : this in ukify man page :

[UKI]
Initrd=early_cpio
Cmdline=quiet rw rhgb
SecureBootPrivateKey=secure-boot-key.pem
SecureBootCertificate=secure-boot-certificate.pem
SignKernel=yes
PCRBanks=sha384,sha512

[PCRSignature:initrd]
PCRPrivateKey=tpm2-pcr-private-key-initrd.pem
PCRPublicKey=tpm2-pcr-public-key-initrd.pem
Phases=enter-initrd

[PCRSignature:system]
PCRPrivateKey=tpm2-pcr-private-key-system.pem
PCRPublicKey=tpm2-pcr-public-key-system.pem
Phases=enter-initrd:leave-initrd
enter-initrd:leave-initrd:sysinit
enter-initrd:leave-initrd:sysinit:ready

#DeviceTree=
#DeviceTreeAuto=
#HWIDs=recommended if auto dev tree
#SBAT=@
#SecureBootSigningTool=systemd-sbsign
#SigningProvider=only if using sd-sbsign
#CertificateProvider=only sd-sbsign
#Splash=
#PCRPKey=
#SigningEngine=openssl

see its archlinux updated man page

@dylanjan313
Copy link
Author

@gitnohubz I've read about this new feature but haven't had the opportunity (use case) to try it out. I'm curious to try it out when I have the time, and I'll be sure to share my findings.

@gitnohubz
Copy link

@dylanjan313

By default, an attacker could make the boot fail by any mean to get an emergency shell, from which he could request the TPM for the key.

Is thats the case even with --tpm2-with-pin ?
Because i really need the emergency shell for emergencies , if thats still the case , isnt there another way to mitigate this without losing emergency functionalty

@dylanjan313
Copy link
Author

@gitnohubz

By default, an attacker could make the boot fail by any mean to get an emergency shell, from which he could request the TPM for the key.

Right, this is why I suggest to add two cmdline options to prevent that.

Is thats the case even with --tpm2-with-pin ?

If a TPM PIN is set, knowledge of the PIN is required to unlock the drive from the boot environment (usual prompt and emergency shell).

isnt there another way to mitigate this without losing emergency functionalty

If your system is broken:

  1. Turn off Secure Boot from firmware's setup
  2. Reboot and press the "space" key get the systemd-boot boot menu
  3. Press "e" to edit the command line and remove both rd.shell=0 and rd.emergency=reboot options
  4. Boot and wait for the issue to happen, you will get an emergency shell if the system fails to boot
  5. From the emergency shell, you need the LUKS recovery passphrase to unlock/mount the root filesystem, the TPM unlocking will not work with Secure Boot off (because we have required Secure Boot to be on when enrolling the LUKS key).
  6. Once the system is fixed, reboot and re-enable Secure Boot
  7. TPM unlocking should work again since you have restored the Secure Boot state as per the sealing policy.

@gitnohubz
Copy link

@dylanjan313 so basically if one did have his tpm2 protected with a pin he wouldnt bother with emergency shell compromise.

If it wasnt so then the need to edit the boot menu shows up

@BatiSB
Copy link

BatiSB commented Aug 14, 2025

I followed this guide with Fedora 42 and it worked perfectly, thank you very much.
Now I need to install the NVIDIA drivers, but they fail due to secure boot. Does anyone know how to integrate them into this setup?

@gitnohubz
Copy link

@BatiSB if you use secureboot with your own keys i dont think you well have a problem , but i didnt test bro since i dont have nvidia

@LaserBread
Copy link

Would this be able to survive a full system upgrade (eg, Fedora 42 to 43)?

@LaserBread
Copy link

Other question: does this account for multiple encrypted partitions? (eg, a root and home partition

@gitnohubz
Copy link

@LaserBread if its a multiple partitions , you well have to use crypttab with the approriate flags , e.g tmp2 auto , & initramfs flag or whatever flag that makes the partition bieng encrypted early in boot if it was a boot critical partition , the man page for crypttab well be your friend here

@gitnohubz
Copy link

@LaserBread it should , but you well.never know , i can conform it survived from 41 to 42

@LaserBread
Copy link

LaserBread commented Oct 1, 2025

@LaserBread if its a multiple partitions , you well have to use crypttab with the approriate flags , e.g tmp2 auto , & initramfs flag or whatever flag that makes the partition bieng encrypted early in boot if it was a boot critical partition , the man page for crypttab well be your friend here

Hmmmm... I did the setup and enrolled both, but it still asks for the LUKS password. I modified /etc/crypttab, but that does nothing. I think I have to regenerate initramfs, but running dracut just tells me

dracut[F]: Can't write to /boot/efi/6bf081ff3d684840a48bfc6a75e73863/6.16.8-200.fc42.x86_64: Directory /boot/efi/6bf081ff3d684840a48bfc6a75e73863/6.16.8-200.fc42.x86_64 does not exist or is not accessible.

I'm sorta stuck here. Do I need to somehow put the initramfs into the UKI? I'm pretty new to this.

Edit Oh yeah, we nuked initramfs from orbit.

@gitnohubz
Copy link

Hmmmm... I did the setup and enrolled both, but it still asks for the LUKS password. I modified /etc/crypttab, but that does nothing. I think I have to regenerate initramfs, but running dracut just tells me

dracut[F]: Can't write to /boot/efi/6bf081ff3d684840a48bfc6a75e73863/6.16.8-200.fc42.x86_64: Directory /boot/efi/6bf081ff3d684840a48bfc6a75e73863/6.16.8-200.fc42.x86_64 does not exist or is not accessible.

@LaserBread bro you know you need to type sudo , & why are you running dracut directlly ? You should just use "sudo kernel-installl add" command , & it well do everything automatically for you

@LaserBread
Copy link

That is with sudo.

@neonica57
Copy link

neonica57 commented Jan 6, 2026

This is such a great guide! Thank you so much. I did this on Fedora 43 Workstation and it worked amazingly. A couple of notes and suggestions for anyone willing to listen. 🙂

  • At least for me (Fedora 43), when enrolling the LUKS keys to the TPM, you must clearly specify PCR 7. (PCR 11 was automatically enrolled).

    • without PIN: sudo systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=7 /dev/sdaXXX

    • with PIN: sudo systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=7 --tpm2-with-pin yes /dev/sdaXXX

    • Verify with sudo cryptsetup luksDump /dev/sdaXXX and look for the tpm2-hash-pcrs and tpm2-pubkey-pcrs fields under systemd-tpm2.

  • You can migrate /boot to your encrypted root drive and delete the /boot partition.

    1. Mount both on a LiveCD and do an archival copy cp -a from the contents of your boot partition to the directory /boot on your encrypted root.
    2. Delete the boot partition entry from /etc/fstab
    3. Delete the boot partition itself (from the partition table). You probably want to expand another drive to use that free space.
  • If using an SSD, you may want to consider modifying your /etc/crypttab and append the following flags to your LUKS container:

    • no-read-workqueue,no-write-workqueue for SSD performance optimizations.
    • allow-discards to enable TRIM on TRIM supported SSDs.
      • Enabling TRIM on the LUKS container will allow the SSD to run more efficiently. However, it will also allow anybody determine which part of your LUKS container is actually just free space, allowing for others to approximate the size of your encrypted data.
      • Note that this is will allow Fedora to automatically trim your LUKS encrypted partitions, assuming your filesystems also support TRIM and the systemd service fstrim.timer is enabled.
  • Lastly, if you want to be super extra, set an entry token configuration so that your EFI stubs have names to them when generating the kernel with kernel-install. example: fedora-6.17.1-300.fc43.x86_64.efi

    sudo echo "fedora" > /etc/kernel/entry-token

@gitnohubz
Copy link

@neonica57 hi ive been doing the same but iam too lazy to make a guide.

What i disagree with you on is that you use sbctl , because instead of using sbctl i just used systemd tools for enrolling the keys , the archwiki is your friend here.

Also ukify has some nich features for UKIs that i didnt manage to explore , specifically the uki profiles thing.

About the no-workqueue optimization thing , i once did benchamrks & i didnt notice any performance gain whatsoever , so i dont recommend using those even though the archwiki suggest them , & the best way with luks2 to give it flags like the allow-discards one , is to do it using cryptseup cmdline rather than /etc/crypttab.

I was using a feature of the TPM thats is related to the pcr11 i well share it with you when i got some free time.

& thank you fkr the entery-token thing where did you know about that ? Is that a new thing ?

I want to add the issues i have with the curret approach is eventually the ESP gets fulled with uki for every kenrel version & i have to delete them manually.

& dont forget to search about UKI profiles thing its damn awesome but unfortunately the documentation for it doesnt seem to provide a configuration file way to do it , only manual cmdline way

@neonica57
Copy link

@gitnohubz Thank you! I will look into it. I found the entry-token in the manpages for kernel-install.

By default, the %posttrans of kernel-core will only remove the type1 of the kernel. Fedora's post-transaction scriptlet does not remove the type2 (our UKI format). (You can see this by running rpm -q --scripts kernel-core)

We can automate UKI cleanup with a dnf plugin that allows you to write custom post-transaction scripts:

sudo dnf install libdnf5-plugin-actions

Make a file /etc/dnf/libdnf5-plugins/actions.d/kernel-core_uki.actions and put the following:

# /etc/dnf/libdnf5-plugins/actions.d/kernel-core_uki.actions

post_transaction:kernel-core:out::/bin/kernel-install remove ${pkg.version}-${pkg.release}.${pkg.arch} --entry-type type2

@gitnohubz
Copy link

gitnohubz commented Jan 26, 2026

@neonica57 the libdnf5-plugin-actions unfortuanatlly doesnt work when updating via software center, because of PackageKit stick with the old school dnf4 way & not friendly with dnf5.

another thing is for the workqueue flags for luks i recommend benchmarking with & without it , for me it did hurt reading speed & had no effect on write speeed, so just benchmark it yourself to know what suits your hardware , use just something like kdiskmark.

for TPM stuff here is a more secure way of doing it :

systemd-cryptenroll /dev/nvme0n1p3 --tpm2-device auto --tpm2-public-key /etc/keys/custom/tpm2/initrd/public_key.pem --tpm2-with-pin yes --tpm2-pcrs 5+7+15:sha256=0000000000000000000000000000000000000000000000000000000000000000

that public key is bind to the pcr11 even though its not specified it is choosen automatically , for the other ones here is the reference : pcr_registry by uapi group & the archwiki for the pcr 15 read the warning.

for the uki profiles i asked you for there is a systemd tool named mkosi that can automate it , but its a whole another crazy shit , very interisting stuff that are out of the scope.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment