mkosi
mkosi copied to clipboard
Support QEMU secure boot
So, to support booting QEMU with secure boot, we need an OVMF VARS file that has the required secure boot keys and certificates enrolled that the unified kernel image we're trying to boot is signed with. We pass the VARS file to qemu and if the unified kernel image is signed with the same keys that have been put in the VARS file, things should just work.
We could just generate the VARS file once manually by using the LockDown.efi binary from the efitools project. If you build this binary, it embeds a set of secure boot keys which it installs into the UEFI firmware upon execution. We'd include this VARS file into the mkosi repository along with the private keys generated by efitools and call it a day. Those private keys would be purely for testing purposes.
This approach won't be possible if the VARS format is not public API and can change between edk2-ovmf releases. If that's the case, the second option is to install the keys as part of the usual run_qemu
workflow. We'd have to generate all the keys using this amazing resource on secure boot, put them in an image along with the UpdateVars.efi executable from the efitools project, pass that image to qemu and instrument the UEFI shell that qemu spawns to run UpdateVars.efi to install each of the necessary keys. We end up with an OVMF vars file again that we can use for secure boot.
I'd prefer approach one if its at all possible since users don't need any complicated dependencies on their machine. If I read https://www.redhat.com/archives/libvirt-users/2017-July/msg00050.html correctly, it does not seem like the variable store is guaranteed to be backwards compatible so we might have to go for the second option. That one won't be easy on centos however since it doesn't provide efitools which would be a dependency.
One other approach is to have efitools build the LockDown.efi which embeds a set of keys (in our case, mkosi ones) and installs them upon execution in a UEFI shell. If we include that in the repo (along with the private keys) or just attach it to a release and get it from there, we only have to run that to generate the vars file without having any extra dependencies. That should be backwards compatible as well.
See https://git.kernel.org/pub/scm/linux/kernel/git/jejb/efitools.git/tree/README for more information on using UpdateVars.efi and LockDown.efi.
cc @mrc0mmand, the third approach seems promising, do you think that could work for you? See http://www.rodsbooks.com/efi-bootloaders/controlling-sb.html to get up to speed on Secure Boot. The VARS approach was inspired by this project: https://github.com/rhuefi/qemu-ovmf-secureboot/blob/c3e16b359661410f8c31e0af1279cbec4ed6fc1f/ovmf-vars-generator
We'll also need to support using pesign instead of sbsign to sign the efi binaries produced by mkosi since centos 8 doesn't provide sbsigntools but does provide pesign.
@DaanDeMeyer thanks a lot for the research and the materials! I guess I'd be okay with any of the options, since I already have to build/rebuild certain dependencies to make mkosi
work on RHEL/CentOS 8 (the whole arch-install-scripts stack in my case, see https://copr.fedorainfracloud.org/coprs/mrc0mmand/archlinux-el8/). Of course, the less missing dependencies, the better.
hmm, so one plan i had for sd-boot is to make it enroll keys automatically on first boot. i.e. if sd-boot detects it runs in an environment where key enrollment is allowed by uefi, then it will enroll keys found at some location in the ESP automatically (or maybe ask first, if configured so), and then lock things down and reboot.
Isn't this a better approach here? Because that would solve this not just for qemu but for everyone.
i.e. we'd define some place in the ESP where you could drop-in certficates. And we'd enumerate them and upload them into the firmware if they are missing there, and we#d check that on each boot, before looking for boot menu items.
This way, we'd have pretty nice workflow: you enable enrollment in the firmware setup, then reboot once into sd-boot, and there you go, everything enrolled and locked down for good and on the second boot you have a fully secured system that will only boot your stuff.
I didn't look at the precise UEFI APIs for key enrollment, but afaik everything we need is readily accessible from the UEFI APIs, i.e. both uploading the certificates, and turning off enrollment mode.
with that in place all that's missing would be a way to force the qemu uefi firmware into the enrollment mode from the outside.
@mrc0mmand One of my latest PRs dropped the requirement for arch-install-scripts. We just use pacman directly now so you can drop one dependency.
@poettering Sounds like a way better solution. I didn't think that sd-boot could do all this for us. I'll see about implementing it (time permitting). I wonder if we should provide support in systemd for signing as well although that seems like something that belongs more in the dracut domain. It generates the entire image already, it might as well sign it I guess.
I wonder if we should provide support in systemd for signing as well although that seems like something that belongs more in the dracut domain. It generates the entire image already, it might as well sign it I guess.
Haven't looked into what it does, but the NEWS file of dracut 50 mentions
- add support for creating secureboot signed UEFI images
@behrmann Awesome! I searched using github search but it didn't return any results. It uses the same method we use in mkosi. We can replace our own implementation with that in the future.
So given that systemd upstream now supports auto-enrolling of keys, I guess we could simply copy the secure boot key form mkosi.secureboot.crt into the ESP at the right place, and things would just magically work!
We support auto enrolling of keys now, so let's close this