Secure Boot in Fedora 27/28

❝How to set up a customized secure boot system in Fedora❞
Contents

In this article I’m going to describe the process of setting up a notebook as a secured UEFI Secure Boot system. This includes installing my own self-generated certificates, which replace the by-default installed certificates that come with the notebook.

In this article we focus on replacing all keys. Therefore it works best with operating systems for which you are able to sign the boot binaries. The ideal case is an operating system for which you manage the boot binaries, such as the kernel, yourself.

First we start off by installing necessary tools. Next we will attempt to build and sign the kernel. Many external sources of information will be used as this is quite a complex process. At least when doing this for the first time. There are a few moments in the process where we need to be very careful about the order of steps. Incorrect order may prematurely lock the system in Secure Boot and you would have to start over to a certain degree and it may block the system from booting while Secure Boot is active.

Introducing Secure Boot

So, what is this “secure boot” stuff anyways? And what is it useful for?

With the introduction of a more advanced firmware for PC’s, the “next generation” BIOS, it included some more advanced features. One of these features is “Secure Boot”. Secure Boot is a way to compose a chain of trust from the initial booting code in the firmware all the way into the operating system.

The underlying idea is simple: If we can verify the integrity of every part in the boot process before we start it, there should be no way to corrupt the boot process. On any deviation from the expected boot process, we would abort the boot process. Hence we avoid running corrupted code, such as that which may contain a virus. This does, however, make the assumption that we can reliably determine that a piece of software is trustworthy as we set it up. We can discover covert changes that have been made, as the system would refuse to boot. Secure Boot, however, does not help you to discover when you have unintentionally corrupted the boot process yourself. For example, by signing a corrupted binary.

UEFI systems contain a set of “root keys” for the first step in the boot process. Only binaries that are signed with these keys are allowed to boot. This ensures that the first step in the boot process is safe. Once a binary is verified using these keys, the system will trust this binary to also do the right thing and as such leaves it to the judgement of this binary. A bad binary would do no further checking and this would leave the system vulnerable from that point onward, similar to the situation before the introduction of Secure Boot. A good binary will use a fool-proof method to judge whether or not the next step binary is reliable and continue execution if-and-only-if its reliability is established. In which way this is established may vary. It can be because the “next-step” binary is also signed with a trusted key, or because the cryptographic hash of the binary is included in a database. In both cases, these are cryptographically secure mechanisms that can be trusted - given that the assumptions and prerequisites for these mechanisms have been met.

By default, UEFI systems contain a pre-installed set of keys that are installed by the hardware vendor in the UEFI firmware. These are the “root keys” that I mentioned before. These secret part of these keys are owned by Microsoft. Microsoft thoroughly validates any binaries that are requested to be signed for use in the Secure Boot process. We rely on the assumption that Microsoft will only sign binaries that are verified to be completely correct, i.e. that contain no flaws. If they miss anything, we open up a weakness in the Secure Boot system and therefore violate this assumption of reliably, trustworthy signed binaries. Hardware vendors may add their own certificates, for instance to verify firmware updates.

Note that we make the assumption here that the default keys are completely trustworthy. There should exist no binaries that have a potential to be used for evil either design as such intentionally or by accident due to a vulnerability. However, there is another option. UEFIs with Secure Boot typically offer a way to replace the “root keys” with a set of root keys provided by the user. Sometimes the keys are managed from within the UEFI firmware and sometimes it may be managed using software in user space if the Secure Boot system is configured in a special Setup mode. By replacing the “root keys”, you prevent all default boot software from being accepted (as they are now signed with the wrong key) and instead you are able to configure your system to only accept your own binaries. Hence you are able to take complete control over how your system is started.

Another option is available, which allows you to insert additional keys. These keys exist on a lower level than the bare hardware and rely on the “root keys” being present and trustworthy, but it does allow you to include trust for your own binaries without revoking the default trust set-up that comes pre-installed in the hardware, such as the support for the largest OSses like Microsoft Windows, the larger GNU/Linux distributions, etc. These keys are called MOKs, or Machine Owner Keys.

This guide focuses solely on the first option, the complete replacement of the Secure Boot root keys, and to adapt your (Linux) operating system to the changed boot requirements. This mechanism can be used for dual booting with Microsoft Windows systems, however we do not discuss how to re-sign Windows boot binaries with your own key. So for dual-booting systems this guide may not be sufficient.

Preparing the system

Let’s start with acquiring the necessary software. 2 Pieces of software are important in this process:

  1. sbsign - a utility for signing binaries that need to be launched by the UEFI firmware, as part of the boot process.
  2. efitools - a collection of utilities such as cert-to-efi-sig-list and sign-efi-sig-list that are used to prepare the signature lists for installation in the UEFI firmware.

Installation of sbsign

This one is easy:

sudo dnf install sbsigntools

Installation of efitools

I have not been able to find a pre-built package for Fedora 27 or 28-beta. We can quite easily build this from sources.

Check out efitools from its git repository.

git clone https://git.kernel.org/pub/scm/linux/kernel/git/jejb/efitools.git

Install packages gnu-efi and gnu-efi-devel and necessary dependencies help2man and perl-File-Slurp.

sudo dnf install gnu-efi gnu-efi-devel help2man perl-File-Slurp

Make the efitools package:

make

Note that if the package does not compile completely from the start, you may need to start over completely. Attempting the resume a half-finished compilation may result in unexplained additional errors. A simple solution is to make clean and start compiling from the beginning.

We are primarily interested in the tools cert-to-efi-sig-list and sign-efi-sig-list, but full compilation will help in case you need to install new keys manually - in case the BIOS/UEFI does not provide you this option.

Prepare your computer’s UEFI firmware configuration

Next we need to prepare the UEFI firmware for accepting the new keys. To accomplish this, we first need to go into Secure Boot “Setup mode”. The Setup mode allows updating the PK without it being signed with the previous PK. This enables you to replace the default PK of which you do not own the secret key material with your own PK for which you do own the secret. Of course, if you could replace PK at any arbitrary moment in time Secure Boot would not be able to protect against anything. Therefore, you are required to reconfigure the UEFI firmware to get into this mode.

The UEFI firmware should provide you with the option to disable Secure Boot. This is typically listed as a toggle (enable/disable) or indirectly by managing the PK:

There may be more variations out there. The key ideas are “if the PK is missing, Secure Boot will not be active” (as there is a key component missing) and “if the Secure Boot is disabled, you are allowed access to the PK without authentication”.

To confirm that Secure Boot is indeed configured in Setup mode, you can use the systemd ‘bootctl’ command in Linux.

bootctl status

This will list the status of the system. In the section ‘System’, you will find the necessary information, which is whether Secure Boot is enabled or disabled and in what mode it is acting.

Build your own Linux kernel

Build your own (vanilla or otherwise) kernel. There are no particular requirements for this kernel, except that the ‘EFI runtime service support’ option should be enabled. Actually, even that feature is probably not required. Next build and install the kernel. Make sure that the kernel actually boots. In this post, I won’t go into the details of how to build your own kernel.

Consider enabling the kernel option to “Require modules to be validly signed” as a way of hardening your system after the boot process has finished. Secure Boot will protect you up to the point where the Operating System becomes active. Allowing only signed modules to load will ensure that after booting too, it is difficult to gain privileged access.

Build UEFI-executable image

First build an executable EFI image using dracut:

sudo dracut --uefi --no-machineid --kver "4.16.5-minimal+" --kernel-image /boot/vmlinuz-4.16.5-minimal+

Next we sign the executable image using sbsign. First switch to user ‘root’ in order to have full access to the EFI partition mounted at /boot/efi.

Dracut generated an image and placed it in /boot/efi/EFI/Linux. Now we need to sign this image.

Call up a root shell:

sudo -i

Go into the directory that contains the generated image:

cd /boot/efi/EFI/Linux

Use the following command to sign the image: (with linux-4.16.5-minimal+.efi being the filename of your image)

sbsign --key <location-of-db-key-file.key> --cert <location-of-db-cert-file.crt> linux-4.16.5-minimal+.efi

A signed copy of the image will be produced at the same location and suffixed with ‘.signed’. This is your signed - thus bootable - linux image.

Next we create a new EFI boot option such that we can actually use the image. Note that the bootable binary’s path is absolute and specified from the root of the EFI partition.

efibootmgr -c -l "\EFI\Linux\linux-4.16.5-minimal+.efi.signed"

With the -c option a new EFI boot option is created and it is given first priority for booting.

Now, before we start installing the keys into the UEFI, let’s try booting the new boot option first. Even if Secure Boot is still disabled, you should be able to boot from this image. If not, fix this first. If the image does not boot, there is no sense in continuing.

Be aware that you should directly boot into the generated linux image. If you find, for example, the Grub menu then you are not testing the image you just generated.

Installing new Secure Boot Keys

The “root keys” mentioned before actually have different names. To be more exact, the keys are called: “PK”, “KEK”, “db” (a.k.a. “boot keys”) and “dbx”. The UEFI firmware supports installing multiple keys of KEK, and db. This is not typically not possible for PK. The PK (“Platform Key”) is the central key that is the root of trust. KEK keys need to be signed with PK. db (or “boot”) keys need to be signed with KEK. There is an additional set of keys “dbx” which is an exclusion list. This can be used to revoke recognition of specific keys that would be trusted if not present in the “dbx” set. For those interested, James Bottomley explains the meaning of Secure Boot keys in this blog post.

Generate security keys

We will start out by generating our own set of keys for PK, KEK and db. We will generate an empty set for dbx. The “CN” value may be changed as desired. We’ll keep it simple in this guide.

openssl req -new -x509 -newkey rsa:2048 -subj "/CN=PK/" -keyout PK.key -out PK.crt -days 3650 -nodes -sha256

openssl req -new -x509 -newkey rsa:2048 -subj "/CN=KEK/" -keyout KEK.key -out KEK.crt -days 3650 -nodes -sha256

openssl req -new -x509 -newkey rsa:2048 -subj "/CN=db/" -keyout db.key -out db.crt -days 3650 -nodes -sha256

Next we need to create the “.esl” (EFI Signature List) format for these keys, which is a special format used in Secure Boot. ESL is a format that may contain multiple keys, in case of KEK and db. The tools can be found in the efitools repository that we previously cloned and built.

First generate the PK signature list:

cert-to-sig-list PK.crt PK.esl

Next, sign the list with the PK:

sign-efi-sig-list -k PK.key -c PK.crt PK PK.esl PK.auth

Similarly, we generate ‘.esl’ files for the KEK and db certificates. PK is used to sign KEK. KEK is used to sign db. Note that in Setup mode it is not required to sign KEK and db, as Setup mode is a sort of “admin” mode. However, with the installation of PK, Secure Boot will immediately switch to User mode (as per specification) and any changes being made afterwards need to be signed with the currently installed PK. That way the system can verify that requested changes are authentic.

To generate an empty dbx (exclusion list), use:

cert-to-sig-list dbx.esl

Why back-up current installed keys

Next we are going to back-up installed keys. For the purpose of this article, this is an optional step, as we want to set up a platform that is completely controlled by our own keys. There are, however, a few situations to take into account before we replace existing keys.

Situation 1: You run a system with multiple OSses. Some OSses are not signed by your key. You do not want to block them from start. Therefore, you need preserve or reinstall the original KEK and/or db keys such that the existing installations remain signed with keys the platform acknowledges.

Situation 2: You want to do firmware updates. Firmware file may be signed with the originally installed keys. Therefore, if we remove the keys, our Secure Boot platform will not allow processing the firmware updates anymore.

Situation 3: Any kinds of software bundled with hardware, such as option ROMs, drivers, etc., which are loaded early in the boot process may be authenticated and therefore blocked. As we replace the keys, these firmwares and softwares would no longer be allowed to run.

The solution for this is to re-add existing KEK and/or db keys while replacing these “databases”. This article does not go into the steps to accomplish this. However, we can be certain this works as there is a Secure Boot requirement (by spec) that the PK must not be used to sign binaries. Therefore it is always possible to replace at least the PK, thus taking control of your platform.

Backing up installed keys

Again, there are multiple options available. The most convenient option is to use the user-space tools provided by efitools. Go into the efitools directory. We’ll assume everything was built previously.

./efi-readvar

This will list all installed certificates in all databases. Next we want to pick a database to back up. The listing shows which “variables” are recognized. These names can be used when downloading the signature lists (*.esl) from the UEFI databases.

For example, to download the db signature list into a file db-current.esl: (Use variable name “KEK” for KEK database.)

./efi-readvar -v db -o db-current.esl

To extract individual certificates, for example to compose a different signature list, use the following command:

./sig-list-to-certs db-current.esl db-certs

This will produce files with prefix ‘db-certs’ for each of the signatures in the signature list.

Alternatively, it is also possible to boot into the USB disk image containing KeyTool. KeyTool offers you the possibility to save the UEFI key databases too. How to produce the USB disk image and start KeyTool is discussed in the next section.

Installing the keys

Next we need to install the keys into the UEFI firmware. This can be done in 2 ways: (In both cases you’ll want to use an USB disk to store the files.)

  1. By using your own UEFI’s update/replace menu options (if available).
  2. By using a small UEFI-runnable software that is able to update or replace keys.

We will go with the second option. If the first option is available for you and works for you then that’s fine too. We will be using a piece of software called KeyTool. It is part of the same software package ‘efitools’ that we built before. Run ‘mkusb.sh’ to generate a small USB disk image with this tooling. Alternatively, you can download the sb-usb.img’ image file and restore to a USB disk, such that you get a UEFI bootable mini system for managing the keys. Of course there is the consideration of how much you would trust a random download on the internet for installing your platform keys. ;-)

To generate the USB image containing KeyTool: (run in efitools directory)

./mkusb.sh "<your-keys-directory>" "." ../sb-img.bin

When generating the USB disk image, it will look for “*.esl” and “*.auth” files as these are the files that are accepted by UEFI firmware. The *.crt and *.key were important earlier in the process and the next chapter urges you to keep the private keys (*.key) safe. However they are not important for installing the public keys into the UEFI firmware’s key databases.

Next write the produced image to the USB disk. This is an raw, unconditional write to the USB device, so make sure you have the correct device and the USB disk does not contain valuable data.

dd if=../sb-img.bin of=<usb-device> bs=1M

Reboot your computer and use boot device selection to boot from the USB disk. Given that Secure Boot is now in Setup mode you will be allowed to boot into the USB disk.

Now start KeyTool, either immediately when booting from the USB disk, or by issuing the command:

KeyTool

Inside KeyTool, you are offered the possibility to install or replace the various keys. Let’s replace the keys with our own keys, starting with KEK, db, dbx and finally PK. With the installation of PK, as per specification, Secure Boot will immediately transition to User mode (away from Setup mode) and therefore any next modification must be authenticated, i.e. signed with the PK. (This is why we update PK last.)

Now reboot the system, by typing ’exit’ and continue booting. If a valid linux image was previously built, and signing succeeded, then you should now be booting into the OS signed using your own boot key, i.e. the key known in db database.

Verify Secure Boot status after successful boot

Now that we are back in our operating system, in this case Fedora, we can verify the status of Secure Boot by running the ‘bootctl’ command again.

bootctl status

This will list the current “System” status. It should now say:

Secure Boot: enabled
Setup Mode: user

You can also verify what keys are installed by running ’efi-readvar’ from ’efitools’ repository again. This should now list your own installed certificates.

./efi-readvar

Safely store the private keys

The generated private keys should be stored away safely. The db (a.k.a. boot) key will still be used to sign the Linux boot image. The other keys are not needed, for now. The PK will need to be used to make changes to the Secure Boot keys configuration. (Or you can clear the keys from the UEFI firmware and start over completely.)

Further reading

References