How to Build a UKI for Qubes Guide (Boot Security)

Intro

This guide details how to build a Unified Kernel Image (UKI) on Qubes, which has the benefit of opening the ability to cryptographically verify the state of all EFI code, as well as kernel boot parameters. This acknowledges the classic form of the “Evil Maid” Attack.

Currently, Qubes OS does not offer strong guarantees for boot security on most devices. The existing options for protecting the unencrypted bootloader include:

  • Enabling Qubes Anti- Evil Maid (AEM): Requires a TPM 1.2 and Intel TXT.
  • Having a separate USB stick: Must keep this on you at all times to be secure.
  • Using an SSD encryption password: Is often not supported, relies on a historically buggy firmware encryption mechanism, and offers no way to tell if you have been compromised.

During the creation of the UKI, your original EFI entry is not modified, so you can still boot to that (with secure boot disabled) as a backup if anything fails.

Building the Unified Kernel Image (UKI)

Through GitHub threads shared with me by user ryona, there is a script called uki-generate that can be used to create a UKI on Qubes using existing files within /boot and an additional xen.cfg. You can find the script here: qubes-core-admin-linux/uki-generate at main · QubesOS/qubes-core-admin-linux · GitHub

Note: This script will be used to generate an EFI binary, which is a very sensitive type of file. After you copy the script code, it is recommended to look over it yourself, or even better, verify the git GPG signature of the last commit modifying the script.

1. Copy the Script to an Offline Qube

I used the vault qube for the entire process, but you can also create a dedicated AppVM for this purpose. If you use an alternative build qube, replace vault with the name of your qube in all of the commands within this guide.

In your build qube:

nano ~/uki-generate

Paste the script, then save the file. Finally, make it executable:

chmod +x ~/uki-generate

2. Copy the Relevant Boot Files to Your Build Qube

In dom0:

# Replace these file names with your actual matching files 
# if you are on a newer version of Qubes.
sudo qvm-copy-to-vm vault \
/boot/efi/EFI/qubes/xen-4.19.3.efi \
/boot/vmlinuz-6.12.7-1.qubes.fc41.x86_64 \
/boot/initramfs-6.12.7-1.qubes.fc41.x86_64.img \

Then, in your build qube:

mv ~/QubesIncoming/dom0/* ~/

3. Create your xen.cfg

In dom0:

cat /proc/cmdline

Copy the boot parameters (everyting after “kernel”; root=/dev/mapper ...), then Copy dom0 clipboard via the clipboard manager applet, and then pass it to your build qube with Ctrl+Shift+V.

In the build qube:

nano ~/xen.cfg

Then, write your config file. You’ll want your xen.cfg file to look like this in the end:

[global]
default=qubes

[qubes]
options=console=none loglvl=all
kernel=placeholder YOUR KERNEL PARAMTERS
ramdisk=placeholder

This is an example using mine. After kernel=placeholder, paste the string you copied from dom0 (root=/dev/mapper...) in place of YOUR KERNEL PARAMETERS.

4. Building the UKI

Finally you can run the script to build your EFI binary. Replace the filenames if they are different for your version of Qubes.

~/uki-generate \
~/xen-4.19.3.efi \
~/xen.cfg \
~/vmlinuz-6.12.59-1.qubes.fc41.x86_64 \
~/initramfs-6.12.59-1.qubes.fc41.x86_64.img \
~/xen-unified.efi

If this goes through without errors, you are ready to test it.

5. Booting to the UKI

Copying to dom0 is very sensitive, more so booting from a copied EFI entry; ensure you have built it securely before doing this step.

In dom0:

qvm-run --pass-io vault 'cat /home/user/xen-unified.efi' > ~/xen-unified.efi
sudo mv ~/xen-unified.efi /boot/efi/efi/qubes/xen-unified.efi

Now, to register this new boot option in your UEFI:

# Replace /dev/nvme0n1 with the path to your
# drive, such as /dev/sda
sudo efibootmgr --create --disk /dev/nvme0n1 \
--part 1 --label "Qubes OS UKI" \
--loader "\EFI\qubes\xen-unified.efi"

Then, run efibootmgr | grep "Qubes OS UKI" to see that your boot option has registered.

Once this is finished, you can now test your UKI boot entry. Reboot and spam whichever key lets you choose boot options (F12 in my instance), or access it from your standard UEFI setup if available. You should find an option called Qubes OS UKI. Boot from this.

The boot process should look normal, except that instead of the GRUB, you see a brief black screen with white text showing certain configuration details. If you can decrypt your disk and log in normally, you’ve now verified that the UKI works.

Security Notes

A UKI alone does not improve your boot security inherently; it simply opens opportunities for proper cryptographic verification. You must use another method to verify the boot process.

To enable Secure Boot, you will want to enroll your own key in the authorized signatures database and self-sign the EFI binary. To enter user mode, this key must be signed by an enrolled KEK that is signed by the PK. Only do this if you know you’re doing, since a custom Secure Boot chain can leave a system unusable until the motherboard is reset, especially on systems with a dGPU.

Secure boot is not a particularly strong protection by itself. If your UEFI can be reset by shorting the CMOS (most hardware), then someone can disable Secure Boot and replace your bootloader with any OS they want. Unless you check your secure boot policy every boot, then you likely won’t notice that this attack has occurred before decrypting your data.

TPM-based attestation is still necessary to reliably ensure boot integrity. The easiest way to do that is through LUKS TPM 2.0 unsealing via systemd-cryptenroll in dom0 with a TPM PIN (bound to PCR 4, or if you have Secure Boot PCR 7). This has its security concerns, as attacks have historically been able to find vulnerabilities in trusted platform modules key storage (FaulTPM, for instance). If you do decide to go this route, ensure that your specific CPU model is not known to be vulnerable, and enable “Pluton TPM” in your UEFI security settings if available.

Many threat models do not permit the potential vulnerabilities with storing encryption keys in the TPM. It may be possible to authenticate the system to the user using a non-static integrity verification code in initramfs, using tpm2-totp for instance, before the user types in their LUKS passphrase. If this is implemented, the user would compare the code on their screen to their own authenticator app such as Aegis. If the codes match, then that means the assigned PCRs (such as PCR 4, and you can include others as well to detect modified firmware, for instance) match the expected values, so the bootloader is safe and the LUKS passphrase can be entered. If you know of an implementation, then please let me know and I will add that to this post.

Another remedy to the TPM trust issue is mathematically requiring both a TPM unsealed key and a software passphrase, but I’m not aware of any software that lets you easily implement this in your initramfs (current TPM PIN implementations for LUKS only use your PIN to authenticate to the TPM, not as a part of the final secret).

5 Likes

Not needed. It is located at /usr/lib/qubes/uki-generate.

Is there a different way? The problem I see is that boot parameter updates on Qubes OS wouldn’t get propagated, once you switch to the UKI approach.

You can get the currently used options from sudo xl dmesg | grep "Command line". But it has the same problem as above.

Is there a reason why you don’t run it in dom0?

2 Likes

Thanks. Feel free to edit the guide to reflect this, since I believe the Qubes forum let’s anyone edit “Community Guide” posts. I’d prefer not to do it myself since I have no easy way to verify this.

I haven’t considered this issue on Qubes, but I would be open to contributions that remedy it. Normally, the solution would involve having a separate machine that generates updated UKIs for a production machine (considering that self-generating and self-signing on the same machine is an improper way to establish boot security), but that wouldn’t work for most users.

I don’t see much reason to run it in dom0, and the most natural-feeling course of action was to do the build in a separate Qube from dom0. It doesn’t really make much of a security difference either way as far as I can tell.

1 Like

Correct, any topic in the Community Guides category can be updated by anyone against the latest Qubes OS version and/or new information.