On 2 Oct 2026, at 5:59 PM, Peter Krempa <pkrempa@redhat.com> wrote:
!-------------------------------------------------------------------| CAUTION: External Email
|-------------------------------------------------------------------!
On Mon, Sep 07, 2026 at 08:55:20 +0000, Abhisek Panda wrote:
Libvirt by default attempt to use the TLS-PSK-enabled VM migration, if the both source and destination support the tls-creds-psk object. The source host adds the pre-shared key in the migration cookie if the source supports the tls-creds-psk object. Upon parsing the migration cookie, the destination host checks for the same capability and informs the source host whether to use TLS X.509 or TLS PSK during VM migration via the migration cookie.
For a migration session, Libvirt generates a random key of the specified length, and then stores the content, "qemu:<random key>", at <tls_psk_state_dir>/$ID-$VMNAME/keys.psk on the source host. This is because QEMU's tls-creds-psk object does not accept a raw key string as a parameter, it only accepts a dir argument pointing to a directory from which it can read the key file. Subsequently, it sends the key to destination by embedding it within the migration cookie. The destination's Libvirt extracts the key from the migration cookie. Upon migration completion or any failure, both source and destination Libvirt must delete the directory containing the session's keys.psk.
Signed-off-by: Abhisek Panda <abhisek.panda1@nutanix.com> --- src/qemu/qemu_capabilities.c | 2 + src/qemu/qemu_capabilities.h | 1 + src/qemu/qemu_conf.c | 4 + src/qemu/qemu_conf.h | 1 + src/qemu/qemu_domain.c | 1 + src/qemu/qemu_domain.h | 1 + src/qemu/qemu_driver.c | 6 ++ src/qemu/qemu_migration.c | 98 +++++++++++++++++++ src/qemu/qemu_migration.h | 3 + src/qemu/qemu_migration_cookie.c | 79 ++++++++++++++- src/qemu/qemu_migration_cookie.h | 5 + src/qemu/qemu_process.c | 3 + .../caps_10.0.0_aarch64.xml | 1 + .../caps_10.0.0_ppc64.xml | 1 + .../caps_10.0.0_s390x.xml | 1 + .../caps_10.0.0_x86_64+amdsev.xml | 1 + .../caps_10.0.0_x86_64.xml | 1 + .../caps_10.1.0_s390x.xml | 1 + .../caps_10.1.0_x86_64+inteltdx.xml | 1 + .../caps_10.1.0_x86_64.xml | 1 + .../caps_10.2.0_aarch64.xml | 1 + .../caps_10.2.0_x86_64+mshv.xml | 1 + .../caps_10.2.0_x86_64.xml | 1 + .../caps_11.0.0_aarch64.xml | 1 + .../caps_11.0.0_s390x.xml | 1 + .../caps_11.0.0_x86_64+sgx.xml | 1 + .../caps_11.0.0_x86_64.xml | 1 + .../caps_11.1.0_aarch64.xml | 1 + .../caps_11.1.0_s390x.xml | 1 + .../caps_11.1.0_x86_64.xml | 1 + tests/qemucapabilitiesdata/caps_7.2.0_ppc.xml | 1 + .../caps_7.2.0_x86_64+hvf.xml | 1 + .../caps_7.2.0_x86_64.xml | 1 + .../caps_8.0.0_x86_64.xml | 1 + .../qemucapabilitiesdata/caps_8.1.0_s390x.xml | 1 + .../caps_8.1.0_x86_64.xml | 1 + .../caps_8.2.0_aarch64.xml | 1 + .../caps_8.2.0_armv7l.xml | 1 + .../caps_8.2.0_loongarch64.xml | 1 + .../qemucapabilitiesdata/caps_8.2.0_s390x.xml | 1 + .../caps_8.2.0_x86_64.xml | 1 + .../qemucapabilitiesdata/caps_9.0.0_sparc.xml | 1 + .../caps_9.0.0_x86_64.xml | 1 + .../caps_9.1.0_riscv64.xml | 1 + .../qemucapabilitiesdata/caps_9.1.0_s390x.xml | 1 + .../caps_9.1.0_x86_64.xml | 1 + .../caps_9.2.0_aarch64+hvf.xml | 1 + .../qemucapabilitiesdata/caps_9.2.0_s390x.xml | 1 + .../caps_9.2.0_x86_64+amdsev.xml | 1 + .../caps_9.2.0_x86_64.xml | 1 + tests/qemumigrationcookiexmltest.c | 12 ++- 51 files changed, 249 insertions(+), 5 deletions(-)
diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index bc2b0412dc..3db4710dee 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -776,6 +776,7 @@ VIR_ENUM_IMPL(virQEMUCaps, "arm-smmuv3.cmdqv", /* QEMU_CAPS_ARM_SMMUV3_CMDQV */ "iothread.poll-weight", /* QEMU_CAPS_IOTHREAD_POLL_WEIGHT */ "win-dmp.guest-aware", /* QEMU_CAPS_WIN_DMP_GUEST_AWARE */ + "tls-creds-psk", /* QEMU_CAPS_OBJECT_TLS_CREDS_PSK */ );
@@ -1490,6 +1491,7 @@ struct virQEMUCapsStringFlags virQEMUCapsObjectTypes[] = { { "uefi-vars-sysbus", QEMU_CAPS_DEVICE_UEFI_VARS }, { "monitor-qmp", QEMU_CAPS_OBJECT_MONITOR_QMP }, { "arm-smmuv3", QEMU_CAPS_DEVICE_ARM_SMMUV3 }, + { "tls-creds-psk", QEMU_CAPS_OBJECT_TLS_CREDS_PSK },
Any reason why the capability is introduced? I didn't see it mentioned in our previous conversation.
In case if the support can be compiled out it's okay to add a capability, but new capabilities need to go into a separate commit because they make existing commits really massive and hard to backport.
So if the capability is needed you'll have to split the hunks that add the capability and the detection into a separate commit which will need to preceed this one which makes use of it.
The rationale behind adding this capability was to check if the source and destination’s QEMU supports PSK before attempting to perform PSK-based migration. Since Libvirt’s minimum supported QEMU version is now >= 7.2, and tls-creds-psk objects have been supported since QEMU 3.0, we can safely assume this capability exists on both the source and destination and drop this capability stuff.
diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index 28303fd8f0..1e432cdc41 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -750,6 +750,7 @@ typedef enum { /* virQEMUCapsFlags grouping marker for syntax-check */ QEMU_CAPS_ARM_SMMUV3_CMDQV, /* arm-smmuv3.cmdqv */ QEMU_CAPS_IOTHREAD_POLL_WEIGHT, /* -object iothread.poll-weight */ QEMU_CAPS_WIN_DMP_GUEST_AWARE, /* 'win-dmp' is offered only to a guest that can use it */ + QEMU_CAPS_OBJECT_TLS_CREDS_PSK, /* -object tls-creds-psk */
QEMU_CAPS_LAST /* this must always be the last item */ } virQEMUCapsFlags; diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index cca7776b80..f5c5437c89 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -168,6 +168,7 @@ virQEMUDriverConfig *virQEMUDriverConfigNew(bool privileged, cfg->cacheDir = g_strdup_printf("%s/cache/qemu", root); cfg->libDir = g_strdup_printf("%s/lib/qemu", root); cfg->swtpmStorageDir = g_strdup_printf("%s/lib/swtpm", root); + cfg->tlsPSKStateDir = g_strdup_printf("%s/run/psk", root);
cfg->saveDir = g_strdup_printf("%s/save", cfg->libDir); cfg->snapshotDir = g_strdup_printf("%s/snapshot", cfg->libDir); @@ -187,6 +188,7 @@ virQEMUDriverConfig *virQEMUDriverConfigNew(bool privileged, cfg->stateDir = g_strdup_printf("%s/libvirt/qemu", RUNSTATEDIR); cfg->swtpmStateDir = g_strdup_printf("%s/swtpm", cfg->stateDir); cfg->channelTargetDir = g_strdup_printf("%s/channel", cfg->stateDir); + cfg->tlsPSKStateDir = g_strdup_printf("%s/psk", cfg->stateDir);
cfg->cacheDir = g_strdup_printf("%s/cache/libvirt/qemu", LOCALSTATEDIR);
@@ -214,6 +216,7 @@ virQEMUDriverConfig *virQEMUDriverConfigNew(bool privileged, cfg->stateDir = g_strdup_printf("%s/qemu/run", rundir); cfg->swtpmStateDir = g_strdup_printf("%s/swtpm", cfg->stateDir); cfg->channelTargetDir = g_strdup_printf("%s/channel", cfg->stateDir); + cfg->tlsPSKStateDir = g_strdup_printf("%s/psk", cfg->stateDir);
cfg->configBaseDir = virGetUserConfigDirectory();
@@ -375,6 +378,7 @@ static void virQEMUDriverConfigDispose(void *obj) g_free(cfg->dbusStateDir); g_free(cfg->rdpStateDir); g_free(cfg->vncStateDir); + g_free(cfg->tlsPSKStateDir);
g_free(cfg->libDir); g_free(cfg->cacheDir); diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index b700829342..2db3440514 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -113,6 +113,7 @@ struct _virQEMUDriverConfig { char *dbusStateDir; char *rdpStateDir; char *vncStateDir; + char *tlsPSKStateDir; /* These two directories are ones QEMU processes use (so must match * the QEMU user/group */ char *libDir; diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index a4e5f92840..f4d470553d 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -1990,6 +1990,7 @@ qemuDomainObjPrivateFree(void *data) virObjectUnref(priv->monConfig); g_free(priv->lockState); g_free(priv->origname); + g_free(priv->migTLSPSK);
virChrdevFree(priv->devs);
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 23e99dc68c..053140f7ec 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -139,6 +139,7 @@ struct _qemuDomainObjPrivate { char *origname; int nbdPort; /* Port used for migration with NBD */ unsigned short migrationPort; + char *migTLSPSK; /* Hex-encoded pre-shared key for TLS-PSK-enabled VM migration session */ unsigned short backupNBDPort; int preMigrationState; unsigned long long preMigrationMemlock; /* Original RLIMIT_MEMLOCK in case diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 8498568623..5548d6f7bd 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -664,6 +664,12 @@ qemuStateInitialize(bool privileged, cfg->vncStateDir); goto error; } + if (virDirCreate(cfg->tlsPSKStateDir, 0700, cfg->user, cfg->group, + VIR_DIR_CREATE_ALLOW_EXIST) < 0) { + virReportSystemError(errno, _("Failed to create TLS PSK state dir %1$s"), + cfg->tlsPSKStateDir); + goto error;
So, recently we've had a few reports about "security" bugs regarding directories owned by "qemu:qemu" which are filled by virtqemud with some objects.
That makes me think two things: 1) does this need to be in a separate directory? We really should have a per-VM directory for all the per-instance helper files which will live in /run so that it's removed on reboot
2) the directory shouldn't be owned by qemu:qemu, but rather root:root for the system instance and have 0711 mode so that qemu can open the files but not create them.
I know this is a more general thing, as our directory in /run/ is nonsensically split to e.g. /run/libvirt/qemu/channel/$VMID rather than have /run/libvirt/qemu/(possibly some subdir such as 'instance'/$ID' which will include all the files.
For this series, what definitely applies is the ownership and permissions point.
+ }
qemu_driver->inhibitor = virInhibitorNew( VIR_INHIBITOR_WHAT_SHUTDOWN, diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 4a43ab83b0..b9971286fd 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -59,6 +59,7 @@ #include "virprocess.h" #include "virdomainsnapshotobjlist.h" #include "virutil.h" +#include "virsecureerase.h"
#define VIR_FROM_THIS VIR_FROM_QEMU
@@ -1503,6 +1504,89 @@ qemuMigrationSrcIsAllowedHostdev(const virDomainDef *def) }
+void +qemuMigrationDeletePSKDir(virQEMUDriver *driver, virDomainObj *vm) +{ + g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver); + qemuDomainObjPrivate *priv = vm->privateData; + g_autofree char *dir_path = NULL; + g_autofree char *shortName = NULL; + + if (priv->migTLSPSK) { + virSecureEraseString(priv->migTLSPSK); + g_clear_pointer(&priv->migTLSPSK, g_free); + } + + if (!(shortName = virDomainDefGetShortName(vm->def)))
This failure ...
+ return; + + dir_path = g_strdup_printf("%s/%s", cfg->tlsPSKStateDir, shortName); + + if (virFileIsDir(dir_path) && + virFileDeleteTree(dir_path) < 0) + VIR_WARN("Failed to delete the directory %s containing the pre-shared keys for migration of domain %s", + dir_path, vm->def->name);
... has the same implication.
+} + + +static int +qemuPersistTLSPSKHelper(int pskFD, + const char *pskPath, + const void *opaque) +{ + const char *key = opaque; + + if (safewrite(pskFD, "qemu:", 5) < 0 || + safewrite(pskFD, key, strlen(key)) < 0) { + virReportSystemError(errno, + _("Unable to write the pre-shared key to file '%1$s'"), + pskPath); + return -1; + } + + return 0; +} + + +static int +qemuMigrationPersistPSK(virQEMUDriver *driver, virDomainObj *vm, const char *tlsPSK) +{ + g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver); + g_autofree char *dir_path = NULL; + g_autofree char *key_path = NULL; + g_autofree char *shortName = NULL; + + if (!(shortName = virDomainDefGetShortName(vm->def))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not get the shortened domain name for domain: %1$s"), + vm->def->name); + return -1; + } + + dir_path = g_strdup_printf("%s/%s", cfg->tlsPSKStateDir, shortName); + key_path = g_strdup_printf("%s/keys.psk", dir_path); + + if (virDirCreate(dir_path, 0700, cfg->user, cfg->group, + VIR_DIR_CREATE_ALLOW_EXIST) < 0) {
Same here, the directory IMO doesn't need to be owned by qemu because qemu will never attempt to create a file here. It needs to be accessible by 'other' though.
+ virReportSystemError(errno, + _("Could not create the directory %1$s for storing PSKs"), + dir_path); + goto error; + } + + if (virFileRewrite(key_path, S_IRUSR, cfg->user, + cfg->group, qemuPersistTLSPSKHelper, + tlsPSK) < 0) + goto error; + + return 0; + + error: + qemuMigrationDeletePSKDir(driver, vm); + return -1; +} + + static int qemuDomainGetMigrationBlockers(virDomainObj *vm, int asyncJob, @@ -2722,6 +2806,9 @@ qemuMigrationSrcBeginXML(virDomainObj *vm, if (priv->origCPU) cookieFlags |= QEMU_MIGRATION_COOKIE_CPU;
+ if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_OBJECT_TLS_CREDS_PSK)) + cookieFlags |= QEMU_MIGRATION_COOKIE_TLS_PSK; + if (!(flags & VIR_MIGRATE_OFFLINE)) cookieFlags |= QEMU_MIGRATION_COOKIE_CAPS;
@@ -2738,6 +2825,9 @@ qemuMigrationSrcBeginXML(virDomainObj *vm, cookieFlags) < 0) return NULL;
+ if (mig->tlsPSK && qemuMigrationPersistPSK(driver, vm, mig->tlsPSK) < 0) + return NULL; + if (xmlin) { g_autoptr(virDomainDef) def = NULL;
@@ -4232,6 +4322,8 @@ qemuMigrationSrcConfirmPhase(virQEMUDriver *driver, privJob->stats.mig.downtime = privMigJob->stats.mig.downtime; }
+ qemuMigrationDeletePSKDir(driver, vm); + if (flags & VIR_MIGRATE_OFFLINE) return 0;
@@ -5274,6 +5366,7 @@ qemuMigrationSrcRun(virQEMUDriver *driver,
error: virErrorPreserveLast(&orig_err); + qemuMigrationDeletePSKDir(driver, vm);
if (qemuDomainObjIsActive(vm)) { int reason; @@ -7029,6 +7122,8 @@ qemuMigrationDstFinishActive(virQEMUDriver *driver, QEMU_MIGRATION_COOKIE_STATS) < 0) VIR_WARN("Unable to encode migration cookie");
+ qemuMigrationDeletePSKDir(driver, vm); + qemuMigrationDstComplete(driver, vm, inPostCopy, VIR_ASYNC_JOB_MIGRATION_IN, vm->job);
@@ -7039,6 +7134,8 @@ qemuMigrationDstFinishActive(virQEMUDriver *driver, * overwrites it. */ virErrorPreserveLast(&orig_err);
+ qemuMigrationDeletePSKDir(driver, vm); + if (qemuDomainObjIsActive(vm)) { if (doKill) { qemuProcessStop(vm, VIR_DOMAIN_SHUTOFF_FAILED, @@ -7197,6 +7294,7 @@ qemuMigrationProcessUnattended(virQEMUDriver *driver, else qemuMigrationSrcComplete(driver, vm, job);
+ qemuMigrationDeletePSKDir(driver, vm); qemuMigrationJobFinish(vm);
if (!virDomainObjIsActive(vm)) diff --git a/src/qemu/qemu_migration.h b/src/qemu/qemu_migration.h index 7e9410e1f7..ce15f8024b 100644 --- a/src/qemu/qemu_migration.h +++ b/src/qemu/qemu_migration.h @@ -279,3 +279,6 @@ int qemuMigrationAnyRefreshStatus(virDomainObj *vm, virDomainAsyncJob asyncJob, virDomainJobStatus *status); + +void +qemuMigrationDeletePSKDir(virQEMUDriver *driver, virDomainObj *vm); diff --git a/src/qemu/qemu_migration_cookie.c b/src/qemu/qemu_migration_cookie.c index 7311a8294b..0cf1ab9590 100644 --- a/src/qemu/qemu_migration_cookie.c +++ b/src/qemu/qemu_migration_cookie.c @@ -20,6 +20,7 @@
#include <gnutls/gnutls.h> #include <gnutls/x509.h> +#include <inttypes.h>
#include "locking/domain_lock.h" #include "virerror.h" @@ -27,6 +28,7 @@ #include "virnetdevopenvswitch.h" #include "virstring.h" #include "virutil.h" +#include "virsecureerase.h"
#include "qemu_domain.h" #include "qemu_migration_cookie.h" @@ -52,6 +54,7 @@ VIR_ENUM_IMPL(qemuMigrationCookieFlag, "allowReboot", "capabilities", "block-dirty-bitmaps", + "psk", );
@@ -165,6 +168,9 @@ qemuMigrationCookieFree(qemuMigrationCookie *mig) g_free(mig->name); g_free(mig->lockState); g_free(mig->lockDriver); + if (mig->tlsPSK) + virSecureEraseString(mig->tlsPSK); + g_free(mig->tlsPSK); g_clear_pointer(&mig->jobData, virDomainJobDataFree); virCPUDefFree(mig->cpu); qemuMigrationCookieCapsFree(mig->caps); @@ -575,6 +581,51 @@ qemuMigrationCookieAddCaps(qemuMigrationCookie *mig, }
+static int +qemuMigrationCookieAddTLSPSK(qemuMigrationCookie *mig, + virQEMUDriver *driver, + virDomainObj *vm) +{ + g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver); + qemuDomainObjPrivate *priv = vm->privateData; + gnutls_datum_t psk_key = {NULL, 0}; + g_autofree char *key = NULL; + size_t key_len; + int ret; + + /* Generate the pre-shared key exactly once for a migration session*/ + if (priv->migTLSPSK) { + mig->tlsPSK = g_strdup(priv->migTLSPSK); + mig->flags |= QEMU_MIGRATION_COOKIE_TLS_PSK; + return 0; + } + + ret = gnutls_key_generate(&psk_key, cfg->migrateTLSPSKLength); + if (ret < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Generation of a pre-shared key failed")); + return -1; + } + key_len = (psk_key.size*2) + 1; + key = g_new0(char, key_len); + + ret = gnutls_hex_encode(&psk_key, key, &key_len); + if (ret < 0) { + gnutls_free(psk_key.data); + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Hex encoding of a PSK key failed")); + return -1; + } + + priv->migTLSPSK = g_strdup(key); + mig->tlsPSK = g_steal_pointer(&key); + mig->flags |= QEMU_MIGRATION_COOKIE_TLS_PSK; + + gnutls_free(psk_key.data); + return 0; +} + + static void qemuMigrationCookieGraphicsXMLFormat(virBuffer *buf, qemuMigrationCookieGraphics *grap) @@ -890,6 +941,9 @@ qemuMigrationCookieXMLFormat(virQEMUDriver *driver, if (mig->flags & QEMU_MIGRATION_COOKIE_BLOCK_DIRTY_BITMAPS) qemuMigrationCookieBlockDirtyBitmapsFormat(buf, mig->blockDirtyBitmaps);
+ if ((mig->flags & QEMU_MIGRATION_COOKIE_TLS_PSK) && mig->tlsPSK) + virBufferAsprintf(buf, "<migration-key>%s</migration-key>\n", mig->tlsPSK); + virBufferAdjustIndent(buf, -2); virBufferAddLit(buf, "</qemu-migration>\n"); return 0; @@ -1396,6 +1450,12 @@ qemuMigrationCookieXMLParse(qemuMigrationCookie *mig, qemuMigrationCookieBlockDirtyBitmapsParse(ctxt, mig) < 0) return -1;
+ if (flags & QEMU_MIGRATION_COOKIE_TLS_PSK) { + mig->tlsPSK = virXPathString("string(./migration-key[1])", ctxt); + if (mig->tlsPSK) + mig->flags |= QEMU_MIGRATION_COOKIE_TLS_PSK; + } + return 0; }
@@ -1471,14 +1531,29 @@ qemuMigrationCookieFormat(qemuMigrationCookie *mig, qemuMigrationCookieAddCaps(mig, dom, party) < 0) return -1;
+ if (flags & QEMU_MIGRATION_COOKIE_TLS_PSK) { + switch (party) { + case QEMU_MIGRATION_SOURCE: + if (qemuMigrationCookieAddTLSPSK(mig, driver, dom) < 0) + return -1; + break; + case QEMU_MIGRATION_DESTINATION: + if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_OBJECT_TLS_CREDS_PSK)) {
IMO any sane build of qemu must have TLS enabled nowadays. I wonder if it even makes sense to have this capability. If tls can be compiled out, techincally it'd mean that there could be such a setup but IMO that would be still insane to have a unencrypted connection.
I'd be okay just assuming that any qemu we support has TLS support. On the offchance that someone uses an insane build they will get an error, but I don't think I'd care for such setups.
IIUC, Libvirt will automatically enforce TLS-PSK encryption for migrations between two newer Libvirt hosts, even if the user does not specify the VIR_MIGRATE_TLS flag. Consequently, TLS-X509 would only be triggered during a forward migration from an older Libvirt host to a newer Libvirt destination (when VIR_MIGRATE_TLS is set). In that scenario, the older source wouldn't include the pre-shared key in the migration cookie, naturally bypassing the PSK pathway. I am assuming we do not need to account for newer-to-older migrations. I wanted to ask a follow up question regarding this design: Given that with encrypted migration, there will be a performance impact to the live migrations of the VMs, due to auxiliary encrypt and decrypt operations at the source and destination, and also due to a lack of MSG_ZEROCOPY support for encrypted migrations in QEMU. Some users might be running VM migration within a cluster of trusted nodes, for which encrypted migration might not be a performant solution. Can we gate this behaviour of by-default enablement of PSK behind a configuration parameter in qemu.conf. This design is as follows: 1. If the configuration parameter is set we do it as per our above discussed design. 2. If the configuration parameter is not set, then we attempt PSK if VIR_MIGRATE_TLS flag is specified.
+ if (mig->tlsPSK) + virSecureEraseString(mig->tlsPSK); + mig->flags &= ~QEMU_MIGRATION_COOKIE_TLS_PSK; + g_clear_pointer(&mig->tlsPSK, g_free); + } + break; + } + } + if (qemuMigrationCookieXMLFormat(driver, priv->qemuCaps, &buf, mig) < 0) return -1;
*cookieoutlen = virBufferUse(&buf) + 1; *cookieout = virBufferContentAndReset(&buf);
- VIR_DEBUG("cookielen=%d cookie=%s", *cookieoutlen, *cookieout); - return 0; }
diff --git a/src/qemu/qemu_migration_cookie.h b/src/qemu/qemu_migration_cookie.h index 254372234d..fd3b4c5a56 100644 --- a/src/qemu/qemu_migration_cookie.h +++ b/src/qemu/qemu_migration_cookie.h @@ -35,6 +35,7 @@ typedef enum { QEMU_MIGRATION_COOKIE_FLAG_ALLOW_REBOOT, QEMU_MIGRATION_COOKIE_FLAG_CAPS, QEMU_MIGRATION_COOKIE_FLAG_BLOCK_DIRTY_BITMAPS, + QEMU_MIGRATION_COOKIE_FLAG_TLS_PSK,
QEMU_MIGRATION_COOKIE_FLAG_LAST } qemuMigrationCookieFlags; @@ -53,6 +54,7 @@ typedef enum { QEMU_MIGRATION_COOKIE_CPU = (1 << QEMU_MIGRATION_COOKIE_FLAG_CPU), QEMU_MIGRATION_COOKIE_CAPS = (1 << QEMU_MIGRATION_COOKIE_FLAG_CAPS), QEMU_MIGRATION_COOKIE_BLOCK_DIRTY_BITMAPS = (1 << QEMU_MIGRATION_COOKIE_FLAG_BLOCK_DIRTY_BITMAPS), + QEMU_MIGRATION_COOKIE_TLS_PSK = (1 << QEMU_MIGRATION_COOKIE_FLAG_TLS_PSK), } qemuMigrationCookieFeatures;
typedef struct _qemuMigrationCookieGraphics qemuMigrationCookieGraphics; @@ -171,6 +173,9 @@ struct _qemuMigrationCookie {
/* If flags & QEMU_MIGRATION_COOKIE_BLOCK_DIRTY_BITMAPS */ GSList *blockDirtyBitmaps; + + /* If flags & QEMU_MIGRATION_COOKIE_TLS_PSK */ + char *tlsPSK; };