Continuing on this foolish path to manage some virtual machines on a remote host with TLS and peer authentication, here I'm going to configure VNC.

The trick here is we're really configuring qemu not libvirtd as qemu itself provides the VNC server. On my Debian system the default configuration file is /etc/libvirt/qemu.conf. By default, TLS for VNC is not enabled.

To listen on all interfaces, use TLS and do peer authentication the following options need to be set.

vnc_listen = "0.0.0.0"
vnc_tls = 1
vnc_tls_x509_verify = 1

The default certificates directories are either the default qemu certificate directory.

/etc/pki/qemu/ca-cert.pem
/etc/pki/qemu/server-cert.pem
/etc/pki/qemu/server-key.pem

Or the qemu VNC certificate directory.

/etc/pki/libvirt-vnc/ca-cert.pem
/etc/pki/libvirt-vnc/server-cert.pem
/etc/pki/libvirt-vnc/server-key.pem

I chose to copy them to the latter.

VNC ports are allocated by qemu starting a port 5900 by default. I'm going to assume these are reused when either a domain is destroyed or deleted and allowed the port range 5900:5910 through the firewall.

I could only find limited documentation about where virt-manager or virt-viewer expects the certificates to be installed for making VNC connections. I ended up using strace to see where it was trying to read them from.

I was a bit surprised to find they actually expect the client certificates in different paths. Here is where virt-manager reads certificates.

~/.pki/CA/cacert.pem
~/.pki/libvirt-vnc/clientcert.pem
~/.pki/libvirt-vnc/private/clientkey.pem

And virt-viewer here, which partially overlaps where libvirt reads its certificates.

~/.pki/CA/cacert.pem
~/.pki/libvirt/clientcert.pem
~/.pki/libvirt/private/clientkey.pem

After copying the missing certificates we should be ready to go.

To create a new virtual machines with a graphical installer, which is what I wanted to do, I still need to download the installer ISO disk image on the server.

$ wget https://channels.nixos.org/nixos-23.05/latest-nixos-gnome-x86_64-linux.iso -P /var/kvm/installers/

It's now possible to create a new virtual machine with virt-manager and then use a graphical installer to install an operating system through a VNC display. 1 2 3 4 5 6

I can also script the virtual machine creation with virt-install which will launch the virt-viewer VNC client. A list of possible --os-variant values can be found run running virt-install --osinfo list.

$ virt-install \
  --connect 'qemu+tls://hypervisor.tarnbarford.net:16514/system' \
  --name demo \
  --memory 2048 \
  --vcpus 2 \
  --disk size=20 \
  --cdrom /var/kvm/installers/latest-nixos-gnome-x86_64-linux.iso \
  --boot cdrom,hd \
  --os-variant nixos-22.05 \
  --graphics vnc

Starting install...
Allocating 'demo.qcow2'                                                                                                    | 3.1 MB  00:00:03 ...
Creating domain...                                                                                                         |    0 B  00:00:00
Running graphical console command: virt-viewer --connect qemu+tls://hypervisor.tarnbarford.net:16514/system --wait demo

Bam! A virt-viewer window appears displying the install media boot screen.

This is pretty great, I can now quickly and easily create new virtual machines on the server and immediately connect to their display and input devices using VPN client.

I'm still not fully satisfied, when I create virtual machines on my laptop I always prefer SPICE over VPN. The main reason is that SPICE has a feature "Redirect USB device" which works really well for many of my workflows that rely on my USB security key. The reason I didn't setup SPICE initially is that it doesn't support TLS peer authentication (!). I have an idea how I could make this work which I'd like to try, but I'll leave that for another time.