21x9.org

Win11 mit QEMU (und Proxmox)

Categories: [blog]
Tags: [linux], [windows 11], [debian], [kvm], [qemu], [proxmox], [tpm], [vtpm], [libtpms], [swtpm], [debuild]

Windows 11 in einer virtuellen QEMU Umgebung zu betreiben, benötigt leider etwas Vorarbeit, da Windows 11 ja ein aktives TPM voraussetzt. Dies steht normalerweise in einer KVM nicht zur Verfügung. Aber dank swtpm existiert eine Lösung für dieses Problem.

Leider ist swtpm für Debian Buster ebenso wenig standardmäßig verfügbar wie für Bullseye. Also ist selbst bauen angesagt.

Die Quellen für swtpm gibt es unter: https://github.com/stefanberger/swtpm

Um swtpm bauen zu können ist aber zunächst libtpms notwendig. Die entsprechenden Quellen gibt es unter: https://github.com/stefanberger/libtpms

Also beginnen wir damit libtpms zu bauen. Erst einmal installieren wir die nötigen Abhängigkeiten und das Build-Environment:

apt install -y build-essential devscripts equivs automake autoconf bash coreutils expect libtool sed fuse net-tools python3 python3-twisted selinux-policy-dev socat trousers gnutls-dev libssl-dev libtasn1.6-dev libtasn1-bin expect libelf1 libevent-2.1-6 libfuse-dev libfuse2 libglib2.0-bin libglib2.0-dev libglib2.0-dev-bin libgmp-dev libgmpxx4ldbl libgnutls-dane0 libgnutls-openssl27 libgnutls28-dev libgnutlsxx28 libidn2-0-dev libidn2-dev libp11-kit-dev libpcre16-3 libpcre3-dev libpcre32-3 libpcrecpp0v5 libselinux1-dev libsepol1-dev libtasn1-6-dev libtcl8.6 libtpm-unseal1 libtspi1 libwrap0 nettle-dev pkg-config python3-asn1crypto python3-attr python3-automat python3-cffi-backend python3-constantly python3-cryptography python3-distutils python3-hyperlink python3-incremental python3-lib2to3 python3-openssl python3-pyasn1 python3-pyasn1-modules python3-service-identity python3-twisted python3-twisted-bin python3-zope.interface socat tcl-expect tpm-tools trousers tzdata zlib1g-dev libjson-glib-dev libseccomp-dev dh-exec unp zip

Hinweis: Ich verwende ab hier die Dateinamen und daraus resultierenden Pfade, der - zum Zeitpunkt der Erstellung dieses Dokuments - aktuellsten Versionen der jeweiligen Quellcodes. Wahrscheinlich sind in der Zwischenzeit neuere Quellen verfügbar und sollten statt dieser hier verwendet werden. Eventuell kann es hierdurch erforderlich sein, weitere Abhängigkeiten zu installieren. Außerdem sind natürlich die nachfolgenden Pfad- und Dateinamen entsprechend anzupassen.

Dann können wir die libtpms-Quellen laden und entpacken:

wget https://github.com/stefanberger/libtpms/archive/refs/tags/v0.9.0.zip && unp v0.9.0.zip && cd libtpms-0.9.0

Nun compilieren wird den Quelltext und installieren die Bibliothek:

./autogen.sh --prefix=/usr --with-tpm2 --with-openssl
make
make check
sudo make install

Da ich das Ganze später in einer Proxmox-Umgebung mit mehreren Servern betreiben möchte, mache ich mir noch schnell Debian-Pakete aus den Quellen, damit ich mir weitere Kompilierungsaktionen sparen kann und eine saubere Installation/Deinstallation der Bibliothek garantieren kann. Im Grunde könnte man sich hierdurch auch das make install sparen, ich mache es aber in der Regel trotzdem um zu schauen ob wirklich alles funktioniert.

debuild -us -uc

Das Ergebnis sollten drei dep-Pakete im übergeordneten Verzeichnis sein:

gpkvt@buildenv:~/libtpms-0.9.0# ls ../libtpms*.deb
../libtpms0_0.9.0_amd64.deb         ../libtpms-dev_0.9.0_amd64.deb
../libtpms0-dbgsym_0.9.0_amd64.deb

Auf den Server benötigen wir nur libtpms0_0.9.0_amd64.deb, wir werden es dort später installieren. Zunächst kümmern wir uns um swtpm selbst:

wget https://github.com/stefanberger/swtpm/archive/refs/tags/v0.6.1.zip && unp v0.6.1.zip && cd cd swtpm-0.6.1
./autogen.sh --prefix=/usr
make
make check
sudo make install

Auch hier bauen wir Debian-Pakete, damit dies klappt müssen wir jedoch eine kleine Anpassung in ./debian/rules vornehmen, da unser libtpms Paket keine korrekte Auflistung der Abhängigkeiten enthält:

cat << EOF >> ./debian/rules
override_dh_shlibdeps:
        dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info
EOF
sed -i 's/        /\t/g' \\t debian/rules

sed ist an dieser Stelle nötig, da debuild zwingend ein Tab vor dh_shlibdeps erwartet, aber cat kann dies nicht erzeugen.

Bevor wir die swtpm-Pakete erstellen können, müssen wir noch weitere Abhängigkeiten installieren:

apt install -y gnutls-bin softhsm2
dpkg -i ../libtpms-dev_0.9.0_amd64.deb

Nun können wir die Debian-Pakete für swtpm erzeugen:

debuild -us -uc

Das Ergebnis sollten folgende Pakete sein:

gpkvt@buildenv:~/swtpm-0.6.1# ls ../swtpm*.deb
../swtpm_0.6.1_amd64.deb         ../swtpm-libs-dbgsym_0.6.1_amd64.deb
../swtpm-dbgsym_0.6.1_amd64.deb  ../swtpm-tools_0.6.1_amd64.deb
../swtpm-dev_0.6.1_amd64.deb     ../swtpm-tools-dbgsym_0.6.1_amd64.deb
../swtpm-libs_0.6.1_amd64.deb

Nun haben wir alle benötigten Pakete zusammen und können diese auf die gewünschten Server aufspielen und mittels dpkg installieren. Benötigt werden hierbei lediglich folgende Pakete:

Am einfachsten geht die Installation, wenn wir alle 4 Pakete in ein gemeinsames Verzeichnis auf den Zielservern kopieren, dann können sie einfach mit dpkg -i *.deb installiert werden:

root@hosting1:~/tpms# dpkg -i *.deb
(Reading database ... 90170 files and directories currently installed.)
Preparing to unpack libtpms0_0.9.0_amd64.deb ...
Unpacking libtpms0:amd64 (0.9.0) over (0.9.0) ...
Selecting previously unselected package swtpm.
Preparing to unpack swtpm_0.6.1_amd64.deb ...
Unpacking swtpm (0.6.1) ...
Selecting previously unselected package swtpm-libs:amd64.
Preparing to unpack swtpm-libs_0.6.1_amd64.deb ...
Unpacking swtpm-libs:amd64 (0.6.1) ...
Selecting previously unselected package swtpm-tools.
Preparing to unpack swtpm-tools_0.6.1_amd64.deb ...
Unpacking swtpm-tools (0.6.1) ...
Setting up libtpms0:amd64 (0.9.0) ...
Setting up swtpm-libs:amd64 (0.6.1) ...
Setting up swtpm (0.6.1) ...
Setting up swtpm-tools (0.6.1) ...
Processing triggers for libc-bin (2.28-10) ...
Processing triggers for man-db (2.8.5-2) ...

Anschließend sollte der Befehl swtpm zur Verfügung stehen. Ein swtpm --version sollte folgenden Output liefern:

TPM emulator version 0.6.1, Copyright (c) 2014-2021 IBM Corp.

Ist dies geschafft, können wir uns daran machen eine VM mit TPM zu erzeugen, erst einmal erzeugen wir eine Imagedatei und fügen ein zuvor heruntergeladenes Win11 ISO hinzu:

qemu-img create -f qcow2 win11.img 64G
qemu-system-x86_64 -hda ~/qemu-images/win11.img -boot d -cdrom ~/Downloads/win11.iso -m 4096 -enable-kvm

Nun aber zum TPM (und Secure Boot):

mkdir /var/run/emulated_tpm
swtpm socket --tpmstate dir=/var/run/emulated_tpm --ctrl type=unixio,path=/var/run/emulated_tpm/swtpm-sock --log level=20 --tpm2 &

Hat alles geklappt sollten wir die VM nun starten können:

qemu-system-x86_64 -hda ~/qemu-images/win11.img -boot d -m 8192 -enable-kvm \
  -chardev socket,id=chrtpm,path=/var/run/emulated_tpm/swtpm-sock \
  -tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0

Alternativ kann die VM natürlich auch wie gewohnt über Proxmox (oder vergleichbare Oberflächen) erzeugt werden. Allerdings bietet zumindest Proxmox 6.4 derzeit keine Oberflächenoption für die TPM-Einbindung an. Die nötige Option kann aber über den args-Parameter in der VM-Config angegeben werden. Ein offizieller vTPM-Support für Proxmox ist bereits in Arbeit.

Die Config-Dateien finden sich in /etc/pve/qemu-server/, die Dateien sind nach der ID der VM benannt, z.B. 300.conf:

agent: 1
bios: ovmf
boot: order=sata0;ide2;net0
cores: 2
efidisk0: NAS:300/vm-300-disk-1.qcow2,size=128K
ide2: NAS:iso/Windows-11-21H2-x64.iso,media=cdrom
machine: pc-q35-5.2
memory: 8192
name: win11-1
net0: e1000=D6:F0:A9:BB:60:FC,bridge=vmbr0,firewall=1
numa: 0
ostype: win10
sata0: NAS:300/vm-300-disk-0.qcow2,size=128G
scsihw: virtio-scsi-pci
smbios1: uuid=ffc7d255-eed0-44ea-8b8b-ebfa0977d0c7
sockets: 2
vmgenid: f1e57733-7e10-41f6-bf46-38b9850b3fbb
args: -chardev socket,id=chrtpm,path=/var/run/emulated_tpm/swtpm-sock -tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0

Wichtig ist hierbei als BIOS OVMF zu nutzen, hierbei sollte automatisch eine EFI-Disk erzeugt werden. Diese benötigt Win11 für Secure-Boot. Außerdem muss bei Machine auf q35 gewechselt werden, da sonst die Hardwareanforderungen nicht den Mindestanforderungen entsprechen.

2021-10-13%2017_20_32-hosting1%20-%20Proxmox%20Virtual%20Environment%20-%20Vivaldi

Nun können wir die VM starten, nachdem der Anykey für das Booten von der DVD gedrückt wurde, kann es sein dass das Proxmox Logo für relativ lange Zeit unverändert angezeigt wird. Hier einfach warten, irgendwann startet der Windows 11 Installer.

Ein Problem bleibt jedoch. Wenn die VM gestoppt wird, beendet sich auch swtpm und stellt den nötigen Socket nicht mehr bereit. Daher schlägt ein erneuter Start der VM fehl, zumindest so lange bis swtpm socket --tpmstate dir=/var/run/emulated_tpm --ctrl type=unixio,path=/var/run/emulated_tpm/swtpm-sock --log level=20 --tpm2 & erneut ausgeführt wird. Ein - wenn auch kruder - Workaround wäre z.B. eine Konfiguration für supervisord zu erstellen, die dafür sorgt, dass swtpm immer wieder automatisch neu gestartet wird.

Unter Proxmox 7 wird alles deutlich einfacher, hier gibt es im Erstellungsdialog einer VM unter System die Option Add TPM. Allerdings läuft Proxmox 7 erst ab Debian Bullseye. Allein hierfür lohnt sich das Update.