Originally written: 4/3/2021
This Web page is provided free of charge and with no annoying outside ads; however, I did take time to prepare it, and Web hosting does cost money. If you find this Web page useful, please consider making a small donation to help keep this site up and running. Thanks!
| Donate $1.00 | Donate $2.50 | Donate $5.00 | Donate $10.00 | Donate another value |
This page is part of my Managing EFI Boot Loaders for Linux document. If a Web search has brought you to this page, you may want to start at the beginning.
Pre-boot malware exists, by definition, in software run before the OS has booted; but other software components can be quite sensitive, too. The OS's kernel is prime among these, and modern Linux distributions that support Secure Boot all provide signed Linux kernels. Boot loaders that honor Secure Boot, including GRUB 2 and rEFInd, refuse to launch a Linux kernel unless it's been signed with a key that matches one in the Secure Boot db or MOK list. If you compile your own kernel, you can sign it yourself with your own key, as described on an earlier page. This isn't the end of the story, though. Linux kernel binaries rely on a series of drivers, known as modules in the Linux world, which may be compiled into the main kernel binary or, more commonly, exist as separate files. These kernel modules help the kernel perform low-level tasks, such as read from and write to hard disks, manage network devices, and interface with USB hardware. If an attacker were to replace a kernel module file with a modified version, the consequences could be as serious as if the attacker replaced the main kernel file or boot loader.
To protect against this possibility, the Linux kernel permits the signing of kernel module files, and provides mechanisms to check that these signatures are valid. If you use a major distribution that supports Secure Boot, such as Fedora, openSUSE, or Ubuntu, this detail is normally transparent, although there are exceptions to this rule. This page exists to help you navigate those exceptions, or to properly sign your kernel modules if you're using a distribution, such as Gentoo, that does not distribute pre-signed kernel modules. Although the kernel's checks on module validity are not technically part of Secure Boot, the two are related, and Secure Boot keys can be used to sign kernel modules.
Broadly speaking, there are two situations in which you may need to sign some or all of your kernel modules:
VirtualBox complains about a missing kernel driver when that driver is not signed but Secure Boot is active.
The first case is likely to produce an error message about a missing kernel module or driver. For instance, when I try to launch a VirtualBox session with an unsigned VirtualBox driver, I receive the error dialog box shown to the right. Attempting to load the missing module using the modprobe command specified in the dialog box produces a command-line error:
$ sudo modprobe vboxdrv modprobe: ERROR: could not insert 'vboxdrv': Operation not permitted
To the uninitiated, these warnings and errors are likely to be frustrating, although the VirtualBox dialog box does at least provide a pointer in the right direction, since it mentions Secure Boot and the need to sign the kernel modules. Starting from the dialog box's hint, though, you'll need to search to find documentation on how to proceed. (Perhaps that's how you found this page!) The following paragraphs should help.
The situation can be even worse with some tools. If a proprietary video driver doesn't load, for instance, you'll get a message similar to the one shown earlier only in response to the modprobe command — and that only if you try to manually load the module. If the module is set to be loaded automatically at boot time, this message may be hidden in log files. Because most proprietary video drivers have open source alternatives, the computer is likely to fall back to the open source driver; but this driver may not provide the level of performance you want, particularly if you're using 3D modeling or gaming software, video playback functions, or other advanced features.
If you've built your own kernel, the situation can be quite different. As described in later sections, the Linux kernel can automatically create its own signing keys, and will typically sign its own modules automatically (although this feature can be disabled). Thus, you're unlikely to run into problems with the kernel being unable to load its own modules. On the other hand, if your kernel is not configured to validate its modules, you might sign the main kernel itself and think you have full Secure Boot protections, when in fact the kernel will happily load any unsigned kernel module, thus partially negating the benefits of Secure Boot. This situation can be difficult to detect, but doing so is described in the upcoming section, Checking Signing Status.
Note that the Linux kernel's module signing feature is technically independent of Secure Boot; it can be enabled even if Secure Boot is inactive, and it relies mostly on its own key sets. There are provisions for passing through Secure Boot keys for use in verifying kernel modules, though, and activating Secure Boot also sometimes activates the kernel's module verification functions. Thus, the two are at least loosely connected in practice.
The easier case is the one in which you need to sign only a few kernel modules, such as those for VirtualBox or a proprietary video driver. To do this, you use the sign-file program, which is distributed with the kernel source code, and is usually installed in a Linux headers package even if you don't download the full Linux source code package. (Note the usually disclaimer, though. You may need to hunt to find this program, or even install and compile a Linux kernel to get it!) To work, this program needs two Secure Boot key files (one with a .key extension and one with a .cer or .der extension), and of course the kernel module file that's to be signed. A single invocation looks something like this:
# sign-file sha256 local.key local.cer /lib/modules/5.8.0/updates/dkms/vboxdrv.ko
Because you will need to sign kernel modules anew whenever you upgrade your kernel, and because there's more to it than signing a file in this way, and because you may need to sign multiple kernel module files, you may want to create a script to do the job with a single simple command. Here's an example of such a script to sign four VirtualBox kernel modules (you can also download it by clicking here):
#!/bin/bash
# sign-vbox script, copyright (c) 2017 by Rod Smith
# Distributed under the terms of the GPLv3
if [ "$#" -ne 1 ] && [ "$#" -ne 0 ]; then
echo "Usage: $0 [ {kernel-version} ]"
exit 1
fi
if [ "$#" == 0 ]; then
kernel_version=$(uname -r)
else
kernel_version="$1"
fi
sign_file=$(find /usr/src/ -name sign-file | tail -n 1)
if [ -z $sign_file ]; then
echo "Can't find the sign-file binary! Exiting!"
exit 1
else
path_to_modules="/lib/modules/$kernel_version/updates/dkms"
if [ ! -f $path_to_modules/vboxdrv.ko ]; then
echo "Could not find $path_to_modules/vboxdrv.ko!"
echo "Is the kernel version correct?"
exit 1
fi
echo "Signing modules for $kernel_version"
$sign_file sha256 /etc/refind.d/keys/refind_local.key /etc/refind.d/keys/refind_local.cer $path_to_modules/vboxdrv.ko
$sign_file sha256 /etc/refind.d/keys/refind_local.key /etc/refind.d/keys/refind_local.cer $path_to_modules/vboxnetadp.ko
$sign_file sha256 /etc/refind.d/keys/refind_local.key /etc/refind.d/keys/refind_local.cer $path_to_modules/vboxnetflt.ko
$sign_file sha256 /etc/refind.d/keys/refind_local.key /etc/refind.d/keys/refind_local.cer $path_to_modules/vboxpci.ko
modprobe vboxdrv
modprobe vboxnetflt
modprobe vboxpci
modprobe vboxnetadp
echo "Loaded vbox modules:"
lsmod | grep vbox
fi
To use this script, copy it to /usr/local/bin and then type sign-vbox to sign the VirtualBox modules for the current kernel or sign-vbox version to sign the modules for the kernel with the specified version. You must run this command as root or using sudo. Recent versions of VirtualBox seem to no longer use the vboxpci.ko module, so the script will complain that this file is missing, but you can ignore these complaints if you see them. At the end, the script should show the list of loaded VirtualBox kernel modules:
vboxnetadp 28672 0 vboxnetflt 28672 1 vboxdrv 491520 4 vboxnetadp,vboxnetflt
This script uses rEFInd's local keys, stored as /etc/refind.d/keys/refind_local.key and /etc/refind.d/keys/refind_local.cer. If you want to use another key set, you'll have to modify the script. Several other key sources should work:
A script similar to the preceding one might be used to sign proprietary video drivers or other non-standard kernel modules. You'll need to track down the specific kernel modules that must be signed and modify the script appropriately. Also, understand that you'll have to run the script again every time you update your kernel. In the case of drivers that are needed to boot the computer or use fundamental features like the display, you should run the script before rebooting. (The preceding script can take a kernel version number as an optional parameter. If launched without that information, the script signs the current kernel's modules.)
If your distribution does not officially support Secure Boot, or if you've built your own kernel, you may need to look into signing all your kernel modules. This may seem like a daunting task, but in practice, it is usually not as difficult as it might at first seem. Before you begin with this task, you may want to check to see whether your current kernel is authenticating its modules. If it's not, then recompiling the kernel, or at least passing a kernel option to enforce signing checks, may be in order.
I know of two ways to tell a kernel to enforce the signing of kernel modules: via the kernel lockdown mechanism and via kernel module signature enforcement. Depending on your kernel's configuration, neither, one, or both mechanisms may be enabled. Checking their status is usually easy:
The kernel lockdown mechanism, if enabled, normally sets the lockdown level in the /sys/kernel/security/lockdown file:
$ cat /sys/kernel/security/lockdown none [integrity] confidentiality
The set of available options is listed, with the active one shown inside square brackets. A value other than none prevents unsigned kernel modules from being loaded.
The module signature enforcement setting can also be checked via the /sys filesystem:
$ cat /sys/module/module/parameters/sig_enforce N
A value of N indicates that the kernel is non-enforcing, whereas Y indicates that the kernel is enforcing.
If either one of these mechanisms is active, unsigned kernel modules will be prevented from loading. If your goal is improved security, activating one of these options, as described in the next two sections, is desirable.
Sometimes the state of the system is unclear. I've seen configurations in which one or the other of these /sys files is not present, for instance. To check the state of the system in a case like this, you might want to compile your own unsigned kernel module for testing. A simple "hello world" kernel module can suffice for this purpose. One I used for testing is available at Luc de Louw's blog, but similar modules are available elsewhere. You'll need to install various software development tools and at least a kernel headers package for your kernel in order to compile the module. When you do, you can try loading it:
$ sudo insmod ./hello.ko insmod: ERROR: could not insert module ./hello.ko: Operation not permitted
This example shows a failure to load the module, which may indicate that the kernel is configured to refuse to load unsigned modules. On the other hand, there can be other reasons for a failure at this point. You can find more information in the output of dmesg, likely on the final line if you check immediately after trying to load the kernel module:
[250035.610170] Lockdown: insmod: unsigned module loading is restricted; see man kernel_lockdown.7
This kernel message indicates that the kernel won't load unsigned modules. If the message indicates some other fault, then you may want to investigate and correct the problem, and then try again.
Another kernel message is relevant if the module loaded:
[ 67.662577] hello: module verification failed: signature and/or required key missing - tainting kernel
The message that the kernel is tainted means, in this context, that the module failed the verification checks but was loaded anyway. This message indicates that the kernel includes the relevant code to check a module's signature, but that code does not enforce the checks. This condition can be changed by passing a kernel option, as described later, in Enforcing Signature Checks or Setting Kernel Options.
If the hello.ko module does load, then it will leave a single line of output in the dmesg output:
[250237.741303] Hello world!
If the unsigned test module loaded, then your kernel is not authenticating modules prior to loading them (or is ignoring the authentication checks), which makes your computer susceptible to malware distributed as kernel modules, should you be tricked into loading such a module or should an intruder be able to break into your computer and obtain root access. In this case, you may want to look into recompiling your kernel with the options set to have it refuse to load unsigned modules, or at least setting kernel boot-time options to ensure it performs module authentication.
If your kernel is currently not enforcing signature checks, you can often harden the system by adjusting the kernel's command-line options. You must set these in your boot loader's configuration file; for instance:
In either case, the options you add are identical:
You can set either one of these options; for purposes of enforcing module signing, they have similar effects. With one of these options in place, you can reboot your computer and check the enforcement status, as described earlier, in Checking Signing Status.
Be aware that, if you must enable these protections on the kernel's command line, they will be relatively easily changed by an intruder who gains root access to your computer. You can also change the default that's hard-coded in the kernel, which makes it more difficult to attack the computer. This is the subject of the next section....
For many years, the Linux kernel lacked any sort of support for Secure Boot. This fact is not surprising, since the Linux kernel predates Secure Boot. It means, though, that Secure Boot support is an optional extra that must be enabled in the kernel configuration. If you're using the stock kernel provided with your distribution, then you cannot change this functionality; these features are controllable only if you compile your own kernel. For some distributions, such as Gentoo (which is intended to be built almost entirely from source code locally), this limitation isn't much of a problem. If you're using a more mainstream distribution such as Ubuntu or Fedora, though, then you don't have direct control over these features unless you compile your own kernel.
Compiling your own kernel has some advantages, even if you don't want to compile all your software. You can optimize kernel settings for your CPU and use case, omit unused features, and make other changes to optimize your kernel for your computer and needs. This page can't provide a complete guide to kernel compilation. Many such guides exist on the Internet. For a basic introduction, see How to Compile a Linux Kernel. For a more detailed look at some of the mechanics of the process, check out Compiling a Linux Kernel. The number of options you can select in the Linux kernel can be quite daunting, and for a full explanation of them, you'll need to look into a book on the subject; but one page that covers some of the bigger options is Configuring and Compiling the Kernel.
Kernel module signature verification is an optional feature of the Linux kernel, set during kernel configuration.
As these sites detail, the kernel relies on a configuration tool to help you pick options. You can launch one of these by typing make menuconfig in the root of the kernel source code directory. The Secure Boot module verification options appear under Enable Loadable Module Support, as shown in the image to the right. Several options are relevant, as described in more detail here:
You can set more kernel module verification options on a second menu.
A few more options are relevant, buried on another menu: Go to Cryptographic API -> Certificates for signature checking. The resulting menu resembles the one to the right. Chances are leaving these options at their defaults will be fine; the kernel will generate its own key and use it to sign modules when it builds itself. If you wanted to use a key you obtained or created in some other way, though, you could point to it on the first line or use the Additional X.509 keys... item to point to it, if your purpose was to simplify using a third-party key — say, one used by a software vendor that provides self-signed kernel modules you want to use.
Once you've set your kernel modules, you can compile and install your kernel in the usual way, such as by typing make && make modules && make modules_install, followed by copying the kernel to /boot and creating an initrd file. These procedures are a standard part of Linux kernel compilation and are covered in general tutorials, such as those referenced earlier, so I won't describe them here.
Depending on configuration options, Secure Boot doesn't end with the launching of an EFI boot loader, or even the launching of a Linux kernel. With every step further along the boot path, Secure Boot can provide more protection against malware. As of early 2021, this extension extends to Linux kernel modules — at least, when the kernel was compiled with the relevant support features enabled. Although this support provides at least a theoretical benefit against malware, it can also be a hassle. If you use third-party modules, you must sign them before you can load them; and if your distribution does not enable this support but you want it, you must jump through some significant extra hoops to get this support.
Go on to "Controlling Secure Boot"
Return to "Managing EFI Boot Loaders for Linux" main page
copyright © 2021 by Roderick W. Smith
If you have problems with or comments about this web page, please e-mail me at rodsmith@rodsbooks.com. Thanks.
Return to my main Web page.