Introduction
With the enforcement of loading only signed Linux Kernel Modules you can greatly enhance the security of your Systems.
There are basically two methods of enforcement: Secure (UEFI) Boot and the other is a grub parameter. When using Secure boot you can sign own (or 3rd party) Kernel modules by yourself and add your public key as a MOK (Machine Owner Key) in UEFI. When not using Secure Boot, you can not load self signed modules due to the lack of a capability of storing MOKs. At least you can prevent loading unsigned Modules.
Unfortunately I was unable to test Secure Boot with a KVM virtual machine, the MOK was not added. Also on hardware it does not seem to work on all machines. I failed with my Lenovo T450s Notebook. Finally I succeeded with my Workstation with a Gigabyte Z97-D3H motherboard using Fedora 25. If someone has a solution with virtual machines, please let me know.
About Secure boot
Basically it is a chain of trust with x509 certificates. UEFI Firmware -> Shim First-Stage Bootloader -> Grub Second Stage Bootloader -> Kernel -> Modules.
This adds complexity. If something goes wrong it’s not always easy to figure out where and why it goes wrong.
Secure Boot is not without some controversy, its dominated by Microsoft, only Microsoft can sign bootloaders. Yes, the Shim bootloader is signed by Microsoft. If Microsoft decides to no longer sign Shim (or any non-MS loader), the whole Linux landscape is not able to use Secure boot anymore. As of today, most UEFI Firmware let users choose to turn off Secure Boot, how about that in the future?
Creating a dummy Kernel Module
First you need to build a unsigned Kernel module. A “Hello Wold” is good enough
Install the required RPMs
yum -y install kernel-devel.x86_64 gcc keyutils mokutil.x86_64
hello.c
#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> MODULE_LICENSE("GPL"); MODULE_AUTHOR("Luc de Louw"); MODULE_DESCRIPTION("Hello World Linux Kernel Module"); static int __init hello_init(void) { printk(KERN_INFO "Hello world!\n"); return 0; } static void __exit hello_exit(void) { printk(KERN_INFO "Unloading Hello world.\n"); } module_init(hello_init); module_exit(hello_exit);
Makefile
obj-m += hello.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean install: cp hello.ko /lib/modules/$(shell uname -r)/extra depmod
Building
make && make install
Testing
modprobe hello
To remove the module afterwards run
rmmod hello
Set up the enforcement of loading only signed modules
This is only needed on machines without secure boot.
Add module.sig_enforce=1 to GRUB_CMDLINE_LINUX in /etc/default/grub
/etc/default/grub
GRUB_TIMEOUT=5 GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)" GRUB_DEFAULT=saved GRUB_DISABLE_SUBMENU=true GRUB_TERMINAL_OUTPUT="console" GRUB_CMDLINE_LINUX="crashkernel=auto rd.lvm.lv=vg_rhel7test/lv_root rd.lvm.lv=vg_rhel7test/lv_swap module.sig_enforce=1" GRUB_DISABLE_RECOVERY="true"
The next step is to update the GRUB configuration. Please check if you are using UEFI or BIOS on your system first.
On systems with UEFI
[root@rhel7uefi ~]# grub2-mkconfig -o /boot/efi/EFI/redhat/grub.cfg
On BIOS systems
[root@rhel7test ~]# grub2-mkconfig -o /boot/grub2/grub.cfg
Reboot your system.
Testing
modprobe hello
The Module will not load. You will see an error message instead:
modprobe: ERROR: could not insert 'hello': Required key not available
Signing the module
Needless to say that this must be done on a protected system and not on production servers.
First you need to create an OpenSSL config file like this:
x509.conf
cat >>/tmp/x509.conf <<EOF [ req ] default_bits = 4096 distinguished_name = req_distinguished_name prompt = no string_mask = utf8only x509_extensions = extensions [ req_distinguished_name ] O = Example, Inc. CN = Example, Inc. Kernel signing key emailAddress = jdoe@example.com [ extensions ] basicConstraints=critical,CA:FALSE keyUsage=digitalSignature subjectKeyIdentifier=hash authorityKeyIdentifier=keyid EOF
Generating the Keypair
[root@rhel7uefi ~]# openssl req -x509 -new -nodes -utf8 -sha256 -days 99999 -batch -config /tmp/x509.conf -outform DER -out pubkey.der -keyout priv.key
Adding the Public key as a MOK (Machine Owner Key)
Note: This does only work on systems with UEFI, on BIOS machines you will get an error.
[root@rhel7uefi ~]# mokutil --import pubkey.der
You will be prompted for a password that will be used for the second part of the MOK enrollment. Reboot your machine, the shim UEFI Key Manager will appear. After waiting 10sec the system continues to boot the normal system.
You can list the enrolled keys with
root@rhel7uefi ~]# mokutil --list-enrolled
Signing the Module
After successfully enroll the MOK you can sign and test the Kernel Module.
First lets have a look at the Module
root@rhel7uefi ~]# modinfo hello filename: /lib/modules/3.10.0-514.16.1.el7.x86_64/extra/hello.ko description: Hello World Linux Kernel Module author: Luc de Louw license: GPL rhelversion: 7.3 srcversion: 4A5235839200E8580493A17 depends: vermagic: 3.10.0-514.16.1.el7.x86_64 SMP mod_unload modversions [root@rhel7uefi ~]#
Sign it.
[root@rhel7uefi ~]# /usr/src/kernels/$(uname -r)/scripts/sign-file sha256 priv.key pubkey.der /lib/modules/$(uname -r)/extra/hello.ko
Lets have a look to the module again.
[root@rhel7uefi ~]# modinfo hello.ko filename: /root/hello.ko description: Hello World Linux Kernel Module author: Luc de Louw license: GPL rhelversion: 7.3 srcversion: 4A5235839200E8580493A17 depends: vermagic: 3.10.0-514.16.1.el7.x86_64 SMP mod_unload modversions signer: Example, Inc. Kernel signing key sig_key: 71:F7:AA:48:60:A0:B5:D9:D8:A8:1D:A4:6F:92:30:DF:87:35:81:19 sig_hashalgo: sha256 [root@rhel7uefi ~]#
Now you should be able to load your module.
[root@rhel7uefi ~]# modprobe hello
If something went wrong, you will see an error message such as
modprobe: ERROR: could not insert 'hello': Required key not available
Syslog and Journald are more verbose:
Request for unknown module key 'Example, Inc. Kernel signing key: 22e37ef0c0784c7a2c1e2690dc8b27c75533b29d' err -11
Further reading
Conclusion
If your hardware works with secure boot, you can easily enhance security and keep the flexibility to load 3rd party Kernel modules by signing them.
On virtual machines you can make use of signing enforcement which prevents to load any 3rd party module. This may, or may not be a problem.
A major drawback I see is scalability. It may be okay to manually enroll keys on a few workstations or notebooks. On a larger enterprise scale I see problems. For really large environments, you can probably talk with the hardware vendor to include the MOK (Machine Owner Key) factory preinstalled.
Have fun 🙂
I signed a module but it was stored as .ko.xz, and now I can’t modinfo it. Did i mistakenly sign a compressed module?
Can I now remove the signature from the .ko.xz file, because when I try modinfo it now I get:
filename: /lib/modules/5.11.7-200.fc33.x86_64/extra/modname.ko.xz
modinfo: ERROR: could not get modinfo from ‘modname’: Invalid argument
No worries… figured out that I needed to uncompress the xz files first, then sign them, then re-compress the signed files with xz. Then everything worked and I could modprobe without issues.
So in a host without UEFI but with sig_enforce=1 you will never be able to run that one because the certificate cannot be imported with mokutil.
I am getting in fact this error:
EFI variables are not supported on this system
Is there a workaround for this? Thanks in advance,
Hi recent machines (approx. 10 years) are all capable to run in UEFI mode, but they need to have a /boot/efi partition
HTH
Luc
“Note: This does only work on systems with UEFI, on BIOS machines you will get an error.”
So, what does one do on BIOS machines? How does one import the public key?
Hi,
With legacy BIOS this is simply not possible. However, machines sold the past approx. 10 years are all capable to do so if *not* using legacy BIOS settings.
HTH,
Luc