Contents
Introduction
These instructions let you setup a virtual machine setup based on KVM and Tianocore which has secure boot on. This setup already has Microsoft and distribution-specific keys built-in.
These instructions should be run as a normal user. We assume sudo has been installed and the user who runs the commands can use sudo to get root privileges.
Manual Setup
Install ovmf and qemu-system-x86:
$ sudo apt install ovmf qemu-system-x86 gpg debian-keyring
Create a directory to store the virtual machine files:
$ mkdir ~/secureboot-vm $ cd ~/secureboot-vm
Download and verify some installation media which supports UEFI and SecureBoot, e.g. a Debian network install AMD64 ISO image (check for the latest version here):
$ base=https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/
$ keyring=/usr/share/keyrings/debian-role-keys.gpg
$ wget $base/SHA512SUMS $base/SHA512SUMS.sign
$ mkdir ~/.gnupg ; chmod 700 ~/.gnupg
$ gpg --no-options --no-default-keyring --keyring $keyring --verify SHA512SUMS.sign SHA512SUMS
$ grep 'debian-[0-9.]*-amd64-netinst.iso$' SHA512SUMS | tr -s ' ' | cut -d' ' -f2 | head -n1 | xargs -i wget -N -c "$base/{}"
$ sha512sum --check --ignore-missing --strict SHA512SUMS
$ rm -f SHA512SUMS*For convenience, create a script to launch the VM:
$ cat > start-vm <<\EOF
#!/bin/bash
set -Eeuxo pipefail
MACHINE_NAME="test"
QEMU_IMG="${MACHINE_NAME}.img"
SSH_PORT="5555"
OVMF_CODE="/usr/share/OVMF/OVMF_CODE_4M.ms.fd"
OVMF_VARS_ORIG="/usr/share/OVMF/OVMF_VARS_4M.ms.fd"
OVMF_VARS="$(basename "${OVMF_VARS_ORIG}")"
if [ ! -e "${QEMU_IMG}" ]; then
qemu-img create -f qcow2 "${QEMU_IMG}" 20G
fi
if [ ! -e "${OVMF_VARS}" ]; then
cp "${OVMF_VARS_ORIG}" "${OVMF_VARS}"
fi
qemu-system-x86_64 \
-enable-kvm \
-cpu host -smp cores=4,threads=1 -m 4096 \
-object rng-random,filename=/dev/urandom,id=rng0 \
-device virtio-rng-pci,rng=rng0 \
-name "${MACHINE_NAME}" \
-drive file="${QEMU_IMG}",format=qcow2 \
-net nic,model=virtio -net user,hostfwd=tcp::${SSH_PORT}-:22 \
-vga virtio \
-machine q35,smm=on \
-global driver=cfi.pflash01,property=secure,value=on \
-drive if=pflash,format=raw,unit=0,file="${OVMF_CODE}",readonly=on \
-drive if=pflash,format=raw,unit=1,file="${OVMF_VARS}" \
$@
EOFAnd make it executable:
$ chmod a+x start-vm
This script will automatically create a QEMU image (default: test.img) and create a local copy of the OVMF vars section (which stores writeable UEFI variables). Additional parameters for QEMU can be passed to the script on execution.
Launch the VM for the first time, with the installation media:
$ ./start-vm -cdrom ./debian-*-amd64-netinst.iso -boot menu=on
For subsequent boots, the installation media and boot menu delay are no longer necessary, so simply launch the VM as follows :
$ cd ~/secureboot-vm $ ./start-vm
Explanation
The last five lines of the qemu-system-x86_64 invocation are the important ones:
-vga virtio - The Debian installer seems to have difficulties working with the standard VGA driver (and virtio should anyway have better performance)
-machine q35,smm=on - A machine type which supports SecureBoot
-global driver=cfi.pflash01,property=secure,value=on - Add a driver for virtual pflash drives containing the firmware
-drive if=pflash,format=raw,unit=0,file="${OVMF_CODE}",readonly=on - Create a readonly pflash drive containing the firmware
-drive if=pflash,format=raw,unit=1,file="${OVMF_VARS}" - Create a writeable pflash drive for storage of firmware variables
Passing -boot menu=on to qemu-system-x86_64 means that you will have more time to press ESC during boot to enter the UEFI menu.
Using if=virtio for the first drive stanza should have better performance, but it means that it won't be recognised by the UEFI firmware (it can be enabled after a system has been installed).
Once installed, you'll be able to SSH to the virtual machine by connecting to localhost, port 5555 (e.g. ssh -p5555 user@localhost).
Of course, the start-vm script above is just an example and can be adapted to suit your own needs.
PXE/Netboot
An interesting alternative to the above approach is to boot qemu in combination with PXE/Netboot, while having secure boot enabled.
First, create a suitable tftp hierarchy (adjust package versions, etc, as necessary):
$ mkdir tftp
$ cd tftp
$ apt download shim-signed
$ dpkg-deb --fsys-tarfile shim-signed_*.deb | tar x ./usr/lib/shim/shimx64.efi.signed > shimx64.efi
$ apt download grub-efi-amd64-signed
$ dpkg-deb --fsys-tarfile grub-efi-amd64-signed_*.deb | tar x ./usr/lib/grub/x86_64-efi-signed/grubnetx64.efi.signed -O > grubx64.efi
$ rm *.deb
$ wget http://ftp.debian.org/debian/dists/trixie/main/installer-amd64/current/images/netboot/debian-installer/amd64/linux
$ wget http://ftp.debian.org/debian/dists/trixie/main/installer-amd64/current/images/netboot/debian-installer/amd64/initrd.gz
$ mkdir grub
$ cat > grub/grub.cfg <<\EOF
set default=0
set timeout=60
menuentry 'Install Debian' {
linux linux <add preseed commands or other interesting things here>
initrd initrd.gz
}
EOF
$ ls -1R
.:
grub
grubx64.efi
initrd.gz
linux
shimx64.efi
./grub:
grub.cfg
$ cd ..Second, change the start-vm script above, replacing this part:
-net nic,model=virtio -net user,hostfwd=tcp::${SSH_PORT}-:22 \with this:
-net user,hostname=test,domainname=example.com,tftp=./tftp,bootfile=shimx64.efi,hostfwd=tcp::5555-:22 \
And launch the VM, as the newly created qemu drive isn't bootable, the VM will continue with PXE booting instead:
$ ./start-vm
Alternatively: using virt-manager
It is really easy to activate secure boot by using virt-manager, ovmf and qemu-system-x86:
Choose Customize configuration before install
Choose Q35 as Chipset and OVMF_CODE_4M.ms.fd as Firmware
- That's all!
Checking if secure boot is active
mokutil
You can check if secure boot is enabled (with root access) using mokutil:
$ mokutil --sb-state SecureBoot enabled
bootctl
You can also check if secure boot is enabled by using bootctl:
$ sudo bootctl
systemd-boot not installed in ESP.
No default/fallback boot loader installed in ESP.
System:
Firmware: n/a (n/a)
Secure Boot: enabled
...
dmesg
When Linux has been booted with secure boot, dmesg should print:
secureboot: Secure boot enabled Kernel is locked down from EFI Secure Boot mode
Otherwise secure boot is not activated.
