Automatic Signing of DKMS-Generated Kernel Modules for Secure Boot (Nvidia Driver on CentOS 8 as Example)
First I thank Nvidia for sponsoring the video card.
Secure Boot isn't exactly easy to configure to work with Linux and disabling it isn't really a good idea. Many modern Linux distributions provide the Microsoft-signed shim EFI binary to interpose between Secure Boot and the grub2 bootloader, making booting Linux easy enough if you only ever use kernels and drivers from the official repos. Still, enabling Secure Boot prevents the loading of kernel or modules without a proper digital signature. For example, the propriatary Nvidia GPU driver won't work, unless your distro really went to great lengths to distribute a signed version of the kernel module.
To make Secure Boot play nicely with the driver (i.e. to work at all), we can generate and import a Machine Owner Key (MOK), and use it to sign the kernel module each time it is rebuilt after updating the kernel and/or the driver. The rebuilding of the kernel module is typically automated by DKMS, but the signature process is not -- because the MOK's generation and usage must be initiated manually by the user.
There have been guides (e.g. Nvidia's own guide and 1 2 3) for signing the modules and even automating it with the POST_BUILD directive (e.g. 1 2).
But after digging into DKMS's man page, I found that the SIGN_TOOL directive may provide a simpler way to configure the automatic signing. So here's a full guide, and if you're only interested in the DKMS part, it's in the Step 2 below:
I used CentOS 8; things can vary slightly in other distros.
Note 2022-05-08: Thank you folks for offering advices and fixes to this gist. I recently switched my personal PC from CentOS to Ubuntu LTS (actually, KDE Neon) due to the uncertainties about IBM/RedHat converting CentOS into CentOS stream. I found that for Ubuntu/Debian, well-packaged DKMS drivers such as nvidia (as packaged by Nvidia with CUDA) and v4l2loopback have some hooks handling MOK signing, thus making this gist unnecessary.
However, they are not perfectly functional. My observations/caveats are:
-
When installing using the installer, it will prompt you to set up a MOK key if you have Secure Boot on and disk encrypted. I use
debootstrapto install Debian-based distros so ain't sure how automated it actually is. -
The hooks do not always trigger when installing the dkms driver packages. If they don't, you can reboot with Secure Boot on, and try to run, e.g.,
dpkg-reconfigure nvidia-dkms-xxxas root. In addition to building and signing the kernel module, it should also prompt for a passphrase twice, and invoke the MOK import EFI interface on a reboot. -
If you can't boot into the desktop due to unsigned GPU driver -- which could happen if you didn't sign the kernel module, or if you did a BIOS update/reset that erased previous MOKs, just try booting into recovery mode from
grub, become root, and do thedpkg-reconfigurefix above.
(Click to expand/collapse)
-
Make sure the packages
openssl,mokutilanddkmsare installed. You need to enable the EPEL repo to getdkmsfor RHEL/CentOS. -
You also need the necessary compilers and other build tools to build your module, like
gccandmake. For CentOS users I suggest simply installing them bydnf groupinstall development. -
The kernel source, provided by
kernel-develin Fedora/CentOS, orlinux-headers-genericin Debian/Ubuntu.
(Click to expand/collapse)
-
Start by becoming root with
sudo -i. -
Generate the key and certificate.
openssl req -new -x509 \ -newkey rsa:2048 -keyout /root/nvidia-driver.key \ -outform DER -out /root/nvidia-driver.der \ -nodes -days 36500 -subj "/CN=Nvidia Driver Kmod Signing MOK"(The key and the certificate filenames, paths, expiration date and subject can be modified to your liking.)
-
Enroll the public key.
mokutil --import /root/nvidia-driver.derYou'll be prompted to create a password. Enter it twice.
-
Reboot the computer. At boot you'll see the MOK Manager EFI interface. Press any key to enter it.
- "Enroll MOK"
- "Continue".
- "Yes".
- Enter the password you set up just now.
- Select "OK" and the computer will reboot again.
-
After reboot, you should be able to see the new key with
cat /proc/keys | grep asymmetrias root.
(Click to expand/collapse)
-
To minimize human effort and troubleshooting, it's best to get the keys, config files and scripts in place before installing any actual drivers.
-
Create a text file
/etc/dkms/nvidia.conf, or/etc/dkms/<module-name>.conffor other modules (with<module-name>part exactly matching the name of the module), which is a one-liner pointing to the signing script.echo "SIGN_TOOL=/root/sign-nvidia-driver.sh" > /etc/dkms/nvidia.conf(I put the script under
/rootbut obviously you can adjust its path.) -
DKMS will pass to our script the kernel version number as
$1and the full path to module file as$2. We'll use thesign-filetool from thekernel-develpackage, which needs to be supplied with more info.Create the script
/root/sign-nvidia-driver.shwhich simply provides the correct argument forsign-fileas follows:#!/bin/bash # sign-nvidia-driver.sh hash_algo=sha256 private_key=/root/nvidia-driver.key x509_cert=/root/nvidia-driver.der prefix=/usr/src/kernels/ # For Debian/Ubuntu, use #prefix=/usr/src/linux-headers- "${prefix}${1}/scripts/sign-file" \ "${hash_algo}" "${private_key}" "${x509_cert}" "${2}" \ && echo "Signed newly-built module ${2} with MOK successfully." >&2 \ && exit 0 echo "Error signing file ${2}." >&2 exit 1Remember to
chmod +x /root/sign-nvidia-driver.sh.The script returns 0 when the signing succeeds and 1 when it fails. A non-zero return value will cause the DKMS build operation to fail. Corresponding message will be printed to
stderr
(Click to expand/collapse)
-
Become root again. Blacklist
nouveauinmodprobe.dand yourinitramfs(the latter will vary with distros).echo "blacklist nouveau" > /etc/modprobe.d/blacklist-nouveau.conf echo 'omit_drivers+="nouveau"' > /etc/dracut.conf.d/blacklist-nouveau.conf dracut -f -
It usually suffices to exit the graphical session with
systemctl isolate multi-userand login onto a text mode console as root. But if the driver install fails, try rebooting into text mode. -
Install the DKMS driver. For Nvidia GPUs, people typically download, chmod and execute the runfile driver from Nvidia website which will ask you to register it with DKMS.
I also have success with the package
kmod-nvidia-latest-dkmsby following Nvidia's instructions to install CUDA. This makes updating easier, but it doesn't come with 32-bit libraries necessary for e.g. Steam. -
After install, run
dkms statusand you should see thenvidiamodule in "installed" state with DKMS.lsmod | grep nvidiashould show you that the modules are sucessfully loaded.Now you can go back to GUI with
systemctl isolate graphical. Viola! -
Note 1: If you previously installed some DKMS driver package without setting up proper signing process, the module will be shown as "built" or "installed" by
dkms statusbut the kernel will refuse to load it unless you disable Secure Boot. In this case, simply remove the modules and rebuild them with dkms with proper signing:dkms remove nvidia/440.64.00 --all dkms add nvidia/440.64.00 dkms autoinstallAgain, replace the
<module-name>/<version>part with your own. -
Note 2: if you are using Nvidia's runfile installer, it is unnecessary to pass the keys as parameters to the installer, because the installer also calls DKMS to do the kmod building which should hook up the signing program automatically with our setup in place. If you did run the installer with Secure Boot on but w/o signing the driver (either by passing keys as parameters or with the method detailed in this gist), the installation would fail because the unsigned module would be rejected by the kernel.
At least for debian users, don't forget that the correct path begins with /usr/src but NOT /usr/src/kernels