Skip to main content

How to use Secure Boot to validate startup software

Secure Boot uses digital key pairs to check that SystemTap and other startup code hasn't been altered by a rootkit or similar mechanism.
Image
Key pair

Photo by Ylanite Koppens on Pexels

There are many aspects to reducing a Linux server's security risks. Protecting the integrity and confidentiality of data is certainly important, and it's also essential to ensure the system is configured properly to manage the server. One aspect of this configuration to consider is Secure Boot.

What is Secure Boot?

Secure Boot is a protocol that enables a safe and trusted path during the Linux boot process. It verifies that the code the firmware loads on a motherboard is the code that the user intends for the computer to load. In other words, it verifies that malicious code hasn't been inserted (by a rootkit or similar mechanism) in place of the kernel that the user has installed.

Secure Boot is part of the Unified Extensible Firmware Interface (UEFI). The protocol defines a process that prevents the loading of unsigned drivers, boot loaders, or kernel modules (or those with unacceptable digital signatures). When Secure Boot is enabled, system boot loaders, the Red Hat Enterprise Linux kernel, and all kernel modules must be cryptographically signed with a private key. This allows them to be authenticated with the corresponding public key.

Module signing makes it harder to load a suspicious module into the kernel. The kernel does the module signature verification. Module signing currently supports the RSA public key encryption standard. To check whether a system uses Secure Boot mode, use the mokutil command:

$ mokutil --sb-state

Sign a kernel module

When signing a standalone kernel module, a public and private X.509 key pair is used to digitally sign kernel modules on a Secure Boot system. The private key is used to sign the kernel module, and the corresponding public key is added to the Secure Boot Machine Owner Key (MOK) to validate the signed module.

First, create a configuration file for key pair generation:

$ sudo cat << EOF >> x509.genkey
[ req ]
default_bits = 4096
distinguished_name = req_distinguished_name
prompt = no
x509_extensions = myexts

[ req_distinguished_name ]
O = ModuleType
CN = ModuleType module signing key

[ myexts ]
basicConstraints=critical,CA:FALSE
keyUsage=digitalSignature
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid
EOF

Create an X.509 public and private key pair using the openssl command:

$  openssl req -x509 -new -nodes -utf8 -sha256 -days 36500 \
-batch -config x509.genkey -outform DER \
-out signing_key.x509 \
-keyout signing_key.priv

The signing_key.x509 file is the public key, and the signing_key.priv is the private key.

Install the keys

Now that you have your key pair, you must add the public key to the MOK list:

$ mokutil --import signing_key.x509
Password: XXX

Now, reboot your system. When Linux boots on a UEFI-based system with Secure Boot enabled, the kernel imports the keys in the MOK list into the system keyring.

A list of choices is displayed:

Continue boot
Enroll MOK
…

[ Download the free guide to implementing DevSecOps. ]

Choose the Enroll MOK option. Next, a different list of choices is displayed:

View
 Continue
 Choose Continue

You're prompted to enroll the key. Choose Yes, enter the key password, and allow the system to reboot.

The system reboots, and the new key is added to the system keyring.

Query the system keyring

You can use the keyctl and mokutil commands to gather information about the key you added to your system's keyring:

$ sudo keyctl list %:.platform
1 key in keyring:
556121735: ---lswrv 0 0 asymmetric: Secure Boot CA 5: dd7e654ae88f…

You can display a more verbose variation of the information with:

$ mokutil -l

The kernel console output shows the keys loaded into the system keyring:

$ dmesg | grep 'integrity: Loaded

Sign a kernel module

If you develop original kernel modules, you must digitally sign them so that they can load into the kernel with modprobe.

First, create a sample module and save it to a file called hello-1.c:

/*  
 *  hello-1.c - The simplest kernel module.
 */

#include <linux/module.h>	/* Needed by all modules */
#include <linux/kernel.h>	/* Needed for KERN_INFO */
MODULE_LICENSE ("GPL v2");

int init_module(void)
{
	/* printk is the kernel message logging facility */
	printk(KERN_INFO "Hello world 1.\n");
            /* A non 0 return means init_module failed; module can't be loaded. */
	return 0;
}
void cleanup_module(void)
{
	printk(KERN_INFO "Goodbye world 1.\n");
}

Next, create a Makefile for the hello-1.c sample module:

obj-m += hello-1.o

all:
make -C /lib/modules/\$(shell uname -r)/build M=\$(PWD) modules

clean:
make -C /lib/modules/\$(shell uname -r)/build M=\$(PWD) clean

[ Find out about the new features in RHEL 9. ]

Now build the module:

$ make

Use the sign-file utility to sign the module with the imported MOK key.

$ /usr/src/kernels/$(uname -r)/scripts/sign-file sha256 \
signing_key.priv \
signing_key.x509 \
my_module.ko

The sign-file command computes and appends the signature directly to the ELF image in your kernel module file. You cannot display the signature with an ELF utility, but you can use the modinfo utility to display information about your module's signature.

$ modinfo my_module.ko
filename:    ~/kernel-module/hello-1.ko
license:     GPL v2
rhelversion: 9.0
srcversion:  0ADA50D36BA76F1D1018A81
depends:         
retpoline:   Y
name:        hello_1
vermagic:    5.14.0-70.5.1.el9_0.x86_64 SMP preempt mod_unload modversions

You can now load your hello-1.c kernel module with the modprobe command.

Sign a SystemTap module

SystemTap translates probes into C code, builds a module, and loads the resulting module into a running Linux kernel. On a Secure Boot system, you must sign the module. There are two methods for signing a SystemTap module. One method uses a SystemTap compile server, and the other does not use a compile server.

Sign a module without a compile server

Starting with SystemTap 4.7, you can sign a module without a compile server, which is similar to the standalone module method used above. The SystemTap command is stap, and the option --sign-module signs a module. If there is no SystemTap MOK key in the system MOK database, then SystemTap creates a key to be imported (using OpenSSL) and instructs you to reboot the system.

Create an X.509 public and private key pair:

$ sudo stap --sign-module -e 'probe syscall.read { printf("%s (%s)\n", name, argstr)}'
Generating an RSA private key
writing new private key to '/DIR/.systemtap/ssl/server/moks/stap-server.VGpxGW/signing_key.priv'
There is no machine owner key (MOK) in common with this system.

This command has failed because there's no MOK key pair available yet. That's normal when using stap to generate a key pair because the MOK can't be set up until a key pair exists. You'll run the same command later after adding the MOK to the keychain.

Use the following command to import a MOK into this system:

$ sudo mokutil --import /DIR/.systemtap/ssl/server/moks/67:c4:f4:b1:f8:e5:01:de:a1:a0:a7:c3:09:94:b0:10:17:bb:97:e2/signing_key.x509

Reboot, and add the public key to the MOK list again:

$ sudo mokutil –import /DIR/.systemtap/ssl/server/moks/67:c4:f4:b1:f8:e5:01:de:a1:a0:a7:c3:09:94:b0:10:17:bb:97:e2/signing_key.x509     

Add the key to the system keyring (as you did above).

Reboot your system and import the SystemTap key as prompted. When SystemTap is invoked again, the SystemTap module is signed and loaded:

$ sudo stap  --sign-module -e 'probe syscall.read { printf("%s (%s)\n", name, argstr)}'

SystemTap keeps the SystemTap key used to sign the module in the directory /DIR/.systemtap/ssl/server/moks/{FINGERPRINT}/ (where {FINGERPRINT} is the actual fingerprint you see listed, such as f3:9e:bb:a5:cb:78:9f:1a:da:e9:0c:a5:3d:a4:cf:6e:a1:a3:79:7b). You can see the public key with the openssl command:

$ sudo openssl x509 -text -inform der -in
 /DIR/.systemtap/ssl/server/moks/<FINGERPRINT>/signing_key.x509

You can use keyctl or mokutil to see the system keyring.

Sign a module with a compile server

You can also use the SystemTap compile server (you can read about it with man stap-server) to sign a module. The SystemTap compile server listens for connections from stap clients on a secure SSL network port and accepts requests to run the stap frontend to build SystemTap modules. The client sends the server a list of the SystemTap keys in the system keyring. If there is no matching SystemTap key, then your computer prompts you to import the key and reboot.

Create an X.509 public and private key pair:

$ stap --list-servers=all
Systemtap Compile Server Status for 'all'
host=fedora 35.local address=192.168.122.127 port=1234 sysinfo="5.14.10-300.fc35.x86_64 x86_64" version=4.7 certinfo="00:bb:39:5d:75" mok_fingerprints="f2:b8:48:74:d6:38:f3:90:a1:bd:2f:da:bf:dd:3b:93:fd:c3:de:b7

$ stap -e 'probe begin {println("Hello From Server");exit()}'  \
 --use-server=fedora 35:1234
Server: No matching machine owner key (MOK) available on the server to sign the module.
Server: The server has no machine owner key (MOK) in common with this
system. Use the following command to import a server MOK into this
system, then reboot:

$ sudo mokutil --import signing_key.x509

Add the public key to the MOK list as instructed:

$ sudo mokutil –import signing_key.x509

Add the key to your system keyring, and then reboot. Import the SystemTap key when prompted.

If there is a matching SystemTap key, the compile server builds and signs the module, which SystemTap runs on the client system:

$ stap -e 'probe begin {println("Hello From Server");exit()}'  \
--use-server=fedora 35:1234`
Hello From Server

Know your binaries

Digitally signing files is an excellent way to verify the identity of binaries that need to run on your system. With signature verification, it's not an option for an attacker to swap out an important module just by giving it the correct name and placing it in the right location. Consider whether to start using Secure Boot on your systems and digitally sign your work.

Check out these related articles on Enable Sysadmin

Image
Typing on a laptop with headphones next to the computer
Red Hat Enterprise Linux 9 offers significant new features for hybrid cloud organizations. Here's how to install it so you can start testing.
Topics:   Security   Linux administration  
Author’s photo

Stan Cox

Stan Cox works in Red Hat's platform tools group on performance tools, primarily SystemTap and Dyninst. Stan came to Red Hat when it acquired Cygnus, where he worked on the gcc compiler. More about me

Red Hat Summit 2022: On Demand

Get the latest on Ansible, Red Hat Enterprise Linux, OpenShift, and more from our virtual event on demand.

Related Content

OUR BEST CONTENT, DELIVERED TO YOUR INBOX