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
@gvisoc
Copy link

gvisoc commented May 12, 2020

Debian 10.2 (kernel from backports)

$ dpkg -S sign-file
linux-kbuild-5.3: /usr/lib/linux-kbuild-5.3/scripts/sign-file
linux-kbuild-4.19: /usr/lib/linux-kbuild-4.19/scripts/sign-file
$ sudo find /usr/src -name sign-file
$ sudo find /usr/lib -name sign-file
/usr/lib/linux-kbuild-4.19/scripts/sign-file
/usr/lib/linux-kbuild-5.3/scripts/sign-file

For Debian 10 there are a few changes to make this work. First, we have to trim some of the version numbers of the uname -r output, and change the location of the sign-file. Debian 10 also removes the modinfo from the root PATH by default, hence a small modification to use /sbin/modinfo instead. I made these changes to this awesome gist.

#!/bin/sh

readonly hash_algo='sha256'
readonly key='MOK.priv'
readonly x509='MOK.der'

readonly name="$(basename $0)"
readonly esc='\\e'
readonly reset="${esc}[0m"
readonly fullver=$(uname -r)
readonly majorminor="$(echo $fullver | cut -d '.' -f1,2)"

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.
# This is for Debian
alias sign-file="/usr/lib/linux-kbuild-${majorminor}/scripts/sign-file"

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

# In Debian, modinfo is not in the PATH even for root users. In other 
# distributions it may not be located at /sbin
for module in $(dirname $(/sbin/modinfo -n vboxdrv))/*.ko; do
  log "Signing $(green ${module})..."
  sign-file "${hash_algo}" "${key}" "${x509}" "${module}"
done

I plan to extend it a bit to work with any module, it's very handy. Kudos to @reillysiemens

@scantisani
Copy link

scantisani commented May 15, 2020

On Fedora 31 (kernel version 5.6.11-200.fc31.x86_64), modprobe was responding with modprobe: ERROR: could not insert 'vboxdrv': Bad message when I tried to load the VirtualBox modules.

I re-generated MOK.priv and MOK.der by following the key generation instructions in the official Fedora 31 docs, then re-signed the modules. Then everything worked fine.

@tomberlin55
Copy link

tomberlin55 commented Aug 7, 2020

I have NO *.ko files on my server.
Running nethserver (Centos).
tried to install
VirtualBox-6.1-6.1.12_139181_el7-1.x86_64.rpm
I managed to install the MOK to my BIOS, I found the sign thing, but I have not on .ko file on my whole computer...

@JJ
Copy link

JJ commented Aug 16, 2020

Slight change for Ubuntu:

alias sign-file="/usr/src/linux-headers-$(uname -r)/scripts/sign-file"

@JJ
Copy link

JJ commented Aug 16, 2020

This doesn't work at all for me, unfortunately. sign-file is in the wrong place:

~$ find /usr/src -name sign-file
/usr/src/linux-headers-4.15.0-50-generic/scripts/sign-file

This worked for me. Thanks!

@jk-1
Copy link

jk-1 commented Oct 13, 2020

Slight change for Ubuntu:

alias sign-file="/usr/src/linux-headers-$(uname -r)/scripts/sign-file"

This worked on Ubuntu 20.04, Thanks!!!

@jkufner
Copy link

jkufner commented Oct 13, 2020

Important detail: If you have a system without Shim, using only Sicherboot or booting the kernel directly, and using your own keys (SecureBoot in the user mode), then you can use db.key instead of the missing MOK key. Therefore:

 find "$module_dir" -type f -name "*.ko" \
        -fprint /dev/stderr \
        -exec "$sign_file" sha256 /etc/sicherboot/keys/db.key /etc/sicherboot/keys/db.cer '{}' \;

See also: julian-klode/sicherboot#11

@GoetzEdinger
Copy link

GoetzEdinger commented Oct 21, 2020

Great Job!
It worked on Fedora 32 after the first 4 steps.
Thank You!

@zaid20485
Copy link

zaid20485 commented Oct 23, 2020

Thank you
worked for me , on fedora 32

@akellehe
Copy link

akellehe commented Nov 22, 2020

Great work on this! ubuntu 20

@wyoguy
Copy link

wyoguy commented Jan 2, 2021

Your instructions worked perfectly for me! Thanks! Running Lubuntu 20.10.
Obviously I had to update the line for the scripts/sign-file. (sudo find / | grep 'sign-file') -- to find the location
for me the whole line reads:
alias sign-file="/usr/src/linux-headers-$(uname -r)/scripts/sign-file"

Side Note (The reason that I needed this, was I could not get my keyboard to work at the MOK efi management utility. I had to go find a PS2 style keyboard that the BIOS could recognize. HA)
Thanks again for your post!

@dtatulea
Copy link

dtatulea commented Jan 2, 2021

Worked on my Fedora 33, thanks!

Based on your instructions I also got the NVIDIA driver to be signed.

@ggbce
Copy link

ggbce commented Feb 6, 2021

Thank you for this script ! It worked for me also under Debian 10 and HP Laptop EFI Secure Boot.

... But I spend time before understanding. I did all steps, then after that I tried to run again /sbin/vboxconfig and I fail again. The "vboxconfig" try to stop service and tell again that I need to sign modules and try somethin that result in something to "corrupt" the already vboxdrv signed file instead understanding the files are aleardy signed.

After installing MOK, generate a certificate, reboot, enforce MOK, sign virtualbox files... Dont' try to run /sbin/vboxconfig again. Just let run "virtualbox" !!!

@int3rsys
Copy link

int3rsys commented Feb 15, 2021

Thanks!! can confirm it works with Ubuntu 20.10 + Thinkpad T14

@baraque
Copy link

baraque commented May 1, 2021

Thanks, it works in Ubuntu 20.04.

One small improvement: print $1 to get username

name="$(getent passwd $(whoami) | awk -F: '{print $1}')"

@masbaehr
Copy link

masbaehr commented Jul 5, 2021

What the heck. This complexity is hilarious for getting VirtualBox to run. A perfect example why Windows is still king for every-day use ;-) I also ended up disabling SecureBoot as it was not working on my Fedora34 installation on a Huawei Matebook

@fsimula
Copy link

fsimula commented Jul 30, 2021

I'd like to add a note; I was in the same situation of @hoxsiew, @LizAinslie and @ChrisRoald, rebooting didn't trigger the MokManager utility so that I couldn't enroll the key and modprobe-ing didn't work, saying that the key was rejected by service.
@m4ldonado suggestion didn't work for me, so I had to resort to explicitly create an UEFI entry with:

sudo efibootmgr -c -d /dev/device_containing_the_EFI_partition -p number_of_EFI_partition -L "MokManager" -l '\efi\boot\fedora\mmx64.efi'

checking which entry number it was given with:

efibootmgr -v

and force it to start that entry just for once on reboot with:

sudo efibootmgr -n mokmanager_new_entry_number

Of course, customize your device_containing_the_EFI_partition, number_of_EFI_partition and mokmanager_new_entry_number as per your setup.

N.B.: I'm on Fedora 34 (with SecureBoot enable, of course) and for that \efi\boot\fedora\mmx64.efi is the specific location of the MokManager file, yours might be different.

2° N.B.: all this works wonderfully for the VirtualBox package of the RPMFusion repo; with the one from the VirtualBox site, since this latter apparently regenerates the modules each reboot so that every time they loose the signature, the procedure works only for the current boot and requires signing again at the next boot...

@fsimula
Copy link

fsimula commented Aug 20, 2021

Ah, I've got an interesting update: at least here (on a Lenovo ThinkCentre M920t) an ordinary (and successful) BIOS update wiped the enrolled key, which needed to be re-enrolled as described.

@dipbha
Copy link

dipbha commented Sep 22, 2021

On Fedora 34 (Thinkpad P14s), mok enrollment happened successfully, but after reboot got stuck. GRUB failed to show even the menu. To get back, i had to switch off Secure Boot, delete the key using mokutility. After the deletion, things were back to normal with Secure Boot on.

Any leads ?

@mathieuraffinot
Copy link

mathieuraffinot commented Oct 25, 2021

had to change the path also in the script and found it using "dpkg -S sign-file" but otherwise this tutorial is very useful. Thanks a lot !

@vadim-or
Copy link

vadim-or commented Feb 9, 2022

Oracle Linux 8 / Dell 7420 experience:

  • It worth to stress that MOK registration is involved process -- doing it step by step is possible only if following the screenshots on other computer. Otherwise it better to memorize the sequence of blue screens.

  • dmesg | grep '[U]EFI.*cert' did not work for me. I used mokutil --list-enrolled | grep Subject: to verify that the key is loaded.

  • I had to modify signing script:

    • I dislike displaying password, thus read -s -p "...." and echo after that.
    • alias did not work. I replaced with the simple shell variable.
    • output colors did not work -- I added -e to the echo commands.

Thank you @reillysiemens ! you saved me tons of time.

@redongh
Copy link

redongh commented Mar 16, 2022

Fedora 35 / Dell XPS 15 9500 experience:

  • for the most part alings to what @vadim-or wrote a few weeks earlier,
  • getting the MOK registration to come up remained pretty mysterious and involved changing the UEFI-bootorder so instead of the only entry reading Fedora, a different one labeled as the NVME-device present in my system needed to be chosen and afterwards the Fedora one re-selected to finally being presented a screen offering the option of Enroll MOK.
  • instead of the also for me not-working I used dmesg | fgrep -i 'loaded X.509 cert' but came to like @vadim-or s solution involving mokutil even better.
  • I also don't like displaying passwords but didn't need to echo anything for doing so.

Also, my sincere thanks for this writeup @reillysiemens as I probably would have been stuck with this forever otherwise! 🙏

@mmtechslv
Copy link

mmtechslv commented May 22, 2022

When installing VirtualBox to Fedora 36 using conventional methods (adding RPM repository and then installing with dnf), I ended up with kernel modules with *.ko.xz extension(vboxdrv.ko.xz, vboxnetadp.ko.xz, vboxnetflt.ko.xz). From what I understand kmods modules were not properly compiled. As a result, when trying to load modules (with modprobe vboxdrv) I got the following error even though I did sign them correctly.

modprobe: ERROR: could not insert 'vboxdrv': Invalid argument

It seems to be an issue specific to Fedora 36, as I had a similar issue with v4l2loopback module. The solution is not to use the default repository to install and instead download the release .rpm files from official sources and install manually. After manually downloading and installing the resulting kmod modules did not have that .xz extension and did load correctly after signing them.

@fsimula
Copy link

fsimula commented May 23, 2022

When installing VirtualBox to Fedora 36 using conventional methods (adding RPM repository and then installing with dnf), I ended up with kernel modules with *.ko.xz extension(vboxdrv.ko.xz, vboxnetadp.ko.xz, vboxnetflt.ko.xz). From what I understand kmods modules were not properly compiled. As a result, when trying to load modules (with modprobe vboxdrv) I got the following error even though I did sign them correctly.

modprobe: ERROR: could not insert 'vboxdrv': Invalid argument

It seems to be an issue specific to Fedora 36, as I had a similar issue with v4l2loopback module. The solution is not to use the default repository to install and instead download the release .rpm files from official sources and install manually. After manually downloading and installing the resulting kmod modules did not have that .xz extension and did load correctly after signing them.

I may add that I found the same issue here, on different machines just updated to Fedora 36 - I managed to:

  1. decompress the modules
  2. sign them as described above
  3. recompress the modules

and everything works as before, but the hassle to have it in the right way is becoming a little too much for my tastes...

@rfliam
Copy link

rfliam commented Jul 2, 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)

@cybusAlanFrancke
Copy link

cybusAlanFrancke commented Jul 6, 2022

The main solution worked for me on Fedora 36, no error message when starting Virtualbox aftwerwads.

However, after a reboot, the error showed up again, and I hat to rerun the script and reload the modules (steps 6 & 7). Any ideas as to why this would happen again?

P.S.: the colors didn't work for me, but that is unimportant...

@sirhennig
Copy link

sirhennig commented Aug 26, 2022

On Debian 11 Bullseye, I had to make some changes but it worked fine overall.

This were the changes:

alias sign-file="/usr/src/linux-headers-5.10.0-17-amd64/scripts/sign-file"

for module in $(dirname $(/sbin/modinfo -n vboxdrv))/*.ko; do

and I needed to execute /sbin/modprobe vboxdrv at the end.

Thanks a lot.

@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.

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