Red Hat Training

A Red Hat training course is available for RHEL 8

Chapter 4. Signing kernel modules for secure boot

You can enhance the security of your system by using signed kernel modules. The following sections describe how to self-sign privately built kernel modules for use with RHEL 8 on UEFI-based build systems where Secure Boot is enabled. These sections also provide an overview of available options for importing your public key into a target system where you want to deploy your kernel modules.

To sign and load kernel modules, you need to:

If Secure Boot is enabled, the UEFI operating system boot loaders, the Red Hat Enterprise Linux kernel, and all kernel modules have to be signed with a private key and authenticated with the corresponding public key. If they are not signed and authenticated, the system will not be allowed to finish the booting process.

The RHEL 8 distribution includes:

  • Signed boot loaders
  • Signed kernels
  • Signed kernel modules

In addition, the signed first-stage boot loader and the signed kernel include embedded Red Hat public keys. These signed executable binaries and embedded keys enable RHEL 8 to install, boot, and run with the Microsoft UEFI Secure Boot Certification Authority keys that are provided by the UEFI firmware on systems that support UEFI Secure Boot. Note that not all UEFI-based systems include support for Secure Boot.

Prerequisites

To be able to sign externally built kernel modules, install the utilities listed in the following table on the build system.

Table 4.1. Required utilities

UtilityProvided by packageUsed onPurpose

openssl

openssl

Build system

Generates public and private X.509 key pair

sign-file

kernel-devel

Build system

Executable file used to sign a kernel module with the private key

mokutil

mokutil

Target system

Optional utility used to manually enroll the public key

keyctl

keyutils

Target system

Optional utility used to display public keys in the system keyring

Note

The build system, where you build and sign your kernel module, does not need to have UEFI Secure Boot enabled and does not even need to be a UEFI-based system.

4.1. Requirements for authenticating kernel modules with X.509 keys

In RHEL 8, when a kernel module is loaded, the kernel checks the signature of the module against the public X.509 keys from the kernel system keyring (.builtin_trusted_keys) and the kernel platform keyring (.platform). The .platform keyring contains keys from third-party platform providers and custom public keys. The keys from the kernel system .blacklist keyring are excluded from verification. The following sections provide an overview of sources of keys, keyrings and examples of loaded keys from different sources in the system. Also, you can see how to authenticate a kernel module.

You need to meet certain conditions to load kernel modules on systems with enabled UEFI Secure Boot functionality.

If UEFI Secure Boot is enabled or if the module.sig_enforce kernel parameter has been specified:

  • You can only load those signed kernel modules whose signatures were authenticated against keys from the system keyring (.builtin_trusted_keys) and the platform keyring (.platform).
  • The public key must not be on the system revoked keys keyring (.blacklist).

If UEFI Secure Boot is disabled and the module.sig_enforce kernel parameter has not been specified:

  • You can load unsigned kernel modules and signed kernel modules without a public key.

If the system is not UEFI-based or if UEFI Secure Boot is disabled:

  • Only the keys embedded in the kernel are loaded onto .builtin_trusted_keys and .platform.
  • You have no ability to augment that set of keys without rebuilding the kernel.

Table 4.2. Kernel module authentication requirements for loading

Module signedPublic key found and signature validUEFI Secure Boot statesig_enforceModule loadKernel tainted

Unsigned

-

Not enabled

Not enabled

Succeeds

Yes

Not enabled

Enabled

Fails

-

Enabled

-

Fails

-

Signed

No

Not enabled

Not enabled

Succeeds

Yes

Not enabled

Enabled

Fails

-

Enabled

-

Fails

-

Signed

Yes

Not enabled

Not enabled

Succeeds

No

Not enabled

Enabled

Succeeds

No

Enabled

-

Succeeds

No

4.2. Sources for public keys

During boot, the kernel loads X.509 keys from a set of persistent key stores into the following keyrings:

  • The system keyring (.builtin_trusted_keys)
  • The .platform keyring
  • The system .blacklist keyring

Table 4.3. Sources for system keyrings

Source of X.509 keysUser can add keysUEFI Secure Boot stateKeys loaded during boot

Embedded in kernel

No

-

.builtin_trusted_keys

UEFI Secure Boot "db"

Limited

Not enabled

No

Enabled

.platform

Embedded in shim.efi boot loader

No

Not enabled

No

Enabled

.builtin_trusted_keys

Machine Owner Key (MOK) list

Yes

Not enabled

No

Enabled

.platform

.builtin_trusted_keys:

  • a keyring that is built on boot
  • contains trusted public keys
  • the root privileges are needed to view the keys

.platform:

  • a keyring that is built on boot
  • contains keys from third-party platform providers and custom public keys
  • the root privileges are needed to view the keys

.blacklist

  • a keyring with X.509 keys which have been revoked
  • a module signed by a key from .blacklist will fail authentication even if your public key is in .builtin_trusted_keys

UEFI Secure Boot db:

  • a signature database
  • stores keys (hashes) of UEFI applications, UEFI drivers, and bootloaders
  • the keys can be loaded on the machine

UEFI Secure Boot dbx:

  • a revoked signature database
  • prevents keys from being loaded
  • the revoked keys from this database are added to the .blacklist keyring

4.3. Generating a public and private key pair

You need to generate a public and private X.509 key pair to succeed in your efforts of using kernel modules on a Secure Boot-enabled system. You will later use the private key to sign the kernel module. You will also have to add the corresponding public key to the Machine Owner Key (MOK) for Secure Boot to validate the signed module.

Some of the parameters for this key pair generation are best specified with a configuration file.

Procedure

  1. Create a configuration file with parameters for the key pair generation:

    # cat << EOF > configuration_file.config
    [ req ]
    default_bits = 4096
    distinguished_name = req_distinguished_name
    prompt = no
    string_mask = utf8only
    x509_extensions = myexts
    
    [ req_distinguished_name ]
    O = Organization
    CN = Organization signing key
    emailAddress = E-mail address
    
    [ myexts ]
    basicConstraints=critical,CA:FALSE
    keyUsage=digitalSignature
    subjectKeyIdentifier=hash
    authorityKeyIdentifier=keyid
    EOF
  2. Create an X.509 public and private key pair as shown in the following example:

    # openssl req -x509 -new -nodes -utf8 -sha256 -days 36500
    -batch -config configuration_file.config -outform DER \
    -out my_signing_key_pub.der \
    -keyout my_signing_key.priv

    The public key will be written to the my_signing_key_pub.der file and the private key will be written to the my_signing_key.priv file.

    Important

    In RHEL 8, the validity dates of the key pair matter. The key does not expire, but the kernel module must be signed within the validity period of its signing key. For example, a key that is only valid in 2019 can be used to authenticate a kernel module signed in 2019 with that key. However, users cannot use that key to sign a kernel module in 2020.

  3. Optionally, you can review the validity dates of your public keys like in the example below:

    # openssl x509 -inform der -text -noout -in <my_signing_key_pub.der>
    
    Validity
                Not Before: Feb 14 16:34:37 2019 GMT
                Not After : Feb 11 16:34:37 2029 GMT
  4. Enroll your public key on all systems where you want to authenticate and load your kernel module.
Warning

Apply strong security measures and access policies to guard the contents of your private key. In the wrong hands, the key could be used to compromise any system which is authenticated by the corresponding public key.

4.4. Example output of system keyrings

You can display information about the keys on the system keyrings using the keyctl utility.

The following is a shortened example output of .builtin_trusted_keys, .platform, and .blacklist keyrings from a RHEL 8 system where UEFI Secure Boot is enabled.

# keyctl list %:.builtin_trusted_keys
6 keys in keyring:
...asymmetric: Red Hat Enterprise Linux Driver Update Program (key 3): bf57f3e87...
...asymmetric: Red Hat Secure Boot (CA key 1): 4016841644ce3a810408050766e8f8a29...
...asymmetric: Microsoft Corporation UEFI CA 2011: 13adbf4309bd82709c8cd54f316ed...
...asymmetric: Microsoft Windows Production PCA 2011: a92902398e16c49778cd90f99e...
...asymmetric: Red Hat Enterprise Linux kernel signing key: 4249689eefc77e95880b...
...asymmetric: Red Hat Enterprise Linux kpatch signing key: 4d38fd864ebe18c5f0b7...

# keyctl list %:.platform
4 keys in keyring:
...asymmetric: VMware, Inc.: 4ad8da0472073...
...asymmetric: Red Hat Secure Boot CA 5: cc6fafe72...
...asymmetric: Microsoft Windows Production PCA 2011: a929f298e1...
...asymmetric: Microsoft Corporation UEFI CA 2011: 13adbf4e0bd82...

# keyctl list %:.blacklist
4 keys in keyring:
...blacklist: bin:f5ff83a...
...blacklist: bin:0dfdbec...
...blacklist: bin:38f1d22...
...blacklist: bin:51f831f...

The .builtin_trusted_keys keyring above shows the addition of two keys from the UEFI Secure Boot "db" keys as well as the Red Hat Secure Boot (CA key 1), which is embedded in the shim.efi boot loader.

The following example shows the kernel console output. The messages identify the keys with an UEFI Secure Boot related source. These include UEFI Secure Boot db, embedded shim, and MOK list.

# dmesg | grep 'EFI: Loaded cert'
[5.160660] EFI: Loaded cert 'Microsoft Windows Production PCA 2011: a9290239...
[5.160674] EFI: Loaded cert 'Microsoft Corporation UEFI CA 2011: 13adbf4309b...
[5.165794] EFI: Loaded cert 'Red Hat Secure Boot (CA key 1): 4016841644ce3a8...

Additional resources

  • keyctl(1), dmesg(1) manual pages

4.5. Enrolling public key on target system by adding the public key to the MOK list

When RHEL 8 boots on a UEFI-based system with Secure Boot enabled, the kernel loads onto the system keyring (.builtin_trusted_keys) all public keys that are in the Secure Boot db key database. At the same time the kernel excludes the keys in the dbx database of revoked keys. The sections below describe different ways of importing a public key on a target system so that the system keyring (.builtin_trusted_keys) is able to use the public key to authenticate a kernel module.

The Machine Owner Key (MOK) facility feature can be used to expand the UEFI Secure Boot key database. When RHEL 8 boots on a UEFI-enabled system with Secure Boot enabled, the keys on the MOK list are also added to the system keyring (.builtin_trusted_keys) in addition to the keys from the key database. The MOK list keys are also stored persistently and securely in the same fashion as the Secure Boot database keys, but these are two separate facilities. The MOK facility is supported by shim.efi, MokManager.efi, grubx64.efi, and the mokutil utility.

Enrolling a MOK key requires manual interaction by a user at the UEFI system console on each target system. Nevertheless, the MOK facility provides a convenient method for testing newly generated key pairs and testing kernel modules signed with them.

Procedure

  1. Request the addition of your public key to the MOK list:

    # mokutil --import my_signing_key_pub.der

    You will be asked to enter and confirm a password for this MOK enrollment request.

  2. Reboot the machine.

    The pending MOK key enrollment request will be noticed by shim.efi and it will launch MokManager.efi to allow you to complete the enrollment from the UEFI console.

  3. Enter the password you previously associated with this request and confirm the enrollment.

    Your public key is added to the MOK list, which is persistent.

Once a key is on the MOK list, it will be automatically propagated to the system keyring on this and subsequent boots when UEFI Secure Boot is enabled.

Note

To facilitate authentication of your kernel module on your systems, consider requesting your system vendor to incorporate your public key into the UEFI Secure Boot key database in their factory firmware image.

4.6. Signing kernel modules with the private key

Users are able to obtain enhanced security benefits on their systems by loading signed kernel modules if the UEFI Secure Boot mechanism is enabled. The following sections describe how to sign kernel modules with the private key.

Prerequisites

Procedure

  • Execute the sign-file utility with parameters as shown in the example below:

    # /usr/src/kernels/$(uname -r)/scripts/sign-file sha256 my_signing_key.priv my_signing_key_pub.der my_module.ko

    sign-file computes and appends the signature directly to the ELF image in your kernel module file. The modinfo utility can be used to display information about the kernel module’s signature, if it is present.

    Note

    The appended signature is not contained in an ELF image section and is not a formal part of the ELF image. Therefore, utilities such as readelf will not be able to display the signature on your kernel module.

    Your kernel module is now ready for loading. Note that your signed kernel module is also loadable on systems where UEFI Secure Boot is disabled or on a non-UEFI system. That means you do not need to provide both a signed and unsigned version of your kernel module.

    Important

    In RHEL 8, the validity dates of the key pair matter. The key does not expire, but the kernel module must be signed within the validity period of its signing key. The sign-file utility will not warn you of this. For example, a key that is only valid in 2019 can be used to authenticate a kernel module signed in 2019 with that key. However, users cannot use that key to sign a kernel module in 2020.

4.7. Loading signed kernel modules

Once your public key is enrolled in the system keyring (.builtin_trusted_keys) and the MOK list, and after you have signed the respective kernel module with your private key, you can finally load your signed kernel module with the the modprobe command as described in the following section.

Prerequisites

Procedure

  1. Verify that your public keys are on the system keyring:

    # keyctl list %:.builtin_trusted_keys
  2. Copy the kernel module into the /extra/ directory of the kernel you want:

    # cp my_module.ko /lib/modules/$(uname -r)/extra/
  3. Update the modular dependency list:

    # depmod -a
  4. Load the kernel module and verify that it was successfully loaded:

    # modprobe -v my_module
    # lsmod | grep my_module
    1. Optionally, to load the module on boot, add it to the /etc/modules-loaded.d/my_module.conf file:

      # echo "my_module" > /etc/modules-load.d/my_module.conf

Additional resources