Skip to main content

Testing Libvirt Over TLS

libvirt is a technology by RedHat that implements a single interface for different types of virtualization methods. Personally I am using it for kvm/qemu and LXC but the list on their website is much larger and includes UML), Xen, OpenVZ and even VMWare ESX and GSX hypervisors.

If you are using KVM directly, you will construct the command line manually and will eventually end up with something like this:

/usr/bin/kvm -S -M pc-1.0 -cpu qemu32 -enable-kvm -m 1024 \
-smp 4,sockets=4,cores=1,threads=1 \
-name plum -uuid 1da8c95b-0586-d41a-8c64-760034aa2453 \
-nodefconfig -nodefaults \
-chardev socket,id=charmonitor,path=/var/lib/libvirt/qemu/plum.monitor,server,nowait \
-mon chardev=charmonitor,id=monitor,mode=control -rtc base=utc -no-shutdown \
-drive file=/dev/vg0/vm-nfs-test,if=none,id=drive-virtio-disk0,format=raw\
...

I've been there and wrote scripts to help maintaining running kvm instances. For a small number of options that's OK. But interacting with the instances through qemu monitor quickly became very tedious.

When I found out about libvirt I was really excited. Well, I am still excited :)

The most user-visible part of libvirt is virt-manager which provides the GUI for libvirt API. It looks like this:

virt-manager UI

It allows creating, reconfiguring, cloning and deleting virtual machines and storage, configure virtual networks and control instance power states – almost everything you need.

Another client is a CLI application called virsh:

virsh # list
Id Name                 State
----------------------------------
4 plum                 running
5 WIN7PNX64EN          running
6 fedora-12            running

It allows modifying the VM description directly (that's an XML file), managing the storage pool... Well, pretty much everything that virt-manager does and a little bit more, such as snapshotting the VM (warning: I am currently investigating the corruption happening with qcow2 disks but have not yet obtained any results).

libvirt is a client library. The server side is implemented by libvirtd. libvirtd can be connected to using Unix domain sockets or remotely via TCP.

To make contacting virtual hosts easier, the clients can connect to remote server through SSH. Basically client creates a SSH session, starts netcat on the remote end and makes it relay the commands from the local end to the Unix domain socket, in this case you will see the following in the process list on the server:

sh -c if nc -q 2>&1 | grep "requires an argument" >/dev/null 2>&1; then \
ARG=-q0;else ARG=;fi;nc $ARG -U /var/run/libvirt/libvirt-sock

The grep is needed because there are 2 versions of netcat in existence, netcat-openbsd and netcat-traditional, each with a bit different options.

The user account on the server should have access to /var/run/libvirt/libvirt-sock file. The easiest thing to make this on Ubuntu is to add the user account to libvirtd group. In Fedora it is customary to use root account and allow root to log in via SSH (which I will [never](/blog/2009/06/permitrootlogin-yes-is-default-value/) do again).

For remote virtualization servers the SSH access has another benefit, you can use VNC console without exposing the ports to an outside world. In this case upon starting the virt-viewer or using graphical console from virt-manager a new SSH connection is being created with...

sh -c nc -q 2>&1 | grep "requires an argument" >/dev/null; \
    if [ $? -eq 0 ] ; then   CMD="nc -q 0 127.0.0.1 5906"; \
    else   CMD="nc 127.0.0.1 5906";fi;eval "$CMD";

That allows kvm to listen on loopback interface only, while still being accessible to somebody with a shell account on the VM server.

Now this is all nice but using SSH has one drawback, it requires a shell account on the server which may not always be good. Additionally every request to virsh requires the SSH session to be set up and then torn down which adds to the total time of virsh command running. And when I got 10 virtual machines, volume listing in virt-manager became really slow, which I attributed completely to the SSH session. So I decided to set up everything for TLS connection and compare the speed of qemu+tls vs qemu+ssh.

I am not using SASL authentication and rely on TLS certificates only.

So, connecting to TLS libvirtd faster? Yes. Is virt-manager working faster? No.

First of all I followed the detailed instruction at libvirt wiki, creating all the necessary keys and certificates. Since I wanted only one account to become administrative one and not the whole host (even though I am the only user on the laptop), I copied the clientcert.pem, clientkey.pem and cacert.pem to ~/.pki/libvirt/.

Then I added -l to the list of libvirtd options on the server in /etc/default/libvirt-bin and restarted the service:

restart libvirt-bin
$ time virsh -c qemu+ssh://fridge.lappyfamily.net/system list
Id Name                 State
----------------------------------
4 plum                 running
6 fedora-12            running

real    0m0.798s
user    0m0.040s
sys 0m0.012s

$ time virsh -c qemu://fridge.lappyfamily.net/system list
Id Name                 State
----------------------------------
4 plum                 running
6 fedora-12            running

real    0m0.134s
user    0m0.052s
sys 0m0.008s

Yes! It is indeed faster! (qemu+tls:// is the same as qemu:// alone)

Checking virt-manager proved that while connecting is indeed faster, all other operations are carried over an existing ssh connection and take exactly the same amount of time, and that needs to be investigated in the code.

Remember that VNC connection is also proxied via SSH? VNC is gone for virt-manager as it relies on proxying. No SSH - no VNC.

virt-manager error

You can get VNC console back if you reconfigure the VNC server to listen on all interfaces. While virt-manager still can't connect to the graphical console, virt-viewer works perfectly.

So, having performed all these steps, I can say it was not really worth it but I will definitely continue using the TLS connection because of initial speed-up. You will want to use TLS connections under very particular circumstances where using SSH may add an additional point of failure (such as automated provisioning of virtual machines) or when you don't want to expose SSH to an external world and still be able to connect to libvirtd (with TLS your traffic is both encrypted and authenticated using SSL). Using SASL will allow to have libvirt-specific accounts separate from your server accounts and that may be useful sometimes too.

Serial console

Now my remote libvirt server is accessible via SSH and TLS socket and, since VNC is not available, I started to look into making the linux virtual machines log to serial console as it is accessible over TLS socket. That's actually quite easy for kernel and getty on Ubuntu, haven't found a way to make grub cooperate yet:

Edit /etc/default/grub and alter the GRUB_CMDLINE_LINUX_DEFAULT to contain the consoles you want the boot messages to appear on:

GRUB_CMDLINE_LINUX_DEFAULT="console=tty0 console=ttyS0"

Run update-grub for the changes to take effect. This command will combine all the files needed to bring grub up and write /boot/grub/grub.cfg file. Never modify that file directly because the changes will be overwritten.

Now we run a login session on ttyS0 with this /etc/init/ttyS0.conf upstart file:

# ttyS0 - getty
#
# This service maintains a getty on tty1 from the point the system is
# started until it is shut down again.

start on stopped rc RUNLEVEL=[2345] and (
            not-container or
            container CONTAINER=lxc or
            container CONTAINER=lxc-libvirt)

stop on runlevel [!2345]

respawn
exec /sbin/getty -8 38400 ttyS0

start ttyS0 to make console appear and run virsh console:

$ virsh console plum
Connected to domain plum
Escape character is ^]
*press <Enter>*
Ubuntu 12.04 LTS plum ttyS0

plum login:

Upon reboot the console will have all the info the kernel prints out.

$ virsh console plum
Connected to domain plum
Escape character is ^]

Ubuntu 12.04 LTS plum ttyS0

plum login: rtg
Password:
[...]
rtg@plum:~$ sudo reboot
[...]
* Will now restart
[  507.815197] Restarting system.
[    0.000000] Initializing cgroup subsys cpuset
[    0.000000] Initializing cgroup subsys cpu
[    0.000000] Linux version 3.2.0-24-generic-pae (buildd@vernadsky) (gcc
version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5) ) #37-Ubuntu SMP Wed Apr 25
10:47:59 UTC 2012 (Ubuntu 3.2.0-24.37-generic-pae 3.2.14)
[...]

Nice, huh?