Introduction
This article will begin with a high-level overview of the Ubuntu boot process and will continue to dig deeper into the role of SecureBoot in it when enabled. Some of the concepts I will be covering include shim, EFI variables, and MOKs. The information presented here was aggregated from the sources listed at the bottom of this article. Commands and example output will also be included to help present the topic in a more comprehensive way.
Understanding the boot process along with SecureBoot is important because without adequate understanding of the inner-workings of a Linux system, it can be very easy to accidentally brick the system when attempting to enable settings such as SecureBoot. These are low-level operations that can do a lot of damage.
I would encourage Windows users to reference this documentation for Secure Boot information relating to Windows.
Typical Boot Process
When an Ubuntu machine boots, it goes through 4 main phases.
- BIOS phase- firmware on motherboard, typically stored as a form of read only memory (ROM), contains code to initialize the hardware components of the computer and obtain the code for the bootloader.
- Bootloader phase- loads the operating system into memory along with an initial ram disk filesystem (initrd).
- Kernel phase- the kernel executes the init script inside the initrd filesystem. This loads hardware drivers and mounts the root partition.
- System startup- the operating system loads system daemons and services, sets up the network, mounts file systems, starts system logging, and performs other initialization tasks.
It is common for a 512-bit partition to be present on Linux systems called the Master Boot Record (MBR). This partition contains the bootloader (GRUB, LILO, yaboot, or others) and boot records. This is one of the places that Linux can be booted from. Other locations include a bootloader from a storage device like USB flash drive or CDR or a bootloader that is transferred over the network such as with Preboot Execution Environment (PXE).
Secure Boot
Secure Boot is a security standard. When the computer is turned on, the Secure Boot process begins with firmware in the motherboard, which will check the cryptographic signatures of each of the boot files. This includes UEFI firmware drivers (aka optional ROMs), EFI applications, and the operating system. Once verified, the computer boots and the firmware gives control to the operating system.
Shim
When enabling Secure Boot, it is important to understand shim. In the context of SecureBoot, a shim is a pre-bootloader program that is designed to work with Secure Boot firmware. It allows for bootloaders and kernel modules to be loaded and executed if they are not included in the Secure Boot database. In Ubuntu, the shim loader is pre-installed and signed by the Microsoft certificate authority.
Secure Boot uses asymmetrical cryptography, meaning that a public and private key are used. The key pair can be generated by the user and the private key is used to sign all programs that are allowed to run, including the GRUB bootloader. The firmware on the BIOS or UEFI will use the public key to verify the checksums and signatures of programs before allowing them to execute.
You can check the signatures of your own shim loader with the sbverify
command that comes with the sbsigntool
package.
# locate your shim binary
$ SHIM=$(sudo find /boot/efi/EFI/ -iname "shim*" 2>/dev/null)
$ sbverify $SHIM
Signature verification OK
UEFI variables
Another concept to be familiar with is the UEFI variables which are stored in firmware non-volatile RAM (NV-RAM). These variables store various data such as boot order preferences, timeout values, network settings, storage device details, and Secure Boot settings. Each UEFI variable will have its own binary file under /sys/firmware/efi/efivars/
. The naming convention for these files is the variable name followed by the vendor GUID. For example, SecureBoot-8be4df61–93ca-11d2-aa0d-00e098032b8c
may be used to store whether Secure Boot is enabled (0×01) or disabled (0×00).
You can view some of these variables by either listing the contents of /sys/firmware/efi/efivars/
or using the efivarfs tool to list and read the values.
$ sudo apt-get install efivar
$ sudo efivar -p -n 8be4df61-93ca-11d2-aa0d-00e098032b8c-SecureBoot
In this particular example, the value of the UEFI variable SecureBoot
is 0×01, which indicates that it is currently enabled. Some of the other important variables for Secure Boot are the following:
8be4df61-93ca-11d2-aa0d-00e098032b8c-PKDefault
8be4df61-93ca-11d2-aa0d-00e098032b8c-KEKDefault
8be4df61-93ca-11d2-aa0d-00e098032b8c-dbDefault
8be4df61-93ca-11d2-aa0d-00e098032b8c-dbxDefault
These values pertain to the key databases which are used to determine whether or not a module is safe to load.
Secure Boot Databases
Secure Boot utilizes 4 key databases. You can read more about them in the official specifications or see a summary here:
- Allowed Signature Database (db) - contains a list of cryptographic signatures that are allowed to load during the boot process.
- Disallowed Signature Database (dbx) - contains a list of the cryptographic signatures that are not allowed to be loaded during the boot process.
- Key Enrollment Key Database (KEK) - contains the key exchange keys used to authenticate other databases.
- Platform Key Database (PK) - contains the public key that is used to verify the signature of any bootloader or firmware that has been signed with its corresponding private key. The recommended platform key on UEFI is RSA-2048.
The key databases essentially set the rules for which signatures are allowed to be loaded and which are not. This is important in Secure Boot because it helps to verify the integrity of modules before they are executed.
Machine Owner Keys (MOKs)
Another component of the boot process is Machine Owner Keys (MOKs). MOKs are an extra database of keys that can be managed by the user. This is separate from the certificate authority key that comes shipped with shim. They give the user more control over which modules can be loaded. For example, when a user enrolls a MOK on the system, the key associated with it is added to the allowed signature database (db). This means that any binary signed with that key will be trusted by the firmware during the boot process.
These are typically located in the /var/lib/shim-signed/mok/
directory under the names MOK.der
, MOK.pem
or MOK.priv
. If you don’t have MOK keys and would like to generate them, the following commands can be used:
# mkdir -p /var/lib/shim-signed/mok/
# cd /var/lib/shim-signed/mok/
# openssl req -new -x509 -newkey rsa:2048 -keyout MOK.priv -outform DER -out MOK.der -days 36500 -subj "/CN=My Name/"
# openssl x509 -inform der -in MOK.der -out MOK.pem
By default, shim provides a management utility called MokManager that can be used to, “enroll keys, remove trusted keys, enroll binary hashes and toggle Secure Boot validation at the shim level,” as described by the Ubuntu documentation. Note that a password is typically required when using the MokManager to authenticate the user that is using it.
MokManager will help to properly configure the keys when key management is required. Once key management has been completed, the system will reboot to enable the key management changes. If things go well, it will continue to boot as expected without a MokManager screen.
Bootloader Configurations and Information
You can futher investigate your Linux machine’s boot process. One way to do this is to view the /proc/cmdline
file. This contains the kernel boot command line arguments that were passed to the kernel during the boot process.
$ cat /proc/cmdline
# example output
BOOT_IMAGE=/boot/vmlinuz-5.11.0-16-generic root=UUID=12345678-1234-1234-1234-1234567890AB ro quiet splash
This command will output the path to the kernel image file that is loaded by the bootloader via BOOT_IMAGE
. The quiet splash
option at the end is commonly used by the GRUB bootloader to specify to suppress verbose boot messages and display a graphical boot splash screen.
A kernel image is a binary file of the operating system core. It contains contains the necessary code and data structures to boot the system, manage memory, handle input/output operations, and execute user programs. They can be used by the bootloader to start the OS. The name of the kernel image will typically represent its version and architecture. For instance, the kernel image vmlinuz-5.4.0–1042-aws
is version 5.4.0–1042
running on AWS archtecture.
The bootloader contains its own file system drivers (initramfs) that you can view if you know the /boot/initrd*
file that corresponds to the kernel image in use. The easiest way to do this is with the tools from initramfs-tools-core:
## list files inside the initramfs
$ lsinitramfs /boot/initrd.img-$(uname -r)
.
kernel
kernel/x86
.
bin
conf
conf/arch.conf
conf/conf.d
conf/conf.d/resume
-- snip --
## extract files from the initramfs
$ unmkinitramfs /boot/initrd.img-$(uname -r) initramfs/
This allows you to navigate the initramfs filesystem and gain a deeper understanding of how things are working as the system boots. If you extracted initramfs
, you’ll notice that upon looking in the directory ./initramfs
, there are 3 folders: early
, early2
, and main
.
$ ls initramfs
early early2 main
In the context of initramfs, early
and early2
refer to the first and second stage of the initial RAM filesystem. During the first stage, the minimal set of drivers and utilities that are needed to initialize the hardware and mount the real root filesystem are loaded. In the second stage, early2
, loads additional drivers and utilities needed to fully initialize the system.
The main
folder of initramfs contains the actual root filesystem image, as well as any additional tools or drivers that may be needed during the boot process. Once the root filesystem has been mounted, the system can proceed to load the regular set of services and daemons needed to run the operating system.
Checking Signatures Manually
You can check which modules are digitally signed using the modinfo
command where a kernel’s signature will appear as a long string of hexadecimal values separated by colons.
$ modinfo example.ko
filename: /lib/modules/5.10.0-5-amd64/kernel/drivers/misc/example.ko
version: 1.0.0
license: GPL
description: Example kernel module
author: John Doe <jdoe@example.com>
srcversion: 12AB34CD5678EF90ABCD1234
sig_key: A0:3B:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF
sig_hashalgo: sha256
signature: 12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF
Conclusion
Thanks for reading this overview of Secure Boot. I would encourage you to do further reading in the sources linked below because this article only scratched the surface. I hope that from this article you were able to come to appreciate the beautifully complex system of a Linux machine turning on.
Sources