Improving QEMU security part 7: TLS support for migration
This blog is part 7 of a series I am writing about work I’ve completed over the past few releases to improve QEMU security related features.
The live migration feature in QEMU allows a running VM to be moved from one host to another with no noticeable interruption in service and minimal performance impact. The live migration data stream will contain a serialized copy of state of all emulated devices, along with all the guest RAM. In some versions of QEMU it is also used to transfer disk image content, but in modern QEMU use of the NBD protocol is preferred for this purpose. The guest RAM in particular can contain sensitive data that needs to be protected against any would be attackers on the network between source and target hosts. There are a number of ways to provide such security using external tools/services including VPNs, IPsec, SSH/stunnel tunnelling. The libvirtd daemon often already has a secure connection between the source and destination hosts for its own purposes, so many years back support was added to libvirt to automatically tunnel the live migration data stream over libvirt’s own secure connection. This solved both the encryption and authentication problems at once, but there are some downsides to this approach. Tunnelling the connection means extra data copies for the live migration traffic and when we look at guests with RAM many GB in size, the number of data copies will start to matter. The libvirt tunnel only supports a tunnelling of a single data connection and in future QEMU may well wish to use multiple TCP connections for the migration data stream to improve performance of post-copy. The use of NBD for storage migration is not supported with tunnelling via libvirt, since it would require extra connections too. IOW while tunnelling over libvirt was a useful short term hack to provide security, it has outlived its practicality.
It is clear that QEMU needs to support TLS encryption natively on its live migration connections. The QEMU migration code has historically had its own distinct I/O layer called QEMUFile which mixes up tracking of migration state with the connection establishment and I/O transfer support. As mentioned in previous blog post, QEMU now has a general purpose I/O channel framework, so the bulk of the work involved converting the migration code over to use the QIOChannel classes and APIs, which greatly reduced the amount of code in the QEMU migration/
sub-folder as well as simplifying it somewhat. The TLS support involves the addition of two new parameters to the migration code. First the “tls-creds
” parameter provides the ID of a previously created TLS credential object, thus enabling use of TLS on the migration channel. This must be set on both the source and target QEMU’s involved in the migration.
On the target host, QEMU would be launched with a set of TLS credentials for a server endpoint:
$ qemu-system-x86_64 -monitor stdio -incoming defer \ -object tls-creds-x509,dir=/home/berrange/security/qemutls,endpoint=server,id=tls0 \ ...other args...
To enable incoming TLS migration 2 monitor commands are then used
(qemu) migrate_set_str_parameter tls-creds tls0 (qemu) migrate_incoming tcp:myhostname:9000
On the source host, QEMU is launched in a similar manner but using client endpoint credentials
$ qemu-system-x86_64 -monitor stdio \ -object tls-creds-x509,dir=/home/berrange/security/qemutls,endpoint=client,id=tls0 \ ...other args...
To enable outgoing TLS migration 2 monitor commands are then used
(qemu) migrate_set_str_parameter tls-creds tls0 (qemu) migrate tcp:otherhostname:9000
The migration code supports a number of different protocols besides just “tcp:
“. In particular it allows an “fd:
” protocol to tell QEMU to use a passed-in file descriptor, and an “exec:
” protocol to tell QEMU to launch an external command to tunnel the connection. It is desirable to be able to use TLS with these protocols too, but when using TLS the client QEMU needs to know the hostname of the target QEMU in order to correctly validate the x509 certificate it receives. Thus, a second “tls-hostname
” parameter was added to allow QEMU to be informed of the hostname to use for x509 certificate validation when using a non-tcp migration protocol. This can be set on the source QEMU prior to starting the migration using the “migrate_set_str_parameter
” monitor command
(qemu) migrate_set_str_parameter tls-hostname myhost.mydomain
This feature has been under development for a while and finally merged into QEMU GIT early in the 2.7.0 development cycle, so will be available for use when 2.7.0 is released in a few weeks. With the arrival of the 2.7.0 release there will finally be TLS support across all QEMU host services where TCP connections are commonly used, namely VNC, SPICE, NBD, migration and character devices.
In this blog series:
Dear Daniel,
When I was trying to use TLS in QEMU, I came up with a few questions. As you described in this doc, TLS parameters are specified as an object in
the QEMU command line:
-object tls-creds-509,id=id,endpoint=endpoint,dir=/path/to/cred/dir …
of which “endpoint” is a type of “QCryptoTLSCredsEndpoint” and can be
either a “server” or a “client”.
My questions:
– When a VM is started with this config, is there a way (e.g. QMP) to
change the value of “endpoint”? If possible, how to do this? or else after the first migration of a VM,the VM has “endpoint=server”, which can’t be migrated without stop / start.
– In which case does the QEMU reload its TLS certificate, e.g. when a QEMU VM has been run longer than the valid period of its TLS certificate?
– The migration is done by using HMP monitor on both source and target
side. Is it possible to do it by using QMP commands?
Furthermore, I saw that this doc has been for long. Is there any updated besides the QEMU repo?
Thank you so much and best wishes,
Yu Zhang @ Compute Platform IONOS