Secure Boot/GRUB

From Gentoo Wiki
Jump to:navigation Jump to:search



This page explains how to set up GRUB to work with Secure Boot.

Understanding The Boot Chain

Before we begin the installation, we need to understand the boot chain: what happens in between powering on a device and being prompted to login.

CODE Boot Chain Visual
# (1)-> +------+ --(2)-> +------+ --(4)---> +---------------------+ /-----> +----------------+
#       | UEFI |         | GRUB |       |   | GRUB-related files/ | |       | Kernel Modules |
#       +------+ <-(3)-- +------+ <-(5)---- | Initramfs/          | | /---- +----------------+
#              ^                    |   |   | Microcode           | | |
#              |                    |   |   +---------------------+ | | /-> +-------------+
#              |                    |   |                           | | |   | Init system |
#              |                    |   `-> +--------+ --(7)--------/ | |   +-------------+
#              |                    |       | Kernel |                | |
#              |                    `------ +--------+ <-(8)----------/ |
#              |                            |        |                  |
#              `-------(6)------------------/        `---(9)------------/
The db, GPG, and openssl keys will be explained later.
Step 1 The device powers on, starting the UEFI.
Step 2 The UEFI chooses the GRUB executable as the secondary bootloader.
Step 3 The UEFI checks the signature of the GRUB executable before running it. The signature is made by the db private key and checked with the public key embedded in the UEFI. If the signature is good, then the UEFI runs the GRUB executable to continue the boot chain.
Step 4 The GRUB executable searches for external GRUB files; these files include the GRUB configuration file, GRUB modules, and other GRUB-related files required to boot; GRUB also searches for Microcodes, Initramfses, Kernels, and any other files required to boot.
Step 5 GRUB checks the signatures of all gathered files before they are loaded/ran. The signatures are made by the GPG private key and checked with the public key embedded in the GRUB executable. If all the signatures are good, then the files can be loaded/ran and the boot chain can continue.
Step 6 This is the step where the kernel would run and do its thing, but there is something that needs to happen first: the UEFI needs to check the signature of the kernel; this is because the kernel itself is an executable, and the UEFI will refuse to run anything not signed with the db key. This means that the kernel is a special file that needs to be signed by two keys: the GPG key, and the db key. If the UEFI determines the kernel's signature to be good, the boot chain can continue.
Step 7 The kernel searches for any external modules required to boot.
Step 8 The kernel checks the signatures of the modules before they are loaded. The signatures are made by the kernel's private key and checked with the public key embedded in the kernel. This key can be made automatically via the kernel itself or manually via openssl. If the modules' signatures are good, the boot chain can continue.
Step 9 The kernel starts the Init system.

Installation

Kernel

For this guide, we will be configuring our kernel manually for those that wish to do so.

root #emerge --ask sys-kernel/gentoo-sources

Installkernel

We will be using sys-kernel/installkernel to automatically generate the initramfs with Dracut and the GRUB configuration. To do this, ensure that USE flags dracut and grub are enabled when we install installkernel.

FILE /etc/portage/package.use/my_installkernelSet USE flags
sys-kernel/installkernel dracut grub
root #emerge --ask sys-kernel/installkernel

USE flags for sys-kernel/installkernel Gentoo fork of installkernel script from debianutils

dracut Generate an initramfs or UKI on each kernel installation
efistub EXPERIMENTAL: Update UEFI configuration on each kernel installation
grub Re-generate grub.cfg on each kernel installation, used grub.cfg is overridable with GRUB_CFG env var
refind Install a Gentoo icon for rEFInd alongside the (unified) kernel image, used icon is overridable with REFIND_ICON env var
systemd Use systemd's kernel-install to install kernels, overridable with SYSTEMD_KERNEL_INSTALL env var
systemd-boot Use systemd-boot's native layout by default
ugrd Generate an initramfs using UGRD on each kernel installation
uki Install UKIs to ESP/EFI/Linux for EFI stub booting and/or bootloaders with support for auto-discovering UKIs
ukify Build an UKI with systemd's ukify on each kernel installation

Tip
Setting the dracut and grub USE flags should also install said tools as dependencies; if not, install them manually.
root #emerge --ask sys-kernel/dracut sys-boot/grub
Note
GRUB has the USE flag secureboot, but enabling this flag only generates a signed GRUB executable in /usr/lib/grub/grub-<arch>.efi.signed which is to be copied to where the GRUB executable is supposed to go. We will not be using this executable, but it's safe to enable the USE flag anyway.

Sbsigntools

We will be using app-crypt/sbsigntools to sign executable files with the db private key.

root #emerge --ask app-crypt/sbsigntools

Efitools

We will be using app-crypt/efitools to enroll the PK, KEK, db, and dbx keys into the UEFI.

root #emerge --ask app-crypt/efitools

Key Creation

Before we install any tools, we can make the key pairs that will be used in the boot chain. Different keys are used depending on which step in the boot chain we're at. There are three types of keys: the ones for the UEFI, GRUB, and the Kernel.

UEFI

The UEFI uses four different types of keys for secure boot: PK, KEK, db, and dbx.

  • PK - Platform Key - Composed of two parts, PKpub (the public key) and PKpriv (the private key), used to sign the KEK.
  • KEK - Key Exchange Key - The key used to sign the Signatures and Forbidden Signatures database, there can be more than one.
  • db - Signature Database - Contains lists of public keys, signatures, and hashes which are allowed as part of the boot chain.
  • dbx - Forbidden Signature Database - The opposite of the signature database, public keys, signatures, and hashes which should never be allowed to boot.


There is only one PK key while there can be multiple KEK, db, and dbx keys.

Imagine the PK key as the root user of a machine -- an omnipotent user that decides who has the ability to allow/deny executable files.

Imagine a KEK key as an unprivileged user that can allow/deny executable files they decide.

An executable file signed by the db key will be allowed, while the dbx key will deny.

There can be multiple users (KEK) each having multiple keys to allow (db) and deny (dbx). All users must be approved (signed) by root (PK).

Making the Keys

We can put the keys in an appropriate path such as /etc/ssl/private. Make a directory under this path so that we can place our secure boot keys in it and set the correct permissions.

root #mkdir -p /etc/ssl/private/secure_boot
root #chmod 700 /etc/ssl/private/secure_boot


UEFI Secure Boot typically uses RSA-2048 and sha256, but some motherboards might support stronger algorithms.

Warning
The following command uses the -noenc option, meaning that the key will *NOT* be protected by a password; anyone with access to the key will be able to use it to sign files!


This is only safe if:


We do this because:

  • The commands that use the key will already need root privileges.
  • Portage's FEATURES=pid-sandbox interfaces with dev-libs/openssl's passphrase prompt.
  • No one wants to manage another password.


root #for key_name in my_pk.pem my_kek.pem my_db.pem my_dbx.pem; do


openssl req -batch -new -noenc -sha256 -key <(openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048) -x509 -out "/etc/ssl/private/secure_boot/$key_name" -keyout "/etc/ssl/private/secure_boot/$key_name"

done

Also set the correct permissions.

root #for key_name in my_pk.pem my_kek.pem my_db.pem my_dbx.pem; do


chmod 400 "/etc/ssl/private/secure_boot/$key_name"

done

Making the ESLs

Next, we make the EFI signature lists.

root #for key_name in my_pk.pem my_kek.pem my_db.pem my_dbx.pem; do


cert-to-efi-sig-list /etc/ssl/private/secure_boot/{"$key_name","${key_name%.*}".esl}

done

(Optional) Combining our Keys and the Factory Keys

We can make a backup of the factory keys (except the PK key) in the UEFI to later concatenate them with our keys. This is only required for tools like Shim to still work.

root #for key_type in KEK db dbx; do


efi-readvar -v "$key_type" -o factory_"${key_type,,}".esl

done

If we want to include the factory esl files, we simply concatenate them with ours (except the PK key) and use the combined files from this point on.

root #for key_type in kek db dbx; do


cat factory_"$key_type".esl my_"$key_type".esl >combined_"$key_type".esl

done

Signing the Keys

The PK and KEK esl files are signed by the PK key.

root #sign-efi-sig-list -c /etc/ssl/private/secure_boot/my_pk.pem -k /etc/ssl/private/secure_boot/my_pk.pem PK /etc/ssl/private/secure_boot/my_pk.{esl,auth}
root #sign-efi-sig-list -c /etc/ssl/private/secure_boot/my_pk.pem -k /etc/ssl/private/secure_boot/my_pk.pem KEK /etc/ssl/private/secure_boot/my_kek.{esl,auth}

The database esl files are signed by the KEK key.

root #sign-efi-sig-list -c /etc/ssl/private/secure_boot/my_kek.pem -k /etc/ssl/private/secure_boot/my_kek.pem db /etc/ssl/private/secure_boot/my_db.{esl,auth}
root #sign-efi-sig-list -c /etc/ssl/private/secure_boot/my_kek.pem -k /etc/ssl/private/secure_boot/my_kek.pem dbx /etc/ssl/private/secure_boot/my_dbx.{esl,auth}

Installing the Keys to the UEFI

Before running the command that installs the keys to the UEFI, the UEFI must be in "Setup Mode"; doing this varies across manufacturers, but the idea is the same. Reboot the machine and boot into the UEFI; there should be a page for "Security" and "Secure Boot" should be in it; there should be an option to clear the current PK, KEK, db, and dbx keys. Clear the keys, save the settings, and reboot.

If done correctly, we should get the following output after we reboot:

root #efi-readvar
Variable PK has no entries
Variable KEK has no entries
Variable db has no entries
Variable dbx has no entries
Variable MokList has no entries

Finally, we install the keys to the UEFI.

root #for key_type in PK KEK db dbx; do


efi-updatevar -f /etc/ssl/private/secure_boot/my_"${key_type,,}".auth "$key_type"

done

If the keys installed correctly, we should get the following output:

root #efi-readvar
Variable PK, length 923
PK: List 0, type X509
    Signature 0, size 895, owner 00000000-0000-0000-0000-000000000000
        Subject:
            C=AU, ST=Some-State, O=Internet Widgits Pty Ltd
        Issuer:
            C=AU, ST=Some-State, O=Internet Widgits Pty Ltd
Variable KEK, length 923
KEK: List 0, type X509
    Signature 0, size 895, owner 00000000-0000-0000-0000-000000000000
        Subject:
            C=AU, ST=Some-State, O=Internet Widgits Pty Ltd
        Issuer:
            C=AU, ST=Some-State, O=Internet Widgits Pty Ltd
Variable db, length 923
db: List 0, type X509
    Signature 0, size 895, owner 00000000-0000-0000-0000-000000000000
        Subject:
            C=AU, ST=Some-State, O=Internet Widgits Pty Ltd
        Issuer:
            C=AU, ST=Some-State, O=Internet Widgits Pty Ltd
Variable dbx, length 923
dbx: List 0, type X509
    Signature 0, size 895, owner 00000000-0000-0000-0000-000000000000
        Subject:
            C=AU, ST=Some-State, O=Internet Widgits Pty Ltd
        Issuer:
            C=AU, ST=Some-State, O=Internet Widgits Pty Ltd
Variable MokList has no entries

GRUB

GRUB will use it's own method of verifying files further in the boot chain via GPG.

GPG

GPG has a command line interface tool to make keys, but it also has a "batch" mode so we can put the command in a script. Make the following GPG parameter file:

Warning
The following file contains the %no-protection option, meaning that the key will *NOT* be protected by a password; anyone with access to the key will be able to use it to sign files!


This is only safe if:


We do this because:

  • The commands that use the key will already need root privileges.
  • No one wants to manage another password.


FILE /root/my_grub_gpg_parameter
# See warning!
%no-protection

# As of GRUB 2.12, only DSA and RSA keys are supported.
Key-Type: RSA
Key-Length: 4096

# GRUB only needs to verify signatures.
Key-Usage: sign

# Information to help identify this key.
Name-Real: grub
Name-Comment: This key is used to sign files for GRUB.

# (0) means to never expire.
Expire-Date: 0

%commit

Now make the key.

root #gpg --batch --full-gen-key /root/my_grub_gpg_parameter

Export the public key so that it can be embedded into the GRUB executable.

root #gpg -o /root/grub_gpg_public_key --export grub

Kernel

We can put the key in an appropriate path such as /etc/ssl/private. Make a directory under this path so that we can place our kernel key in it and set the correct permissions.

root #mkdir -p /etc/ssl/private/kernel
root #chmod 700 /etc/ssl/private/kernel


The kernel only supports a specific set of key types depending on the version.

KERNEL Check which key types are supported
-*- Cryptographic API ---> Search for <code>CONFIG_CRYPTO</code> to find this item.
        Certificates for signature checking --->
            Type of module signing key to be generated (<key type>) --->

Where "<key type>" is the type of key the kernel will generate if one is not provided.

As of Linux kernel version 6.12.1, the supported types are:

  • RSA
  • ECDSA

We will be creating an ECDSA key with the curve P-521 and hash SHA3-512 (the strongest curve and hash that the kernel supports). If these are not available, use the strongest possible for the kernel.

Warning
The following command uses the -noenc option, meaning that the key will *NOT* be protected by a password; anyone with access to the key will be able to use it to sign files!


This is only safe if:


We do this because:

  • The commands that use the key will already need root privileges.
  • Portage's FEATURES=pid-sandbox interfaces with dev-libs/openssl's passphrase prompt.
  • No one wants to manage another password.


root #openssl req -batch -new -noenc -utf8 -sha3-512 -key <(openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-521) -x509 -outform PEM -out /etc/ssl/private/kernel/my_kernel_key.pem -keyout /etc/ssl/private/kernel/my_kernel_key.pem

Also set the correct permissions.

root #chmod 400 /etc/ssl/private/kernel/my_kernel_key.pem

Set Variables

Portage

Secure Boot

On packages with the secureboot global USE flag, this flag can be enabled to automatically sign any EFI binaries installed by the package. When this flag is enabled the SECUREBOOT_SIGN_KEY and SECUREBOOT_SIGN_CERT environment variables must be used to specify the path (or pkcs11 URI) of the db key and certificate to use for signing in PEM format.

FILE /etc/portage/make.conf
USE="... secureboot ..."

SECUREBOOT_SIGN_KEY="/etc/ssl/private/secure_boot/my_db.pem"
SECUREBOOT_SIGN_CERT="/etc/ssl/private/secure_boot/my_db.pem"

Update all packages with the secureboot USE flag now enabled.

root #emerge --ask -uND @world

Kernel

In addition to the kernel itself, the kernel modules must also be signed to boot successfully with secure boot enabled. For this purpose, the modules-sign global USE flag can be used in addition to the MODULES_SIGN_KEY and MODULES_SIGN_CERT environment variables.

Important
MODULES_SIGN_HASH must be set to the same algorithm as the kernel's!
FILE /etc/portage/make.conf
USE="... modules-sign ..."

MODULES_SIGN_KEY="/etc/ssl/private/kernel/my_kernel_key.pem"
MODULES_SIGN_CERT="/etc/ssl/private/kernel/my_kernel_key.pem"
MODULES_SIGN_HASH="sha3-512"

Update all packages with the modules-sign USE flag now enabled; this will also sign the modules.

root #emerge --ask -uND @world
Tip
To only update packages with out-of-tree kernel modules:
root #emerge --ask @module-rebuild

Configuration

Kernel

There are many kernel options to increase security; for this page, we will only be going over what is needed to get secure boot working. For additional kernel settings for secure boot, see Handbook:AMD64/Installation/Kernel.

Important
Ensure that MODULE_SIG_KEY is set to where we stored our kernel key!
KERNEL Enable support for signed kernel modules
[*] Enable loadable module support ---> Search for <code>CONFIG_MODULES</code> to find this item.
    -*- Module signature verification Search for <code>CONFIG_MODULE_SIG</code> to find this item.
    [*]     Require modules to be validly signed Search for <code>CONFIG_MODULE_SIG_FORCE</code> to find this item.
    [*]     Automatically sign all modules Search for <code>CONFIG_MODULE_SIG_ALL</code> to find this item.
        Hash algorithm to sign modules (SHA3-512) ---> Search for <code>CONFIG_MODULE_SIG_SHA3_512</code> to find this item.
-*- Cryptographic API ---> Search for <code>CONFIG_CRYPTO</code> to find this item.
    Certificates for signature checking --->
        (/etc/ssl/private/kernel/my_kernel_key.pem) File name or PKCS#11 URI of module signing key Search for <code>CONFIG_MODULE_SIG_KEY</code> to find this item.

Usage

Kernel

After we've made all the changes to our kernel, we can compile it.

root #cd /usr/src/linux[version]
root #make -j$(nproc) && make -j$(nproc) modules_install && make install

Then sign the kernel with the db key for the UEFI to verify.

root #sbsign --cert /etc/ssl/private/secure_boot/my_db.pem --key /etc/ssl/private/secure_boot/my_db.pem /boot/vmlinuz-<version>-gentoo --output /boot/vmlinuz-<version>-gentoo

GRUB

We need to make and sign the GRUB executable. For the installation, we will need three parameters in addition to the ones that we already use to install GRUB to our machine. For more information on how to install GRUB for a specific machine, see GRUB. The three parameters are as follows:

  • --disable-shim-lock -- Disable the use of Shim as we are not using it.
  • --pubkey -- Embed the GPG public key to verify signatures of files signed by the GPG private key.
  • --modules -- Embed GRUB modules needed to verify signatures of files signed by the GPG private key.


Note
All available GRUB modules are in /boot/grub/<architecture>
root #grub-install --disable-shim-lock --pubkey=/root/grub_gpg_public_key --modules="pgp gcry_sha512 gcry_rsa" [other parameters for machine]

Now sign the GRUB executable with the db key for the UEFI to verify.

root #sbsign --cert /etc/ssl/private/secure_boot/my_db.pem --key /etc/ssl/private/secure_boot/my_db.pem /path/to/grub.efi --output /path/to/grub.efi


Next, we need to sign all the files GRUB needs to verify; we can do this with a script.

Important
Signing the files GRUB needs to verify must come after signing the kernel because the kernel embeds the db signature, which would change the GRUB detached signature!
FILE /root/sign_file_for_grub
sign_file_for_grub()
{

	# Sign all files that GRUB will use in the boot chain; see 'info grub' section 19.2. The list of files to sign include:
	# - GRUB-related files (configs, environment, modules).
	# - kernel.
	# - initramfs.
	# - CPU microcode.
	local valid_file=''
	for valid_file in $(find /boot -regextype posix-extended -regex '.*(\.cfg|\.lst|\.mod|vmlinuz.*gentoo(-r[[:digit:]]+)?(\.old)?|init.*(-r[[:digit:]]+)?\.img(\.old)?|grubenv|-uc\.img)$'); do
	
		# Delete the old signature.
		[[ -f "$valid_file".sig ]] && rm "$valid_file".sig
	
		# Make a new detached signature for a file that GRUB will verify on boot.
		gpg --batch -bu grub --digest-algo SHA512 "$valid_file" || break
	done
}

sign_file_for_grub "$@"
root #chmod +x /root/sign_file_for_grub
root #/root/sign_file_for_grub

Verify

At this point, secure boot is set up; the only thing left to do is reboot into the UEFI and ensure that secure boot is enabled.

root #reboot

After we reboot and make it past GRUB and the login, we can check if secure boot is enabled by running the following command:

root #dmesg | grep -i secure
[   0.00123] [   T0] Secure boot enabled

Automation

For future kernel installations, the signing process can be fully automated when we install our kernel.

FILE /root/my_install_kernel
my_install_kernel()
{
	local -r CURRENT_PATH="$(pwd -P)"

	# Check to see if we are in the correct directory.
	if [[ -z "$(echo "$CURRENT_PATH" | grep -E '^/usr/src/linux-')" ]]; then
		echo "This needs to run in /usr/src/linux[...]!"
		return
	fi

	# Get the Linux version (and the revision if available) so that we know which 'vmlinuz' to sign with 'sbsign'. Older kernels will already have the
	# embedded signature.
	local -r LINUX_VERSION="$(echo "$CURRENT_PATH" | sed -E 's/^\/usr\/src\/linux-([[:digit:].]+).*/\1/g')"
	local -r LINUX_REVISION="$(echo "$CURRENT_PATH" | grep -E -- '-r[[:digit:]]+$' | sed -E 's/.*-r([[:digit:]]+)$/\1/g')"

	# Compile and install the kernel via sys-kernel/installkernel as usual.
	make -j$(nproc) && make -j$(nproc) modules_install && make install || return

	# The newly compiled kernel needs to be signed with the UEFI 'db' key.
	sbsign --cert "/etc/ssl/private/secure_boot/my_db.pem" --key "/etc/ssl/private/secure_boot/my_db.pem" "/boot/vmlinuz-${LINUX_VERSION}-gentoo${LINUX_REVISION:+-r"$LINUX_REVISION"}" --output "/boot/vmlinuz-${LINUX_VERSION}-gentoo${LINUX_REVISION:+-r"$LINUX_REVISION"}"

	sign_file_for_grub
}

sign_file_for_grub()
{

	# Sign all files that GRUB will use in the boot chain; see 'info grub' section 19.2. The list of files to sign include:
	# - GRUB-related files (configs, environment, modules).
	# - kernel.
	# - initramfs.
	# - CPU microcode.
	local valid_file=''
	for valid_file in $(find /boot -regextype posix-extended -regex '.*(\.cfg|\.lst|\.mod|vmlinuz.*gentoo(-r[[:digit:]]+)?(\.old)?|init.*(-r[[:digit:]]+)?\.img(\.old)?|grubenv|-uc\.img)$'); do
	
		# Delete the old signature.
		[[ -f "$valid_file".sig ]] && rm "$valid_file".sig
	
		# Make a new detached signature for a file that GRUB will verify on boot.
		gpg --batch -bu grub --digest-algo SHA512 "$valid_file" || break
	done
}

my_install_kernel "$@"
root #chmod +x /root/my_install_kernel
root #/root/my_install_kernel

Troubleshooting

GRUB

Error: prohibited by secure boot policy.

Ensure that all needed external GRUB modules in /boot/grub/<architecture> are signed by the GPG key. This error happens when secure boot is enabled in the UEFI and GRUB loads successfully, but GRUB can't load any of its modules.

Error: shim_lock protocol not found.

Ensure that --disable-shim-lock is used. GRUB uses Shim by default, so we need to tell GRUB to disable it because we are using our own keys for secure boot.

root #grub-install --disable-shim-lock ...

Error: you need to load the kernel first.

Ensure the kernel is signed by the db UEFI key *THEN* signed by the GRUB GPG key; the db signature will be embedded into the kernel while the GPG signature will be detached.

Error: verification requested but nobody cares: (<drive>,<partition>)/grub/<architecture>/<grub module>.mod.

Ensure that the pgp GRUB module is embedded into the GRUB executable; GRUB needs this module embedded because this is the module that verifies other modules and files.

root #grub-install --modules="pgp" ...

Error: loading initial key: bad signature

Ensure that the GPG key generated is supported by GRUB and that the public key is exported correctly.

root #gpg -o /path/to/grub_gpg_public_key --export grub

Ensure the GPG public key is embedded into the GRUB executable with --pubkey.

root #grub-install --pubkey=/path/to/grub_gpg_public_key ...

Removal

If secure boot is no longer wanted, it can be disabled in the UEFI; the other signature checking performed by GRUB and the kernel can be kept.

See also

External resources

References