jwboyer (jwboyer) wrote,

Booting custom kernels in F18 with Secure Boot

I build a lot of kernels. I build 90% of them locally on a machine sitting under my desk. This has worked for a long time for quick testing, verification, and development of kernel features. A lot of other people build custom kernels too. So now that Secure Boot is shipping on new hardware, does that mean we won't be able to do this any longer? In short, no. We can still build and boot local kernels. I'll describe how below.


As discussed multiple times before, Secure Boot requires a trusted OS environment. To support this, Fedora provides kernels signed with an official Fedora certificate in the Fedora build system. That certificate is accepted by the shim and grub2 components, which lets our kernels boot on a Secure Boot enabled machine. However, locally built kernels aren't signed by this Fedora certificate, so the issue to solve is booting a locally built kernel while leaving Secure Boot enabled.

This post will assume that you're familiar with building your own kernels. Rebuilding the Fedora kernel SRPM with local changes, or building a kernel from git will both work. If you aren't familiar with building your own kernel, take a few minutes to try it out and read up on relevant documentation. Google has lots of links.

There are a few technical requirements you need to be able to do what is described below. Here is a list the package versions necessary, all of which are already in Fedora 18:

shim-unsigned-0.2-3.fc18.x86_64 or newer, pesign-0.100-1.fc18.x86_64 or newer, selinux-policy-3.11.1-63.fc18.noarch or newer. Additionally, your kernel has to support the efivarfs filesystem and carry the Secure Boot patchset. Fedora kernel-3.6.10-4.fc18 or newer, or rawhide kernel-3.7.0-6.fc19 or newer meets these requirements. If you're building from a kernel git tree, you'll need to patch things in.

Certs, UEFI, and tools

There are three things that need to happen in order to boot a locally built kernel in a Secure Boot environment:

1) You need a valid certificate to sign the kernel with
2) You need a tool to do said signing
3) You need the certificate included in UEFI so that binaries signed with it are considered trusted

Fortunately, Fedora provides ways to accomplish all three things.


The tool we use in Fedora to sign kernels (and grub2 and shim) is called 'pesign'. This was written by Peter Jones, and is relatively easy to use. It's available in Fedora 18 and is a build requirement for the Fedora kernel. Essentially what the tool does it take an X.509 certificate/key pair and sign a PE-COFF binary with it. Now that might sound confusing, because PE-COFF binaries are not typically associated with Linux. With the introduction of UEFI, the Linux kernel has an efi boot stub that wraps the bzImage file as a PE-COFF binary. This lets people boot kernels directly from UEFI without any form of bootloader if they want, and it also means the pesign tool can perform the required signing.

Certificates and keys

But where does pesign get the certificate to sign from? In the Fedora build system, there are dedicated machines that store these certificates on smart cards. So for an official build, pesign interacts with those machines and uses the official Fedora certs. Clearly that won't be possible for local builds. Further, it isn't even possible for scratch builds done in koji. So in addition to using the official Fedora certs/keys, pesign also includes test certificates/keys in the package itself. Before the smart card solution was in place, all Fedora kernels were signed with these "dummy" certificates and by default any local Fedora SRPM or scratch build done today will still be signed by this certificate. An astute reader will then realize that they can import these test certificates into UEFI and boot locally built kernels. That reader will be correct, however:

CAUTION: If you import these test certificates, that means that any binary signed with those will be bootable. Since anyone that installs pesign has access to these, anyone can generate a kernel signed with them. This is great for local kernels you build, however you need to exercise caution with what kernels you download and install. Really though, that was always the case even before we had signed kernels. Just remember: If you don't trust the person that built it (or their build environment), don't download and install it.

Eventually there will be a tool in Fedora that makes generating a certificate/key pair in the form UEFI expects fairly simple. That is being worked on and will perhaps show up as an update to Fedora 18. In the meantime, we'll use the test certificates as an example. To do that, we need to extract the certificates from the cert database that pesign ships with. There are actually two certificates, the signing certificate and the CA certificate. With pesign-0.100, we just need the CA certificate, so we use 'certutil' and extract it like this:
 sudo certutil -d /etc/pki/pesign/ -L -n "Red Hat Test CA" -r > rhca.cer 
Now you have the CA cert sitting in a local file called 'rhca.cer'. Let's look at how to import that into UEFI.


There is a utility called 'mokutil' in Fedora that we can use to easily import certificates into UEFI. Matthew Garrett has previously written about MoK and mokutil, but in brief MoK stands for "Machine owner Keys". The concept is to have a separate variable in UEFI that acts similar to the 'db' variable, and has a consistent and easy to use interface for managing this set of keys.

So to import our rhca.cer certificate into the MoK variable, we use mokutil. We need to mount the efivarfs filesystem first, as that isn't automatically mounted as of yet in Fedora 18:
 sudo mount -t efivarfs none /sys/firmware/efi/efivars 

Then we import the certificate:
[jwboyer@localhost ~]$ sudo mokutil --import ~/rhca.cer 
input password (8~16 characters): 
input password again: 
[jwboyer@localhost ~]$
The password mokutil asks for is required. If you haven't set one up, it will set it what you type there. You can change this with the --password option. When you reboot, shim will go into the management interface for MoK. It will look like this:
Shim UEFI key management

  Continue boot
  Enroll MOK
  Enroll key from disk
  Enroll hash from disk
You will want to select the Enroll MOK option and it will go to this screen:
Input the key number to show the details of the key or
type '0' to continue

1 key(s) in the new key list

Key Number: 
You can type 1 to see the key you're importing. I won't cover what that looks like here for brevity, but it just dumps the cert out. Once you're satisfied it is what you want to import, you type 0 to continue:
Input the key number to show the details of the key or
type '0' to continue

1 key(s) in the new key list

Key Number: 0

Enroll the key(s)? (y/n): y
Password(8-16 characters):
and enter the password you set earlier. Then you select the 'Continue boot' option and the boot process will proceed. Your cert is now enrolled.

Signing the kernel

OK, now we're assuming you've built your kernel. If you built it locally by either rebuilding a Fedora kernel SRPM or building from the Fedora kernel git tree with 'fedpkg local', then you already have a shiny new RPM sitting there with a signed kernel. Congrats. Install it and reboot and it should come up just fine. For those of you that like to roll your own, let's take a look at what magically happened.

The kernel.spec file has a macro call that looks like this:
%pesign -s -i $KernelImage -o vmlinuz.signed
. The %pesign macro expands two one of two different things. If it's called on an official Fedora signing machine, it uses the Fedora smart card signing and signs the official builds. If it is run on any other machine, it just calls pesign directly to use the test certificates. The latter is what we're interested, and it expands to:
/usr/bin/pesign -c 'Red Hat Test Certificate' -i arch/x86/boot/bzImage -o vmlinuz.signed -s
That will spit out the vmlinuz.signed binary that has been signed with the test certificates. If you run hexdump on that and look at the end of the binary, you will see the certificate and signature information in there. Assuming you've already done the normal 'make modules_install; make install;' dance, you have a kernel binary sitting in /boot that corresponds to the one you just signed. Now you just need to move that signed kernel to /boot and reboot.

What happens when you reboot is that shim will start grub2, and you select your new local kernel from the grub2 menu. The grub2 binary will validate that this kernel is signed and signed with a valid cert. It does this by interacting with shim, which looks in the db and mok variables. It will find your imported cert in Mok and verify the kernel is validly signed. Then grub2 does the rest of the things it does and the kernel boots up. You can verify the machine is still in Secure Boot mode
[jwboyer@localhost linux]$ uname -a
Linux localhost.localdomain 3.7.0+ #2 SMP Thu Dec 20 11:03:14 EST 2012 x86_64 x86_64 x86_64 GNU/Linux
[jwboyer@localhost linux]$ dmesg | grep Secure
[    0.000000] Secure boot enabled
[jwboyer@localhost linux]$ 

TODOs, Cleanups, Enhancements, and Follow-ons

Overall it isn't too difficult to boot locally built kernels. Still, there are some things we'd like to work on to make it even more user friendly. After all, this is your machine so making it usable for you would be nice. In no specific order, these are:

1) Utility to generate your own certificate and key to use for signing
2) Automatically mount the efivarfs filesystem (already done in upstream systemd!)
3) Ability to delete a single key from MoK. Currently you can only delete them all.

As a follow on, I'd like to write up a post about signing your own modules. This shares some of the same concepts as signing your own kernel, but the tools to do it are slightly different. Stay tuned for that write up.
Tags: fedora
  • Post a new comment


    default userpic

    Your IP address will be recorded