Manually Self-Signing a Kernel Module on RHEL with Secure Boot Enabled

This document describes how to self-sign a third-party kernel module on RHEL 9 so it can be loaded when Secure Boot is enabled.

Scope:

  • Applies to any out-of-tree / third-party kernel module (.ko).

  • Does not cover how to build or install the module itself — only how to sign and load it with Secure Boot enabled.

0. Preconditions

  • RHEL 9.x with Secure Boot enabled

  • The kernel module is already built for the running kernel and installed somewhere under:

    • /lib/modules/$(uname -r)/...
      or you know the full path to the .ko file.

  • Root access and ability to:

    • Reboot the system

    • Interact with the MOK Manager screen at boot

Install required packages:

sudo dnf install -y \
  mokutil \
  openssl \
  "kernel-devel-$(uname -r)" \
  "kernel-headers-$(uname -r)"

1. Confirm the kernel module exists

Replace <module_name> with the module you want to sign (for example, mydriver):

sudo -i
modinfo <module_name> || echo "module not found"
  • If modinfo prints metadata, the module is present and associated with the running kernel.

  • If you see "module not found", make sure:

    • The module has been built for $(uname -r)

    • It is installed under /lib/modules/$(uname -r)/...

You can also locate the .ko path directly:

modinfo -n <module_name>

This will print something like:

/lib/modules/$(uname -r)/extra/<module_name>.ko

2. Generate a local MOK key pair

Create a directory for your local signing key (example path):

mkdir -p /root/module-mok
cd /root/module-mok

Generate a self-signed certificate and private key:

openssl req -new -x509 -newkey rsa:2048 \
  -keyout MOK.priv -out MOK.pem \
  -nodes -days 3650 \
  -subj "/CN=Local Secure Boot Module Key/"

Secure the private key:

chmod 600 /root/module-mok/MOK.priv

What this does

  • Creates:

    • MOK.priv → private key (used to sign modules)

    • MOK.pem → public certificate (to be enrolled via MOK)

  • This key pair is local to this system (or systems where you choose to copy it) and can be reused to sign multiple modules.

3. Convert PEM to DER and import via MOK

mokutil expects a certificate in DER format.

cd /root/module-mok

openssl x509 -in MOK.pem -inform PEM -outform DER -out MOK.der

mokutil --import /root/module-mok/MOK.der

You will be prompted to:

  • Set a password (twice).

    • This is not your root password.

    • It’s a one-time password you will type in the MOK Manager screen during the next boot to confirm the key enrollment.

Confirm the key is queued for enrollment:

mokutil --list-new

You should see an entry with:

  • Subject: CN=Local Secure Boot Module Key

4. Enroll the key in MOK Manager (boot-time)

Reboot:

reboot

During the next boot, you will see the blue MOK Manager screen.

Follow these steps:

  1. Select Enroll MOK

  2. (Optional) View key 0 to confirm the CN (you should see something like Local Secure Boot Module Key)

  3. Select Continue

  4. Select Yes to enroll the key

  5. Enter the password you set when you ran mokutil --import

  6. Confirm and let the system reboot again

After the system boots back into RHEL, verify the key is enrolled:

mokutil --list-enrolled | grep "Local Secure Boot Module Key"

You should see:

  • Issuer: CN=Local Secure Boot Module Key

  • Subject: CN=Local Secure Boot Module Key

5. Sign the kernel module

  1. Get the full path to the module:

    sudo -i
    MOD=$(modinfo -n <module_name>)
    echo "$MOD"
    

    This should be something like:

    /lib/modules/5.14.0-.../extra/<module_name>.ko
    
  2. Set helper variables:

    KDIR=/usr/src/kernels/$(uname -r)
    KEYDIR=/root/module-mok
    
  3. Sign the module using the kernel’s sign-file script:

    "$KDIR"/scripts/sign-file sha256 \
      "$KEYDIR"/MOK.priv \
      "$KEYDIR"/MOK.pem \
      "$MOD"
    
  4. Verify that the signature was applied:

    modinfo "$MOD" | egrep 'signer|sig_key|sig_hashalgo'
    

    You should see output similar to:

    signer:         Local Secure Boot Module Key
    sig_key:        <some fingerprint>
    sig_hashalgo:   sha256

6. Load the module and verify Secure Boot acceptance

Reload module dependencies and load the module:

depmod -a
modprobe <module_name>
  • If Secure Boot accepts the signature, modprobe should return with no error.

  • You should not see:

    Key was rejected by service
    

Confirm the module is loaded:

lsmod | grep <module_name>

You should see a line containing <module_name> if it’s loaded.

If there is an issue, check the kernel log:

dmesg | tail -n 50

Look for messages related to module signing or Secure Boot.

Notes about kernel updates

When the kernel is updated:

  • Out-of-tree modules may need to be:

    1. Rebuilt for the new kernel version

    2. Re-signed using the same MOK.priv / MOK.pem key pair

General pattern after a kernel update:

# After installing and booting into the new kernel:
uname -r                              # confirm new kernel
modinfo <module_name>                 # ensure it's built for this kernel
MOD=$(modinfo -n <module_name>)       # get new .ko path
KDIR=/usr/src/kernels/$(uname -r)
KEYDIR=/root/module-mok

"$KDIR"/scripts/sign-file sha256 \
  "$KEYDIR"/MOK.priv \
  "$KEYDIR"/MOK.pem \
  "$MOD"

depmod -a
modprobe <module_name>

The MOK enrollment is persistent. You normally only generate/import/enroll the key once; future kernels just require rebuilding and re-signing modules with the same key.

Comments