You can find the other Yocto hardening posts from here!
Another day, another new way to harden your Yocto systems. This time, we have a relatively simple and effective hardening measure that may prevent big headaches: kernel module signing. I’ve been adding this feature to my secure Yocto distribution Sulka, and I thought that I might as well write a blog post about it.
Module Signing
So, this time we will investigate how to sign Linux kernel modules. Modules are binaries that are built to be run in the kernel with high privileges. A common example of this is device drivers. These modules may be built by the kernel during the build process, or outside the kernel build by third parties. Signing these modules allows the kernel maintainer to control which code runs in the operating system.
Allowing the user (or adversary) to run arbitrary code in the operating system can result in hazardous situations. As the modules are stored in user-space, commonly in the root file system, an adversary can fairly easily create a malicious module that can act for example as a keylogger, rootkit, or backdoor. Loading modules requires superuser privileges, so it is not completely trivial to run malicious code, but still.
We should sign the kernel modules to prevent this. In addition to that, the kernel itself must be configured to require the signatures. Asymmetric cryptography is used here: the kernel modules are signed with a private signing key, and the public signing key is built into the kernel. This should ensure that the modules originate from a trusted source. To take the security one step further, we can also sign the public signing key with a certificate authority private key, and add the certificate authority public key to the kernel trusted keyring. This ensures that the signing keys are trusted.

According to the kernel documentation, CONFIG_MODULE_SIG
is the kernel configuration flag that should be enabled to enable kernel module signing facility. In addition to that, CONFIG_MODULE_SIG_FORCE
should be enabled to ensure that the unsigned modules do not get loaded. Once the signing facility is enabled, the modules built by the kernel are signed with keys defined in the kernel configuration. The keys can either be auto-generated or manually provided using CONFIG_MODULE_SIG_KEY
option.
Signing Kernel Modules in Yocto
Let’s move to the Yocto side of things. Like so many times before, we are going to be using meta-security, specifically sub-layer meta-integrity. Long-time readers may remember this layer from the IMA & EVM hardening text. I noticed that meta-secure-core also has a sub-layer meta-integrity that has a module signing feature that works pretty much the same, so as far as I know, it should also work. However, I’ll be sticking to meta-security for now.
Generating Keys
When enabling the module signing in Yocto, we want to manually provide the keys to the build system. This way, the build process and key management are clearly separated. The first step is generating a certificate authority that will be used to sign the module signing certificate. The meta-integrity layer has ima-gen-local-ca.sh
script for doing this. Before running it, open the script and edit the [ req_distinguished_name ]
section with the correct information. Then, running the script is as simple as:
meta-security/meta-integrity/scripts/ima-gen-local-ca.sh
This command will generate the following files:
ima-local-ca.genkey # Information used for generating the CA
ima-local-ca.pem # CA public certificate in plaintext
ima-local-ca.priv # CA private key. Keep this secret!
ima-local-ca.x509 # CA public certificate in DER format
Once this is done, we can create a certificate signing request and sign it using the certificate authority. The meta-integrity layer has a script for this as well. This script again needs to be updated with correct information, so open the script and edit the [ req_distinguished_name ]
section as needed. Once that is done, run the script:
meta-security/meta-integrity/scripts/ima-gen-CA-signed.sh
This command will generate the following files:
csr_ima.pem # Certificate signing request in plaintext
ima.genkey # Information used for generating the CSR
privkey_ima.pem # Private key of the CSR. Keep this secret as well!
x509_ima.der # Certificate signed by the CA in DER format
Note that all the file names have _ima
part in them. I suppose this script is usually used for generating the IMA keys, but it works for module signing keys as well. Just change the _ima
to _modsign
, e.g. x509_ima.der
to x509_modsign.der
, and you’re golden.
Finally, we need to convert the DER file into PEM format. This can be done with the openssl
using the following command:
openssl x509 -inform DER -in x509_modsign.der -outform PEM -out x509_modsign.crt
After this, you should have the following files that will be used in the Yocto build:
ima-local-ca.pem # Trust anchor CA that will be built into the kernel
privkey_modsign.pem # Private key used to sign the kernel modules
x509_modsign.crt # Public certificate built in kernel for module signature verification

Configuring Yocto
The next steps are enabling the Yocto features to sign the modules, enforcing the module signing in the kernel, and configuring the keys for the build. Let’s begin with the keys.
In your build configuration (e.g. local.conf
) you should set the following variables:
MODSIGN_KEY_DIR = "<PATH/TO/KEY/DIRECTORY>"
IMA_EVM_ROOT_CA = "${MODSIGN_KEY_DIR}/ima-local-ca.pem"
Despite the name, IMA_EVM_ROOT_CA
can be used as the certificate authority for module signing keys. You could consider it to be a more generic integrity key certificate authority than strictly a module signing CA. If you changed the names of the generated keys more than just by replacing _ima
with _modsign
, you’ll need to define the individual key files in the configuration. If not, the default names should get automatically picked up.
Moving on from the keys to enabling the distro features. First, you’ll want to enable the generic integrity
feature to activate the meta-integrity layer. Then, you’ll want to enable modsign
feature to enable module signing. In addition to these, you’ll need to enable ima
distro feature as well.
At least at the time of writing, it seems that the certificate authority does not get added to the kernel configuration without ima
feature (unsigned module signing keys might work, but I didn’t try). Enabling ima
distro feature enables the IMA configuration in the kernel, but does not really enable the feature itself. That requires setting up an IMA policy. So, enabling the IMA feature should not affect your system that much in practice.
Long story short, add the following features to your build configuration:
DISTRO_FEATURES:append = " integrity ima modsign "
And that should be all that’s required. The build system configures the kernel based on these distro features, so we don’t need to touch the kernel configuration. This should work for both in-tree and out-of-tree modules, as long as you are building the out-of-tree modules with a correctly configured kernel. Note that this feature does not sign the pre-built modules that might be distributed by vendors, and that has to be done manually with sign-file
script that can be found in the kernel sources.
Testing It Out
To test the module signing, we can first check from the kernel logs that the desired keys actually get loaded:
test@qemux86-64:~$ sudo dmesg |grep cert
[ 3.529384] Loaded X.509 cert '[example.com](http://example.com/): meta-intel-iot-security example signing key: 460501e7a2143d52e3abaeb1d24d378d824bff54'
[ 3.546836] Loaded X.509 cert '[example.com](http://example.com/): meta-intel-iot-security example certificate signing key: e16733db10cf8d84f5b52aeafe18daead1746c0d'
Uh oh, seems like someone didn’t update their distinguished name information before generating the keys. Shame on me. Before probing modules, let’s first try running modinfo
command on one of the modules in the system to confirm the existence of a signature:
test@qemux86-64:~$ sudo modinfo hello
filename: /lib/modules/6.6.84-yocto-standard/updates/hello.ko
license: GPL
depends:
retpoline: Y
name: hello
vermagic: 6.6.84-yocto-standard SMP preempt mod_unload modversions RANDSTRUCT_6412be83e6a0d7d324039d767555aa87b3efbbf555f150fa215355374b9cd01a
sig_id: PKCS#7
signer: meta-intel-iot-security example certificate signing key
sig_key: 65:57:D4:6C:85:3F:A3:96:6D:5C:F1:9C:ED:3E:BF:04:9B:DF:9E:62
sig_hashalgo: sha256
signature: 30:44:02:20:36:F4:9A:17:8E:B5:35:DB:99:EF:D2:F8:9A:EF:07:0A:
61:1E:63:7F:06:1C:42:AC:79:4B:E1:D7:28:D8:62:5F:02:20:6F:C6:
7D:60:D6:36:6B:3D:07:93:0B:DA:3A:8E:5F:80:50:22:BC:CF:05:46:
DE:CE:E2:FD:7B:93:90:54:9B:64
We can see that the module is signed from the last few lines, namely from the signature
field. Next, we should try to probe it:
test@qemux86-64:~$ sudo insmod /lib/modules/6.6.84-yocto-standard/updates/hello.ko
[ 183.837366] hello: loading out-of-tree module taints kernel.
[ 183.850445] Hello World!
test@qemux86-64:~$
Of course, this wasn’t all that fascinating. I guess we’ve all loaded a module at some point. So, what you could do instead is try to load an unsigned module, or a module signed with the wrong key to see what happens. Here are examples of such failures:
# Module with the wrong signing key
test@qemux86-64:~$ sudo insmod /tmp/hello.ko
[ 119.908851] Loading of module with unavailable key is rejected
insmod: ERROR: could not insert module /tmp/hello.ko: Key was rejected by service
# Module without signature
test@qemux86-64:/lib/modules/6.6.84-yocto-standard$ sudo insmod /tmp/hello_unsigned.ko
[ 697.716526] Loading of unsigned module is rejected
insmod: ERROR: could not insert module /tmp/hello_unsigned.ko: Key was rejected by service
But now that I showed you what happens in the error cases, it’s not that exciting anymore I guess.

Conclusion
That’s all for this time. Hopefully, you found this useful and can enable the signing feature. Well, enabling the signing itself is quite straightforward, but dealing with the signing keys and the certificates can be cumbersome if you haven’t set up an infrastructure for that. But, all things considered, module signing is a useful security feature that can help to harden your system. More security advice coming soon, so stay tuned!
As a reminder, if you’re interested you can find the previous Yocto hardening posts from here!
Recommended Reading
- Linux Kernel Documentation – Kernel Module Signing Facility: All things considered, the official feature documentation is probably the most useful thing to read.
- Arch Wiki – Signed kernel modules: Some useful complementary information in Arch Wiki, as usual.
- Gentoo Wiki – Signed kernel module support: Gentoo Wiki also contains some useful information, like how to strip the signature from a module or how to manually sign the module.
- Linux Kernel Documentation – Asymmetric / Public-key Cryptography Key Type: Information about asymmetric keys in the Linux kernel.