On Wed, Jul 29, 2026 at 08:58:58 +0000, Abhisek Panda wrote: > Enable TLS-PSK based secure migration, if and only if the > VIR_MIGRATE_TLS flag is set and the destination host lacks necessary > X.509 credentials (ca-cert.pem, server-cert.pem and server-key.pem). > > Suggested-by: Tejus GK <[email protected]> > Signed-off-by: Abhisek Panda <[email protected]> > --- > include/libvirt/libvirt-domain.h | 11 +++-- > src/qemu/qemu_migration.c | 83 +++++++++++++++++++++----------- > 2 files changed, 64 insertions(+), 30 deletions(-) > > diff --git a/include/libvirt/libvirt-domain.h > b/include/libvirt/libvirt-domain.h > index 5b67f8f897..70d955fd31 100644 > --- a/include/libvirt/libvirt-domain.h > +++ b/include/libvirt/libvirt-domain.h > @@ -1089,9 +1089,14 @@ typedef enum { > VIR_MIGRATE_POSTCOPY = (1 << 15), > > /* Setting the VIR_MIGRATE_TLS flag will cause the migration to attempt > - * to use the TLS environment configured by the hypervisor in order to > - * perform the migration. If incorrectly configured on either source or > - * destination, the migration will fail. > + * to use either the TLS X.509 or TLS pre-shared key (PSK) authentication > + * mechanisms. If valid X.509 certificates and keys exist on the > destination host, > + * then Libvirt will use TLS X.509 authentication. Otherwise, Libvirt > falls back to > + * TLS PSK authentication. If the certificate or the key file is > corrupted or not > + * properly configured on either source or destination, the migration > will fail. > + * > + * Note: Since 12.6.0, Libvirt supports the TLS pre-shared key (PSK) > authentication > + * mechanism along with the TLS X.509 authentication scheme.
Per <https://lists.libvirt.org/archives/list/[email protected]/message/PLXLRIV33ZGREYCL745JXCF34TOLARGE/> freeze is on monday, so the version statement might need to be bumped. Once again I'm sorry about the review delay, but there's a lot of other stuff I need to deal with. > * > * Since: 3.2.0 > */ > diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c > index 943478fe68..5e381b96e9 100644 > --- a/src/qemu/qemu_migration.c > +++ b/src/qemu/qemu_migration.c > @@ -3508,14 +3508,28 @@ qemuMigrationDstPrepareActive(virQEMUDriver *driver, > /* Save original migration parameters */ > qemuDomainSaveStatus(vm); > > - /* Migrations using TLS need to add the "tls-creds-x509" object and > + /* Migrations using TLS need to add the "tls-creds-x509" object if the > cert files are > + * present on the host, else fallback to adding the "tls-creds-psk" > object. Additionally, > * set the migration TLS parameters */ > if (flags & VIR_MIGRATE_TLS) { > - if (qemuMigrationParamsEnableTLSx509(driver, vm, true, > - VIR_ASYNC_JOB_MIGRATION_IN, > - &tlsAlias, NULL, > - migParams) < 0) > - goto error; > + if (!mig->tlsPSK) { > + if (qemuMigrationParamsEnableTLSx509(driver, vm, true, > + VIR_ASYNC_JOB_MIGRATION_IN, > + &tlsAlias, NULL, > + migParams) < 0) > + goto error; > + } else { > + ret = qemuMigrationPersistPSK(driver, vm, mig->tlsPSK); > + if (ret < 0) { > + virSecureEraseString(mig->tlsPSK); > + g_clear_pointer(&mig->tlsPSK, g_free); > + goto error; As noted before this belongs to the common cleanup. Also at a failure at this point the key was not used yet so there's no need to consider it a secret. > + } > + if (qemuMigrationParamsEnableTLSPSK(driver, vm, true, > + VIR_ASYNC_JOB_MIGRATION_IN, > + &tlsAlias, migParams) < 0) > + goto error; > + } For future proofing (enabling PSK tls even without explicit flag) the logic should be: 1) if PSK exists enable TLS psk 2) if VIR_MIGRATE_TLS and x509 certs exist enable x509 3) otherwise do nothing > } else { > if (qemuMigrationParamsDisableTLS(vm, migParams) < 0) > goto error; > @@ -3622,6 +3636,13 @@ qemuMigrationDstPrepareFresh(virQEMUDriver *driver, > g_autofree char *xmlout = NULL; > unsigned int cookieFlags = 0; > bool taint_hook = false; > + unsigned int parseCookieFlags = QEMU_MIGRATION_COOKIE_LOCKSTATE | > + QEMU_MIGRATION_COOKIE_NBD | > + QEMU_MIGRATION_COOKIE_MEMORY_HOTPLUG | > + QEMU_MIGRATION_COOKIE_CPU_HOTPLUG | > + QEMU_MIGRATION_COOKIE_CPU | > + QEMU_MIGRATION_COOKIE_CAPS | > + > QEMU_MIGRATION_COOKIE_BLOCK_DIRTY_BITMAPS; > > VIR_DEBUG("name=%s, origname=%s, protocol=%s, port=%hu, " > "listenAddress=%s, nbdPort=%d, nbdURI=%s, flags=0x%x", > @@ -3633,6 +3654,11 @@ qemuMigrationDstPrepareFresh(virQEMUDriver *driver, > QEMU_MIGRATION_COOKIE_CAPS; > } > > + if (flags & VIR_MIGRATE_TLS) { > + parseCookieFlags |= QEMU_MIGRATION_COOKIE_TLS_PSK; > + cookieFlags |= QEMU_MIGRATION_COOKIE_TLS_PSK; Always parse PSK on destination. There's no need to gate this on VIR_MIGRATE_TLS > + } > + > /* Let migration hook filter domain XML */ > if (virHookPresent(VIR_HOOK_DRIVER_QEMU)) { > g_autofree char *xml = NULL; > @@ -3679,14 +3705,7 @@ qemuMigrationDstPrepareFresh(virQEMUDriver *driver, > * domain list. Parsing/validation may fail and there's no > * point in having the domain in the list at that point. */ > if (!(mig = qemuMigrationCookieParse(driver, NULL, *def, origname, NULL, > - cookiein, cookieinlen, > - QEMU_MIGRATION_COOKIE_LOCKSTATE | > - QEMU_MIGRATION_COOKIE_NBD | > - > QEMU_MIGRATION_COOKIE_MEMORY_HOTPLUG | > - QEMU_MIGRATION_COOKIE_CPU_HOTPLUG | > - QEMU_MIGRATION_COOKIE_CPU | > - QEMU_MIGRATION_COOKIE_CAPS | > - > QEMU_MIGRATION_COOKIE_BLOCK_DIRTY_BITMAPS))) > + cookiein, cookieinlen, > parseCookieFlags))) > goto cleanup; > > if (!(vm = virDomainObjListAdd(driver->domains, def, > @@ -5112,6 +5131,9 @@ qemuMigrationSrcRun(virQEMUDriver *driver, > if (storageMigration) > cookieFlags |= QEMU_MIGRATION_COOKIE_NBD; > > + if (flags & VIR_MIGRATE_TLS) > + cookieFlags |= QEMU_MIGRATION_COOKIE_TLS_PSK; > + > if (virLockManagerPluginUsesState(driver->lockManager) && > !cookieout) { > virReportError(VIR_ERR_INTERNAL_ERROR, > @@ -5163,19 +5185,26 @@ qemuMigrationSrcRun(virQEMUDriver *driver, > qemuDomainSaveStatus(vm); > > if (flags & VIR_MIGRATE_TLS) { > - const char *hostname = NULL; > - > - /* We need to add tls-hostname whenever QEMU itself does not > - * connect directly to the destination. */ > - if (spec->destType == MIGRATION_DEST_CONNECT_HOST || > - spec->destType == MIGRATION_DEST_FD) > - hostname = spec->dest.host.name; > - > - if (qemuMigrationParamsEnableTLSx509(driver, vm, false, > - VIR_ASYNC_JOB_MIGRATION_OUT, > - &tlsAlias, hostname, > - migParams) < 0) > - goto error; > + if (mig->flags & QEMU_MIGRATION_COOKIE_TLS_PSK) { > + if (qemuMigrationParamsEnableTLSPSK(driver, vm, false, > + VIR_ASYNC_JOB_MIGRATION_OUT, > + &tlsAlias, migParams) < 0) > + goto error; > + } else { > + const char *hostname = NULL; > + > + /* We need to add tls-hostname whenever QEMU itself does not > + * connect directly to the destination. */ > + if (spec->destType == MIGRATION_DEST_CONNECT_HOST || > + spec->destType == MIGRATION_DEST_FD) > + hostname = spec->dest.host.name; > + > + if (qemuMigrationParamsEnableTLSx509(driver, vm, false, > + VIR_ASYNC_JOB_MIGRATION_OUT, > + &tlsAlias, hostname, > + migParams) < 0) > + goto error; > + } Same note as before. If the PSK got through the handshake, you can always enable it rather than gating it behind VIR_MIGRATE_TLS. x509 needs to be gated behind VIR_MIGRATE_TLS but only if PSK is not agreed upon > } else { > if (qemuMigrationParamsDisableTLS(vm, migParams) < 0) > goto error; > -- > 2.43.7 >
