Safeguard
Supply Chain Security•

Kernel Module Signing and DKMS Explained: sig_enforce, MOK, Secure Boot

Linux checks a signature appended to each .ko file. Learn how sig_enforce, lockdown, MOK keys and DKMS decide whether an out-of-tree driver loads.

Safeguard Security Team
40 min read

Linux kernel module signing appends a cryptographic signature to each .ko file, and the kernel checks it against its trusted keys before the module runs. With CONFIG_MODULE_SIG_FORCE, module.sig_enforce=1 or Secure Boot lockdown, unsigned modules are refused. DKMS rebuilds out-of-tree drivers for each new kernel and signs them with a Machine Owner Key (MOK) that you enrol through shim.

That is the short answer. The rest of this guide goes one level deeper than our post on Secure Boot with your own keys, which covers the firmware side (PK, KEK and db enrolment). Here we stay inside Linux: where the signature lives in a module file, which keyrings the kernel trusts, what permissive and enforcing modes actually do, how shim's MOK list extends trust to your own key, and how DKMS keeps a GPU, VirtualBox, ZFS or Wi-Fi driver loading after every kernel update. Every option name, path and behaviour below comes from the kernel documentation, the kernel source and Kconfig help text, the DKMS and mokutil manual pages, and the Ubuntu, Debian and Red Hat Enterprise Linux documentation listed under Sources. Where distributions behave differently, we say which one does what.

Key takeaways

  • The signature is appended, not embedded. A signed module ends with the marker string ~Module signature appended~. The marker only shows that a signature is present, not that it is valid, and stripping a module after signing breaks it.
  • Permissive is the default. With CONFIG_MODULE_SIG=y alone, an unsigned module or one signed by an unknown key still loads, and the kernel is marked tainted with the E flag. A module with a bad signature from a known key, or an unparsable signature, is always refused.
  • Enforcing has three switches. CONFIG_MODULE_SIG_FORCE at build time, module.sig_enforce=1 on the command line, or kernel lockdown. Many distributions turn lockdown on when the machine boots with Secure Boot, which is why an unsigned DKMS driver that worked yesterday stops loading after you enable Secure Boot.
  • MOK is shim's own key list. mokutil --import only queues a request. A person at the console has to confirm it in MokManager on the next boot, which keeps malware with root from silently trusting its own key.
  • DKMS rebuilds and signs. It compiles out-of-tree source against each installed kernel and signs the result with /var/lib/dkms/mok.key by default (Ubuntu points it at /var/lib/shim-signed/mok/).
  • The private key is now part of your trusted computing base. Anyone who can read it can sign a module your kernel will trust. Ubuntu's own documentation says a MOK kept on a root readable filesystem "effectively eliminates the security boundary between root and kernel mode."
  • Out-of-tree drivers are dependencies. Track their source, version and signing key in the same inventory as your other components.

What is kernel module signing?

A loadable kernel module is an ELF object (.ko, often compressed as .ko.xz or .ko.zst) that runs with full kernel privilege once loaded. The kernel's module signing facility, described in the kernel admin guide, "cryptographically signs modules during installation and then checks the signature upon loading the module." The point is to make it harder to load a malicious module, and because the check happens inside the kernel, it does not depend on trusted userspace tools. insmod, modprobe, init_module() and finit_module() work exactly as they do for unsigned modules; the kernel does all the checking.

When was it added?

Signed modules arrived in Linux 3.7, released on 10 December 2012, according to the Kernel Newbies release notes for that version. Those notes describe the goal plainly: the kernel can refuse modules not signed with the correct key "even for root users," so an attacker who gains root cannot simply load a rootkit through the module loader.

What the build options do

All of the following live under "Enable Loadable Module Support" in the kernel configuration. The descriptions come from the kernel documentation and the kernel/module/Kconfig help text.

OptionWhat it does
CONFIG_MODULE_SIGTurns on signature verification at load time. The Kconfig help notes it adds the OpenSSL development packages as a build dependency.
CONFIG_MODULE_SIG_FORCE"Require modules to be validly signed." Rejects unsigned modules and modules signed with a key the kernel does not hold. Without it, those modules only taint the kernel.
CONFIG_MODULE_SIG_ALLSigns every module automatically during make modules_install. Defaults to yes. Without it you sign by hand with scripts/sign-file.
CONFIG_MODULE_SIG_SHA256 and siblingsPicks the hash used when signing: SHA-256, SHA-384, SHA-512, SHA3-256, SHA3-384 or SHA3-512. The Kconfig default is SHA-512. The chosen algorithm is built into the kernel so it can verify modules without a dependency loop.
CONFIG_MODULE_SIG_KEYThe file (private key plus X.509 certificate in PEM) or PKCS#11 URI used for signing. Changing it from the default certs/signing_key.pem stops the build from generating a throwaway key.
CONFIG_SYSTEM_TRUSTED_KEYSA PEM file of extra certificates compiled into the built-in keyring.
CONFIG_MODVERSIONSNot a signing option, but the docs say to enable it (or give each configuration its own release string) if one key signs modules for several kernel configurations, so a module cannot be loaded into the wrong kernel.

The current kernel documentation lists RSA, NIST P-384 ECDSA and NIST FIPS 204 ML-DSA as supported signature algorithms. When the build generates its own key, you can choose RSA 4096, P-384 or ML-DSA 44, 65 or 87. The docs strongly recommend supplying your own certs/x509.genkey so the certificate does not carry the default common name "Build time autogenerated kernel key".

Where the signature lives in the file

The kernel documentation is blunt about this: "A signed module has a digital signature simply appended at the end." The signature sits outside the ELF container, after the last section, followed by a small fixed size descriptor and the marker ~Module signature appended~ followed by a newline. Three consequences follow.

  1. Tools that only read ELF sections cannot see it. Red Hat's documentation notes that utilities such as readelf cannot display a module's signature because it is not part of the ELF image. Use modinfo instead.
  2. Stripping breaks it. The whole file, including any debug information present at signing time, is the signed payload. The Kconfig help warns that this includes the debuginfo strip some packagers do (it names rpmbuild) and initramfs tools that shrink modules. Sign last, after stripping.
  3. The marker proves nothing. The docs say the marker "confirms that a signature is present but it does not confirm that the signature is valid!" Only the kernel's check at load time, or a verification against the right certificate, tells you that.

The kernel source (kernel/module/signing.c) shows one more detail worth knowing. If a module is loaded with flags that tell the kernel to ignore version magic or symbol versions (what modprobe --force does), the kernel does not even look for the signature, because "a module with version information removed is no longer the module that was signed." On an enforcing system, a forced load therefore fails as unsigned.

Which keys does the kernel trust?

Signatures are checked against keyrings, not a single key. The relevant ones are documented in the kernel admin guide, the certs/Kconfig and security/integrity/Kconfig help text, and Red Hat's kernel guide.

KeyringWhere its keys come fromRole for modules
.builtin_trusted_keysCompiled into the kernel image (the build signing key and CONFIG_SYSTEM_TRUSTED_KEYS)Always trusted for module signatures
.secondary_trusted_keysKeys added at runtime, but only if vouched for by a built-in key, the machine keyring (if configured) or a key already in the secondary ringUpstream module verification uses this ring, which includes the built-in keys
.machineMachine Owner Keys from shim's MOK list, when CONFIG_INTEGRITY_MACHINE_KEYRING is setThe Kconfig help says that unlike platform keys, keys in .machine "will be trusted within the kernel"
.platformUEFI db and, depending on distribution and configuration, MOK keysUpstream uses it for kexec image verification; Red Hat's documentation says RHEL checks module signatures against .builtin_trusted_keys and .platform
.blacklistRevoked hashes and certificates, including entries from UEFI dbxA module signed by a blacklisted key fails even if the key is also trusted

The difference between upstream and Red Hat's description is real and worth noticing. Upstream module_sig_check() verifies with the secondary trusted keyring. Red Hat's RHEL 10 guide says that when Secure Boot is enabled, keys on the MOK list are propagated to .platform and modules are checked against .builtin_trusted_keys and .platform. There is also a CA mode: with CONFIG_INTEGRITY_CA_MACHINE_KEYRING, only CA keys go into .machine and every other MOK key lands in .platform. If a MOK enrolled key does not seem to be trusted for modules on a custom kernel, check which of these options your kernel was built with before assuming the key is wrong.

You can list keyrings as root with keyctl list %:.builtin_trusted_keys (Red Hat's example) or by reading /proc/keys.

Permissive versus enforcing: sig_enforce, MODULE_SIG_FORCE and lockdown

The kernel documentation calls the two modes "permissive" and "restrictive." Here is what each actually does, taken from the docs and the module_sig_check() source.

Permissive (CONFIG_MODULE_SIG=y, no force, no sig_enforce, no lockdown):

  • A validly signed module from a trusted key loads with no taint.
  • An unsigned module loads, and the kernel is tainted with flag E (bit 13, value 8192 in /proc/sys/kernel/tainted).
  • A module signed by a key the kernel does not have loads with the same taint.
  • A module whose signature does not match a key the kernel does have is refused.
  • A module with a signature block the kernel cannot parse is refused "out of hand."

Restrictive (any of the switches below): only modules with a valid signature from a key the kernel holds will load. The kernel logs a notice such as "Loading of unsigned module is rejected" and returns an error.

There are three ways into restrictive mode.

  1. CONFIG_MODULE_SIG_FORCE=y. Fixed at build time. The kernel parameters documentation notes that with this set, module.sig_enforce "does nothing" because enforcement is always on.
  2. module.sig_enforce on the kernel command line. The source declares the parameter as bool_enable_only with mode 0644, so you can turn enforcement on at runtime by writing to /sys/module/module/parameters/sig_enforce, but you cannot turn it back off without rebooting.
  3. Kernel lockdown. If enforcement is off, the source still asks the lockdown LSM whether unsigned modules are allowed. In lockdown, they are not.

Red Hat's guide summarises the result as a decision table, which matches the upstream logic:

ModuleKey found and signature validSecure Bootsig_enforceLoads?Taints kernel?
Unsignedn/aOffOffYesYes
Unsignedn/aOffOnNon/a
Unsignedn/aOnanyNon/a
SignedNoOffOffYesYes
SignedNoOffOnNon/a
SignedNoOnanyNon/a
SignedYesanyanyYesNo

What lockdown adds under Secure Boot

Kernel lockdown is a Linux Security Module that arrived in Linux 5.4, according to the kernel_lockdown(7) manual page. It exists to stop root from modifying the running kernel, and in its stricter mode, from reading secrets out of it. Its relevance here fits in one paragraph: in lockdown, "only validly signed modules may be loaded" (waived only when IMA appraisal vouches for the file), and lockdown also blocks module parameters that pass hardware addresses to drivers, /dev/mem, /dev/kmem, unsigned kexec, debugfs and unencrypted hibernation. The lockdown= kernel parameter takes integrity or confidentiality. Upstream, the default is no lockdown (LOCK_DOWN_KERNEL_FORCE_NONE); turning it on automatically under Secure Boot is a distribution choice. Debian's Secure Boot page states that using Secure Boot activates lockdown, and that by default this blocks out-of-tree modules, including DKMS managed drivers, until you sign them with a key you enrol through MOK. The Kconfig help for CONFIG_MODULE_SIG adds a warning in the other direction: if you enable lockdown without module signing, "unsigned modules will be loadable regardless of the lockdown policy."

You can see the current state in /sys/kernel/security/lockdown, where the active mode is shown in square brackets.

What is DKMS, and why do out-of-tree drivers need it?

An out-of-tree module is one whose source is not in the kernel tree you are running. The kernel build documentation describes how such modules are built against a configured kernel source or header tree with make -C /lib/modules/$(uname -r)/build M=$PWD. Common examples include proprietary or separately released GPU drivers, VirtualBox host modules, ZFS, some Wi-Fi and Ethernet drivers, and vendor storage and monitoring agents. The tainted kernels documentation gives these their own flag: O, bit 12, value 4096, "externally-built ('out-of-tree') module was loaded."

The catch is that a compiled module only fits the exact kernel it was built against. Every kernel update needs a rebuild. The DKMS manual page describes DKMS as "a framework which allows kernel modules to be dynamically built for each kernel on your system in a simplified and organized fashion."

How DKMS rebuilds a module on every kernel update

DKMS keeps module source under /usr/src/<module>-<version>/ with a dkms.conf file that says how to build it. The lifecycle is:

StepCommandWhat happens
Adddkms addRegisters the source tree in the DKMS tree
Builddkms build -k <kernel>Compiles against that kernel's headers, then signs the module before it is compressed
Installdkms install -k <kernel>Copies the module into /lib/modules/<kernel>/ (Debian uses updates/dkms) and runs depmod
Autoinstalldkms autoinstallInstalls the latest version of every module that was installed for other kernels
Statusdkms statusShows each module, version, kernel and state

Distributions hook dkms autoinstall into kernel package installation, so headers for the new kernel arrive, DKMS builds and signs, and the module is ready before you reboot. The manual page also describes a boot time autoinstaller service (dkms_autoinstaller or dkms.service, depending on distribution) that installs modules with AUTOINSTALL="yes" in their dkms.conf. If several versions of one module are in the tree, that service does nothing and asks for manual intervention.

A few dkms.conf directives decide whether a rebuild is even attempted. BUILD_EXCLUSIVE_KERNEL, BUILD_EXCLUSIVE_KERNEL_MIN, BUILD_EXCLUSIVE_KERNEL_MAX, BUILD_EXCLUSIVE_ARCH and BUILD_EXCLUSIVE_CONFIG restrict which kernels a module builds for. A mismatch exits with code 77, which dkms autoinstall treats as "skip," not as failure. That is correct behaviour, and it is also why a driver can quietly be missing after an upgrade to a kernel outside its supported range. BUILD_DEPENDS lists other DKMS modules a module needs; the manual describes how unresolved dependencies leave a module in the "added" state until the next autoinstall run.

How DKMS signs modules

The DKMS README is the authoritative description:

  • By default, DKMS generates a self signed certificate and private key the first time it runs and stores them in /var/lib/dkms (mok.key and mok.pub). It signs every module it builds before compressing it.
  • You can generate the pair in advance with dkms generate_mok.
  • /etc/dkms/framework.conf can point DKMS at a different key with mok_signing_key and mok_certificate, and at a different signing tool with sign_file. Paths can include ${kernelver}.
  • If either file named by mok_signing_key or mok_certificate does not exist, DKMS recreates both. That is convenient on a laptop and surprising in a fleet, as we discuss below.
  • mok_signing_key can be a pkcs11: URI if your sign_file supports it.
  • From DKMS 3.1.7, the generated certificate carries extendedKeyUsage codeSigning, for kernels built with CONFIG_CHECK_CODESIGN_EKU (a patch some vendors carry for the NIAP general purpose OS protection profile). Keys generated by earlier DKMS versions need to be regenerated on those systems.

How do MOK keys work with shim and mokutil?

Secure Boot firmware trusts what is in its db. Distribution kernels are trusted because a small first stage loader, shim, is signed by a key in db (on most PCs, the third party UEFI CA) and carries the distribution's own CA certificate inside it. The shim README describes shim as "a trivial EFI application" that tries the firmware's normal image loading first, and if that fails, validates the next stage against its built-in certificate.

Machine Owner Keys are the third source of trust. The mokutil(1) manual page describes them as letting "system owners to manage their own list of trusted certificates (MokList) and forbidden certificates (MokListX) independently of the system's firmware." Debian's wiki explains why the design matters: the distribution CA is fixed inside shim, but MOK lets the owner add keys, and changes to MOK "may only be confirmed directly from the console at boot time."

The enrolment flow

  1. Queue the request. mokutil --import key.der writes a pending request (the MokNew UEFI variable). The key must be in DER format. mokutil asks for a one-time password.
  2. Reboot. shim sees the pending variable and starts MokManager instead of GRUB.
  3. Confirm at the console. In MokManager choose Enroll MOK, view the key and its fingerprint, choose Continue, then Yes, and type the password you set.
  4. Reboot again. The key is now in MokList. shim exposes it to the running system as MokListRT, and the kernel loads it into the machine or platform keyring at boot. You will see a log line such as "Loading X.509 certificate: UEFI:MokListRT" in dmesg, as Debian's wiki shows.

Useful mokutil commands, all from the manual page:

CommandPurpose
mokutil --sb-stateShows whether Secure Boot is enabled
mokutil --is-sb-enabledSame, but exit status 0 when enabled and 1 when disabled or in Setup Mode, for scripts
mokutil --list-enrolledLists keys in MokListRT
mokutil --list-newLists keys waiting for confirmation at the next boot
mokutil --test-key key.derTells you whether a key is already enrolled or pending
mokutil --import key.derQueues an enrolment
mokutil --delete key.derQueues a removal
mokutil --revoke-importCancels a pending enrolment
mokutil --mokx --import key.derQueues a key for the forbidden list MokListX
mokutil --db, --dbx, --pk, --kekLists the firmware databases
mokutil --timeout NSets MokManager's prompt timeout; the built-in default is 10 seconds
mokutil --trust-mok and --untrust-mokTells the kernel whether to trust MOK keys in its keyring
mokutil --disable-validationAsks shim to stop verifying signatures, confirmed at the console; not recommended

By default mokutil also checks whether a key is already in the kernel's built-in trusted keyring and skips enrolment if it is; --ignore-keyring overrides that.

How Ubuntu signs DKMS modules

Ubuntu's security documentation describes a largely automatic flow. On a new UEFI installation that needs third party modules, the installer asks for a password, generates a MOK, builds and signs the needed drivers with it, and MokManager prompts you to enrol the key at first boot. On upgrades, the shim-signed package's post install step generates a MOK if needed and prompts for a password, and rebuilt third party modules are signed automatically. Once a valid MOK exists and appears to be enrolled, "the installation of any new DKMS module (third-party driver) proceeds to sign the built module with the MOK" without interaction. Ubuntu stores this key under /var/lib/shim-signed/mok/ (MOK.priv, MOK.der), and the DKMS README shows the two framework.conf lines that point DKMS at it.

Two Ubuntu specific points matter for security. First, these generated keys carry a module signing only key usage OID, 1.3.6.1.4.1.2312.16.1.2. Ubuntu says that starting with shim 15.4, introduced in Ubuntu 21.04, keys with that OID are enrolled but ignored when shim or GRUB validate boot images, so a module signing key cannot be used to sign a bootloader or kernel. Second, Ubuntu is candid about the trade off: the key is kept as root owned, read-only files, and Ubuntu says administrators "should be aware that this weakens Secure Boot's protections."

How Debian signs DKMS modules

Debian's Secure Boot wiki says that since DKMS modules are compiled on each user's machine, they cannot be signed with Debian's keys, so DKMS signs them with a MOK at /var/lib/dkms/mok.key and /var/lib/dkms/mok.pub, generated on the first DKMS build or with dkms generate_mok. You must enrol the public key yourself with mokutil --import /var/lib/dkms/mok.pub, check it with mokutil --list-new, and confirm at the next boot. The wiki also covers using Ubuntu's /var/lib/shim-signed/mok/ location because some third party packages expect it, signing modules by hand with sign-file, and running update-initramfs if a re-signed module is needed at boot. It records a 2022 observation that shim may hang with a 4096 bit RSA MOK and suggests 2048 bit keys; treat that as a dated note and test on your hardware.

How Red Hat Enterprise Linux and its rebuilds sign modules

Red Hat's kernel guide (the RHEL 10 edition; RHEL 9 has the same chapter) uses NSS tooling. You install pesign, openssl, kernel-devel, mokutil and keyutils, generate a key pair with efikeygen into /etc/pki/pesign, export the certificate with certutil, enrol it with mokutil --import, export the private key, and sign with /usr/src/kernels/$(uname -r)/scripts/sign-file sha256 sb_cert.priv sb_cert.cer my_module.ko. Red Hat flags a detail many people miss: the key's validity dates matter. "The kernel module must be signed within the validity period of its signing key," and sign-file does not warn you. A module signed after the certificate expired will not authenticate. Red Hat also suggests asking your hardware vendor to add your public key to the factory db if you need it trusted without MOK. Distributions rebuilt from RHEL sources generally follow the same procedure; check their own documentation for package names.

Milestones at a glance

Only versions confirmed in the cited primary sources are listed. Where a source gives no date, none is given here.

MilestoneVersionSource
Cryptographically signed kernel modules mergedLinux 3.7, released 10 December 2012Kernel Newbies release notes
Kernel lockdown LSM addedLinux 5.4kernel_lockdown(7)
Module signing only MOKs ignored for boot image validationshim 15.4, introduced in Ubuntu 21.04Ubuntu security documentation
DKMS generated MOK certificates carry the codeSigning extended key usageDKMS 3.1.7DKMS README
ML-DSA (FIPS 204) listed alongside RSA and P-384 ECDSA for module signaturesCurrent kernel documentationKernel module signing facility

Comparison: how each kind of module behaves under Secure Boot

Module typeWho signs itSecure Boot with lockdownUpdate pathMain risk
In-tree module shipped with the distribution kernelDistribution build key, embedded in the kernelLoadsArrives with each kernel package; signature matches that kernel's built-in keyLow, as long as you take kernel updates
Out-of-tree module packaged and signed by the distribution or a vendor repositoryDistribution or vendor key, trusted via kernel, db or a MOK the vendor asks you to enrolLoads if the signing key is trustedPackage must be rebuilt for each kernel ABI; can lag behind kernel releasesVersion lag; trusting a vendor key trusts everything that vendor signs
DKMS module signed with a local MOKYour machine's MOK private keyLoads once the MOK is enrolledRebuilt and re-signed by DKMS on every kernel installPrivate key on disk; silent build failures after upgrades
Unsigned moduleNobodyRefusedManual rebuildWorks only with Secure Boot off and enforcement off, and taints the kernel
Module signed with an expired or unenrolled keyYour keyRefusedRe-sign or enrolLooks signed in modinfo, still fails

Step by step: check, sign and verify a DKMS module

These steps assume a UEFI machine with Secure Boot on, a distribution kernel, and one DKMS module (we use a placeholder called examplemod). Run them on a test machine first. Nothing here erases data, but enrolling the wrong key or disabling validation changes what your machine will boot.

Step 1: Check Secure Boot, enforcement and lockdown

mokutil --sb-state
cat /sys/module/module/parameters/sig_enforce     # Y means enforcing
cat /sys/kernel/security/lockdown                 # e.g. none [integrity] confidentiality
grep -o 'module.sig_enforce[^ ]*' /proc/cmdline
grep CONFIG_MODULE_SIG /boot/config-$(uname -r)

sig_enforce reading N while lockdown shows [integrity] is normal on distributions that enforce through lockdown: the module loader still refuses unsigned modules.

Step 2: Inspect a module's signature

modinfo -F signer   examplemod
modinfo -F sig_key  examplemod
modinfo -F sig_hashalgo examplemod
modinfo -n examplemod                    # path of the file that would load

An empty signer means no signature. Debian's wiki shows the full set of fields for a signed module: sig_id (PKCS#7), signer, sig_key, sig_hashalgo and the signature bytes. A signer name you do not recognise means some key you did not expect is signing modules on this host. Compare sig_key with the serial or key ID of your enrolled MOK:

sudo mokutil --list-enrolled --short

For a file that is not installed yet, modinfo /path/to/examplemod.ko works the same way. Remember the marker check is not proof:

tail -c 28 /path/to/examplemod.ko | strings    # shows the marker if present

Step 3: Check whether anything unsigned or out-of-tree is already loaded

cat /proc/sys/kernel/tainted
for i in $(seq 0 19); do echo "$i $(( ($(cat /proc/sys/kernel/tainted) >> i) & 1 ))"; done

Bit 12 set means an out-of-tree module was loaded; bit 13 set means an unsigned module was loaded. The taint stays until reboot, even after the module is removed.

Step 4: Generate a MOK key pair

If you want DKMS to create its default pair:

sudo dkms generate_mok
sudo ls -l /var/lib/dkms/mok.key /var/lib/dkms/mok.pub

To create one yourself (the Debian wiki form, with a module signing EKU added):

sudo install -d -m 700 /root/mok
cd /root/mok
sudo openssl req -new -x509 -newkey rsa:2048 -nodes -days 1095 \
  -subj "/CN=Example Corp DKMS module signing 2026/" \
  -addext "extendedKeyUsage=codeSigning" \
  -keyout MOK.priv -outform DER -out MOK.der
sudo chmod 600 MOK.priv

Warning: keep the private key off shared machines. Anyone who can read MOK.priv can sign a module your kernel will load with full privilege. Do not copy it to a home directory, a shared drive, a container image or a configuration management repository. Choose the validity period deliberately: Red Hat notes the module must be signed within it.

Step 5: Enrol the public key

sudo mokutil --import /var/lib/dkms/mok.pub      # or /root/mok/MOK.der
sudo mokutil --list-new
sudo reboot

At the MokManager screen, choose Enroll MOK, check that the fingerprint matches the one you expect, choose Continue and Yes, enter the one-time password, then reboot. Afterwards:

sudo mokutil --test-key /var/lib/dkms/mok.pub
sudo dmesg | grep -i 'MokListRT\|X.509'
sudo keyctl list %:.machine 2>/dev/null || sudo keyctl list %:.platform

Step 6: Point DKMS at the key

Edit /etc/dkms/framework.conf, or add a file ending in .conf under /etc/dkms/framework.conf.d/, which the DKMS manual says is loaded as well:

mok_signing_key="/root/mok/MOK.priv"
mok_certificate="/root/mok/MOK.der"

Then rebuild and confirm:

sudo dkms status
sudo dkms build examplemod/1.2.3 -k "$(uname -r)" --force
sudo dkms install examplemod/1.2.3 -k "$(uname -r)" --force
modinfo -F signer examplemod
sudo modprobe examplemod

Step 7: Verify after the next kernel update

The real test is the update. After installing a new kernel package and before rebooting:

NEW=6.x.y-z-generic    # replace with the kernel you just installed
dkms status | grep "$NEW"
modinfo -k "$NEW" -F signer examplemod
ls /lib/modules/"$NEW"/updates/dkms/ 2>/dev/null

If dkms status does not show the module as installed for the new kernel, read the build output DKMS printed and the build directory under /var/lib/dkms/examplemod/1.2.3/build/ before rebooting. If the module is needed to boot (a storage or root file system driver, for example), rebuild the initramfs for the new kernel too (update-initramfs -u -k "$NEW" on Debian and Ubuntu, dracut -f --kver "$NEW" on Fedora and RHEL family systems).

Step 8: Sign one module by hand (when DKMS is not involved)

KVER=$(uname -r)
sudo /usr/src/linux-headers-$KVER/scripts/sign-file sha256 \
  /root/mok/MOK.priv /root/mok/MOK.der ./examplemod.ko

On RHEL family systems the script lives at /usr/src/kernels/$KVER/scripts/sign-file. If the private key has a passphrase, export it as KBUILD_SIGN_PIN first. Compressed modules have to be decompressed, signed and recompressed, which is one reason to let DKMS do it.

Who holds the signing key in a fleet, and how do you protect and rotate it?

On one laptop, a DKMS generated key on the local disk is a reasonable compromise. Across a fleet it becomes a design question, and there are three common patterns.

PatternHow it worksStrengthsWeaknesses
Per machine keyEach host generates its own MOK; someone enrols it at the consoleCompromise of one key affects one hostConsole confirmation on every host; no central view of keys; root on the host can sign modules
Central build and signingA build service compiles drivers per kernel version, signs them with an organisation key held in an HSM or PKCS#11 token, and ships signed packages; hosts enrol one public certificatePrivate key never on endpoints; one place to audit what was signedYou must build for every kernel you run, and keep up with kernel updates
Vendor signed packagesHardware or software vendor ships pre-signed modules and asks you to enrol their certificateNo signing work for youYou trust everything that vendor signs, on every host that enrolled it

Whichever pattern you choose, a few rules hold.

  • Keep the private key where code cannot read it. The kernel docs say the build key "must be either destroyed or moved to a secure location." For a central signer, CONFIG_MODULE_SIG_KEY, DKMS's mok_signing_key and sign-file all accept PKCS#11 URIs, so the key can stay in a token while signing.
  • Give module signing keys a narrow purpose. Use a certificate meant for module signing only (Ubuntu's OID, or at least the code signing EKU), so the same key cannot also sign a bootloader.
  • Keep an inventory of enrolled certificates. mokutil --list-enrolled on each host, collected centrally, tells you which keys can load modules where. An unexpected certificate is worth investigating.
  • Rotate deliberately. Rotation means generating a new key, enrolling the new certificate everywhere (each enrolment needs console confirmation unless you are deploying through firmware db with your own Secure Boot keys), re-signing modules for every installed kernel, confirming they load, and only then queuing mokutil --delete for the old certificate. If an old key is compromised rather than retired, add it to MokListX with mokutil --mokx --import so it is refused even if a copy is still trusted elsewhere.
  • Watch the DKMS regeneration trap. Because DKMS recreates both files if either is missing, a cleanup script that deletes only one, or an image build that omits the key, results in a fresh, unenrolled key on the next kernel update. Modules get signed with it and then fail to load at boot. Monitor for that, and keep framework.conf under configuration management.

Supply chain angle: out-of-tree drivers are dependencies too

Kernel modules from outside the kernel tree are software components with their own source, versions, maintainers and vulnerabilities. They tend to fall outside both the operating system inventory (because the distribution did not ship them) and the application dependency inventory (because no application manifest lists them). That gap is where they get forgotten.

Practical steps that close it:

  • Record each driver as a component. Name, upstream source, version, the kernel versions it was built for, and which certificate signed it. Most SBOM formats can represent this: list the driver as a component with its version and source, and put the signer and target kernel in properties.
  • Use the package manager's view. On Debian and Ubuntu, dpkg -l '*-dkms' lists DKMS source packages; on RHEL family systems, rpm -qa | grep -i dkms or rpm -qa 'kmod-*' lists packaged kernel modules. dkms status gives the built state per kernel. Feed those into your inventory.
  • Track the source supply chain. Where does the DKMS source come from: a distribution archive, a vendor repository, a GitHub release, a script someone ran once? Pin the source, verify its signature or checksum when it is published with one, and build from that pinned copy.
  • Watch for advisories against the driver itself. Out-of-tree drivers receive security fixes on their own release schedule, not the kernel's. Your vulnerability matching only covers them if they are in the inventory you match against.
  • Remember that a signature proves origin, not safety. A module signed by your own MOK loads because you signed it. If the source you signed contained a bug or a backdoor, the signature now vouches for it. MITRE ATT&CK tracks loadable kernel modules as a persistence technique; see our post on T1547.006 kernel modules and extensions for the defensive view.

Our posts on Linux firmware blobs and kernel live patching cover the neighbouring pieces: the device firmware many of these drivers load, and what a kernel update does and does not fix without a reboot.

Common mistakes

  • Disabling Secure Boot to make a driver load. It works, and it switches off lockdown and module enforcement for everything else on the machine too. mokutil --disable-validation has the same effect for shim's checks. Sign the module and enrol a key instead.
  • Leaving the MOK private key world readable. Check with sudo ls -l /var/lib/dkms/mok.key /var/lib/shim-signed/mok/MOK.priv. It should be readable by root only, and it should never be copied into images, backups without encryption, or shared folders.
  • Forgetting DKMS failures after kernel upgrades. A failed or skipped build often only shows up at the next boot, when the GPU driver or ZFS pool does not come back. Check dkms status for the new kernel before rebooting, and alert on it in fleet tooling.
  • Trusting a module because it loaded. Loading proves the signature matched a trusted key. It says nothing about who wrote the code or whether it is the version you intended. Check modinfo -F signer and the version against your inventory.
  • Assuming the marker means a valid signature. ~Module signature appended~ only means something was appended.
  • Stripping or recompressing after signing. Any change to the file breaks the signature. Sign after strip and let the tooling compress.
  • Signing outside the certificate's validity period. On RHEL the module will not authenticate, and sign-file gives no warning.
  • Enrolling a key nobody verified. At the MokManager prompt, compare the fingerprint with the one you generated. The console confirmation is the whole security control; clicking through it defeats it.
  • Letting DKMS silently make a new key. If one of the configured key files disappears, DKMS creates both again, and the new key is not enrolled.
  • Using modprobe --force on an enforcing system. Forced loads strip version checks and are treated as unsigned, so they fail, and on a permissive system they taint the kernel.

FAQ

What does module.sig_enforce=1 do?

It makes the kernel refuse any module that does not carry a valid signature from a key the kernel trusts. It only has an effect if the kernel was built with CONFIG_MODULE_SIG. If CONFIG_MODULE_SIG_FORCE is already set, enforcement is permanent and the parameter does nothing. You can also turn it on at runtime by writing to /sys/module/module/parameters/sig_enforce, but not off again.

How do I check if a kernel module is signed?

Run modinfo -F signer <module>. A name means the file carries a signature that names that signer; empty output means it is unsigned. modinfo does not prove the signature is valid against your keys; the kernel checks that when it loads the module.

Why does my GPU or VirtualBox driver fail to load with Secure Boot on?

Because the module is out-of-tree and either unsigned or signed with a key the kernel does not trust, and Secure Boot turned on lockdown. Enrol the DKMS MOK with mokutil --import, confirm it at the next boot, and rebuild the module so it is signed. Do not disable Secure Boot to work around it.

What is the difference between MOK and the UEFI db?

The db is a firmware database controlled through the PK and KEK hierarchy. MOK is shim's own list of trusted keys, stored in UEFI variables, that the machine owner can change with mokutil and a console confirmation, without touching the firmware keys. Both can feed keys into the kernel's keyrings.

Where does DKMS store its signing key?

By default in /var/lib/dkms/mok.key and /var/lib/dkms/mok.pub. Ubuntu uses /var/lib/shim-signed/mok/MOK.priv and MOK.der, and DKMS can be pointed there through mok_signing_key and mok_certificate in /etc/dkms/framework.conf.

Do I need to re-enrol the MOK after every kernel update?

No. The MOK stays enrolled in firmware variables across kernel updates. DKMS rebuilds and re-signs the module for each new kernel with the same key. You only enrol again if the key changes.

What does the E taint flag mean?

It means an unsigned module was loaded into a kernel that supports module signatures. It is bit 13 (value 8192) of /proc/sys/kernel/tainted. The O flag, bit 12, means an out-of-tree module was loaded.

Is it safe to use mokutil --disable-validation?

It tells shim to stop verifying the images it loads, and the request has to be confirmed at the console. It removes the protection Secure Boot gives you. Use it only for short, deliberate troubleshooting on a machine you control, and turn validation back on with mokutil --enable-validation.

Can I sign kernel modules with a hardware token?

Yes. The kernel's CONFIG_MODULE_SIG_KEY accepts a PKCS#11 URI, sign-file accepts one in place of the private key file, and DKMS accepts a pkcs11: value in mok_signing_key as long as the signing tool supports it. A token PIN can be passed with KBUILD_SIGN_PIN.

Does kernel lockdown block all module loading?

No. The kernel_lockdown(7) page says lockdown still permits driver modules to be loaded. It requires them to be validly signed (or vouched for by IMA appraisal), and it blocks module parameters that pass raw hardware addresses.

Where Safeguard fits

Safeguard's fit here is indirect, and it is worth being exact about it. Safeguard does not inspect kernel modules, verify module signatures, manage MOK keys or Secure Boot state, or manage endpoints. Those jobs belong to your distribution tooling and your device management. Where Safeguard helps is the software around the drivers. If you maintain in-house tooling that builds, signs and packages drivers, Safeguard can scan that repository and its build containers for vulnerable dependencies (safeguard scan --dir . or --image <name>). If you produce an SBOM for a host or a golden image that lists your out-of-tree drivers as components, you can scan that file with safeguard scan --sbom <file>, and the platform shows which projects contain a given component and version, with policies and gates evaluated against it. The coverage you get depends on the inventory you feed it.

Kernel drivers are rarely the only third party code on a machine that runs them. The signing scripts, build images and agents around them pull open source components like any other software, so the same dependency hygiene applies there.

Fix it without waiting for upstream

Whatever the vulnerability, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.

1. Autonomous remediation: pull requests, not tickets

When a fixed version exists, Safeguard's Griffin models open a tested, policy gated pull request that moves the affected dependency to the safe version. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation categories include dependencies, licenses, security posture and findings, and each one is set separately. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Autonomous remediation ships switched off, with every category set to Manual, so your team decides how much runs on its own and what still needs a human approval.

2. Gold Open Source: the safe version in seconds, free

gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds.

3. Gold Registry: start from components that are already fixed

Gold Registry provides hardened, drop in replacements for upstream packages and container images. Every Gold artifact is held continuously to zero exploitable critical or high CVEs and a completed license check, with signed build provenance (signed with Safeguard's key and verifiable with cosign, SLSA Build Level 3 as the design target) and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.

When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened, compatibility tested build of a specific package. Ask which packages it covers before you plan around it.

Your situationWhat to use
A fixed version existsAutonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version
You need to know today whether you are affectedGold Open Source lookup and "Am I affected?" checker, free and without login
You want vulnerable versions to stop shippingSafeguard policies and gates in CI
Upstream has no fix yet, or will not ship oneA custom Gold request; fork and patch
You want to start clean on new projectsPull hardened components and images from Gold Registry

One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.

See how Safeguard would help

The demos below show Safeguard on ordinary software projects, not on kernel modules or Secure Boot keys. They apply to the repositories around a driver program: the build scripts, packaging and signing tooling, and the containers that compile drivers for each kernel.

The first demo is a real Safeguard CLI session. After install, safeguard opens the interactive terminal, you ask it to fix the lodash CVE in a repository, and it reads the finding and advisory, asks before any command that changes something, edits package.json to lodash 4.17.21, runs npm install and npm test with your approval, prints a summary and pushes nothing. It ends with /permissions, /model and /scan. The same review loop suits a driver tooling repository, where every change to a build or signing script should be seen by a person.

Click through it at your own pace, or open it full screen.

Signing hosts are usually kept off the general network. The local runner demo shows how to set up the runner on your own machine, approve it with its device code, and then scan from the terminal. A runner on a build host can scan the driver build tree and its containers where they live, and the module signing key never has to leave that machine.

Click through it at your own pace, or open it full screen.

If your team uses Claude, the third demo shows how to connect Claude to the Safeguard MCP server with OAuth and choose which tools Claude can use. An engineer can then ask about known vulnerabilities in the projects that hold your driver build and packaging tooling, using only the tools you allowed.

Click through it at your own pace, or open it full screen.

To try it on your own repositories, download Safeguard and follow the CLI documentation.

Quick check

Secure Boot is off and sig_enforce is off. You load an unsigned module. What happens?

It loads, and the kernel is tainted with the E flag (bit 13 of /proc/sys/kernel/tainted). If the module is also out-of-tree, the O flag (bit 12) is set too.

You ran mokutil --import and the key still is not trusted. What step did you probably skip?

The console confirmation. mokutil only queues a request. You have to reboot, choose Enroll MOK in MokManager, check the fingerprint and enter the one-time password before the key is enrolled.

modinfo shows a signer for your module but it still fails to load with Secure Boot on. Name two likely causes.

The signing key is not enrolled (or not in a keyring the kernel uses for modules), or on RHEL the module was signed outside the certificate's validity period. A blacklisted key or a module changed after signing will also fail.

Sources

Last reviewed: 25 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.