Fedora

Secure Boot on Fedora: MOK, Module Signing and the 2023 CA

With Secure Boot on, a Fedora kernel refuses any module that is not signed by a key it trusts. The error it prints, Key was rejected by service, tells you nothing about which key or how to add one, and that is the problem most people hit first. The quieter one sits further up the chain: the Microsoft UEFI CA 2011 that signs shim expired in June 2026, and a machine whose firmware only trusts that certificate will not boot a future shim signed with the 2023 CA alone.

Original content from computingforgeeks.com - post 175919

Below, you read the Secure Boot chain on Fedora, add the 2023 Microsoft CA with fwupd, then sign a module with your own Machine Owner Key (MOK), enroll it and remove it. Every step ran on a VM on Proxmox with OVMF firmware, and the firmware screens are real captures from it. All of it was tested in October 2026 on Fedora 45 with Secure Boot enforcing and the kernel in lockdown integrity mode, so no step turns either off.

The chain of trust on a Fedora machine

Four key stores decide what boots, and mokutil reads all of them without root. Start by confirming Secure Boot is actually enforcing, because every later step assumes it is:

mokutil --sb-state
cat /sys/kernel/security/lockdown

Secure Boot also puts the kernel into lockdown, and the bracketed word in the second line is the active mode:

SecureBoot enabled
none [integrity] confidentiality

Integrity mode blocks kexec of unsigned kernels and writes to kernel memory from user space, and with Secure Boot on, Fedora also enforces module signatures. If your machine prints SecureBoot disabled, none of the module signing below is enforced, and the first thing to fix is the firmware setting, not the module. Next, list who owns each store:

mokutil --pk | grep -E 'Subject:'
mokutil --kek | grep -E 'Subject:'
mokutil --db | grep -E 'Subject:'
mokutil --list-enrolled | grep -E 'Subject:'

On our VM the four stores held this, in the order of the commands:

        Subject: O=Debian, CN=Debian UEFI Secure Boot (PK\/KEK key)/[email protected]
        Subject: O=Debian, CN=Debian UEFI Secure Boot (PK\/KEK key)/[email protected]
        Subject: C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Corporation KEK CA 2011
        Subject: C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Windows Production PCA 2011
        Subject: C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Corporation UEFI CA 2011
        Subject: C=US, ST=Massachusetts, L=Cambridge, O=Red Hat, Inc., OU=Fedora Secure Boot CA 20200709, CN=fedoraca

Read it from the top. The platform key (PK) belongs to whoever built the firmware, which is Debian here because Proxmox ships Debian’s OVMF; on a laptop it is the vendor. The KEK holds the keys allowed to change db, and Microsoft’s 2011 KEK is the one that matters for updates. db is the allow list the firmware checks before it runs anything, and it held only the two 2011 Microsoft CAs. The last line is the MOK list that shim keeps, which already trusts Fedora’s own CA, so Fedora kernels and modules load without you doing anything.

The kernel logs every certificate it loads at boot. Fedora’s own signing keys go into the built-in keyring, while the firmware and MOK certificates go into the .platform keyring, which Fedora also accepts module signatures from, as Red Hat documents for its kernels:

sudo dmesg | grep -E "Loaded X.509 cert"

The first three lines are the built-in keys; the lines tagged integrity are the firmware and MOK certificates:

[    0.802558] Loaded X.509 cert 'Fedora kernel signing key: 925079f85d8cf849b2cfc1ccc8e5eb56d336c8f3'
[    0.814575] Loaded X.509 cert 'Fedora IMA CA: a8a00c31663f853f9c6ff2564872e378af026b28'
[    0.823128] Loaded X.509 cert 'Fedora kernel signing key: 925079f85d8cf849b2cfc1ccc8e5eb56d336c8f3'
[    0.831817] integrity: Loaded X.509 cert 'Microsoft Windows Production PCA 2011: a92902398e16c49778cd90f99e4f9ae17c55af53'
[    0.833353] integrity: Loaded X.509 cert 'Microsoft Corporation UEFI CA 2011: 13adbf4309bd82709c8cd54f316ed522988a1bd4'
[    0.836372] integrity: Loaded X.509 cert 'Red Hat, Inc.: fedoraca: b280c7ae6b884e0f4d2a0d8724c25eaf6c65c326'

Shim signatures and the 2011 CA expiry

An expired CA does not stop an already signed shim from booting, because UEFI firmware does not check signature dates. The risk is the next shim. Once Microsoft stops signing with the 2011 key, new shims carry only the 2023 signature, and firmware whose db lacks the 2023 CA will refuse them. See which signatures your shim carries with sbverify from sbsigntools; keyutils provides the keyctl tool used later:

sudo dnf install -y sbsigntools keyutils
sudo sbverify --list /boot/efi/EFI/fedora/shimx64.efi

The current Fedora shim is signed twice, once by each CA:

signature 1
image signature issuers:
 - /C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Corporation UEFI CA 2011
image signature certificates:
 - subject: /C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Windows UEFI Driver Publisher
   issuer:  /C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Corporation UEFI CA 2011
 - subject: /C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Corporation UEFI CA 2011
   issuer:  /C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Corporation Third Party Marketplace Root
signature 2
image signature issuers:
 - /C=US/O=Microsoft Corporation/CN=Microsoft UEFI CA 2023
image signature certificates:
 - subject: /C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft UEFI CA 2023 signer
   issuer:  /C=US/O=Microsoft Corporation/CN=Microsoft UEFI CA 2023
 - subject: /C=US/O=Microsoft Corporation/CN=Microsoft UEFI CA 2023
   issuer:  /C=US/O=Microsoft Corporation/CN=Microsoft RSA Devices Root CA 2021
warning: data remaining[908000 vs 1036008]: gaps between PE/COFF sections?

That dual signature is why this VM boots today with only 2011 certificates in db: the firmware matches signature 1 and ignores signature 2. The trailing sbverify warning is about padding in the PE file and appeared on every run without affecting the result. A shim with only signature 2 would not boot here, which is the case the next section closes. The Fedora Magazine note on the expiry covers the project’s position, and if the same machines also run Windows or are Proxmox VMs, the Secure Boot certificate update guide for Windows and Proxmox covers the KEK step those need.

The 2023 Microsoft CA added with fwupd

fwupd can append the 2023 CAs to db from inside Fedora, using an update that Microsoft’s 2011 KEK has signed, so the firmware accepts it without any vendor tool. Before you run it on real hardware, know the risk. Changing Secure Boot variables changes the PCR 7 value the TPM measures at the next boot, and Google’s guidance for its own VMs warns that BitLocker then boots to its recovery screen. Fedora’s default passphrase-only LUKS is not affected, but any disk unlocked by the TPM against PCR 7 is, so have your LUKS or BitLocker recovery keys at hand; on a Fedora and Windows dual boot, that means both. On a VM, take a snapshot first; we did. Install fwupd and refresh its metadata:

sudo dnf install -y fwupd
sudo fwupdmgr refresh --force

After a long run of progress lines, the refresh reports what it found. A count of updatable devices only means fwupd can talk to them, so the next command is what tells you whether a Secure Boot update is actually on offer:

Successfully downloaded new metadata:
 • 5 devices are updatable
 • 2 devices are supported in the enabled remotes (an update has been published)

List the pending updates. fwupd shows each Secure Boot certificate as its own device:

sudo fwupdmgr get-updates --no-unreported-check

The part that matters is the UEFI CA device, moving from 2011 to 2023:

Devices with no available firmware updates:
 • Windows Production PCA
 • KEK CA
 • SBAT
 • UEFI Secure Boot (PK/KEK key)
Devices with the latest available firmware version:
 • UEFI dbx
QEMU Standard PC (Q35 + ICH9, 2009)
│
└─UEFI CA:
  │   Device ID:          5bc922b7bd1adb5b6f99592611404036bd9f42d0
  │   Current version:    2011
  │   Vendor:             Microsoft (UEFI:Microsoft)
  │   GUIDs:              26f42cba-9bf6-5365-802b-e250eb757e96 ← UEFI\VENDOR_Microsoft&NAME_Microsoft-UEFI-CA
  │                       c34a7e6a-bd86-5244-8bd0-7db66fd3c073 ← UEFI\CRT_E30CF09DABEAB32A6E3B07A7135245DE05FFB658
  │   Device Flags:       • Internal device
  │                       • Updatable
  │                       • Supported on remote server
  │                       • Needs a reboot after installation
  │                       • Signed Payload
  │                       • Can tag for emulation
  │
  └─Secure Boot Signature Database Configuration Update:
        New version:      2023
        Remote ID:        lvfs
        Release ID:       116503
        Summary:          UEFI Secure Boot Signature Database
        License:          Proprietary
        Size:             10.0 kB
        Created:          2025-04-29 00:00:00
        Urgency:          High

The KEK has no update here, and that is expected. A new KEK has to be signed by the platform key, and only the firmware’s owner holds that key, which is Debian on this VM and your hardware vendor on a laptop. The db update needs only the old Microsoft KEK, so it applies anywhere that KEK is present. Install it with the device ID from your own output:

sudo fwupdmgr update 5bc922b7bd1adb5b6f99592611404036bd9f42d0 -y --no-reboot-check --no-unreported-check

We filtered out the progress lines, and the run ended like this. The KEK still shows no update, because nothing in this payload touches it, so a clean finish here says nothing about the KEK either way:

Successfully installed firmware
Devices with no available firmware updates:
 • Windows Production PCA
 • KEK CA
 • SBAT
 • UEFI Secure Boot (PK/KEK key)

fwupd flags this update as needing a reboot, but on our OVMF firmware the new entries were readable straight away. Check db again:

mokutil --db | grep -E 'Subject:'

Two 2023 CAs now sit next to the 2011 ones, which stay in place so older signed binaries keep working:

        Subject: C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Windows Production PCA 2011
        Subject: C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Corporation UEFI CA 2011
        Subject: C=US, O=Microsoft Corporation, CN=Microsoft UEFI CA 2023
        Subject: C=US, O=Microsoft Corporation, CN=Microsoft Option ROM UEFI CA 2023

Reboot anyway, because that is the real test: the machine must come back with Secure Boot still enabled. Ours did, and after the reboot fwupd reported the UEFI CA device at version 2023. If yours does not boot, enter the firmware setup and restore the factory keys there; that is why the recovery keys and snapshot came first. Do not disable Secure Boot to get past a failed update, because that also switches off every check this guide relies on.

Why an unsigned module is refused

Any module you build yourself, from a vendor tarball or your own code, is unsigned, and the kernel’s signature enforcement rejects it. A minimal test module shows the failure without risking a real driver. Install the build tools for the running kernel first, plus pesign for the key tools used later. kernel-devel-$(uname -r) only resolves if you are running the newest kernel in the repositories, so if you updated without rebooting, reboot first:

sudo dnf install -y kernel-devel-$(uname -r) gcc make pesign
mkdir -p ~/hello && cd ~/hello

Create the module source. We kept it to a module that does nothing except log, so if it fails to load, the signature check is the only thing that can be to blame:

vim ~/hello/hello.c

It logs a line when it loads and another when it unloads:

#include <linux/module.h>
#include <linux/init.h>

static int __init hello_init(void)
{
	pr_info("hello: loaded\n");
	return 0;
}

static void __exit hello_exit(void)
{
	pr_info("hello: unloaded\n");
}

module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Secure Boot signing test module");

Then the Makefile, which hands the build to the kernel’s own build system. Building against /lib/modules/$(uname -r)/build matters because a module built for another kernel fails to load for a different reason, and you would be chasing the wrong error:

vim ~/hello/Makefile

The indented lines must start with a tab, not spaces:

obj-m := hello.o

all:
	$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(CURDIR) modules

clean:
	$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(CURDIR) clean

Build it, confirm it has no signer, and try to load it:

make
modinfo -F signer ~/hello/hello.ko
sudo insmod ~/hello/hello.ko

modinfo prints nothing for the signer, and the load fails. The insmod message never mentions signatures, which is why this error sends so many people looking for a missing driver instead:

insmod: ERROR: could not insert module /home/mercy/hello/hello.ko: Key was rejected by service

The kernel log says what happened. Check it right after any refused load:

sudo dmesg | tail -1

For a module with no signature at all, it is explicit:

[ 3167.907671] Loading of unsigned module is rejected

Out-of-tree drivers from RPM Fusion avoid this by building through akmods, which signs with a key it creates per host. That key still needs the same MOK enrollment shown below, and the Fedora CoreOS on VirtualBox guide walks through the akmods key enrollment for the VirtualBox modules on a Fedora host, including a module built before its key existed.

A module signing key of your own

Red Hat’s documented route creates the key with efikeygen from pesign, which adds Red Hat’s module-signing key usage (1.3.6.1.4.1.2312.16.1.2) next to Code Signing. Run it as root, because the key lands in the NSS database under /etc/pki/pesign:

sudo efikeygen --dbdir /etc/pki/pesign --self-sign --module --common-name 'CN=sb-lab01 module signing key' --nickname 'sb-lab01 module key'

On our VM it printed two errors, and you can ignore them:

cms_common.c:find_certificate_by_callback:884: Could not find certificate:SEC_ERROR_EXTENSION_NOT_FOUND:Certificate extension not found.
efikeygen.c:add_trust:100: Could not find certificate

Both come from the step that marks the new certificate as trusted inside the NSS database, which signing does not need. The key pair itself was created, and the export below proves it. sign-file wants the certificate in DER form and the private key as a plain PEM file, so export both. Use a throwaway password for the intermediate PKCS#12 file:

P12_PASS=$(openssl rand -hex 8)
sudo certutil -d /etc/pki/pesign -n 'sb-lab01 module key' -Lr | sudo tee /root/sb_cert.cer > /dev/null
sudo pk12util -o /root/sb_cert.p12 -n 'sb-lab01 module key' -d /etc/pki/pesign -W "$P12_PASS"
sudo openssl pkcs12 -in /root/sb_cert.p12 -out /root/sb_cert.priv -nocerts -noenc -passin "pass:$P12_PASS"
sudo chmod 600 /root/sb_cert.priv /root/sb_cert.p12
sudo openssl x509 -inform DER -in /root/sb_cert.cer -noout -subject -enddate -ext extendedKeyUsage

The certificate check shows the subject, a long validity period and the two key usages that matter, code signing plus the Red Hat module signing OID:

pk12util: PKCS12 EXPORT SUCCESSFUL
subject=CN=sb-lab01 module signing key
notAfter=Jan 19 03:14:07 2037 GMT
X509v3 Extended Key Usage:
    Code Signing, 1.3.6.1.4.1.2312.16.1.2

/root/sb_cert.priv is an unencrypted private key. Anyone who copies it can sign modules your kernel will load once the certificate is enrolled, so keep it root-only, back it up offline, and never put it on a shared build box. Now sign the module with the kernel’s own sign-file tool and check the result:

sudo /usr/src/kernels/$(uname -r)/scripts/sign-file sha256 /root/sb_cert.priv /root/sb_cert.cer ~/hello/hello.ko
modinfo ~/hello/hello.ko | grep -E '^(signer|sig_hashalgo)'
sudo insmod ~/hello/hello.ko

The module now names its signer, and the kernel still refuses it:

signer:         sb-lab01 module signing key
sig_hashalgo:   sha256
insmod: ERROR: could not insert module /home/mercy/hello/hello.ko: Key was rejected by service

That second refusal is correct. A signature only counts when the kernel trusts the key behind it, and nothing on this machine trusts yours yet. The kernel log now reads Loading of module with unavailable key is rejected, which is how you tell this case apart from an unsigned module.

The key enrolled through MokManager

Shim’s MOK list is the one store you can add to without the firmware vendor, but only from the console during boot, so a remote attacker with root can queue a key and still cannot enroll it. Queue the certificate and set a one-time password:

sudo mokutil --import /root/sb_cert.cer
sudo mokutil --list-new | grep -E 'Subject:|Fingerprint'

mokutil asks for the password twice, then lists the queued key. Note the fingerprint, because MokManager shows the same certificate before you confirm it:

input password:
input password again:
SHA1 Fingerprint: 90:96:28:2d:06:54:bd:27:f2:08:67:a8:d7:53:73:75:8b:2c:6f:3f
        Subject: CN=sb-lab01 module signing key

Reboot and stay at the console. MokManager waits only 10 seconds for a key press, and if the countdown runs out the request is dropped and you have to run mokutil --import again. It also reads the keyboard as US QWERTY, so on another layout, pick a password whose characters sit in the same place on both. The screens come in this order:

  1. Press any key at “Press any key to perform MOK management”.
  2. Select Enroll MOK, then Continue (View key 0 shows the certificate).
  3. At “Enroll the key(s)?”, move from No to Yes.
  4. Type the password from mokutil.
  5. Select Reboot.

Step 2 is where you can still check that the key on screen is yours before anything changes:

MokManager Enroll MOK screen on Fedora 45 with View key 0 and Continue options

After the reboot, confirm the key is in all three places it needs to be: the MOK list, the kernel’s .platform keyring, and the module load path:

mokutil --list-enrolled | grep -E 'Subject:'
sudo mokutil --test-key /root/sb_cert.cer
sudo keyctl list %:.platform
sudo insmod ~/hello/hello.ko
sudo dmesg | grep -E 'hello: loaded|sb-lab01 module'

The new key sits next to Fedora’s CA, the keyring also shows the two 2023 Microsoft CAs from the fwupd update, and the module loads:

        Subject: C=US, ST=Massachusetts, L=Cambridge, O=Red Hat, Inc., OU=Fedora Secure Boot CA 20200709, CN=fedoraca
        Subject: CN=sb-lab01 module signing key
/root/sb_cert.cer is already enrolled
6 keys in keyring:
381065450: ---lswrv     0     0 asymmetric: Microsoft Windows Production PCA 2011: a92902398e16c49778cd90f99e4f9ae17c55af53
1008803177: ---lswrv     0     0 asymmetric: sb-lab01 module signing key: e8b9d739a4fab5511511a4dd1203662955659418
931618260: ---lswrv     0     0 asymmetric: Red Hat, Inc.: fedoraca: b280c7ae6b884e0f4d2a0d8724c25eaf6c65c326
556162938: ---lswrv     0     0 asymmetric: Microsoft Option ROM UEFI CA 2023: 514fbf937fa46fb57bf07af8bed84b3b864b1711
765142149: ---lswrv     0     0 asymmetric: Microsoft UEFI CA 2023: 81aa6b3244c935bce0d6628af39827421e32497d
579756019: ---lswrv     0     0 asymmetric: Microsoft Corporation UEFI CA 2011: 13adbf4309bd82709c8cd54f316ed522988a1bd4
[    0.874214] integrity: Loaded X.509 cert 'sb-lab01 module signing key: e8b9d739a4fab5511511a4dd1203662955659418'
[   15.510753] hello: loaded

Every key in that keyring can vouch for a kernel module, which is the point to take away. Enroll a key only if you control its private half, and remove it when you stop needing it. Unload the test module with sudo rmmod hello when you are done.

Removing a key you no longer trust

A MOK key stays trusted until you remove it, even if the private key leaks or the machine changes hands. Removal is the same console ritual in reverse, so a compromised system cannot quietly drop your keys either:

sudo mokutil --delete /root/sb_cert.cer
sudo mokutil --list-delete | grep -E 'Subject:'

mokutil asks for a new one-time password twice, exactly as it did for the import, then lists the key queued for removal:

input password: 
input password again: 
        Subject: CN=sb-lab01 module signing key

Reboot, stay at the console, and press a key inside the same 10 second window. The screens mirror the enrollment:

  1. Select Delete MOK, then Continue.
  2. At “Delete the key(s)?”, move from No to Yes.
  3. Type the new password, then select Reboot.

The confirmation also defaults to No, so a habitual Enter leaves the key enrolled:

MokManager Delete the key(s) confirmation on Fedora 45 with No selected by default

Once you have confirmed it with Yes and the password, check that the key is gone and that the module is refused again:

mokutil --list-enrolled | grep -E 'Subject:'
sudo mokutil --test-key /root/sb_cert.cer
sudo insmod ~/hello/hello.ko

Only Fedora’s CA is left, the module signed with the removed key no longer loads, and the kernel log reports Loading of module with unavailable key is rejected:

        Subject: C=US, ST=Massachusetts, L=Cambridge, O=Red Hat, Inc., OU=Fedora Secure Boot CA 20200709, CN=fedoraca
/root/sb_cert.cer is not enrolled
insmod: ERROR: could not insert module /home/mercy/hello/hello.ko: Key was rejected by service

Removing the key from MOK does not remove it from the disk. If you are retiring it rather than rotating it, delete the private half too, from both the NSS database and the exported files:

sudo certutil -d /etc/pki/pesign -F -n 'sb-lab01 module key'
sudo shred -u /root/sb_cert.priv /root/sb_cert.p12

Afterwards sudo certutil -d /etc/pki/pesign -L no longer lists the key, and only the public sb_cert.cer is left in /root, which is harmless to keep as a record of what you removed.

What to verify on every signing host

Run through these on every Fedora machine you sign modules for. Each one is a condition that should hold, with the command from this guide that proves it:

  1. mokutil --sb-state reports SecureBoot enabled and lockdown shows [integrity].
  2. sbverify --list shows a Microsoft UEFI CA 2023 signature on shimx64.efi.
  3. mokutil --db lists Microsoft UEFI CA 2023 before any 2023-only shim arrives.
  4. mokutil --list-enrolled holds only keys whose private half you control.
  5. Private signing keys are mode 600, owned by root, and backed up offline.
  6. Every module you load reports a signer in modinfo.
  7. Disk encryption recovery keys are stored somewhere you can reach when the firmware keys change.

Items 2 and 3 are the ones the 2026 expiry adds. If item 3 fails and fwupd offers no db update for your machine, check your hardware vendor’s firmware updates before the next shim release; the dual-signed shim keeps you booting until then. For NVIDIA’s proprietary driver, which uses the same akmods signing path, the NVIDIA drivers on Fedora guide covers the enrollment for that module.

Keep reading

UFW Firewall Commands with Examples on Ubuntu 26.04 / 24.04 Security UFW Firewall Commands with Examples on Ubuntu 26.04 / 24.04 PHP2Shell (CVE-2026-109618, CVE-2026-109619): Check and Patch PHP Php PHP2Shell (CVE-2026-109618, CVE-2026-109619): Check and Patch PHP PHP2Shell on End-of-Life PHP 8.1, 8.0 and 7.4 (WHMCS, ionCube) Php PHP2Shell on End-of-Life PHP 8.1, 8.0 and 7.4 (WHMCS, ionCube) Fix ptrace Operation not permitted for gdb and strace on Fedora Fedora Fix ptrace Operation not permitted for gdb and strace on Fedora Fedora 45 vs Bazzite for Gaming: Steam, Updates and NVIDIA Fedora Fedora 45 vs Bazzite for Gaming: Steam, Updates and NVIDIA Setup WireGuard VPN on Fedora 45 / 44 Fedora Setup WireGuard VPN on Fedora 45 / 44

Leave a Comment

Press ESC to close