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 withsystemd-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.
Usually, this is accomplished by clearing the Secure Boot keys from the UEFI firmware setup. Check the motherboard's manual for more details.
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.
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.
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
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
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 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).
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
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.
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.
The system will be without asking the LUKS recovery key, but may ask the TPM PIN if set.
@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