kvm: encrypt the KVM live-migration data stream with QEMU-native TLS (VIR_MIGRATE_TLS) - #1
Draft
calvix wants to merge 1 commit into
Draft
kvm: encrypt the KVM live-migration data stream with QEMU-native TLS (VIR_MIGRATE_TLS)#1calvix wants to merge 1 commit into
calvix wants to merge 1 commit into
Conversation
CloudStack's secure KVM live migration only encrypts the libvirt control channel; the guest RAM (and, for storage migration, non-shared disk) data stream is transferred in plaintext over TCP. This adds an opt-in QEMU-native TLS path (VIR_MIGRATE_TLS) for the migration data stream, reusing the certificates the CA framework already provisions - mirroring the existing VNC-TLS pattern. - New zone-scoped global setting 'kvm.migrate.tls' (default false) on StorageManager; registered by StorageManagerImpl. - The agent advertises the 'host.migrate.tls' capability in StartupRoutingCommand when the QEMU migration certificates exist under /etc/pki/qemu and qemu.conf sets migrate_tls_x509_cert_dir. - The management server sets MigrateCommand.migrateTls only when the setting is enabled for the zone and BOTH the source and destination hosts advertise support; otherwise it silently falls back to the plaintext data stream, so mixed / partially-upgraded fleets keep migrating (VirtualMachineManagerImpl and StorageSystemDataMotionStrategy). - MigrateKVMAsync ORs in VIR_MIGRATE_TLS; the data URI stays "tcp:" because libvirt has no "tls:" migration URI scheme (valid: tcp/rdma/unix/fd) - TLS is negotiated over the normal tcp: connection. - Agent scripts provision the /etc/pki/qemu certificate set (keystore-cert-import) and write the qemu.conf migrate_tls_* keys (serviceConfig.py). - Unit test asserts the data URI stays "tcp:" when migrateTls is enabled. - PendingReleaseNotes entry. Verified end-to-end on a 3-node KVM/Ceph dev cluster (libvirt 12): a live migration between two secured hosts shows a TLS 1.2 handshake and ciphertext on the data ports (no QEVM magic / no legible guest RAM), with migration still succeeding.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
CloudStack's Secure Live VM Migration for KVM (CLOUDSTACK-10333 / apache#2505) secures the libvirt
control channel (
qemu+tls://, port 16514) between secured hosts. However, the actual migrationdata stream — the guest's memory, and for storage migration the non-shared disk — is still
transferred in plaintext over TCP (libvirt/QEMU data ports 49152+). So on the migration network the
guest RAM is exposed to an L2 eavesdropper / MITM, which is exactly the "leakage of VM state and
memory" that the original secure-migration feature set out to prevent.
This PR adds an opt-in path that encrypts and mutually-authenticates the migration data stream
using QEMU-native TLS (
VIR_MIGRATE_TLS), reusing the certificates the CA framework alreadyprovisions for the host — mirroring the existing VNC-TLS pattern. It does not change any default
behaviour: with the new setting off (the default), migration is byte-for-byte what it is today.
How it works
kvm.migrate.tls(defaultfalse), onStorageManager.host.migrate.tlscapability inStartupRoutingCommandwhen the QEMUmigration certificates exist under
/etc/pki/qemuandqemu.confsetsmigrate_tls_x509_cert_dir.MigrateCommand.migrateTlsonly when the setting is enabled for thezone and both the source and destination host advertise
host.migrate.tls— otherwise itsilently falls back to the plaintext data stream, so mixed / partially-upgraded fleets keep
migrating (
VirtualMachineManagerImpl,StorageSystemDataMotionStrategy).MigrateKVMAsyncOR-s inVIR_MIGRATE_TLS(flag65536). The data URI staystcp:—libvirt has no
tls:migration URI scheme (valid schemes aretcp/rdma/unix/fd); TLS isnegotiated over the normal
tcp:connection, only the flag changes. (This was a real trap duringdevelopment: setting the URI to
tls:fails with "unsupported scheme tls in migration URI".)keystore-cert-importcreates the/etc/pki/qemucert set (symlinking theagent cert, exactly like the existing VNC set), and
serviceConfig.pywrites theqemu.confmigrate_tls_x509_cert_dir+migrate_tls_x509_verify=1keys.Operations covered
kvm.migrate.tls=false(default)host.migrate.tlsEnabling it (operator steps)
addHostdoes this automatically; existing hosts via"Deploy Host Keys" (the CA-framework
provisionCertificate), which now also lays down/etc/pki/qemu.kvm.migrate.tls=truefor the zone.Types of changes
Feature/Enhancement Scale or Bug Severity
Feature/Enhancement Scale
Screenshots (if appropriate):
N/A — agent/orchestration change, no UI (the setting appears under Global Settings).
How Has This Been Tested?
Environment: 3-node KVM + Ceph dev cluster, Ubuntu 26.04, libvirt 12.0.0, QEMU 8.2,
management 4.22.1.0, hosts already secured via the CA framework.
Unit test —
MigrateKVMAsyncTestasserts the migration data URI staystcp:whenmigrateTlsis enabled (guarding the "don't use atls:URI" trap) and that the flag is set.End-to-end on the dev cluster — live-migrated a running guest between two secured hosts with
kvm.migrate.tls=trueand captured the data ports on the destination:tcpdump -i any 'tcp portrange 49152-49215 and host <srcIP>' -w mig.pcap.strings mig.pcap | grep -c QEVM→ non-zero, legible guestRAM strings present → plaintext.
QEVMmagic count = 0, no legible RAM strings, aTLS 1.2 handshake is visible on the data connection, and the migration still succeeds.
Non-shared disk (storage) migration — separately verified the disk (NBD) stream is also
encrypted, reproducing CloudStack's exact libvirt invocation (
VIR_MIGRATE_TLSwith the data URIset to the destination IP, i.e.
VIR_MIGRATE_PARAM_URI = "tcp:"+destIpasMigrateKVMAsyncdoes) between two secured hosts, on a domain whose local raw disk was pre-filled with a unique
marker and captured with
--copy-storage-all:--tlsoff): the disk marker appears 43,668× in the captured NBD stream,alongside the
QEVMRAM magic — both readable.QEVM0×, TLS records throughout, migration still succeeds.This confirms
VIR_MIGRATE_TLSencrypts both the guest-memory and the non-shared-disk (NBD)streams under the same
migrate_tls_x509_cert_dir— consistent with libvirt/QEMU's TLS-over-NBD(requires libvirt ≥ 4.4 / QEMU ≥ 2.11; the hosts here run libvirt 12 / QEMU 8.2).
The memory and NBD connections both follow the data URI, which CloudStack sets to the destination
host's private IP (
buildMigrateCommand→destination.getHost().getPrivateIpAddress()). The CAframework always issues each host certificate with that private IP as a Subject Alternative Name
(
CAManagerImpl.provisionCertificatepassesprivateIp/publicIp/storageIpas the cert's IP SANs), soQEMU's mutual-TLS verification against the IP always matches — no additional hostname/SAN
configuration is required for this to work.
Mixed fleet — with one host lacking the certs/capability, the management server does not set the
flag and the migration proceeds over plaintext (no failure), confirming the fallback.
How did you try to break this feature and the system with this change?
host.migrate.tls→ falls back to plaintext instead of failing.tls:data URI → reproduced libvirt's "unsupported scheme tls" error; kepttcp:+flag as the correct form (now asserted by the unit test).
/etc/pki/qemu→ capability not advertised → host treated as unsupported.Notes / caveats
fleet is upgraded and host certs are re-provisioned.
back, so this is safe to roll out gradually.
the data stream.