Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save mdaguete/11b428c2ca64848fd91f2e91f822617f to your computer and use it in GitHub Desktop.
Save mdaguete/11b428c2ca64848fd91f2e91f822617f to your computer and use it in GitHub Desktop.
Automatic Signing of DKMS-Generated Kernel Modules for Secure Boot

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.

Step 0. Prequisites

(Click to expand/collapse)
  • Make sure the packages openssl, mokutil and dkms are installed. You need to enable the EPEL repo to get dkms for RHEL/CentOS.

  • You also need the necessary compilers and other build tools to build your module, like gcc and make. For CentOS users I suggest simply installing them by dnf groupinstall development.

  • The kernel source, provided by kernel-devel in Fedora/CentOS, or linux-headers-generic in Debian/Ubuntu.

Step 1. Generating a MOK and Enrolling It in Secure Boot

(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.der
    

    You'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 asymmetri as root.

Step 2. Configuring DKMS to Sign Newly-Built Modules Automatically with the MOK.

(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>.conf for 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 /root but obviously you can adjust its path.)

  • DKMS will pass to our script the kernel version number as $1 and the full path to module file as $2. We'll use the sign-file tool from the kernel-devel package, which needs to be supplied with more info.

    Create the script /root/sign-nvidia-driver.sh which simply provides the correct argument for sign-file as follows:

    #!/bin/bash
    # sign-nvidia-driver.sh
    
    hash_algo=sha256
    private_key=/root/nvidia-driver.key
    x509_cert=/root/nvidia-driver.der
    
    "/usr/src/kernels/${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 1
    

    Remember 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

Step 3. Installing the Nvidia Driver and Registering It With DKMS.

(Click to expand/collapse)
  • Become root again. Blacklist nouveau in modprobe.d and your initramfs (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-user and 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-dkms by 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 status and you should see the nvidia module in "installed" state with DKMS. lsmod | grep nvidia should 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 status but the kernel will refuse to load it unlessyou 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 autoinstall
    

    Again, 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 ran 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment