[libvirt] [PATCH v3 0/7] Add TLS support for migration

v2: http://www.redhat.com/archives/libvir-list/2017-February/msg01374.html NB: Parts of the original series were split out, patches posted, ACK'd, and committed separately, see: http://www.redhat.com/archives/libvir-list/2017-March/msg00037.html Changes since v2: First and foremost after some gentle encouragement and lots of hours cursing at no one in particular, I was finally able to configure a couple of nested host guests, create a guest on each, and actually migrate that guest without TLS back and forth between the two nested hosts. Suffice to say there are dribs and drabs of "howto"'s that help, but it is quite a confusing setup and I was certainly left wondering how customers do this. OK beyond paying someone set things up for them! Now that I had the basics, the next task was creating the TLS environment. MANY more curse words uttered, including some I didn't know I knew, but I was actually able to test the changes in my environment. Suffice to say it's "somewhat confusing" because the TLS environment that QEMU expects (ok, the filenames) differ slightly than those that a libvirt TLS environment would expects. What really got frustrating is that all I'd get back is the following error message after 'migrate' started on the client and after the server was essentially waiting for the migration: error: operation failed: migration job: unexpectedly failed Turns out that I was missing the '/etc/pki/qemu/client-cert.pem' and '/etc/pki/qemu/client-key.pem' files, but got no message. Of course I did have something in my /etc/pki/libvirt directory, but of course those weren't being used. Still my brain wasn't exactly telling me that. The other piece I was missing was the "details" about when "tls-hostname" must be provided. In the previous patches, I would search on "fd" or "spec" in spec->dest.host.protocol; however, that wasn't quite right. What I needed to search on was MIGRATION_DEST_CONNECT_HOST or MIGRATION_DEST_FD in spec->destType (and _CONNECT_HOST only because it turns into _FD 'eventually'). Anyway, so happy days are here again and I can now liberally celebrate St Patrick's Day (not that I wasn't going to before, but now moreso!). Beyond the above description from my testing the real "key" behind all of this is ensuring that the 'right' QEMU bits are installed by fetching the "tls-creds" via qemuMonitorGetMigrationParams and as long as *something* is returned, then we are assured that we can proceed. This relates to QEMU commit '4af245dc3' (after v2.9.0-rc0) which sets a default for tls-creds and more importantly for our purposes allows us to "clear" the tls-creds and tls-hostname migration parameters by passing a "" (empty string). With that in place the rest of the logic falls into place with regard to qemuMigrationCheckSetupTLS ensuring that TLS for migration is available for libvirt to use. Once we know we can use TLS, we'll use qemuMigrationAddTLSObjects to add the "secret" and "tls-creds-x509" objects to the domain for either server (target) or client (source). Doing this also required an adjustment to the existing code to allow the Async monitor usage. Once those objects are created, we'll set the "tls-creds" and "tls-hostname" parameters appropriate. If TLS isn't being used, we'll be sure to clear them. This is only made tricky by needing to ensure the qemu that's running can support setting them to "", hence the qemuMigrationCheckTLSCreds prior to that clearing. If this wasn't done, older QEMU would be less than pleased to find "" in tls-creds and tls-hostname. Finally when the migration is successful, call qemuMigrationDeconstructTLS (naming credit goes to my wife!). Failures will call just qemuMigrationResetTLS in order to achieve the same call; however, for those paths we do not still have the original 'tlsAlias' and 'secAlias' available, so we have to recreate it on the fly' Have I won the prize for the longest cover letter yet? John Ferlan (7): qemu: Create #define for TLS configuration setup. conf: Introduce migrate_tls_x509_cert_dir Add new migration flag VIR_MIGRATE_TLS qemu: Add TLS params to _qemuMonitorMigrationParams qemu: Add job for qemuDomain{Add|Del}TLSObjects qemu: Set up the migrate TLS objects for target qemu: Set up the migration TLS objects for source include/libvirt/libvirt-domain.h | 8 + src/qemu/libvirtd_qemu.aug | 5 + src/qemu/qemu.conf | 37 ++++ src/qemu/qemu_conf.c | 45 ++-- src/qemu/qemu_conf.h | 4 + src/qemu/qemu_domain.c | 7 +- src/qemu/qemu_domain.h | 91 ++++---- src/qemu/qemu_driver.c | 4 +- src/qemu/qemu_hotplug.c | 24 ++- src/qemu/qemu_hotplug.h | 2 + src/qemu/qemu_migration.c | 423 ++++++++++++++++++++++++++++++++++++- src/qemu/qemu_migration.h | 9 +- src/qemu/qemu_monitor.c | 11 +- src/qemu/qemu_monitor.h | 3 + src/qemu/qemu_monitor_json.c | 28 +++ src/qemu/test_libvirtd_qemu.aug.in | 3 + tests/qemumonitorjsontest.c | 25 ++- tools/virsh-domain.c | 7 + 18 files changed, 668 insertions(+), 68 deletions(-) -- 2.9.3

Create GET_CONFIG_TLS_CERT to set up the TLS for 'chardev' TLS setting. Soon to be reused. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_conf.c | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 0a338d7..9db2bc3 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -530,22 +530,33 @@ int virQEMUDriverConfigLoadFile(virQEMUDriverConfigPtr cfg, if (virConfGetValueBool(conf, "spice_auto_unix_socket", &cfg->spiceAutoUnixSocket) < 0) goto cleanup; +#define GET_CONFIG_TLS_CERTINFO(val) \ + do { \ + if ((rv = virConfGetValueBool(conf, #val "_tls_x509_verify", \ + &cfg->val## TLSx509verify)) < 0) \ + goto cleanup; \ + if (rv == 0) \ + cfg->val## TLSx509verify = cfg->defaultTLSx509verify; \ + if (virConfGetValueString(conf, #val "_tls_x509_cert_dir", \ + &cfg->val## TLSx509certdir) < 0) \ + goto cleanup; \ + if (virConfGetValueString(conf, \ + #val "_tls_x509_secret_uuid", \ + &cfg->val## TLSx509secretUUID) < 0) \ + goto cleanup; \ + if (!cfg->val## TLSx509secretUUID && \ + cfg->defaultTLSx509secretUUID) { \ + if (VIR_STRDUP(cfg->val## TLSx509secretUUID, \ + cfg->defaultTLSx509secretUUID) < 0) \ + goto cleanup; \ + } \ + } while (false); + if (virConfGetValueBool(conf, "chardev_tls", &cfg->chardevTLS) < 0) goto cleanup; - if (virConfGetValueString(conf, "chardev_tls_x509_cert_dir", &cfg->chardevTLSx509certdir) < 0) - goto cleanup; - if ((rv = virConfGetValueBool(conf, "chardev_tls_x509_verify", &cfg->chardevTLSx509verify)) < 0) - goto cleanup; - if (rv == 0) - cfg->chardevTLSx509verify = cfg->defaultTLSx509verify; - if (virConfGetValueString(conf, "chardev_tls_x509_secret_uuid", - &cfg->chardevTLSx509secretUUID) < 0) - goto cleanup; - if (!cfg->chardevTLSx509secretUUID && cfg->defaultTLSx509secretUUID) { - if (VIR_STRDUP(cfg->chardevTLSx509secretUUID, - cfg->defaultTLSx509secretUUID) < 0) - goto cleanup; - } + GET_CONFIG_TLS_CERTINFO(chardev); + +#undef GET_CONFIG_TLS_CERTINFO if (virConfGetValueUInt(conf, "remote_websocket_port_min", &cfg->webSocketPortMin) < 0) goto cleanup; -- 2.9.3

On Fri, Mar 17, 2017 at 14:38:55 -0400, John Ferlan wrote:
Create GET_CONFIG_TLS_CERT to set up the TLS for 'chardev' TLS setting. Soon to be reused.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_conf.c | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-)
diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 0a338d7..9db2bc3 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -530,22 +530,33 @@ int virQEMUDriverConfigLoadFile(virQEMUDriverConfigPtr cfg, if (virConfGetValueBool(conf, "spice_auto_unix_socket", &cfg->spiceAutoUnixSocket) < 0) goto cleanup;
+#define GET_CONFIG_TLS_CERTINFO(val) \ + do { \ ... + } while (false);
"while (0)" is usually used in such macros... not a big deal, but the semicolon must not be there. ... ACK with the semicolon removed. Jirka

Add a new TLS X.509 certificate type - "migrate". This will handle the creation of a TLS certificate capability (and possibly repository) to be used for migrations. Similar to chardev's, credentials will be handled via a libvirt secrets; however, unlike chardev's enablement and usage will be via a CLI flag instead of a conf flag and a domain XML attribute. The migrations will also require the client-cert.pem and client-key.pem files to be present in the clients TLS directory. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/libvirtd_qemu.aug | 5 +++++ src/qemu/qemu.conf | 37 +++++++++++++++++++++++++++++++++++++ src/qemu/qemu_conf.c | 6 ++++++ src/qemu/qemu_conf.h | 4 ++++ src/qemu/test_libvirtd_qemu.aug.in | 3 +++ 5 files changed, 55 insertions(+) diff --git a/src/qemu/libvirtd_qemu.aug b/src/qemu/libvirtd_qemu.aug index 82bae9e..e1983d1 100644 --- a/src/qemu/libvirtd_qemu.aug +++ b/src/qemu/libvirtd_qemu.aug @@ -54,6 +54,10 @@ module Libvirtd_qemu = | bool_entry "chardev_tls_x509_verify" | str_entry "chardev_tls_x509_secret_uuid" + let migrate_entry = str_entry "migrate_tls_x509_cert_dir" + | bool_entry "migrate_tls_x509_verify" + | str_entry "migrate_tls_x509_secret_uuid" + let nogfx_entry = bool_entry "nographics_allow_host_audio" let remote_display_entry = int_entry "remote_display_port_min" @@ -116,6 +120,7 @@ module Libvirtd_qemu = | vnc_entry | spice_entry | chardev_entry + | migrate_entry | nogfx_entry | remote_display_entry | security_entry diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf index 9925ac9..40bcec3 100644 --- a/src/qemu/qemu.conf +++ b/src/qemu/qemu.conf @@ -13,6 +13,11 @@ # # dh-params.pem - the DH params configuration file # +# When using TLS for migrations, the directory must also contain +# +# client-cert.pem - the client certificate signed with the ca-cert.pem +# client-key.pem - the client private key +# #default_tls_x509_cert_dir = "/etc/pki/qemu" @@ -238,6 +243,38 @@ #chardev_tls_x509_secret_uuid = "00000000-0000-0000-0000-000000000000" +# In order to override the default TLS certificate location for migration +# certificates, supply a valid path to the certificate directory. If the +# provided path does not exist then the default_tls_x509_cert_dir path +# will be used. Once/if a default certificate is enabled/defined, migration +# will then be able to use the certificate via migration API flags. +# +#migrate_tls_x509_cert_dir = "/etc/pki/libvirt-migrate" + + +# The default TLS configuration only uses certificates for the server +# allowing the client to verify the server's identity and establish +# an encrypted channel. +# +# It is possible to use x509 certificates for authentication too, by +# issuing a x509 certificate to every client who needs to connect. +# +# Enabling this option will reject any client who does not have a +# certificate signed by the CA in /etc/pki/libvirt-migrate/ca-cert.pem +# +#migrate_tls_x509_verify = 1 + + +# Uncomment and use the following option to override the default secret +# UUID provided in the default_tls_x509_secret_uuid parameter. +# +# NB This default all-zeros UUID will not work. Replace it with the +# output from the UUID for the TLS secret from a 'virsh secret-list' +# command and then uncomment the entry +# +#migrate_tls_x509_secret_uuid = "00000000-0000-0000-0000-000000000000" + + # By default, if no graphical front end is configured, libvirt will disable # QEMU audio output since directly talking to alsa/pulseaudio may not work # with various security settings. If you know what you're doing, enable diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 9db2bc3..4c271cd 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -280,6 +280,7 @@ virQEMUDriverConfigPtr virQEMUDriverConfigNew(bool privileged) SET_TLS_X509_CERT_DEFAULT(vnc); SET_TLS_X509_CERT_DEFAULT(spice); SET_TLS_X509_CERT_DEFAULT(chardev); + SET_TLS_X509_CERT_DEFAULT(migrate); #undef SET_TLS_X509_CERT_DEFAULT @@ -395,6 +396,9 @@ static void virQEMUDriverConfigDispose(void *obj) VIR_FREE(cfg->chardevTLSx509certdir); VIR_FREE(cfg->chardevTLSx509secretUUID); + VIR_FREE(cfg->migrateTLSx509certdir); + VIR_FREE(cfg->migrateTLSx509secretUUID); + while (cfg->nhugetlbfs) { cfg->nhugetlbfs--; VIR_FREE(cfg->hugetlbfs[cfg->nhugetlbfs].mnt_dir); @@ -556,6 +560,8 @@ int virQEMUDriverConfigLoadFile(virQEMUDriverConfigPtr cfg, goto cleanup; GET_CONFIG_TLS_CERTINFO(chardev); + GET_CONFIG_TLS_CERTINFO(migrate); + #undef GET_CONFIG_TLS_CERTINFO if (virConfGetValueUInt(conf, "remote_websocket_port_min", &cfg->webSocketPortMin) < 0) diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index e585f81..1407eef 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -137,6 +137,10 @@ struct _virQEMUDriverConfig { bool chardevTLSx509verify; char *chardevTLSx509secretUUID; + char *migrateTLSx509certdir; + bool migrateTLSx509verify; + char *migrateTLSx509secretUUID; + unsigned int remotePortMin; unsigned int remotePortMax; diff --git a/src/qemu/test_libvirtd_qemu.aug.in b/src/qemu/test_libvirtd_qemu.aug.in index 6f03898..3e317bc 100644 --- a/src/qemu/test_libvirtd_qemu.aug.in +++ b/src/qemu/test_libvirtd_qemu.aug.in @@ -25,6 +25,9 @@ module Test_libvirtd_qemu = { "chardev_tls_x509_cert_dir" = "/etc/pki/libvirt-chardev" } { "chardev_tls_x509_verify" = "1" } { "chardev_tls_x509_secret_uuid" = "00000000-0000-0000-0000-000000000000" } +{ "migrate_tls_x509_cert_dir" = "/etc/pki/libvirt-migrate" } +{ "migrate_tls_x509_verify" = "1" } +{ "migrate_tls_x509_secret_uuid" = "00000000-0000-0000-0000-000000000000" } { "nographics_allow_host_audio" = "1" } { "remote_display_port_min" = "5900" } { "remote_display_port_max" = "65535" } -- 2.9.3

On Fri, Mar 17, 2017 at 14:38:56 -0400, John Ferlan wrote:
Add a new TLS X.509 certificate type - "migrate". This will handle the creation of a TLS certificate capability (and possibly repository) to be used for migrations. Similar to chardev's, credentials will be handled via a libvirt secrets; however, unlike chardev's enablement and usage will be via a CLI flag instead of a conf flag and a domain XML attribute. The migrations will also require the client-cert.pem and client-key.pem files to be present in the clients TLS directory.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/libvirtd_qemu.aug | 5 +++++ src/qemu/qemu.conf | 37 +++++++++++++++++++++++++++++++++++++ src/qemu/qemu_conf.c | 6 ++++++ src/qemu/qemu_conf.h | 4 ++++ src/qemu/test_libvirtd_qemu.aug.in | 3 +++ 5 files changed, 55 insertions(+)
diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf index 9925ac9..40bcec3 100644 --- a/src/qemu/qemu.conf +++ b/src/qemu/qemu.conf ... +# In order to override the default TLS certificate location for migration +# certificates, supply a valid path to the certificate directory. If the +# provided path does not exist then the default_tls_x509_cert_dir path +# will be used. Once/if a default certificate is enabled/defined, migration +# will then be able to use the certificate via migration API flags. +# +#migrate_tls_x509_cert_dir = "/etc/pki/libvirt-migrate" + + +# The default TLS configuration only uses certificates for the server +# allowing the client to verify the server's identity and establish +# an encrypted channel. +# +# It is possible to use x509 certificates for authentication too, by +# issuing a x509 certificate to every client who needs to connect.
s/a x509/an x509/
+# +# Enabling this option will reject any client who does not have a +# certificate signed by the CA in /etc/pki/libvirt-migrate/ca-cert.pem
"ca-cert.pem in migrate_tls_x509_cert_dir" or something like that. Mentioning /etc/pki/libvirt-migrate might be quite confusing.
+# +#migrate_tls_x509_verify = 1 ...
ACK with the comments fixed. Jirka

On 03/22/2017 12:26 PM, Jiri Denemark wrote:
On Fri, Mar 17, 2017 at 14:38:56 -0400, John Ferlan wrote:
Add a new TLS X.509 certificate type - "migrate". This will handle the creation of a TLS certificate capability (and possibly repository) to be used for migrations. Similar to chardev's, credentials will be handled via a libvirt secrets; however, unlike chardev's enablement and usage will be via a CLI flag instead of a conf flag and a domain XML attribute. The migrations will also require the client-cert.pem and client-key.pem files to be present in the clients TLS directory.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/libvirtd_qemu.aug | 5 +++++ src/qemu/qemu.conf | 37 +++++++++++++++++++++++++++++++++++++ src/qemu/qemu_conf.c | 6 ++++++ src/qemu/qemu_conf.h | 4 ++++ src/qemu/test_libvirtd_qemu.aug.in | 3 +++ 5 files changed, 55 insertions(+)
diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf index 9925ac9..40bcec3 100644 --- a/src/qemu/qemu.conf +++ b/src/qemu/qemu.conf ... +# In order to override the default TLS certificate location for migration +# certificates, supply a valid path to the certificate directory. If the +# provided path does not exist then the default_tls_x509_cert_dir path +# will be used. Once/if a default certificate is enabled/defined, migration +# will then be able to use the certificate via migration API flags. +# +#migrate_tls_x509_cert_dir = "/etc/pki/libvirt-migrate" + + +# The default TLS configuration only uses certificates for the server +# allowing the client to verify the server's identity and establish +# an encrypted channel. +# +# It is possible to use x509 certificates for authentication too, by +# issuing a x509 certificate to every client who needs to connect.
s/a x509/an x509/
+# +# Enabling this option will reject any client who does not have a +# certificate signed by the CA in /etc/pki/libvirt-migrate/ca-cert.pem
"ca-cert.pem in migrate_tls_x509_cert_dir" or something like that. Mentioning /etc/pki/libvirt-migrate might be quite confusing.
The is a cut-n-paste of the libvirt-vnc and libvirt-chardev - would you like to see those changed as well (in a separate patch). It now reads: # Enabling this option will reject any client who does not have a # ca-cert.pem certificate signed by the CA in migrate_tls_x509_cert_dir # (or default_tls_x509_cert_dir). John <grumble, grumble> if certificates were any less confusing they may actually be more widely used. It's really confusing that libvirtd expects one set of names, while a different set of names is expected by qemu - so while one could conceivably share "copied" .pem files one could not share the libvirtd and qemu TLS directories unless both files were present... qemu expects in say /etc/pki/qemu: ca-cert.pem client-cert.pem client-key.pem server-cert.pem server-key.pem libvirtd expects: /etc/pki/CA/cacert.pem /etc/pki/libvirt/clientcert.pem /etc/pki/libvirt/servercert.pem /etc/pki/libvirt/private/clientkey.pem /etc/pki/libvirt/private/serverkey.pem
+# +#migrate_tls_x509_verify = 1 ...
ACK with the comments fixed.
Jirka

On Wed, Mar 22, 2017 at 14:52:28 -0400, John Ferlan wrote:
On 03/22/2017 12:26 PM, Jiri Denemark wrote:
On Fri, Mar 17, 2017 at 14:38:56 -0400, John Ferlan wrote:
Add a new TLS X.509 certificate type - "migrate". This will handle the creation of a TLS certificate capability (and possibly repository) to be used for migrations. Similar to chardev's, credentials will be handled via a libvirt secrets; however, unlike chardev's enablement and usage will be via a CLI flag instead of a conf flag and a domain XML attribute. The migrations will also require the client-cert.pem and client-key.pem files to be present in the clients TLS directory.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/libvirtd_qemu.aug | 5 +++++ src/qemu/qemu.conf | 37 +++++++++++++++++++++++++++++++++++++ src/qemu/qemu_conf.c | 6 ++++++ src/qemu/qemu_conf.h | 4 ++++ src/qemu/test_libvirtd_qemu.aug.in | 3 +++ 5 files changed, 55 insertions(+)
diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf index 9925ac9..40bcec3 100644 --- a/src/qemu/qemu.conf +++ b/src/qemu/qemu.conf ... +# In order to override the default TLS certificate location for migration +# certificates, supply a valid path to the certificate directory. If the +# provided path does not exist then the default_tls_x509_cert_dir path +# will be used. Once/if a default certificate is enabled/defined, migration +# will then be able to use the certificate via migration API flags. +# +#migrate_tls_x509_cert_dir = "/etc/pki/libvirt-migrate" + + +# The default TLS configuration only uses certificates for the server +# allowing the client to verify the server's identity and establish +# an encrypted channel. +# +# It is possible to use x509 certificates for authentication too, by +# issuing a x509 certificate to every client who needs to connect.
s/a x509/an x509/
+# +# Enabling this option will reject any client who does not have a +# certificate signed by the CA in /etc/pki/libvirt-migrate/ca-cert.pem
"ca-cert.pem in migrate_tls_x509_cert_dir" or something like that. Mentioning /etc/pki/libvirt-migrate might be quite confusing.
The is a cut-n-paste of the libvirt-vnc and libvirt-chardev - would you like to see those changed as well (in a separate patch).
Yeah, I think it would make sense to fix them too. Jirka

Signed-off-by: John Ferlan <jferlan@redhat.com> --- include/libvirt/libvirt-domain.h | 8 ++++++++ src/qemu/qemu_migration.h | 3 ++- tools/virsh-domain.c | 7 +++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index c490d71..620606c 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -815,6 +815,14 @@ typedef enum { * post-copy mode. See virDomainMigrateStartPostCopy for more details. */ 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. + */ + VIR_MIGRATE_TLS = (1 << 16), + } virDomainMigrateFlags; diff --git a/src/qemu/qemu_migration.h b/src/qemu/qemu_migration.h index 14c6178..bcebf06 100644 --- a/src/qemu/qemu_migration.h +++ b/src/qemu/qemu_migration.h @@ -45,7 +45,8 @@ typedef qemuMigrationCompression *qemuMigrationCompressionPtr; VIR_MIGRATE_ABORT_ON_ERROR | \ VIR_MIGRATE_AUTO_CONVERGE | \ VIR_MIGRATE_RDMA_PIN_ALL | \ - VIR_MIGRATE_POSTCOPY) + VIR_MIGRATE_POSTCOPY | \ + VIR_MIGRATE_TLS) /* All supported migration parameters and their types. */ # define QEMU_MIGRATION_PARAMETERS \ diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 09a9f82..ebd4b33 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -10222,6 +10222,10 @@ static const vshCmdOptDef opts_migrate[] = { .type = VSH_OT_STRING, .help = N_("filename containing updated persistent XML for the target") }, + {.name = "tls", + .type = VSH_OT_BOOL, + .help = N_("use TLS for migration") + }, {.name = NULL} }; @@ -10463,6 +10467,9 @@ doMigrate(void *opaque) if (vshCommandOptBool(cmd, "postcopy")) flags |= VIR_MIGRATE_POSTCOPY; + if (vshCommandOptBool(cmd, "tls")) + flags |= VIR_MIGRATE_TLS; + if (flags & VIR_MIGRATE_PEER2PEER || vshCommandOptBool(cmd, "direct")) { if (virDomainMigrateToURI3(dom, desturi, params, nparams, flags) == 0) ret = '0'; -- 2.9.3

On Fri, Mar 17, 2017 at 14:38:57 -0400, John Ferlan wrote:
Signed-off-by: John Ferlan <jferlan@redhat.com> --- include/libvirt/libvirt-domain.h | 8 ++++++++ src/qemu/qemu_migration.h | 3 ++- tools/virsh-domain.c | 7 +++++++ 3 files changed, 17 insertions(+), 1 deletion(-)
ACK Jirka

Add the fields to support setting tls-creds and tls-hostname during a migration (either source or target). Modify the query migration function to check for the presence and set the field for future consumers to determine which of 3 conditions is being met (not present, present and set to "", or present and sent to something). Modify code paths that either allocate or use stack space in order to call qemuMigrationParamsClear or qemuMigrationParamsFree for cleanup. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_driver.c | 4 +++- src/qemu/qemu_migration.c | 26 +++++++++++++++++++++++++- src/qemu/qemu_migration.h | 6 ++++++ src/qemu/qemu_monitor.c | 11 ++++++++--- src/qemu/qemu_monitor.h | 3 +++ src/qemu/qemu_monitor_json.c | 28 ++++++++++++++++++++++++++++ tests/qemumonitorjsontest.c | 25 ++++++++++++++++++++++++- 7 files changed, 97 insertions(+), 6 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index dcd823f..03e3f8d 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -11845,6 +11845,7 @@ qemuDomainMigratePerform(virDomainPtr dom, flags, dname, resource, false); cleanup: + qemuMigrationParamsClear(&migParams); VIR_FREE(compression); return ret; } @@ -12253,6 +12254,7 @@ qemuDomainMigratePerform3(virDomainPtr dom, flags, dname, resource, true); cleanup: + qemuMigrationParamsClear(&migParams); VIR_FREE(compression); return ret; } @@ -12343,7 +12345,7 @@ qemuDomainMigratePerform3Params(virDomainPtr dom, flags, dname, bandwidth, true); cleanup: VIR_FREE(compression); - VIR_FREE(migParams); + qemuMigrationParamsFree(&migParams); VIR_FREE(migrate_disks); return ret; } diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index f5711bc..66a5062 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -3508,6 +3508,28 @@ qemuMigrationSetCompression(virQEMUDriverPtr driver, } +void +qemuMigrationParamsClear(qemuMonitorMigrationParamsPtr migParams) +{ + if (!migParams) + return; + + VIR_FREE(migParams->migrateTLSAlias); + VIR_FREE(migParams->migrateTLSHostname); +} + + +void +qemuMigrationParamsFree(qemuMonitorMigrationParamsPtr *migParams) +{ + if (!*migParams) + return; + + qemuMigrationParamsClear(*migParams); + VIR_FREE(*migParams); +} + + qemuMonitorMigrationParamsPtr qemuMigrationParams(virTypedParameterPtr params, int nparams, @@ -3549,7 +3571,7 @@ qemuMigrationParams(virTypedParameterPtr params, return migParams; error: - VIR_FREE(migParams); + qemuMigrationParamsFree(&migParams); return NULL; } @@ -3909,6 +3931,7 @@ qemuMigrationPrepareAny(virQEMUDriverPtr driver, virDomainObjRemoveTransientDef(vm); qemuDomainRemoveInactive(driver, vm); } + qemuMigrationParamsClear(&migParams); virDomainObjEndAPI(&vm); qemuDomainEventQueue(driver, event); qemuMigrationCookieFree(mig); @@ -5244,6 +5267,7 @@ static int doPeer2PeerMigrate2(virQEMUDriverPtr driver, virSetError(orig_err); virFreeError(orig_err); } + qemuMigrationParamsClear(&migParams); VIR_FREE(uri_out); VIR_FREE(cookie); VIR_FREE(compression); diff --git a/src/qemu/qemu_migration.h b/src/qemu/qemu_migration.h index bcebf06..4c8f2c9 100644 --- a/src/qemu/qemu_migration.h +++ b/src/qemu/qemu_migration.h @@ -121,6 +121,12 @@ int qemuMigrationCompressionDump(qemuMigrationCompressionPtr compression, int *maxparams, unsigned long *flags); +void +qemuMigrationParamsClear(qemuMonitorMigrationParamsPtr migParams); + +void +qemuMigrationParamsFree(qemuMonitorMigrationParamsPtr *migParams); + qemuMonitorMigrationParamsPtr qemuMigrationParams(virTypedParameterPtr params, int nparams, diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 79da472..ee0e116 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -2530,12 +2530,15 @@ qemuMonitorSetMigrationParams(qemuMonitorPtr mon, { VIR_DEBUG("compressLevel=%d:%d compressThreads=%d:%d " "decompressThreads=%d:%d cpuThrottleInitial=%d:%d " - "cpuThrottleIncrement=%d:%d", + "cpuThrottleIncrement=%d:%d tlsAlias=%s " + "tlsHostname=%s", params->compressLevel_set, params->compressLevel, params->compressThreads_set, params->compressThreads, params->decompressThreads_set, params->decompressThreads, params->cpuThrottleInitial_set, params->cpuThrottleInitial, - params->cpuThrottleIncrement_set, params->cpuThrottleIncrement); + params->cpuThrottleIncrement_set, params->cpuThrottleIncrement, + NULLSTR(params->migrateTLSAlias), + NULLSTR(params->migrateTLSHostname)); QEMU_CHECK_MONITOR_JSON(mon); @@ -2543,7 +2546,9 @@ qemuMonitorSetMigrationParams(qemuMonitorPtr mon, !params->compressThreads_set && !params->decompressThreads_set && !params->cpuThrottleInitial_set && - !params->cpuThrottleIncrement_set) + !params->cpuThrottleIncrement_set && + !params->migrateTLSAlias && + !params->migrateTLSHostname) return 0; return qemuMonitorJSONSetMigrationParams(mon, params); diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index c3d3f2f..315f361 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -571,6 +571,9 @@ struct _qemuMonitorMigrationParams { bool cpuThrottleIncrement_set; int cpuThrottleIncrement; + + char *migrateTLSAlias; + char *migrateTLSHostname; }; int qemuMonitorGetMigrationParams(qemuMonitorPtr mon, diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 553544a..125cc6a 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -2566,6 +2566,7 @@ qemuMonitorJSONGetMigrationParams(qemuMonitorPtr mon, virJSONValuePtr result; virJSONValuePtr cmd = NULL; virJSONValuePtr reply = NULL; + const char *tlsStr = NULL; memset(params, 0, sizeof(*params)); @@ -2595,6 +2596,21 @@ qemuMonitorJSONGetMigrationParams(qemuMonitorPtr mon, #undef PARSE + /* NB: First supported in QEMU 2.7; however, there was no way to + * clear, so 2.9 altered the definition to allow using an empty string + * to disable. Additionally, it defined the variable to an empty string + * by default if not defined ever. Use this as our marker to determine + * whether TLS can be supported or not. */ + if ((tlsStr = virJSONValueObjectGetString(result, "tls-creds"))) { + if (VIR_STRDUP(params->migrateTLSAlias, tlsStr) < 0) + goto cleanup; + } + + if ((tlsStr = virJSONValueObjectGetString(result, "tls-hostname"))) { + if (VIR_STRDUP(params->migrateTLSHostname, tlsStr) < 0) + goto cleanup; + } + ret = 0; cleanup: virJSONValueFree(cmd); @@ -2637,6 +2653,18 @@ qemuMonitorJSONSetMigrationParams(qemuMonitorPtr mon, #undef APPEND + /* See query, value will be either NULL, "", or something valid. + * NULL will indicate no support, while "" will indicate to disable */ + if (params->migrateTLSAlias && + virJSONValueObjectAppendString(args, "tls-creds", + params->migrateTLSAlias) < 0) + goto cleanup; + + if (params->migrateTLSHostname && + virJSONValueObjectAppendString(args, "tls-hostname", + params->migrateTLSHostname) < 0) + goto cleanup; + if (virJSONValueObjectAppend(cmd, "arguments", args) < 0) goto cleanup; args = NULL; diff --git a/tests/qemumonitorjsontest.c b/tests/qemumonitorjsontest.c index d0f9381..0f802eb 100644 --- a/tests/qemumonitorjsontest.c +++ b/tests/qemumonitorjsontest.c @@ -1789,7 +1789,9 @@ testQemuMonitorJSONqemuMonitorJSONGetMigrationParams(const void *data) " \"cpu-throttle-increment\": 10," " \"compress-threads\": 8," " \"compress-level\": 1," - " \"cpu-throttle-initial\": 20" + " \"cpu-throttle-initial\": 20," + " \"tls-creds\": \"tls0\"," + " \"tls-hostname\": \"\"" " }" "}") < 0) { goto cleanup; @@ -1821,9 +1823,30 @@ testQemuMonitorJSONqemuMonitorJSONGetMigrationParams(const void *data) #undef CHECK +#define CHECK(VAR, FIELD, VALUE) \ + do { \ + if (!params.VAR) { \ + virReportError(VIR_ERR_INTERNAL_ERROR, "%s is not set", FIELD); \ + goto cleanup; \ + } \ + if (STRNEQ(params.VAR, VALUE)) { \ + virReportError(VIR_ERR_INTERNAL_ERROR, \ + "Invalid %s:'%s', expected '%s'", \ + FIELD, params.VAR, VALUE); \ + goto cleanup; \ + } \ + } while (0) + + CHECK(migrateTLSAlias, "tls-creds", "tls0"); + CHECK(migrateTLSHostname, "tls-hostname", ""); + +#undef CHECK + ret = 0; cleanup: + VIR_FREE(params.migrateTLSAlias); + VIR_FREE(params.migrateTLSHostname); qemuMonitorTestFree(test); return ret; } -- 2.9.3

On Fri, Mar 17, 2017 at 14:38:58 -0400, John Ferlan wrote:
Add the fields to support setting tls-creds and tls-hostname during a migration (either source or target). Modify the query migration function to check for the presence and set the field for future consumers to determine which of 3 conditions is being met (not present, present and set to "", or present and sent to something).
Modify code paths that either allocate or use stack space in order to call qemuMigrationParamsClear or qemuMigrationParamsFree for cleanup.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_driver.c | 4 +++- src/qemu/qemu_migration.c | 26 +++++++++++++++++++++++++- src/qemu/qemu_migration.h | 6 ++++++ src/qemu/qemu_monitor.c | 11 ++++++++--- src/qemu/qemu_monitor.h | 3 +++ src/qemu/qemu_monitor_json.c | 28 ++++++++++++++++++++++++++++ tests/qemumonitorjsontest.c | 25 ++++++++++++++++++++++++- 7 files changed, 97 insertions(+), 6 deletions(-) ... diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index f5711bc..66a5062 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -3508,6 +3508,28 @@ qemuMigrationSetCompression(virQEMUDriverPtr driver, }
+void +qemuMigrationParamsClear(qemuMonitorMigrationParamsPtr migParams) +{ + if (!migParams) + return; + + VIR_FREE(migParams->migrateTLSAlias); + VIR_FREE(migParams->migrateTLSHostname); +} + + +void +qemuMigrationParamsFree(qemuMonitorMigrationParamsPtr *migParams)
Our *Free functions don't usually get double pointers.
+{ + if (!*migParams) + return; + + qemuMigrationParamsClear(*migParams); + VIR_FREE(*migParams); +} + + qemuMonitorMigrationParamsPtr qemuMigrationParams(virTypedParameterPtr params, int nparams, ... diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 553544a..125cc6a 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c ... @@ -2595,6 +2596,21 @@ qemuMonitorJSONGetMigrationParams(qemuMonitorPtr mon,
#undef PARSE
+ /* NB: First supported in QEMU 2.7; however, there was no way to + * clear, so 2.9 altered the definition to allow using an empty string + * to disable. Additionally, it defined the variable to an empty string + * by default if not defined ever. Use this as our marker to determine + * whether TLS can be supported or not. */
This comment could make some sense in the commit message (unlike describing which paths are changed by the patch), but I don't think it's any useful here. Describing that NULL means unsupported and "" means unset would be enough I think. And even better if this is documented inside struct _qemuMonitorMigrationParams.
+ if ((tlsStr = virJSONValueObjectGetString(result, "tls-creds"))) { + if (VIR_STRDUP(params->migrateTLSAlias, tlsStr) < 0) + goto cleanup; + } + + if ((tlsStr = virJSONValueObjectGetString(result, "tls-hostname"))) { + if (VIR_STRDUP(params->migrateTLSHostname, tlsStr) < 0) + goto cleanup; + } + ret = 0; cleanup: virJSONValueFree(cmd); @@ -2637,6 +2653,18 @@ qemuMonitorJSONSetMigrationParams(qemuMonitorPtr mon,
#undef APPEND
+ /* See query, value will be either NULL, "", or something valid. + * NULL will indicate no support, while "" will indicate to disable */
Yeah, this is what I was thinking about (except for the "See query" part). And I still think it would make sense to move it to struct _qemuMonitorMigrationParams.
+ if (params->migrateTLSAlias && + virJSONValueObjectAppendString(args, "tls-creds", + params->migrateTLSAlias) < 0) + goto cleanup; + + if (params->migrateTLSHostname && + virJSONValueObjectAppendString(args, "tls-hostname", + params->migrateTLSHostname) < 0) + goto cleanup; + if (virJSONValueObjectAppend(cmd, "arguments", args) < 0) goto cleanup; args = NULL;
Jirka

On 03/22/2017 12:26 PM, Jiri Denemark wrote:
On Fri, Mar 17, 2017 at 14:38:58 -0400, John Ferlan wrote:
Add the fields to support setting tls-creds and tls-hostname during a migration (either source or target). Modify the query migration function to check for the presence and set the field for future consumers to determine which of 3 conditions is being met (not present, present and set to "", or present and sent to something).
Modify code paths that either allocate or use stack space in order to call qemuMigrationParamsClear or qemuMigrationParamsFree for cleanup.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_driver.c | 4 +++- src/qemu/qemu_migration.c | 26 +++++++++++++++++++++++++- src/qemu/qemu_migration.h | 6 ++++++ src/qemu/qemu_monitor.c | 11 ++++++++--- src/qemu/qemu_monitor.h | 3 +++ src/qemu/qemu_monitor_json.c | 28 ++++++++++++++++++++++++++++ tests/qemumonitorjsontest.c | 25 ++++++++++++++++++++++++- 7 files changed, 97 insertions(+), 6 deletions(-) ... diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index f5711bc..66a5062 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -3508,6 +3508,28 @@ qemuMigrationSetCompression(virQEMUDriverPtr driver, }
+void +qemuMigrationParamsClear(qemuMonitorMigrationParamsPtr migParams) +{ + if (!migParams) + return; + + VIR_FREE(migParams->migrateTLSAlias); + VIR_FREE(migParams->migrateTLSHostname); +} + + +void +qemuMigrationParamsFree(qemuMonitorMigrationParamsPtr *migParams)
Our *Free functions don't usually get double pointers.
True, but that's not necessarily a correct approach *and* we've been bitten by use after free before too. Since the VIR_FREE() operates on a local variable, only this function would see migParams being set to NULL - the caller though would not see that and thus (as in other cases) we are forced to place a migParams = NULL; after a vir*Free() call. I prefer this mechanism and quite frankly would like to see other vir*Free() functions follow this, but I don't have the time or desire to write that pile of patches.
+{ + if (!*migParams) + return; + + qemuMigrationParamsClear(*migParams); + VIR_FREE(*migParams); +} + + qemuMonitorMigrationParamsPtr qemuMigrationParams(virTypedParameterPtr params, int nparams, ... diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 553544a..125cc6a 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c ... @@ -2595,6 +2596,21 @@ qemuMonitorJSONGetMigrationParams(qemuMonitorPtr mon,
#undef PARSE
+ /* NB: First supported in QEMU 2.7; however, there was no way to + * clear, so 2.9 altered the definition to allow using an empty string + * to disable. Additionally, it defined the variable to an empty string + * by default if not defined ever. Use this as our marker to determine + * whether TLS can be supported or not. */
This comment could make some sense in the commit message (unlike describing which paths are changed by the patch), but I don't think it's any useful here. Describing that NULL means unsupported and "" means unset would be enough I think. And even better if this is documented inside struct _qemuMonitorMigrationParams.
I didn't want to see it lost as it's a really important distinction. I will move into the struct. I disagree about having stuff like this in a commit message. When I'm reading code - I'm not reading it as part of a commit message, I'm reading it literally. The one concern I'd have about moving it to the struct is someone not reading it... John
+ if ((tlsStr = virJSONValueObjectGetString(result, "tls-creds"))) { + if (VIR_STRDUP(params->migrateTLSAlias, tlsStr) < 0) + goto cleanup; + } + + if ((tlsStr = virJSONValueObjectGetString(result, "tls-hostname"))) { + if (VIR_STRDUP(params->migrateTLSHostname, tlsStr) < 0) + goto cleanup; + } + ret = 0; cleanup: virJSONValueFree(cmd); @@ -2637,6 +2653,18 @@ qemuMonitorJSONSetMigrationParams(qemuMonitorPtr mon,
#undef APPEND
+ /* See query, value will be either NULL, "", or something valid. + * NULL will indicate no support, while "" will indicate to disable */
Yeah, this is what I was thinking about (except for the "See query" part). And I still think it would make sense to move it to struct _qemuMonitorMigrationParams.
+ if (params->migrateTLSAlias && + virJSONValueObjectAppendString(args, "tls-creds", + params->migrateTLSAlias) < 0) + goto cleanup; + + if (params->migrateTLSHostname && + virJSONValueObjectAppendString(args, "tls-hostname", + params->migrateTLSHostname) < 0) + goto cleanup; + if (virJSONValueObjectAppend(cmd, "arguments", args) < 0) goto cleanup; args = NULL;
Jirka

Add an asyncJob argument for add/delete TLS Objects. A future patch will add/delete TLS objects from a migration which may hae a job to join. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_hotplug.c | 24 ++++++++++++++++-------- src/qemu/qemu_hotplug.h | 2 ++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index ddcbc5e..9adb04a 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1531,6 +1531,7 @@ qemuDomainAttachHostPCIDevice(virQEMUDriverPtr driver, void qemuDomainDelTLSObjects(virQEMUDriverPtr driver, virDomainObjPtr vm, + qemuDomainAsyncJob asyncJob, const char *secAlias, const char *tlsAlias) { @@ -1542,7 +1543,8 @@ qemuDomainDelTLSObjects(virQEMUDriverPtr driver, orig_err = virSaveLastError(); - qemuDomainObjEnterMonitor(driver, vm); + if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0) + goto cleanup; if (tlsAlias) ignore_value(qemuMonitorDelObject(priv->mon, tlsAlias)); @@ -1552,6 +1554,7 @@ qemuDomainDelTLSObjects(virQEMUDriverPtr driver, ignore_value(qemuDomainObjExitMonitor(driver, vm)); + cleanup: if (orig_err) { virSetError(orig_err); virFreeError(orig_err); @@ -1562,6 +1565,7 @@ qemuDomainDelTLSObjects(virQEMUDriverPtr driver, int qemuDomainAddTLSObjects(virQEMUDriverPtr driver, virDomainObjPtr vm, + qemuDomainAsyncJob asyncJob, const char *secAlias, virJSONValuePtr *secProps, const char *tlsAlias, @@ -1574,7 +1578,8 @@ qemuDomainAddTLSObjects(virQEMUDriverPtr driver, if (!tlsAlias && !secAlias) return 0; - qemuDomainObjEnterMonitor(driver, vm); + if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0) + return -1; if (secAlias) { rc = qemuMonitorAddObject(priv->mon, "secret", @@ -1601,7 +1606,7 @@ qemuDomainAddTLSObjects(virQEMUDriverPtr driver, virSetError(orig_err); virFreeError(orig_err); } - qemuDomainDelTLSObjects(driver, vm, secAlias, tlsAlias); + qemuDomainDelTLSObjects(driver, vm, asyncJob, secAlias, tlsAlias); return -1; } @@ -1682,8 +1687,8 @@ qemuDomainAddChardevTLSObjects(virConnectPtr conn, goto cleanup; dev->data.tcp.tlscreds = true; - if (qemuDomainAddTLSObjects(driver, vm, *secAlias, &secProps, - *tlsAlias, &tlsProps) < 0) + if (qemuDomainAddTLSObjects(driver, vm, QEMU_ASYNC_JOB_NONE, + *secAlias, &secProps, *tlsAlias, &tlsProps) < 0) goto cleanup; ret = 0; @@ -1773,7 +1778,8 @@ int qemuDomainAttachRedirdevDevice(virConnectPtr conn, virSetError(orig_err); virFreeError(orig_err); } - qemuDomainDelTLSObjects(driver, vm, secAlias, tlsAlias); + qemuDomainDelTLSObjects(driver, vm, QEMU_ASYNC_JOB_NONE, + secAlias, tlsAlias); goto audit; } @@ -2034,7 +2040,8 @@ int qemuDomainAttachChrDevice(virConnectPtr conn, virFreeError(orig_err); } - qemuDomainDelTLSObjects(driver, vm, secAlias, tlsAlias); + qemuDomainDelTLSObjects(driver, vm, QEMU_ASYNC_JOB_NONE, + secAlias, tlsAlias); goto audit; } @@ -2186,7 +2193,8 @@ qemuDomainAttachRNGDevice(virConnectPtr conn, virFreeError(orig_err); } - qemuDomainDelTLSObjects(driver, vm, secAlias, tlsAlias); + qemuDomainDelTLSObjects(driver, vm, QEMU_ASYNC_JOB_NONE, + secAlias, tlsAlias); goto audit; } diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h index 73f2b1f..f06f232 100644 --- a/src/qemu/qemu_hotplug.h +++ b/src/qemu/qemu_hotplug.h @@ -36,11 +36,13 @@ int qemuDomainChangeEjectableMedia(virQEMUDriverPtr driver, void qemuDomainDelTLSObjects(virQEMUDriverPtr driver, virDomainObjPtr vm, + qemuDomainAsyncJob asyncJob, const char *secAlias, const char *tlsAlias); int qemuDomainAddTLSObjects(virQEMUDriverPtr driver, virDomainObjPtr vm, + qemuDomainAsyncJob asyncJob, const char *secAlias, virJSONValuePtr *secProps, const char *tlsAlias, -- 2.9.3

On Fri, Mar 17, 2017 at 14:38:59 -0400, John Ferlan wrote:
Add an asyncJob argument for add/delete TLS Objects. A future patch will add/delete TLS objects from a migration which may hae a job to join.
s/hae/have/ I guess.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_hotplug.c | 24 ++++++++++++++++-------- src/qemu/qemu_hotplug.h | 2 ++ 2 files changed, 18 insertions(+), 8 deletions(-)
ACK Jirka

If the migration flags indicate this migration will be using TLS, then set up the destination during the prepare phase once the target domain has been started to add the TLS objects to perform the migration. This will create at least an "-object tls-creds-x509,endpoint=server,..." and potentially an "-object secret,..." to handle the passphrase response to access the TLS credentials. The alias/id used for the TLS objects will contain "libvirt_migrate" as a mechanism to signify that libvirt started the migration on the target (reaping benefits possibly). Once the objects are created, the code will set the "tls-creds" and "tls-hostname" migration parameters to signify usage of TLS. During the Finish phase we'll be sure to attempt to clear the migration parameters and delete those objects (whether or not they were created). Since incoming migrations that don't reach the Finish stage will be killed in qemuProcessRecoverMigrationIn and the only purpose at that point would be to free memory, it's not necessary to set up any sort of recovery. Additionally, subsequent migrations will check if the migration parameters are set and adjust them appropriately if for some reason libvirtd restarts after setting the Finish marker, but before actually resetting the environment. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_domain.c | 7 +- src/qemu/qemu_domain.h | 91 +++++++----- src/qemu/qemu_migration.c | 344 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 403 insertions(+), 39 deletions(-) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index c239a06..f4636ed 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -782,7 +782,7 @@ qemuDomainSecretAESClear(qemuDomainSecretAES secret) } -static void +void qemuDomainSecretInfoFree(qemuDomainSecretInfoPtr *secinfo) { if (!*secinfo) @@ -1186,7 +1186,7 @@ qemuDomainSecretInfoNew(virConnectPtr conn, * * Returns qemuDomainSecretInfoPtr or NULL on error. */ -static qemuDomainSecretInfoPtr +qemuDomainSecretInfoPtr qemuDomainSecretInfoTLSNew(virConnectPtr conn, qemuDomainObjPrivatePtr priv, const char *srcAlias, @@ -1677,6 +1677,9 @@ qemuDomainObjPrivateFree(void *data) VIR_FREE(priv->libDir); VIR_FREE(priv->channelTargetDir); + + qemuDomainSecretInfoFree(&priv->migSecinfo); + VIR_FREE(priv->migTLSAlias); qemuDomainMasterKeyFree(priv); VIR_FREE(priv); diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 1f266bf..1dd3b1c 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -175,6 +175,43 @@ VIR_ENUM_DECL(qemuDomainNamespace) bool qemuDomainNamespaceEnabled(virDomainObjPtr vm, qemuDomainNamespace ns); +/* Type of domain secret */ +typedef enum { + VIR_DOMAIN_SECRET_INFO_TYPE_PLAIN = 0, + VIR_DOMAIN_SECRET_INFO_TYPE_AES, /* utilize GNUTLS_CIPHER_AES_256_CBC */ + + VIR_DOMAIN_SECRET_INFO_TYPE_LAST +} qemuDomainSecretInfoType; + +typedef struct _qemuDomainSecretPlain qemuDomainSecretPlain; +typedef struct _qemuDomainSecretPlain *qemuDomainSecretPlainPtr; +struct _qemuDomainSecretPlain { + char *username; + uint8_t *secret; + size_t secretlen; +}; + +# define QEMU_DOMAIN_AES_IV_LEN 16 /* 16 bytes for 128 bit random */ + /* initialization vector */ +typedef struct _qemuDomainSecretAES qemuDomainSecretAES; +typedef struct _qemuDomainSecretAES *qemuDomainSecretAESPtr; +struct _qemuDomainSecretAES { + char *username; + char *alias; /* generated alias for secret */ + char *iv; /* base64 encoded initialization vector */ + char *ciphertext; /* encoded/encrypted secret */ +}; + +typedef struct _qemuDomainSecretInfo qemuDomainSecretInfo; +typedef qemuDomainSecretInfo *qemuDomainSecretInfoPtr; +struct _qemuDomainSecretInfo { + qemuDomainSecretInfoType type; + union { + qemuDomainSecretPlain plain; + qemuDomainSecretAES aes; + } s; +}; + typedef struct _qemuDomainObjPrivate qemuDomainObjPrivate; typedef qemuDomainObjPrivate *qemuDomainObjPrivatePtr; struct _qemuDomainObjPrivate { @@ -246,47 +283,18 @@ struct _qemuDomainObjPrivate { /* note whether memory device alias does not correspond to slot number */ bool memAliasOrderMismatch; -}; -# define QEMU_DOMAIN_PRIVATE(vm) \ - ((qemuDomainObjPrivatePtr) (vm)->privateData) + /* for migrations using TLS with a secret (not to be saved in our */ + /* private XML). */ + qemuDomainSecretInfoPtr migSecinfo; -/* Type of domain secret */ -typedef enum { - VIR_DOMAIN_SECRET_INFO_TYPE_PLAIN = 0, - VIR_DOMAIN_SECRET_INFO_TYPE_AES, /* utilize GNUTLS_CIPHER_AES_256_CBC */ - - VIR_DOMAIN_SECRET_INFO_TYPE_LAST -} qemuDomainSecretInfoType; - -typedef struct _qemuDomainSecretPlain qemuDomainSecretPlain; -typedef struct _qemuDomainSecretPlain *qemuDomainSecretPlainPtr; -struct _qemuDomainSecretPlain { - char *username; - uint8_t *secret; - size_t secretlen; + /* Used when fetching/storing the current 'tls-creds' migration setting */ + /* (not to be saved in our private XML). */ + char *migTLSAlias; }; -# define QEMU_DOMAIN_AES_IV_LEN 16 /* 16 bytes for 128 bit random */ - /* initialization vector */ -typedef struct _qemuDomainSecretAES qemuDomainSecretAES; -typedef struct _qemuDomainSecretAES *qemuDomainSecretAESPtr; -struct _qemuDomainSecretAES { - char *username; - char *alias; /* generated alias for secret */ - char *iv; /* base64 encoded initialization vector */ - char *ciphertext; /* encoded/encrypted secret */ -}; - -typedef struct _qemuDomainSecretInfo qemuDomainSecretInfo; -typedef qemuDomainSecretInfo *qemuDomainSecretInfoPtr; -struct _qemuDomainSecretInfo { - qemuDomainSecretInfoType type; - union { - qemuDomainSecretPlain plain; - qemuDomainSecretAES aes; - } s; -}; +# define QEMU_DOMAIN_PRIVATE(vm) \ + ((qemuDomainObjPrivatePtr) (vm)->privateData) # define QEMU_DOMAIN_DISK_PRIVATE(disk) \ ((qemuDomainDiskPrivatePtr) (disk)->privateData) @@ -730,6 +738,9 @@ int qemuDomainMasterKeyCreate(virDomainObjPtr vm); void qemuDomainMasterKeyRemove(qemuDomainObjPrivatePtr priv); +void qemuDomainSecretInfoFree(qemuDomainSecretInfoPtr *secinfo) + ATTRIBUTE_NONNULL(1); + void qemuDomainSecretDiskDestroy(virDomainDiskDefPtr disk) ATTRIBUTE_NONNULL(1); @@ -739,6 +750,12 @@ bool qemuDomainSecretDiskCapable(virStorageSourcePtr src) bool qemuDomainDiskHasEncryptionSecret(virStorageSourcePtr src) ATTRIBUTE_NONNULL(1); +qemuDomainSecretInfoPtr +qemuDomainSecretInfoTLSNew(virConnectPtr conn, + qemuDomainObjPrivatePtr priv, + const char *srcAlias, + const char *secretUUID); + int qemuDomainSecretDiskPrepare(virConnectPtr conn, qemuDomainObjPrivatePtr priv, virDomainDiskDefPtr disk) diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 66a5062..42074f0 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -85,6 +85,8 @@ VIR_ENUM_IMPL(qemuMigrationCompressMethod, QEMU_MIGRATION_COMPRESS_LAST, "mt", ); +#define QEMU_MIGRATION_TLS_ALIAS_BASE "libvirt_migrate" + enum qemuMigrationCookieFlags { QEMU_MIGRATION_COOKIE_FLAG_GRAPHICS, QEMU_MIGRATION_COOKIE_FLAG_LOCKSTATE, @@ -1488,6 +1490,164 @@ qemuMigrationEatCookie(virQEMUDriverPtr driver, return NULL; } +/* qemuMigrationCheckTLSCreds + * @driver: pointer to qemu driver + * @vm: domain object + * @asyncJob: migration job to join + * + * Query the migration parameters looking for the 'tls-creds' parameter. + * The parameter was initially supported in QEMU 2.7; however, there was + * no mechanism provided to clear the parameter. For QEMU 2.9, a change + * was made to allow setting the parameter to an empty string in order + * to clear. An additional change was made to initialize the parameter + * to the empty string. Although still not perfect since it's possible + * that a pre-2.9 release set the string to something and we should not + * set it to the empty string, at least it's better than nothing. So let's + * check if the parameter has been set to something to detect the whether + * the parameter exists. If it's been set to something, then save the + * value in our private domain structures so that future decision makers + * can decide how they should proceed based upon the setting. + * + * Returns 0 if we were able to successfully fetch the params and + * additionally if the tls-creds parameter exists, saves it in the + * private domain structure. Returns -1 on failure. + */ +static int +qemuMigrationCheckTLSCreds(virQEMUDriverPtr driver, + virDomainObjPtr vm, + qemuDomainAsyncJob asyncJob) +{ + int ret = -1; + qemuDomainObjPrivatePtr priv = vm->privateData; + qemuMonitorMigrationParams migParams = { 0 }; + + if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0) + goto cleanup; + + if (qemuMonitorGetMigrationParams(priv->mon, &migParams) < 0) + goto cleanup; + + /* NB: Could steal NULL pointer too! Let caller decide what to do. */ + VIR_STEAL_PTR(priv->migTLSAlias, migParams.migrateTLSAlias); + + ret = 0; + + cleanup: + if (qemuDomainObjExitMonitor(driver, vm) < 0) + ret = -1; + + qemuMigrationParamsClear(&migParams); + + return ret; +} + + +/* qemuMigrationCheckSetupTLS + * @conn: Connection pointer + * @driver: pointer to qemu driver + * @vm: domain object + * @cfg: configuration pointer + * @asyncJob: migration job to join + * + * Check if TLS is possible and set up the environment. Assumes the caller + * desires to use TLS (e.g. caller found VIR_MIGRATE_TLS flag). + * + * Ensure the qemu.conf has been properly configured to add an entry for + * "migrate_tls_x509_cert_dir". Also check if the "tls-creds" parameter + * was present from a query of migration parameters + * + * Returns 0 on success, -1 on error/failure + */ +static int +qemuMigrationCheckSetupTLS(virConnectPtr conn, + virQEMUDriverPtr driver, + virQEMUDriverConfigPtr cfg, + virDomainObjPtr vm, + qemuDomainAsyncJob asyncJob) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + + if (!cfg->migrateTLSx509certdir) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("host migration TLS directory not configured")); + return -1; + } + + if (qemuMigrationCheckTLSCreds(driver, vm, asyncJob) < 0) + return -1; + + if (!priv->migTLSAlias) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("get/set empty migration parameter 'tls-creds' is " + "not supported")); + return -1; + } + + /* If there's a secret, then grab/store it now using the connection */ + if (cfg->migrateTLSx509secretUUID && + !(priv->migSecinfo = + qemuDomainSecretInfoTLSNew(conn, priv, QEMU_MIGRATION_TLS_ALIAS_BASE, + cfg->migrateTLSx509secretUUID))) + return -1; + + return 0; +} + + +/* qemuMigrationAddTLSObjects + * @driver: pointer to qemu driver + * @vm: domain object + * @cfg: configuration pointer + * @tlsListen: server or client + * @asyncJob: Migration job to join + * @tlsAlias: alias to be generated for TLS object + * @secAlias: alias to be generated for a secinfo object + * @migParams: migration parameters to set + * + * Create the TLS objects for the migration and set the migParams value + * + * Returns 0 on success, -1 on failure + */ +static int +qemuMigrationAddTLSObjects(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virQEMUDriverConfigPtr cfg, + bool tlsListen, + qemuDomainAsyncJob asyncJob, + char **tlsAlias, + char **secAlias, + qemuMonitorMigrationParamsPtr migParams) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + virJSONValuePtr tlsProps = NULL; + virJSONValuePtr secProps = NULL; + + if (qemuDomainGetTLSObjects(priv->qemuCaps, priv->migSecinfo, + cfg->migrateTLSx509certdir, tlsListen, + cfg->migrateTLSx509verify, + QEMU_MIGRATION_TLS_ALIAS_BASE, + &tlsProps, tlsAlias, &secProps, secAlias) < 0) + return -1; + + /* Ensure the domain doesn't already have the TLS objects defined... + * This should prevent any issues just in case some cleanup wasn't + * properly completed (both src and dst use the same alias) or + * some other error path between now and perform . */ + qemuDomainDelTLSObjects(driver, vm, asyncJob, *secAlias, *tlsAlias); + + /* Add the migrate TLS objects to the domain */ + if (qemuDomainAddTLSObjects(driver, vm, asyncJob, *secAlias, &secProps, + *tlsAlias, &tlsProps) < 0) + return -1; + + /* Set the param used for 'tls-creds' */ + if (VIR_STRDUP(migParams->migrateTLSAlias, *tlsAlias) < 0) + return -1; + + return 0; +} + + static void qemuMigrationStoreDomainState(virDomainObjPtr vm) { @@ -3530,6 +3690,47 @@ qemuMigrationParamsFree(qemuMonitorMigrationParamsPtr *migParams) } +/* qemuMigrationSetEmptyTLSParams + * @priv: Pointer to private domain data + * @migParams: Pointer to a migration parameters block + * + * If the qemuMigrationCheckTLSCreds query finds a non empty alias and it + * is set to the alias that libvirt set, then we need to set the migration + * parameters to "" in order to force clearing the TLS values from our + * previous migration that may not have been cleared properly if libvirtd + * restarted during the finish phase before the ResetTLSParams was run. + * + * Returns 0 on success, -1 on failure + */ +static int +qemuMigrationSetEmptyTLSParams(qemuDomainObjPrivatePtr priv, + qemuMonitorMigrationParamsPtr migParams) +{ + char *tlsAlias = NULL; + + if (priv->migTLSAlias) { + if (*priv->migTLSAlias == '\0') + return 0; + + if (!(tlsAlias = + qemuAliasTLSObjFromSrcAlias(QEMU_MIGRATION_TLS_ALIAS_BASE))) + return -1; + + if (STRNEQ(priv->migTLSAlias, tlsAlias)) { + VIR_FREE(tlsAlias); + return 0; + } + VIR_FREE(tlsAlias); + + if (VIR_STRDUP(migParams->migrateTLSAlias, "") < 0 || + VIR_STRDUP(migParams->migrateTLSHostname, "") < 0) + return -1; + } + + return 0; +} + + qemuMonitorMigrationParamsPtr qemuMigrationParams(virTypedParameterPtr params, int nparams, @@ -3601,6 +3802,110 @@ qemuMigrationSetParams(virQEMUDriverPtr driver, } +/* qemuMigrationResetTLSParams + * @driver: pointer to qemu driver + * @vm: domain object + * @asyncJob: migration job to join + * @tlsAlias: alias used for TLS object + * + * If we configured the migration TLS params, then let's clear the setting + * of the tls-creds and tls-hostname. + * + * Returns 0 on success, -1 on failure with error message set + */ +static int +qemuMigrationResetTLSParams(virQEMUDriverPtr driver, + virDomainObjPtr vm, + qemuDomainAsyncJob asyncJob, + const char *tlsAlias) +{ + int ret = -1; + qemuDomainObjPrivatePtr priv = vm->privateData; + qemuMonitorMigrationParams migParams = { 0 }; + + if (!priv->migTLSAlias) + return 0; + + if (STREQ_NULLABLE(priv->migTLSAlias, tlsAlias)) { + if (VIR_STRDUP(migParams.migrateTLSAlias, "") < 0 || + VIR_STRDUP(migParams.migrateTLSHostname, "") < 0) + goto cleanup; + + if (qemuMigrationSetParams(driver, vm, asyncJob, &migParams) < 0) + goto cleanup; + } + + ret = 0; + + cleanup: + qemuMigrationParamsClear(&migParams); + return ret; +} + + +/* qemuMigrationDeconstructTLS + * @driver: pointer to qemu driver + * @vm: domain object + * @asyncJob: migration job to join + * @tlsAlias: alias generated for TLS object + * @secAlias: alias generated for a secinfo object + * + * Deconstruct all the setup possibly done for TLS - various objects, secinfo, + * and migration parameters. + * + * Returns 0 on success, -1 on failure + */ +static int +qemuMigrationDeconstructTLS(virQEMUDriverPtr driver, + virDomainObjPtr vm, + qemuDomainAsyncJob asyncJob, + const char *tlsAlias, + const char *secAlias) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + + qemuDomainDelTLSObjects(driver, vm, asyncJob, secAlias, tlsAlias); + qemuDomainSecretInfoFree(&priv->migSecinfo); + + return qemuMigrationResetTLSParams(driver, vm, asyncJob, tlsAlias); +} + + +/* qemuMigrationResetTLS + * @driver: pointer to qemu driver + * @vm: domain object + * @asyncJob: migration job to join + * + * Wrapper to qemuMigrationDeconstructTLS that generates the expected + * tlsAlias and secAlias for migration paths without them set (e.g. Finish) + * + * Returns 0 on success, -1 on failure + */ +static int +qemuMigrationResetTLS(virQEMUDriverPtr driver, + virDomainObjPtr vm, + qemuDomainAsyncJob asyncJob) +{ + char *tlsAlias = NULL; + char *secAlias = NULL; + int ret; + + /* NB: If either or both fail to allocate memory we can still proceed + * since the next time we migrate another deletion attempt will be + * made after successfully generating the aliases. */ + tlsAlias = qemuAliasTLSObjFromSrcAlias(QEMU_MIGRATION_TLS_ALIAS_BASE); + secAlias = qemuDomainGetSecretAESAlias(QEMU_MIGRATION_TLS_ALIAS_BASE, + false); + + ret = qemuMigrationDeconstructTLS(driver, vm, asyncJob, tlsAlias, secAlias); + + VIR_FREE(tlsAlias); + VIR_FREE(secAlias); + + return ret; +} + + static int qemuMigrationPrepareAny(virQEMUDriverPtr driver, virConnectPtr dconn, @@ -3623,6 +3928,7 @@ qemuMigrationPrepareAny(virQEMUDriverPtr driver, { virDomainObjPtr vm = NULL; virObjectEventPtr event = NULL; + virQEMUDriverConfigPtr cfg = NULL; int ret = -1; int dataFD[2] = { -1, -1 }; qemuDomainObjPrivatePtr priv = NULL; @@ -3636,6 +3942,8 @@ qemuMigrationPrepareAny(virQEMUDriverPtr driver, bool stopProcess = false; bool relabel = false; int rv; + char *tlsAlias = NULL; + char *secAlias = NULL; qemuMonitorMigrationParams migParams = { 0 }; virNWFilterReadLockFilterUpdates(); @@ -3829,6 +4137,32 @@ qemuMigrationPrepareAny(virQEMUDriverPtr driver, compression, &migParams) < 0) goto stopjob; + /* Migrations using TLS need to add the "tls-creds-x509" object and + * set the migration TLS parameters */ + if (flags & VIR_MIGRATE_TLS) { + cfg = virQEMUDriverGetConfig(driver); + if (qemuMigrationCheckSetupTLS(dconn, driver, cfg, vm, + QEMU_ASYNC_JOB_MIGRATION_IN) < 0) + goto stopjob; + + if (qemuMigrationAddTLSObjects(driver, vm, cfg, true, + QEMU_ASYNC_JOB_MIGRATION_IN, + &tlsAlias, &secAlias, &migParams) < 0) + goto stopjob; + + /* Force reset of 'tls-hostname', just in case */ + if (VIR_STRDUP(migParams.migrateTLSHostname, "") < 0) + goto stopjob; + + } else { + /* If we support setting the tls-creds, be sure to always reset + * the migration parameters when this migration isn't using TLS */ + if ((qemuMigrationCheckTLSCreds(driver, vm, + QEMU_ASYNC_JOB_MIGRATION_IN) < 0) || + (qemuMigrationSetEmptyTLSParams(priv, &migParams) < 0)) + goto stopjob; + } + if (STREQ_NULLABLE(protocol, "rdma") && virProcessSetMaxMemLock(vm->pid, vm->def->mem.hard_limit << 10) < 0) { goto stopjob; @@ -3914,6 +4248,9 @@ qemuMigrationPrepareAny(virQEMUDriverPtr driver, ret = 0; cleanup: + VIR_FREE(tlsAlias); + VIR_FREE(secAlias); + virObjectUnref(cfg); qemuProcessIncomingDefFree(incoming); VIR_FREE(xmlout); VIR_FORCE_CLOSE(dataFD[0]); @@ -3940,6 +4277,10 @@ qemuMigrationPrepareAny(virQEMUDriverPtr driver, return ret; stopjob: + ignore_value(qemuMigrationDeconstructTLS(driver, vm, + QEMU_ASYNC_JOB_MIGRATION_IN, + tlsAlias, secAlias)); + if (stopProcess) { unsigned int stopFlags = VIR_QEMU_PROCESS_STOP_MIGRATED; if (!relabel) @@ -6415,6 +6756,9 @@ qemuMigrationFinish(virQEMUDriverPtr driver, QEMU_ASYNC_JOB_MIGRATION_IN); } + if (qemuMigrationResetTLS(driver, vm, QEMU_ASYNC_JOB_MIGRATION_IN) < 0) + goto endjob; + qemuMigrationJobFinish(driver, vm); if (!virDomainObjIsActive(vm)) qemuDomainRemoveInactive(driver, vm); -- 2.9.3

On Fri, Mar 17, 2017 at 14:39:00 -0400, John Ferlan wrote:
If the migration flags indicate this migration will be using TLS, then set up the destination during the prepare phase once the target domain has been started to add the TLS objects to perform the migration.
This will create at least an "-object tls-creds-x509,endpoint=server,..." and potentially an "-object secret,..." to handle the passphrase response
Looks like a leftover from previous versions where the objects were not hotplugged on both sides of migration.
to access the TLS credentials. The alias/id used for the TLS objects will contain "libvirt_migrate" as a mechanism to signify that libvirt started the migration on the target (reaping benefits possibly).
Once the objects are created, the code will set the "tls-creds" and "tls-hostname" migration parameters to signify usage of TLS.
During the Finish phase we'll be sure to attempt to clear the migration parameters and delete those objects (whether or not they were created).
Since incoming migrations that don't reach the Finish stage will be killed in qemuProcessRecoverMigrationIn and the only purpose at that point would be to free memory, it's not necessary to set up any sort of recovery. Additionally, subsequent migrations will check if the migration parameters are set and adjust them appropriately if for some reason libvirtd restarts after setting the Finish marker, but before actually resetting the environment.
Sure, migrations will do that, but what about snapshots, saving a domain to a file and similar stuff which internally uses migration too. Do we properly reset the parameters for them too? I think we should delete the objects and reset migration parameters in qemuProcessRecoverMigrationIn anyway.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_domain.c | 7 +- src/qemu/qemu_domain.h | 91 +++++++----- src/qemu/qemu_migration.c | 344 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 403 insertions(+), 39 deletions(-)
...
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 1f266bf..1dd3b1c 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h ... @@ -246,47 +283,18 @@ struct _qemuDomainObjPrivate {
/* note whether memory device alias does not correspond to slot number */ bool memAliasOrderMismatch; -};
-# define QEMU_DOMAIN_PRIVATE(vm) \ - ((qemuDomainObjPrivatePtr) (vm)->privateData) + /* for migrations using TLS with a secret (not to be saved in our */ + /* private XML). */ + qemuDomainSecretInfoPtr migSecinfo;
-/* Type of domain secret */ -typedef enum { - VIR_DOMAIN_SECRET_INFO_TYPE_PLAIN = 0, - VIR_DOMAIN_SECRET_INFO_TYPE_AES, /* utilize GNUTLS_CIPHER_AES_256_CBC */ - - VIR_DOMAIN_SECRET_INFO_TYPE_LAST -} qemuDomainSecretInfoType; - -typedef struct _qemuDomainSecretPlain qemuDomainSecretPlain; -typedef struct _qemuDomainSecretPlain *qemuDomainSecretPlainPtr; -struct _qemuDomainSecretPlain { - char *username; - uint8_t *secret; - size_t secretlen; + /* Used when fetching/storing the current 'tls-creds' migration setting */ + /* (not to be saved in our private XML). */ + char *migTLSAlias;
I'm not quite sure why we need to store the original value.
}; ... diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 66a5062..42074f0 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -85,6 +85,8 @@ VIR_ENUM_IMPL(qemuMigrationCompressMethod, QEMU_MIGRATION_COMPRESS_LAST, "mt", );
+#define QEMU_MIGRATION_TLS_ALIAS_BASE "libvirt_migrate" + enum qemuMigrationCookieFlags { QEMU_MIGRATION_COOKIE_FLAG_GRAPHICS, QEMU_MIGRATION_COOKIE_FLAG_LOCKSTATE, @@ -1488,6 +1490,164 @@ qemuMigrationEatCookie(virQEMUDriverPtr driver, return NULL; }
+/* qemuMigrationCheckTLSCreds + * @driver: pointer to qemu driver + * @vm: domain object + * @asyncJob: migration job to join + * + * Query the migration parameters looking for the 'tls-creds' parameter. + * The parameter was initially supported in QEMU 2.7; however, there was + * no mechanism provided to clear the parameter. For QEMU 2.9, a change + * was made to allow setting the parameter to an empty string in order + * to clear. An additional change was made to initialize the parameter + * to the empty string. Although still not perfect since it's possible + * that a pre-2.9 release set the string to something and we should not
How would this happen? QEMU won't magically set it to something by itself. And we don't need to care about someone messing up with the monitor or command line manually. Anyway, I don't see a real value in repeating the story over and over. We just ask for the default parameter values and if TLS parameters are not there, they are not supported from our point of view. That's pretty clear and version agnostic.
+ * set it to the empty string, at least it's better than nothing. So let's + * check if the parameter has been set to something to detect the whether + * the parameter exists. If it's been set to something, then save the + * value in our private domain structures so that future decision makers + * can decide how they should proceed based upon the setting. + * + * Returns 0 if we were able to successfully fetch the params and + * additionally if the tls-creds parameter exists, saves it in the + * private domain structure. Returns -1 on failure. + */ +static int +qemuMigrationCheckTLSCreds(virQEMUDriverPtr driver, + virDomainObjPtr vm, + qemuDomainAsyncJob asyncJob) +{ + int ret = -1; + qemuDomainObjPrivatePtr priv = vm->privateData; + qemuMonitorMigrationParams migParams = { 0 }; + + if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0) + goto cleanup; + + if (qemuMonitorGetMigrationParams(priv->mon, &migParams) < 0) + goto cleanup; + + /* NB: Could steal NULL pointer too! Let caller decide what to do. */ + VIR_STEAL_PTR(priv->migTLSAlias, migParams.migrateTLSAlias); + + ret = 0; + + cleanup: + if (qemuDomainObjExitMonitor(driver, vm) < 0) + ret = -1; + + qemuMigrationParamsClear(&migParams); + + return ret; +} + + +/* qemuMigrationCheckSetupTLS + * @conn: Connection pointer + * @driver: pointer to qemu driver + * @vm: domain object + * @cfg: configuration pointer + * @asyncJob: migration job to join + * + * Check if TLS is possible and set up the environment. Assumes the caller + * desires to use TLS (e.g. caller found VIR_MIGRATE_TLS flag). + * + * Ensure the qemu.conf has been properly configured to add an entry for + * "migrate_tls_x509_cert_dir". Also check if the "tls-creds" parameter + * was present from a query of migration parameters + * + * Returns 0 on success, -1 on error/failure + */ +static int +qemuMigrationCheckSetupTLS(virConnectPtr conn, + virQEMUDriverPtr driver, + virQEMUDriverConfigPtr cfg, + virDomainObjPtr vm, + qemuDomainAsyncJob asyncJob) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + + if (!cfg->migrateTLSx509certdir) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("host migration TLS directory not configured")); + return -1; + } + + if (qemuMigrationCheckTLSCreds(driver, vm, asyncJob) < 0) + return -1; + + if (!priv->migTLSAlias) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("get/set empty migration parameter 'tls-creds' is " + "not supported"));
Heh, it took me a while to understand what you were trying to say and it is certainly not something we'd want to tell users who enable TLS with old QEMU. How about "TLS migration is not supported with this QEMU binary"? And the code should be VIR_ERR_OPERATION_UNSUPPORTED.
+ return -1; + } + + /* If there's a secret, then grab/store it now using the connection */ + if (cfg->migrateTLSx509secretUUID && + !(priv->migSecinfo = + qemuDomainSecretInfoTLSNew(conn, priv, QEMU_MIGRATION_TLS_ALIAS_BASE, + cfg->migrateTLSx509secretUUID))) + return -1; + + return 0; +} + + +/* qemuMigrationAddTLSObjects + * @driver: pointer to qemu driver + * @vm: domain object + * @cfg: configuration pointer + * @tlsListen: server or client + * @asyncJob: Migration job to join + * @tlsAlias: alias to be generated for TLS object + * @secAlias: alias to be generated for a secinfo object + * @migParams: migration parameters to set + * + * Create the TLS objects for the migration and set the migParams value + * + * Returns 0 on success, -1 on failure + */ +static int +qemuMigrationAddTLSObjects(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virQEMUDriverConfigPtr cfg, + bool tlsListen, + qemuDomainAsyncJob asyncJob, + char **tlsAlias, + char **secAlias, + qemuMonitorMigrationParamsPtr migParams) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + virJSONValuePtr tlsProps = NULL; + virJSONValuePtr secProps = NULL; + + if (qemuDomainGetTLSObjects(priv->qemuCaps, priv->migSecinfo, + cfg->migrateTLSx509certdir, tlsListen, + cfg->migrateTLSx509verify, + QEMU_MIGRATION_TLS_ALIAS_BASE, + &tlsProps, tlsAlias, &secProps, secAlias) < 0) + return -1; + + /* Ensure the domain doesn't already have the TLS objects defined... + * This should prevent any issues just in case some cleanup wasn't + * properly completed (both src and dst use the same alias) or + * some other error path between now and perform . */ + qemuDomainDelTLSObjects(driver, vm, asyncJob, *secAlias, *tlsAlias); + + /* Add the migrate TLS objects to the domain */
Useless comment.
+ if (qemuDomainAddTLSObjects(driver, vm, asyncJob, *secAlias, &secProps, + *tlsAlias, &tlsProps) < 0) + return -1; + + /* Set the param used for 'tls-creds' */
And this one too.
+ if (VIR_STRDUP(migParams->migrateTLSAlias, *tlsAlias) < 0) + return -1; + + return 0; +} + + static void qemuMigrationStoreDomainState(virDomainObjPtr vm) { @@ -3530,6 +3690,47 @@ qemuMigrationParamsFree(qemuMonitorMigrationParamsPtr *migParams) }
+/* qemuMigrationSetEmptyTLSParams + * @priv: Pointer to private domain data + * @migParams: Pointer to a migration parameters block + * + * If the qemuMigrationCheckTLSCreds query finds a non empty alias and it + * is set to the alias that libvirt set, then we need to set the migration + * parameters to "" in order to force clearing the TLS values from our + * previous migration that may not have been cleared properly if libvirtd + * restarted during the finish phase before the ResetTLSParams was run. + * + * Returns 0 on success, -1 on failure + */ +static int +qemuMigrationSetEmptyTLSParams(qemuDomainObjPrivatePtr priv, + qemuMonitorMigrationParamsPtr migParams) +{ + char *tlsAlias = NULL; + + if (priv->migTLSAlias) { + if (*priv->migTLSAlias == '\0') + return 0; + + if (!(tlsAlias = + qemuAliasTLSObjFromSrcAlias(QEMU_MIGRATION_TLS_ALIAS_BASE))) + return -1; + + if (STRNEQ(priv->migTLSAlias, tlsAlias)) { + VIR_FREE(tlsAlias); + return 0;
Why? We should just always reset the parameters if VIR_MIGRATE_TLS is not set. No matter what was the previous value.
+ } + VIR_FREE(tlsAlias); + + if (VIR_STRDUP(migParams->migrateTLSAlias, "") < 0 || + VIR_STRDUP(migParams->migrateTLSHostname, "") < 0) + return -1; + } + + return 0; +} + + qemuMonitorMigrationParamsPtr qemuMigrationParams(virTypedParameterPtr params, int nparams, @@ -3601,6 +3802,110 @@ qemuMigrationSetParams(virQEMUDriverPtr driver, }
+/* qemuMigrationResetTLSParams + * @driver: pointer to qemu driver + * @vm: domain object + * @asyncJob: migration job to join + * @tlsAlias: alias used for TLS object + * + * If we configured the migration TLS params, then let's clear the setting + * of the tls-creds and tls-hostname. + * + * Returns 0 on success, -1 on failure with error message set + */ +static int +qemuMigrationResetTLSParams(virQEMUDriverPtr driver, + virDomainObjPtr vm, + qemuDomainAsyncJob asyncJob, + const char *tlsAlias) +{ + int ret = -1; + qemuDomainObjPrivatePtr priv = vm->privateData; + qemuMonitorMigrationParams migParams = { 0 }; + + if (!priv->migTLSAlias) + return 0; + + if (STREQ_NULLABLE(priv->migTLSAlias, tlsAlias)) { + if (VIR_STRDUP(migParams.migrateTLSAlias, "") < 0 || + VIR_STRDUP(migParams.migrateTLSHostname, "") < 0) + goto cleanup; + + if (qemuMigrationSetParams(driver, vm, asyncJob, &migParams) < 0) + goto cleanup; + } + + ret = 0; + + cleanup: + qemuMigrationParamsClear(&migParams); + return ret; +}
And how does this differ from qemuMigrationSetEmptyTLSParams? Well, one function generates its own tlsAlias while the other gets it as a parameter, but we should not check it anyway... Oh I see, Reset actually sends the parameters to QEMU. It could be just a convenient wrapper around SetEmpty then.
+ + +/* qemuMigrationDeconstructTLS + * @driver: pointer to qemu driver + * @vm: domain object + * @asyncJob: migration job to join + * @tlsAlias: alias generated for TLS object + * @secAlias: alias generated for a secinfo object + * + * Deconstruct all the setup possibly done for TLS - various objects, secinfo, + * and migration parameters. + * + * Returns 0 on success, -1 on failure + */ +static int +qemuMigrationDeconstructTLS(virQEMUDriverPtr driver, + virDomainObjPtr vm, + qemuDomainAsyncJob asyncJob, + const char *tlsAlias, + const char *secAlias) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + + qemuDomainDelTLSObjects(driver, vm, asyncJob, secAlias, tlsAlias); + qemuDomainSecretInfoFree(&priv->migSecinfo); + + return qemuMigrationResetTLSParams(driver, vm, asyncJob, tlsAlias); +} + + +/* qemuMigrationResetTLS + * @driver: pointer to qemu driver + * @vm: domain object + * @asyncJob: migration job to join + * + * Wrapper to qemuMigrationDeconstructTLS that generates the expected + * tlsAlias and secAlias for migration paths without them set (e.g. Finish) + * + * Returns 0 on success, -1 on failure + */ +static int +qemuMigrationResetTLS(virQEMUDriverPtr driver, + virDomainObjPtr vm, + qemuDomainAsyncJob asyncJob)
Can't you just create a single qemuMigrationResetTLS function which takes both aliases and generates them if they are NULL? I know you like the "Deconstruct" part of the function name, but it all just looks more complicated than it needs to be.
+{ + char *tlsAlias = NULL; + char *secAlias = NULL; + int ret; + + /* NB: If either or both fail to allocate memory we can still proceed + * since the next time we migrate another deletion attempt will be + * made after successfully generating the aliases. */ + tlsAlias = qemuAliasTLSObjFromSrcAlias(QEMU_MIGRATION_TLS_ALIAS_BASE); + secAlias = qemuDomainGetSecretAESAlias(QEMU_MIGRATION_TLS_ALIAS_BASE, + false); + + ret = qemuMigrationDeconstructTLS(driver, vm, asyncJob, tlsAlias, secAlias); + + VIR_FREE(tlsAlias); + VIR_FREE(secAlias); + + return ret; +} + + static int qemuMigrationPrepareAny(virQEMUDriverPtr driver, virConnectPtr dconn, ... @@ -3829,6 +4137,32 @@ qemuMigrationPrepareAny(virQEMUDriverPtr driver, compression, &migParams) < 0) goto stopjob;
+ /* Migrations using TLS need to add the "tls-creds-x509" object and + * set the migration TLS parameters */ + if (flags & VIR_MIGRATE_TLS) { + cfg = virQEMUDriverGetConfig(driver); + if (qemuMigrationCheckSetupTLS(dconn, driver, cfg, vm, + QEMU_ASYNC_JOB_MIGRATION_IN) < 0) + goto stopjob; + + if (qemuMigrationAddTLSObjects(driver, vm, cfg, true, + QEMU_ASYNC_JOB_MIGRATION_IN, + &tlsAlias, &secAlias, &migParams) < 0) + goto stopjob; + + /* Force reset of 'tls-hostname', just in case */
The "just in case" part is confusing. What about replacing it with something about tls-hostname making no sense on destination?
+ if (VIR_STRDUP(migParams.migrateTLSHostname, "") < 0) + goto stopjob; + + } else { + /* If we support setting the tls-creds, be sure to always reset + * the migration parameters when this migration isn't using TLS */
Exactly, "always" is the important part, it doesn't say "if something" as in the current qemuMigrationSetEmptyTLSParams implementation.
+ if ((qemuMigrationCheckTLSCreds(driver, vm,
s/((/(/
+ QEMU_ASYNC_JOB_MIGRATION_IN) < 0) ||
s/0)/0/
+ (qemuMigrationSetEmptyTLSParams(priv, &migParams) < 0))
s/(//; s/0)/0/
+ goto stopjob; + } + if (STREQ_NULLABLE(protocol, "rdma") && virProcessSetMaxMemLock(vm->pid, vm->def->mem.hard_limit << 10) < 0) { goto stopjob; ...
Jirka

On 03/22/2017 12:26 PM, Jiri Denemark wrote:
On Fri, Mar 17, 2017 at 14:39:00 -0400, John Ferlan wrote:
If the migration flags indicate this migration will be using TLS, then set up the destination during the prepare phase once the target domain has been started to add the TLS objects to perform the migration.
This will create at least an "-object tls-creds-x509,endpoint=server,..." and potentially an "-object secret,..." to handle the passphrase response
Looks like a leftover from previous versions where the objects were not hotplugged on both sides of migration.
Hmm. thought I adjusted this already... Must've been thinking about it...
to access the TLS credentials. The alias/id used for the TLS objects will contain "libvirt_migrate" as a mechanism to signify that libvirt started the migration on the target (reaping benefits possibly).
Once the objects are created, the code will set the "tls-creds" and "tls-hostname" migration parameters to signify usage of TLS.
During the Finish phase we'll be sure to attempt to clear the migration parameters and delete those objects (whether or not they were created).
Since incoming migrations that don't reach the Finish stage will be killed in qemuProcessRecoverMigrationIn and the only purpose at that point would be to free memory, it's not necessary to set up any sort of recovery. Additionally, subsequent migrations will check if the migration parameters are set and adjust them appropriately if for some reason libvirtd restarts after setting the Finish marker, but before actually resetting the environment.
Sure, migrations will do that, but what about snapshots, saving a domain to a file and similar stuff which internally uses migration too. Do we properly reset the parameters for them too? I think we should delete the objects and reset migration parameters in qemuProcessRecoverMigrationIn anyway.
Why would someone use TLS for snapshots, saving domain to a fail, and other similar stuff? What am I missing? I don't mind adding the extra call. If the TLS parameters *only* get adjusted by libvirt during PrepareAny and Run, but both those phases cause reset of the migration back to the original source on failure *and* Cancel does the reset - then I'm missing the connection.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_domain.c | 7 +- src/qemu/qemu_domain.h | 91 +++++++----- src/qemu/qemu_migration.c | 344 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 403 insertions(+), 39 deletions(-)
...
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 1f266bf..1dd3b1c 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h ... @@ -246,47 +283,18 @@ struct _qemuDomainObjPrivate {
/* note whether memory device alias does not correspond to slot number */ bool memAliasOrderMismatch; -};
-# define QEMU_DOMAIN_PRIVATE(vm) \ - ((qemuDomainObjPrivatePtr) (vm)->privateData) + /* for migrations using TLS with a secret (not to be saved in our */ + /* private XML). */ + qemuDomainSecretInfoPtr migSecinfo;
-/* Type of domain secret */ -typedef enum { - VIR_DOMAIN_SECRET_INFO_TYPE_PLAIN = 0, - VIR_DOMAIN_SECRET_INFO_TYPE_AES, /* utilize GNUTLS_CIPHER_AES_256_CBC */ - - VIR_DOMAIN_SECRET_INFO_TYPE_LAST -} qemuDomainSecretInfoType; - -typedef struct _qemuDomainSecretPlain qemuDomainSecretPlain; -typedef struct _qemuDomainSecretPlain *qemuDomainSecretPlainPtr; -struct _qemuDomainSecretPlain { - char *username; - uint8_t *secret; - size_t secretlen; + /* Used when fetching/storing the current 'tls-creds' migration setting */ + /* (not to be saved in our private XML). */ + char *migTLSAlias;
I'm not quite sure why we need to store the original value.
It determines whether or not TLS is possible... It can ensure that when not using TLS for migration that we set the params to "" if they aren't already set that way.
}; ... diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 66a5062..42074f0 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -85,6 +85,8 @@ VIR_ENUM_IMPL(qemuMigrationCompressMethod, QEMU_MIGRATION_COMPRESS_LAST, "mt", );
+#define QEMU_MIGRATION_TLS_ALIAS_BASE "libvirt_migrate" + enum qemuMigrationCookieFlags { QEMU_MIGRATION_COOKIE_FLAG_GRAPHICS, QEMU_MIGRATION_COOKIE_FLAG_LOCKSTATE, @@ -1488,6 +1490,164 @@ qemuMigrationEatCookie(virQEMUDriverPtr driver, return NULL; }
+/* qemuMigrationCheckTLSCreds + * @driver: pointer to qemu driver + * @vm: domain object + * @asyncJob: migration job to join + * + * Query the migration parameters looking for the 'tls-creds' parameter. + * The parameter was initially supported in QEMU 2.7; however, there was + * no mechanism provided to clear the parameter. For QEMU 2.9, a change + * was made to allow setting the parameter to an empty string in order + * to clear. An additional change was made to initialize the parameter + * to the empty string. Although still not perfect since it's possible + * that a pre-2.9 release set the string to something and we should not
How would this happen? QEMU won't magically set it to something by itself. And we don't need to care about someone messing up with the monitor or command line manually.
Yes it does - 2.9-rc0 initializes it to "" while it was NULL beforehand. See QEMU commit '4af245dc3'.
Anyway, I don't see a real value in repeating the story over and over. We just ask for the default parameter values and if TLS parameters are not there, they are not supported from our point of view. That's pretty clear and version agnostic.
Well I always find it difficult to stumble upon code and wonder why something is being done a particular way. So if I over state what something does, is it really a bad thing? I can work to reduce the repetition though...
+ * set it to the empty string, at least it's better than nothing. So let's + * check if the parameter has been set to something to detect the whether + * the parameter exists. If it's been set to something, then save the + * value in our private domain structures so that future decision makers + * can decide how they should proceed based upon the setting. + * + * Returns 0 if we were able to successfully fetch the params and + * additionally if the tls-creds parameter exists, saves it in the + * private domain structure. Returns -1 on failure. + */ +static int +qemuMigrationCheckTLSCreds(virQEMUDriverPtr driver, + virDomainObjPtr vm, + qemuDomainAsyncJob asyncJob) +{ + int ret = -1; + qemuDomainObjPrivatePtr priv = vm->privateData; + qemuMonitorMigrationParams migParams = { 0 }; + + if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0) + goto cleanup; + + if (qemuMonitorGetMigrationParams(priv->mon, &migParams) < 0) + goto cleanup; + + /* NB: Could steal NULL pointer too! Let caller decide what to do. */ + VIR_STEAL_PTR(priv->migTLSAlias, migParams.migrateTLSAlias); + + ret = 0; + + cleanup: + if (qemuDomainObjExitMonitor(driver, vm) < 0) + ret = -1; + + qemuMigrationParamsClear(&migParams); + + return ret; +} + + +/* qemuMigrationCheckSetupTLS + * @conn: Connection pointer + * @driver: pointer to qemu driver + * @vm: domain object + * @cfg: configuration pointer + * @asyncJob: migration job to join + * + * Check if TLS is possible and set up the environment. Assumes the caller + * desires to use TLS (e.g. caller found VIR_MIGRATE_TLS flag). + * + * Ensure the qemu.conf has been properly configured to add an entry for + * "migrate_tls_x509_cert_dir". Also check if the "tls-creds" parameter + * was present from a query of migration parameters + * + * Returns 0 on success, -1 on error/failure + */ +static int +qemuMigrationCheckSetupTLS(virConnectPtr conn, + virQEMUDriverPtr driver, + virQEMUDriverConfigPtr cfg, + virDomainObjPtr vm, + qemuDomainAsyncJob asyncJob) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + + if (!cfg->migrateTLSx509certdir) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("host migration TLS directory not configured")); + return -1; + } + + if (qemuMigrationCheckTLSCreds(driver, vm, asyncJob) < 0) + return -1; + + if (!priv->migTLSAlias) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("get/set empty migration parameter 'tls-creds' is " + "not supported"));
Heh, it took me a while to understand what you were trying to say and it is certainly not something we'd want to tell users who enable TLS with old QEMU. How about "TLS migration is not supported with this QEMU binary"? And the code should be VIR_ERR_OPERATION_UNSUPPORTED.
OK that's fine.
+ return -1; + } + + /* If there's a secret, then grab/store it now using the connection */ + if (cfg->migrateTLSx509secretUUID && + !(priv->migSecinfo = + qemuDomainSecretInfoTLSNew(conn, priv, QEMU_MIGRATION_TLS_ALIAS_BASE, + cfg->migrateTLSx509secretUUID))) + return -1; + + return 0; +} + + +/* qemuMigrationAddTLSObjects + * @driver: pointer to qemu driver + * @vm: domain object + * @cfg: configuration pointer + * @tlsListen: server or client + * @asyncJob: Migration job to join + * @tlsAlias: alias to be generated for TLS object + * @secAlias: alias to be generated for a secinfo object + * @migParams: migration parameters to set + * + * Create the TLS objects for the migration and set the migParams value + * + * Returns 0 on success, -1 on failure + */ +static int +qemuMigrationAddTLSObjects(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virQEMUDriverConfigPtr cfg, + bool tlsListen, + qemuDomainAsyncJob asyncJob, + char **tlsAlias, + char **secAlias, + qemuMonitorMigrationParamsPtr migParams) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + virJSONValuePtr tlsProps = NULL; + virJSONValuePtr secProps = NULL; + + if (qemuDomainGetTLSObjects(priv->qemuCaps, priv->migSecinfo, + cfg->migrateTLSx509certdir, tlsListen, + cfg->migrateTLSx509verify, + QEMU_MIGRATION_TLS_ALIAS_BASE, + &tlsProps, tlsAlias, &secProps, secAlias) < 0) + return -1; + + /* Ensure the domain doesn't already have the TLS objects defined... + * This should prevent any issues just in case some cleanup wasn't + * properly completed (both src and dst use the same alias) or + * some other error path between now and perform . */ + qemuDomainDelTLSObjects(driver, vm, asyncJob, *secAlias, *tlsAlias); + + /* Add the migrate TLS objects to the domain */
Useless comment.
+ if (qemuDomainAddTLSObjects(driver, vm, asyncJob, *secAlias, &secProps, + *tlsAlias, &tlsProps) < 0) + return -1; + + /* Set the param used for 'tls-creds' */
And this one too.
Fine - I'll remove them
+ if (VIR_STRDUP(migParams->migrateTLSAlias, *tlsAlias) < 0) + return -1; + + return 0; +} + + static void qemuMigrationStoreDomainState(virDomainObjPtr vm) { @@ -3530,6 +3690,47 @@ qemuMigrationParamsFree(qemuMonitorMigrationParamsPtr *migParams) }
+/* qemuMigrationSetEmptyTLSParams + * @priv: Pointer to private domain data + * @migParams: Pointer to a migration parameters block + * + * If the qemuMigrationCheckTLSCreds query finds a non empty alias and it + * is set to the alias that libvirt set, then we need to set the migration + * parameters to "" in order to force clearing the TLS values from our + * previous migration that may not have been cleared properly if libvirtd + * restarted during the finish phase before the ResetTLSParams was run. + * + * Returns 0 on success, -1 on failure + */ +static int +qemuMigrationSetEmptyTLSParams(qemuDomainObjPrivatePtr priv, + qemuMonitorMigrationParamsPtr migParams) +{ + char *tlsAlias = NULL; + + if (priv->migTLSAlias) { + if (*priv->migTLSAlias == '\0') + return 0; + + if (!(tlsAlias = + qemuAliasTLSObjFromSrcAlias(QEMU_MIGRATION_TLS_ALIAS_BASE))) + return -1; + + if (STRNEQ(priv->migTLSAlias, tlsAlias)) { + VIR_FREE(tlsAlias); + return 0;
Why? We should just always reset the parameters if VIR_MIGRATE_TLS is not set. No matter what was the previous value.
Perhaps I'm over thinking... I guess I was going down the path of clearing them when we didn't set them, but then again - I don't change the "migTLSAlias" so sure I can remove the above 3 checks.
+ } + VIR_FREE(tlsAlias); + + if (VIR_STRDUP(migParams->migrateTLSAlias, "") < 0 || + VIR_STRDUP(migParams->migrateTLSHostname, "") < 0) + return -1; + } + + return 0; +} + + qemuMonitorMigrationParamsPtr qemuMigrationParams(virTypedParameterPtr params, int nparams, @@ -3601,6 +3802,110 @@ qemuMigrationSetParams(virQEMUDriverPtr driver, }
+/* qemuMigrationResetTLSParams + * @driver: pointer to qemu driver + * @vm: domain object + * @asyncJob: migration job to join + * @tlsAlias: alias used for TLS object + * + * If we configured the migration TLS params, then let's clear the setting + * of the tls-creds and tls-hostname. + * + * Returns 0 on success, -1 on failure with error message set + */ +static int +qemuMigrationResetTLSParams(virQEMUDriverPtr driver, + virDomainObjPtr vm, + qemuDomainAsyncJob asyncJob, + const char *tlsAlias) +{ + int ret = -1; + qemuDomainObjPrivatePtr priv = vm->privateData; + qemuMonitorMigrationParams migParams = { 0 }; + + if (!priv->migTLSAlias) + return 0; + + if (STREQ_NULLABLE(priv->migTLSAlias, tlsAlias)) { + if (VIR_STRDUP(migParams.migrateTLSAlias, "") < 0 || + VIR_STRDUP(migParams.migrateTLSHostname, "") < 0) + goto cleanup; + + if (qemuMigrationSetParams(driver, vm, asyncJob, &migParams) < 0) + goto cleanup; + } + + ret = 0; + + cleanup: + qemuMigrationParamsClear(&migParams); + return ret; +}
And how does this differ from qemuMigrationSetEmptyTLSParams? Well, one function generates its own tlsAlias while the other gets it as a parameter, but we should not check it anyway... Oh I see, Reset actually sends the parameters to QEMU. It could be just a convenient wrapper around SetEmpty then.
One is called from Cancel which wouldn't have the aliases on input while qemuMigrationRun and qemuMigrationPrepareAny... The SetEmptyTLSParams is used to ensure when we're doing a non TLS migration that the tls-creds and tls-hostname if of course the parameters are supported. Guess it wasn't self documenting.
+ + +/* qemuMigrationDeconstructTLS + * @driver: pointer to qemu driver + * @vm: domain object + * @asyncJob: migration job to join + * @tlsAlias: alias generated for TLS object + * @secAlias: alias generated for a secinfo object + * + * Deconstruct all the setup possibly done for TLS - various objects, secinfo, + * and migration parameters. + * + * Returns 0 on success, -1 on failure + */ +static int +qemuMigrationDeconstructTLS(virQEMUDriverPtr driver, + virDomainObjPtr vm, + qemuDomainAsyncJob asyncJob, + const char *tlsAlias, + const char *secAlias) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + + qemuDomainDelTLSObjects(driver, vm, asyncJob, secAlias, tlsAlias); + qemuDomainSecretInfoFree(&priv->migSecinfo); + + return qemuMigrationResetTLSParams(driver, vm, asyncJob, tlsAlias); +} + + +/* qemuMigrationResetTLS + * @driver: pointer to qemu driver + * @vm: domain object + * @asyncJob: migration job to join + * + * Wrapper to qemuMigrationDeconstructTLS that generates the expected + * tlsAlias and secAlias for migration paths without them set (e.g. Finish) + * + * Returns 0 on success, -1 on failure + */ +static int +qemuMigrationResetTLS(virQEMUDriverPtr driver, + virDomainObjPtr vm, + qemuDomainAsyncJob asyncJob)
Can't you just create a single qemuMigrationResetTLS function which takes both aliases and generates them if they are NULL? I know you like the "Deconstruct" part of the function name, but it all just looks more complicated than it needs to be.
I'll look to rework the reset logic.
+{ + char *tlsAlias = NULL; + char *secAlias = NULL; + int ret; + + /* NB: If either or both fail to allocate memory we can still proceed + * since the next time we migrate another deletion attempt will be + * made after successfully generating the aliases. */ + tlsAlias = qemuAliasTLSObjFromSrcAlias(QEMU_MIGRATION_TLS_ALIAS_BASE); + secAlias = qemuDomainGetSecretAESAlias(QEMU_MIGRATION_TLS_ALIAS_BASE, + false); + + ret = qemuMigrationDeconstructTLS(driver, vm, asyncJob, tlsAlias, secAlias); + + VIR_FREE(tlsAlias); + VIR_FREE(secAlias); + + return ret; +} + + static int qemuMigrationPrepareAny(virQEMUDriverPtr driver, virConnectPtr dconn, ... @@ -3829,6 +4137,32 @@ qemuMigrationPrepareAny(virQEMUDriverPtr driver, compression, &migParams) < 0) goto stopjob;
+ /* Migrations using TLS need to add the "tls-creds-x509" object and + * set the migration TLS parameters */ + if (flags & VIR_MIGRATE_TLS) { + cfg = virQEMUDriverGetConfig(driver); + if (qemuMigrationCheckSetupTLS(dconn, driver, cfg, vm, + QEMU_ASYNC_JOB_MIGRATION_IN) < 0) + goto stopjob; + + if (qemuMigrationAddTLSObjects(driver, vm, cfg, true, + QEMU_ASYNC_JOB_MIGRATION_IN, + &tlsAlias, &secAlias, &migParams) < 0) + goto stopjob; + + /* Force reset of 'tls-hostname', just in case */
The "just in case" part is confusing. What about replacing it with something about tls-hostname making no sense on destination?
The destination cannot have tls-hostname set - it's a source parameter. If the target was a source and for some reason the parameter was set, then just in case we'll be sure to clear it.
+ if (VIR_STRDUP(migParams.migrateTLSHostname, "") < 0) + goto stopjob; + + } else { + /* If we support setting the tls-creds, be sure to always reset + * the migration parameters when this migration isn't using TLS */
Exactly, "always" is the important part, it doesn't say "if something" as in the current qemuMigrationSetEmptyTLSParams implementation.
+ if ((qemuMigrationCheckTLSCreds(driver, vm,
s/((/(/
+ QEMU_ASYNC_JOB_MIGRATION_IN) < 0) ||
s/0)/0/
+ (qemuMigrationSetEmptyTLSParams(priv, &migParams) < 0))
s/(//; s/0)/0/
OK - here and in the next patch too. John
+ goto stopjob; + } + if (STREQ_NULLABLE(protocol, "rdma") && virProcessSetMaxMemLock(vm->pid, vm->def->mem.hard_limit << 10) < 0) { goto stopjob; ...
Jirka

On Wed, Mar 22, 2017 at 21:40:04 -0400, John Ferlan wrote:
On 03/22/2017 12:26 PM, Jiri Denemark wrote:
On Fri, Mar 17, 2017 at 14:39:00 -0400, John Ferlan wrote: ...
Since incoming migrations that don't reach the Finish stage will be killed in qemuProcessRecoverMigrationIn and the only purpose at that point would be to free memory, it's not necessary to set up any sort of recovery. Additionally, subsequent migrations will check if the migration parameters are set and adjust them appropriately if for some reason libvirtd restarts after setting the Finish marker, but before actually resetting the environment.
Sure, migrations will do that, but what about snapshots, saving a domain to a file and similar stuff which internally uses migration too. Do we properly reset the parameters for them too? I think we should delete the objects and reset migration parameters in qemuProcessRecoverMigrationIn anyway.
Why would someone use TLS for snapshots, saving domain to a fail, and other similar stuff? What am I missing? I don't mind adding the extra call. If the TLS parameters *only* get adjusted by libvirt during PrepareAny and Run, but both those phases cause reset of the migration back to the original source on failure *and* Cancel does the reset - then I'm missing the connection.
If libvirtd restarts after entering QEMU_MIGRATION_PHASE_FINISH3 but before completely finishing the migration we need to decide whether to keep the domain running. The decision is based on the domain state, we kill the domain unless its state is already VIR_DOMAIN_RUNNING. But the TLS parameters may still be set at that point. If we don't reset them when libvirtd starts again they will still be set when someone tries to do a snapshot (or similar thing which uses "migrate" QMP command). Thus QEMU will try to use TLS, which will obviously fail. That's why we can't rely on just PrepareAny/Run to clear them, we need to make sure they are always cleared when migration finishes. ...
@@ -246,47 +283,18 @@ struct _qemuDomainObjPrivate {
/* note whether memory device alias does not correspond to slot number */ bool memAliasOrderMismatch; -};
-# define QEMU_DOMAIN_PRIVATE(vm) \ - ((qemuDomainObjPrivatePtr) (vm)->privateData) + /* for migrations using TLS with a secret (not to be saved in our */ + /* private XML). */ + qemuDomainSecretInfoPtr migSecinfo;
-/* Type of domain secret */ -typedef enum { - VIR_DOMAIN_SECRET_INFO_TYPE_PLAIN = 0, - VIR_DOMAIN_SECRET_INFO_TYPE_AES, /* utilize GNUTLS_CIPHER_AES_256_CBC */ - - VIR_DOMAIN_SECRET_INFO_TYPE_LAST -} qemuDomainSecretInfoType; - -typedef struct _qemuDomainSecretPlain qemuDomainSecretPlain; -typedef struct _qemuDomainSecretPlain *qemuDomainSecretPlainPtr; -struct _qemuDomainSecretPlain { - char *username; - uint8_t *secret; - size_t secretlen; + /* Used when fetching/storing the current 'tls-creds' migration setting */ + /* (not to be saved in our private XML). */ + char *migTLSAlias;
I'm not quite sure why we need to store the original value.
It determines whether or not TLS is possible... It can ensure that when not using TLS for migration that we set the params to "" if they aren't already set that way.
Which is in fact only needed because the check is done in the Begin phase while the parameters are set in the Perform phase. It could have been just a bool flag, though. Not a big deal. ...
+/* qemuMigrationCheckTLSCreds + * @driver: pointer to qemu driver + * @vm: domain object + * @asyncJob: migration job to join + * + * Query the migration parameters looking for the 'tls-creds' parameter. + * The parameter was initially supported in QEMU 2.7; however, there was + * no mechanism provided to clear the parameter. For QEMU 2.9, a change + * was made to allow setting the parameter to an empty string in order + * to clear. An additional change was made to initialize the parameter + * to the empty string. Although still not perfect since it's possible + * that a pre-2.9 release set the string to something and we should not
How would this happen? QEMU won't magically set it to something by itself. And we don't need to care about someone messing up with the monitor or command line manually.
Yes it does - 2.9-rc0 initializes it to "" while it was NULL beforehand. See QEMU commit '4af245dc3'.
We don't care about TLS migration with QEMU older than 2.9 since tls-creds won't be listed in the reply from query-migrate-parameters. All QEMU versions between 2.7 and 2.9 support tls-cred but the default value disables the use of TLS. That's why any comment mentioning this in the code is irrelevant. We just need to check whether tls-creds is there and either set it to something or "" depending on VIR_MIGRATE_TLS.
Anyway, I don't see a real value in repeating the story over and over. We just ask for the default parameter values and if TLS parameters are not there, they are not supported from our point of view. That's pretty clear and version agnostic.
Well I always find it difficult to stumble upon code and wonder why something is being done a particular way. So if I over state what something does, is it really a bad thing?
It's fine to describe what we do for QEMU >= 2.9 which reports tls-creds. We don't do anything if tls-creds is not reported so why should we confuse people reading the code with details about QEMU versions which support TLS but don't advertise tls-creds? If you don't want to keep this info just in the commit message, I think the documentation of this (qemuMigrationCheckTLSCreds) function is the right place to keep it. But repeating it elsewhere is not very useful. ...
+/* qemuMigrationSetEmptyTLSParams + * @priv: Pointer to private domain data + * @migParams: Pointer to a migration parameters block + * + * If the qemuMigrationCheckTLSCreds query finds a non empty alias and it + * is set to the alias that libvirt set, then we need to set the migration + * parameters to "" in order to force clearing the TLS values from our + * previous migration that may not have been cleared properly if libvirtd + * restarted during the finish phase before the ResetTLSParams was run. + * + * Returns 0 on success, -1 on failure + */ +static int +qemuMigrationSetEmptyTLSParams(qemuDomainObjPrivatePtr priv, + qemuMonitorMigrationParamsPtr migParams) +{ + char *tlsAlias = NULL; + + if (priv->migTLSAlias) { + if (*priv->migTLSAlias == '\0') + return 0; + + if (!(tlsAlias = + qemuAliasTLSObjFromSrcAlias(QEMU_MIGRATION_TLS_ALIAS_BASE))) + return -1; + + if (STRNEQ(priv->migTLSAlias, tlsAlias)) { + VIR_FREE(tlsAlias); + return 0;
Why? We should just always reset the parameters if VIR_MIGRATE_TLS is not set. No matter what was the previous value.
Perhaps I'm over thinking... I guess I was going down the path of clearing them when we didn't set them, but then again - I don't change the "migTLSAlias" so sure I can remove the above 3 checks.
The checks make sure we don't reset the parameters if we didn't set them. Which is not OK, we need to always reset them to make sure TLS is not used if VIR_MIGRATE_TLS is not set. ...
@@ -3829,6 +4137,32 @@ qemuMigrationPrepareAny(virQEMUDriverPtr driver, compression, &migParams) < 0) goto stopjob;
+ /* Migrations using TLS need to add the "tls-creds-x509" object and + * set the migration TLS parameters */ + if (flags & VIR_MIGRATE_TLS) { + cfg = virQEMUDriverGetConfig(driver); + if (qemuMigrationCheckSetupTLS(dconn, driver, cfg, vm, + QEMU_ASYNC_JOB_MIGRATION_IN) < 0) + goto stopjob; + + if (qemuMigrationAddTLSObjects(driver, vm, cfg, true, + QEMU_ASYNC_JOB_MIGRATION_IN, + &tlsAlias, &secAlias, &migParams) < 0) + goto stopjob; + + /* Force reset of 'tls-hostname', just in case */
The "just in case" part is confusing. What about replacing it with something about tls-hostname making no sense on destination?
The destination cannot have tls-hostname set - it's a source parameter. If the target was a source and for some reason the parameter was set, then just in case we'll be sure to clear it.
Exactly. That's why something like "..., it's a source-only parameter" would be better than "..., just in case". Jirka

https://bugzilla.redhat.com/show_bug.cgi?id=1300769 If the migration flags indicate this migration will be using TLS, then while we have connection in the Begin phase check and setup the TLS environment that will be used by virMigrationRun during the Perform phase for the source to configure TLS. This creates at least an "-object tls-creds-x509,endpoint=client,..." and potentially an "-object secret,..." to handle the passphrase response to access the TLS credentials. The alias/id used for the TLS objects will contain "libvirt_migrate" as a mechanism to signify that libvirt started the migration on the source (reaping benefits possibly). Once the objects are created, the code will set the "tls-creds" and "tls-hostname" migration parameters to signify usage of TLS. Since qemuProcessRecoverMigrationOut will cancel outgoing migrations that are still in the QEMU_MIGRATION_PHASE_PERFORM{2|3} stages, there's no need to do anything special as the Perform cleanup and Cancel phases will reset the environment. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_migration.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 42074f0..5acae6e 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -3453,6 +3453,7 @@ qemuMigrationBegin(virConnectPtr conn, unsigned long flags) { virQEMUDriverPtr driver = conn->privateData; + virQEMUDriverConfigPtr cfg = NULL; char *xml = NULL; qemuDomainAsyncJob asyncJob; @@ -3486,6 +3487,12 @@ qemuMigrationBegin(virConnectPtr conn, nmigrate_disks, migrate_disks, flags))) goto endjob; + if (flags & VIR_MIGRATE_TLS) { + cfg = virQEMUDriverGetConfig(driver); + if (qemuMigrationCheckSetupTLS(conn, driver, cfg, vm, asyncJob) < 0) + goto endjob; + } + if ((flags & VIR_MIGRATE_CHANGE_PROTECTION)) { /* We keep the job active across API calls until the confirm() call. * This prevents any other APIs being invoked while migration is taking @@ -3502,6 +3509,7 @@ qemuMigrationBegin(virConnectPtr conn, } cleanup: + virObjectUnref(cfg); virDomainObjEndAPI(&vm); return xml; @@ -5010,8 +5018,11 @@ qemuMigrationRun(virQEMUDriverPtr driver, { int ret = -1; unsigned int migrate_flags = QEMU_MONITOR_MIGRATE_BACKGROUND; + virQEMUDriverConfigPtr cfg = NULL; qemuDomainObjPrivatePtr priv = vm->privateData; qemuMigrationCookiePtr mig = NULL; + char *tlsAlias = NULL; + char *secAlias = NULL; qemuMigrationIOThreadPtr iothread = NULL; int fd = -1; unsigned long migrate_speed = resource ? resource : priv->migMaxBandwidth; @@ -5075,6 +5086,38 @@ qemuMigrationRun(virQEMUDriverPtr driver, if (qemuDomainMigrateGraphicsRelocate(driver, vm, mig, graphicsuri) < 0) VIR_WARN("unable to provide data for graphics client relocation"); + if (flags & VIR_MIGRATE_TLS) { + cfg = virQEMUDriverGetConfig(driver); + + /* Begin/CheckSetupTLS already set up migTLSAlias, the following + * assumes that and adds the TLS objects to the domain. */ + if (qemuMigrationAddTLSObjects(driver, vm, cfg, false, + QEMU_ASYNC_JOB_MIGRATION_OUT, + &tlsAlias, &secAlias, migParams) < 0) + goto cleanup; + + /* We need to add the tls-hostname only for special circumstances, + * e.g. for a fd: or exec: based migration. As it turns out the + * CONNECT_HOST turns into an FD migration (see below). */ + if (spec->destType == MIGRATION_DEST_CONNECT_HOST || + spec->destType == MIGRATION_DEST_FD) { + if (VIR_STRDUP(migParams->migrateTLSHostname, + spec->dest.host.name) < 0) + goto cleanup; + } else { + /* Be sure there's nothing from a previous migration */ + if (VIR_STRDUP(migParams->migrateTLSHostname, "") < 0) + goto cleanup; + } + } else { + /* If we support setting the tls-creds, be sure to always reset + * the migration parameters when this migration isn't using TLS */ + if ((qemuMigrationCheckTLSCreds(driver, vm, + QEMU_ASYNC_JOB_MIGRATION_OUT) < 0) || + (qemuMigrationSetEmptyTLSParams(priv, migParams) < 0)) + goto cleanup; + } + if (migrate_flags & (QEMU_MONITOR_MIGRATE_NON_SHARED_DISK | QEMU_MONITOR_MIGRATE_NON_SHARED_INC)) { if (mig->nbd) { @@ -5255,6 +5298,14 @@ qemuMigrationRun(virQEMUDriverPtr driver, ret = -1; } + if (qemuMigrationDeconstructTLS(driver, vm, QEMU_ASYNC_JOB_MIGRATION_OUT, + tlsAlias, secAlias) < 0) + ret = -1; + + VIR_FREE(tlsAlias); + VIR_FREE(secAlias); + virObjectUnref(cfg); + if (spec->fwdType != MIGRATION_FWD_DIRECT) { if (iothread && qemuMigrationStopTunnel(iothread, ret < 0) < 0) ret = -1; @@ -6958,6 +7009,8 @@ qemuMigrationCancel(virQEMUDriverPtr driver, if (qemuDomainObjExitMonitor(driver, vm) < 0 || (storage && !blockJobs)) goto endsyncjob; + ignore_value(qemuMigrationResetTLS(driver, vm, QEMU_ASYNC_JOB_NONE)); + if (!storage) { ret = 0; goto cleanup; -- 2.9.3

On Fri, Mar 17, 2017 at 14:39:01 -0400, John Ferlan wrote:
https://bugzilla.redhat.com/show_bug.cgi?id=1300769
If the migration flags indicate this migration will be using TLS, then while we have connection in the Begin phase check and setup the TLS environment that will be used by virMigrationRun during the Perform phase for the source to configure TLS.
This creates at least an "-object tls-creds-x509,endpoint=client,..." and potentially an "-object secret,..." to handle the passphrase response to access the TLS credentials. The alias/id used for the TLS objects will contain "libvirt_migrate" as a mechanism to signify that libvirt started the migration on the source (reaping benefits possibly).
Once the objects are created, the code will set the "tls-creds" and "tls-hostname" migration parameters to signify usage of TLS.
Looks like a copy&paste from the previous patch. Is it necessary?
Since qemuProcessRecoverMigrationOut will cancel outgoing migrations that are still in the QEMU_MIGRATION_PHASE_PERFORM{2|3} stages, there's no need to do anything special as the Perform cleanup and Cancel phases will reset the environment.
Sure, TLS will be reset because you added a call to qemuMigrationResetTLS() in qemuMigrationCancel(). Just drop this paragraph which contradicts what you actually did.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_migration.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+)
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 42074f0..5acae6e 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c
...
@@ -5075,6 +5086,38 @@ qemuMigrationRun(virQEMUDriverPtr driver, if (qemuDomainMigrateGraphicsRelocate(driver, vm, mig, graphicsuri) < 0) VIR_WARN("unable to provide data for graphics client relocation");
+ if (flags & VIR_MIGRATE_TLS) { + cfg = virQEMUDriverGetConfig(driver); + + /* Begin/CheckSetupTLS already set up migTLSAlias, the following + * assumes that and adds the TLS objects to the domain. */ + if (qemuMigrationAddTLSObjects(driver, vm, cfg, false, + QEMU_ASYNC_JOB_MIGRATION_OUT, + &tlsAlias, &secAlias, migParams) < 0) + goto cleanup; + + /* We need to add the tls-hostname only for special circumstances, + * e.g. for a fd: or exec: based migration. As it turns out the + * CONNECT_HOST turns into an FD migration (see below). */
Specifically, we need to add tls-hostname whenever QEMU itself does not connect directly to the destination, which means MIGRATION_DEST_CONNECT_HOST and MIGRATION_DEST_FD.
+ if (spec->destType == MIGRATION_DEST_CONNECT_HOST || + spec->destType == MIGRATION_DEST_FD) { + if (VIR_STRDUP(migParams->migrateTLSHostname, + spec->dest.host.name) < 0) + goto cleanup; + } else { + /* Be sure there's nothing from a previous migration */ + if (VIR_STRDUP(migParams->migrateTLSHostname, "") < 0) + goto cleanup; + } + } else { + /* If we support setting the tls-creds, be sure to always reset + * the migration parameters when this migration isn't using TLS */ + if ((qemuMigrationCheckTLSCreds(driver, vm, + QEMU_ASYNC_JOB_MIGRATION_OUT) < 0) || + (qemuMigrationSetEmptyTLSParams(priv, migParams) < 0)) + goto cleanup; + } + if (migrate_flags & (QEMU_MONITOR_MIGRATE_NON_SHARED_DISK | QEMU_MONITOR_MIGRATE_NON_SHARED_INC)) { if (mig->nbd) {
Jirka

On 03/22/2017 12:26 PM, Jiri Denemark wrote:
On Fri, Mar 17, 2017 at 14:39:01 -0400, John Ferlan wrote:
https://bugzilla.redhat.com/show_bug.cgi?id=1300769
If the migration flags indicate this migration will be using TLS, then while we have connection in the Begin phase check and setup the TLS environment that will be used by virMigrationRun during the Perform phase for the source to configure TLS.
This creates at least an "-object tls-creds-x509,endpoint=client,..." and potentially an "-object secret,..." to handle the passphrase response to access the TLS credentials. The alias/id used for the TLS objects will contain "libvirt_migrate" as a mechanism to signify that libvirt started the migration on the source (reaping benefits possibly).
Once the objects are created, the code will set the "tls-creds" and "tls-hostname" migration parameters to signify usage of TLS.
Looks like a copy&paste from the previous patch. Is it necessary?
I can reword
Since qemuProcessRecoverMigrationOut will cancel outgoing migrations that are still in the QEMU_MIGRATION_PHASE_PERFORM{2|3} stages, there's no need to do anything special as the Perform cleanup and Cancel phases will reset the environment.
Sure, TLS will be reset because you added a call to qemuMigrationResetTLS() in qemuMigrationCancel(). Just drop this paragraph which contradicts what you actually did.
OK - so as I figured out things the RecoverMigrationOut only mattered when certain migration phase requirements were met - that wouldn't happen because the setting and clearing of the parameters is done in the PrepareAny and Run phases as well as Cancel.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_migration.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+)
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 42074f0..5acae6e 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c
...
@@ -5075,6 +5086,38 @@ qemuMigrationRun(virQEMUDriverPtr driver, if (qemuDomainMigrateGraphicsRelocate(driver, vm, mig, graphicsuri) < 0) VIR_WARN("unable to provide data for graphics client relocation");
+ if (flags & VIR_MIGRATE_TLS) { + cfg = virQEMUDriverGetConfig(driver); + + /* Begin/CheckSetupTLS already set up migTLSAlias, the following + * assumes that and adds the TLS objects to the domain. */ + if (qemuMigrationAddTLSObjects(driver, vm, cfg, false, + QEMU_ASYNC_JOB_MIGRATION_OUT, + &tlsAlias, &secAlias, migParams) < 0) + goto cleanup; + + /* We need to add the tls-hostname only for special circumstances, + * e.g. for a fd: or exec: based migration. As it turns out the + * CONNECT_HOST turns into an FD migration (see below). */
Specifically, we need to add tls-hostname whenever QEMU itself does not connect directly to the destination, which means MIGRATION_DEST_CONNECT_HOST and MIGRATION_DEST_FD.
Sure - something that wasn't obvious on my first pass through the code. In fact having CONNECT_HOST change into FD later on wasn't as obvious to me until I dug through the code. I can change the comment to: /* We need to add tls-hostname whenever QEMU itself does not * connect directly to the destination. NB: The CONNECT_HOST * turns into a FD migration below in qemuMigrationConnect */
+ if (spec->destType == MIGRATION_DEST_CONNECT_HOST || + spec->destType == MIGRATION_DEST_FD) { + if (VIR_STRDUP(migParams->migrateTLSHostname, + spec->dest.host.name) < 0) + goto cleanup; + } else { + /* Be sure there's nothing from a previous migration */ + if (VIR_STRDUP(migParams->migrateTLSHostname, "") < 0) + goto cleanup; + } + } else { + /* If we support setting the tls-creds, be sure to always reset + * the migration parameters when this migration isn't using TLS */ + if ((qemuMigrationCheckTLSCreds(driver, vm, + QEMU_ASYNC_JOB_MIGRATION_OUT) < 0) || + (qemuMigrationSetEmptyTLSParams(priv, migParams) < 0))
and I've fixed the extra () here as well John
+ goto cleanup; + } + if (migrate_flags & (QEMU_MONITOR_MIGRATE_NON_SHARED_DISK | QEMU_MONITOR_MIGRATE_NON_SHARED_INC)) { if (mig->nbd) {
Jirka

On Wed, Mar 22, 2017 at 21:42:49 -0400, John Ferlan wrote:
On 03/22/2017 12:26 PM, Jiri Denemark wrote:
On Fri, Mar 17, 2017 at 14:39:01 -0400, John Ferlan wrote: ...
@@ -5075,6 +5086,38 @@ qemuMigrationRun(virQEMUDriverPtr driver, if (qemuDomainMigrateGraphicsRelocate(driver, vm, mig, graphicsuri) < 0) VIR_WARN("unable to provide data for graphics client relocation");
+ if (flags & VIR_MIGRATE_TLS) { + cfg = virQEMUDriverGetConfig(driver); + + /* Begin/CheckSetupTLS already set up migTLSAlias, the following + * assumes that and adds the TLS objects to the domain. */ + if (qemuMigrationAddTLSObjects(driver, vm, cfg, false, + QEMU_ASYNC_JOB_MIGRATION_OUT, + &tlsAlias, &secAlias, migParams) < 0) + goto cleanup; + + /* We need to add the tls-hostname only for special circumstances, + * e.g. for a fd: or exec: based migration. As it turns out the + * CONNECT_HOST turns into an FD migration (see below). */
Specifically, we need to add tls-hostname whenever QEMU itself does not connect directly to the destination, which means MIGRATION_DEST_CONNECT_HOST and MIGRATION_DEST_FD.
Sure - something that wasn't obvious on my first pass through the code. In fact having CONNECT_HOST change into FD later on wasn't as obvious to me until I dug through the code.
I can change the comment to:
/* We need to add tls-hostname whenever QEMU itself does not * connect directly to the destination. NB: The CONNECT_HOST * turns into a FD migration below in qemuMigrationConnect */
The important thing is the semantics of MIGRATION_DEST_CONNECT_HOST and not the fact that it changes into MIGRATION_DEST_FD. I guess having the semantics documented for each member of the qemuMigrationDestinationType enum would help quite a bit :-) With this documentation in place, the "NB:..." part would not be needed. Jirka

Signed-off-by: John Ferlan <jferlan@redhat.com> --- docs/news.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/news.xml b/docs/news.xml index 3501f89..25d920c 100644 --- a/docs/news.xml +++ b/docs/news.xml @@ -39,6 +39,16 @@ the QEMU binary supports it. </description> </change> + <change> + <summary> + qemu: Add support to migrate using TLS + </summary> + <description> + Add the ability to migrate QEMU guests using TLS via a new flag + VIR_MIGRATE_TLS or virsh '--tls' option. Requires using at least + QEMU 2.9.0 in order to work properly. + </description> + </change> </section> <section title="Improvements"> <change> -- 2.9.3

On Sat, Mar 18, 2017 at 09:47:04 -0400, John Ferlan wrote:
Signed-off-by: John Ferlan <jferlan@redhat.com> --- docs/news.xml | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/docs/news.xml b/docs/news.xml index 3501f89..25d920c 100644 --- a/docs/news.xml +++ b/docs/news.xml @@ -39,6 +39,16 @@ the QEMU binary supports it. </description> </change> + <change> + <summary> + qemu: Add support to migrate using TLS + </summary> + <description> + Add the ability to migrate QEMU guests using TLS via a new flag + VIR_MIGRATE_TLS or virsh '--tls' option. Requires using at least
I think "virsh migrate '--tls'" would be a bit better.
+ QEMU 2.9.0 in order to work properly. + </description> + </change> </section> <section title="Improvements"> <change>
ACK Jirka
participants (2)
-
Jiri Denemark
-
John Ferlan