Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Signing VirtualBox Kernel Modules

Signing VirtualBox Kernel Modules

These are the steps I followed enable VirtualBox on my laptop without disabling UEFI Secure Boot. They're nearly identical to the process described on Øyvind Stegard's blog, save for a few key details. The images here are borrowed from the Systemtap UEFI Secure Boot Wiki.

  1. Install the VirtualBox package (this might be different for your platform).

    src='https://download.virtualbox.org/virtualbox/rpm/fedora/virtualbox.repo'
    dst='/etc/yum.repos.d/virtualbox.repo'
    sudo curl ${src} > ${dst}
    dnf check-update
    sudo dnf install VirtualBox-6.0
  2. Create an RSA key pair to sign kernel modules.

    name="$(getent passwd $(whoami) | awk -F: '{print $5}')"
    out_dir='/root/module-signing'
    sudo mkdir ${out_dir}
    sudo openssl \
        req \
        -new \
        -x509 \
        -newkey \
        rsa:2048 \
        -keyout ${out_dir}/MOK.priv \
        -outform DER \
        -out ${out_dir}/MOK.der \
        -days 36500 \  # This is probably waaay too long.
        -subj "/CN=${name}/"
    sudo chmod 600 ${out_dir}/MOK*

    Note the absence of the -nodes option from Øyvind's post. With this option openssl will create a private key with no passphrase. The omission of this option prompts for a passphrase, which seems like a good idea for something as important as a kernel module signing key.

  3. Import the MOK ("Machine Owner Key") so it can be trusted by the system.

    sudo mokutil --import /root/module-signing/MOK.der

    This will prompt for a password. The password is only temporary and will be used on the next boot. It does not have to be the same as the signing key passphrase.

  4. Reboot your machine to enter the MOK manager EFI utility.

    • Select Enroll MOK.

    Enroll MOK

    • Select Continue.

    Continue

    • Select Yes to enroll the keys.

    Confirm

    • Enter the password from earlier.

    Enter password

    • Select OK to reboot.

    Reboot

    • Verify the key has been loaded by finding the it in the output of
    dmesg | grep '[U]EFI.*cert'
  5. Create a script for signing all the VirtualBox kernel modules.

    #!/bin/sh
    
    readonly hash_algo='sha256'
    readonly key='/root/module-signing/MOK.priv'
    readonly x509='/root/module-signing/MOK.der'
    
    readonly name="$(basename $0)"
    readonly esc='\\e'
    readonly reset="${esc}[0m"
    
    green() { local string="${1}"; echo "${esc}[32m${string}${reset}"; }
    blue() { local string="${1}"; echo "${esc}[34m${string}${reset}"; }
    log() { local string="${1}"; echo "[$(blue $name)] ${string}"; }
    
    # The exact location of `sign-file` might vary depending on your platform.
    alias sign-file="/usr/src/kernels/$(uname -r)/scripts/sign-file"
    
    [ -z "${KBUILD_SIGN_PIN}" ] && read -p "Passphrase for ${key}: " KBUILD_SIGN_PIN
    export KBUILD_SIGN_PIN
    
    for module in $(dirname $(modinfo -n vboxdrv))/*.ko; do
      log "Signing $(green ${module})..."
      sign-file "${hash_algo}" "${key}" "${x509}" "${module}"
    done

    This script differs from Øyvind's in two aspects. First, and most importantly, it has C O L O R S . Second, it uses the magic $KBUILD_SIGN_PIN environment variable that doesn't appear anywhere in the sign-file usage. I went spelunking in the Linux source for it, but in hindsight I could have just read the docs on manual module signing... I wrote this script to /root/bin/sign-vbox-modules as that's usually on root's $PATH.

  6. Execute the aforementioned script as root.

    sudo chmod 700 /root/bin/sign-vbox-modules
    sudo -i sign-vbox-modules
  7. Load the vboxdrv module.

    sudo modprobe vboxdrv
@edupr91
Copy link

edupr91 commented Sep 1, 2022

#!/bin/sh

readonly hash_algo='sha256'
readonly key='/root/module-signing/MOK.priv'
readonly x509='/root/module-signing/MOK.der'

readonly name="$(basename $0)"
readonly esc='\\e'
readonly reset="${esc}[0m"

green() { local string="${1}"; echo "${esc}[32m${string}${reset}"; }
blue() { local string="${1}"; echo "${esc}[34m${string}${reset}"; }
log() { local string="${1}"; echo "[$(blue $name)] ${string}"; }

# The exact location of `sign-file` might vary depending on your platform.
alias sign-file="/usr/src/kernels/$(uname -r)/scripts/sign-file"

[ -z "${KBUILD_SIGN_PIN}" ] && read -p "Passphrase for ${key}: " KBUILD_SIGN_PIN
export KBUILD_SIGN_PIN

for module in $(dirname $(modinfo -n vboxdrv))/*.ko.xz; do
    if [[ $(basename "${module}" .xz) != "${module}" ]]; then
	log "decompress module ${module}"
	xz -d ${module}
    fi
    path=$(dirname "${module}")
    decompressed=$(basename "${module}" .xz)
    log "Signing $(green ${module})..."
    sign-file "${hash_algo}" "${key}" "${x509}" "${path}/${decompressed}"
    if [[ $(basename "${module}" .xz) != "${module}" ]]; then
	log "recompress module ${module}"
	xz "${path}"/"${decompressed}" 
    fi
done

This deals with compressed kernel modules if found (fedora36)

The official package does not provide compressed modules. Note that this script probably is meant for another rpmfusion package maybe?

@vash83a
Copy link

vash83a commented Sep 25, 2022

@edupr91 @mmtechslv @fsimula @rfliam @cybusAlanFrancke @sirhennig
Hi guys!
I found this post because I've just updated to Fedora 36 and looked for a solution for the same issue.
Everything you found out is true and I thank you a lot for everything, but I wanted tell you I've found a clever shortcut.
During the issue investigation process, I found out the *.ko.xz modules were already signed by akmod. It was easily discoverable using modinfo.
I was able to discover where the akmod keypair was stored and then I imported that public key in MOK .
Doing that, my expectation is that everytime VirtualBox will be updated, the modules will be automatically loaded. So, signing modules everytime you upgrade Virtualbox it won't be needed anymore.
I've never tested a VirtualBox upgrade process yet, because I've just installed the latest VirtualBox version. But, I can surely tell that the already signed modules by akmod work correctly.

What do you think about this solution?

The only thing that worries me is about the security perspective. The akmod private key is not password protected, so... just in case someone takes control of my machine, he/she could signs a malicious module using akmod. That module would be correctly loaded because that key had already been imported by me using MOK. Any consideration about that?

If you need more information fell free to ask.
Thanks a lot.

@vash83a
Copy link

vash83a commented Sep 28, 2022

I've just updated the kernel and everything went like a charm.
It's just to confirm my solution actually made things easier.

@tartansandal
Copy link

The only thing that worries me is about the security perspective. The akmod private key is not password protected, so... just in case someone takes control of my machine, he/she could signs a malicious module using akmod. That module would be correctly loaded because that key had already been imported by me using MOK. Any consideration about that?

@vash83a I put my keys on an encrypted thumb drive that I only insert and mount when I'm resigning modules. You need to tweak some paths to get it to work, but it resolves the larger security issue.

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