From: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
To: "Daniel P. Berrange" <berrange@redhat.com>
Cc: Juan Quintela <quintela@redhat.com>,
qemu-devel@nongnu.org, Gerd Hoffmann <kraxel@redhat.com>,
Amit Shah <amit.shah@redhat.com>,
Paolo Bonzini <pbonzini@redhat.com>
Subject: Re: [Qemu-devel] [PATCH FYI 45/46] migration: support TLS encryption with TCP migration backend
Date: Mon, 7 Sep 2015 17:23:01 +0100 [thread overview]
Message-ID: <20150907162301.GI2337@work-vm> (raw)
In-Reply-To: <1441294768-8712-46-git-send-email-berrange@redhat.com>
* Daniel P. Berrange (berrange@redhat.com) wrote:
> This extends the TCP migration backend so that it can make use
> of QIOChannelTLS to provide transparent TLS encryption. To
> trigger enablement the URI on the incoming and outgoing sides
> should have 'tls-creds=ID' appended, eg
>
> tcp:$HOST:$PORT,tls-creds=ID
What makes this tcp specifc? Would it work on any bidirectional
transport?
Dave
> where ID is the object identifier of a set of TLS credentials
> previously created using object_add / -object. There is not
> currently any support for checking the migration client
> certificate names against ACLs. This is pending a conversion
> of the ACL code to QOM.
>
> There is no support for dynamically negotiating use of TLS
> between the incoming/outgoing side. Both sides must agree
> on use of TLS out of band and set the URI accordingly. In
> practice it is expected that the administrator will just
> turn on use of TLS on their hosts in the libvirt config
> and then libvirt will instruct QEMU to use TLS for migration.
>
> Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
> ---
> migration/tcp.c | 273 +++++++++++++++++++++++++++++++++++++++++++++++++++++---
> qemu-options.hx | 7 +-
> 2 files changed, 264 insertions(+), 16 deletions(-)
>
> diff --git a/migration/tcp.c b/migration/tcp.c
> index 2347d9d..68ecaa4 100644
> --- a/migration/tcp.c
> +++ b/migration/tcp.c
> @@ -22,6 +22,8 @@
> #include "migration/migration.h"
> #include "migration/qemu-file.h"
> #include "io/channel-socket.h"
> +#include "io/channel-tls.h"
> +#include "crypto/tlscreds.h"
>
> //#define DEBUG_MIGRATION_TCP
>
> @@ -33,6 +35,22 @@
> do { } while (0)
> #endif
>
> +typedef struct {
> + MigrationState *s;
> + QCryptoTLSCreds *tlscreds;
> + char *hostname;
> +} TCPConnectData;
> +
> +typedef struct {
> + MigrationState *s;
> + QCryptoTLSCreds *tlscreds;
> +} TCPListenData;
> +
> +typedef struct {
> + MigrationState *s;
> + QIOChannel *ioc;
> +} TCPConnectTLSData;
> +
>
> static SocketAddress *tcp_build_address(const char *host_port, Error **errp)
> {
> @@ -51,21 +69,174 @@ static SocketAddress *tcp_build_address(const char *host_port, Error **errp)
> }
>
>
> +static void tcp_connect_data_free(gpointer opaque)
> +{
> + TCPConnectData *data = opaque;
> + if (!data) {
> + return;
> + }
> + g_free(data->hostname);
> + object_unref(OBJECT(data->tlscreds));
> + g_free(data);
> +}
> +
> +
> +static void tcp_listen_data_free(gpointer opaque)
> +{
> + TCPListenData *data = opaque;
> + if (!data) {
> + return;
> + }
> + object_unref(OBJECT(data->tlscreds));
> + g_free(data);
> +}
> +
> +
> +static void tcp_connect_tls_data_free(gpointer opaque)
> +{
> + TCPConnectTLSData *data = opaque;
> + if (!data) {
> + return;
> + }
> + object_unref(OBJECT(data->ioc));
> + g_free(data);
> +}
> +
> +
> +static char *tcp_get_opt_str(const char *host_port,
> + const char *key)
> +{
> + const char *offset, *end;
> +
> + offset = strstr(host_port, key);
> + if (!offset) {
> + return NULL;
> + }
> +
> + offset += strlen(key);
> + if (offset[0] != '=') {
> + return NULL;
> + }
> + offset++;
> + end = strchr(offset, ',');
> + if (end) {
> + return g_strndup(offset, end - offset);
> + } else {
> + return g_strdup(offset);
> + }
> +}
> +
> +
> +static QCryptoTLSCreds *tcp_get_tls_creds(const char *host_port,
> + bool is_listen,
> + Error **errp)
> +{
> + char *credname = NULL;
> + Object *creds;
> + QCryptoTLSCreds *ret;
> +
> + credname = tcp_get_opt_str(host_port, "tls-creds");
> + if (!credname) {
> + return NULL;
> + }
> +
> + creds = object_resolve_path_component(
> + object_get_objects_root(), credname);
> + if (!creds) {
> + error_setg(errp, "No TLS credentials with id '%s'",
> + credname);
> + goto error;
> + }
> + ret = (QCryptoTLSCreds *)object_dynamic_cast(
> + creds, TYPE_QCRYPTO_TLS_CREDS);
> + if (!ret) {
> + error_setg(errp, "Object with id '%s' is not TLS credentials",
> + credname);
> + goto error;
> + }
> + if (is_listen) {
> + if (ret->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
> + error_setg(errp, "%s",
> + "Expected TLS credentials for server endpoint");
> + goto error;
> + }
> + } else {
> + if (ret->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) {
> + error_setg(errp, "%s",
> + "Expected TLS credentials for client endpoint");
> + goto error;
> + }
> + }
> +
> + g_free(credname);
> + object_ref(OBJECT(ret));
> + return ret;
> +
> + error:
> + g_free(credname);
> + return NULL;
> +}
> +
> +
> +static void tcp_outgoing_migration_tls(Object *src,
> + Error *err,
> + gpointer opaque)
> +{
> + TCPConnectTLSData *data = opaque;
> + QIOChannel *ioc = QIO_CHANNEL(src);
> +
> + if (err) {
> + DPRINTF("migrate connect TLS error: %s\n", error_get_pretty(err));
> + data->s->file = NULL;
> + migrate_fd_error(data->s);
> + } else {
> + DPRINTF("migrate connect success\n");
> + data->s->file = qemu_fopen_channel_output(ioc);
> + migrate_fd_connect(data->s);
> + }
> +}
> +
> +
> static void tcp_outgoing_migration(Object *src,
> Error *err,
> gpointer opaque)
> {
> - MigrationState *s = opaque;
> + TCPConnectData *data = opaque;
> QIOChannel *sioc = QIO_CHANNEL(src);
>
> if (err) {
> DPRINTF("migrate connect error: %s\n", error_get_pretty(err));
> - s->file = NULL;
> - migrate_fd_error(s);
> + data->s->file = NULL;
> + migrate_fd_error(data->s);
> } else {
> - DPRINTF("migrate connect success\n");
> - s->file = qemu_fopen_channel_output(sioc);
> - migrate_fd_connect(s);
> + if (data->tlscreds) {
> + Error *local_err = NULL;
> + QIOChannelTLS *tioc = qio_channel_tls_new_client(
> + sioc, data->tlscreds, data->hostname,
> + &local_err);
> + if (!tioc) {
> + DPRINTF("migrate tls setup error: %s\n",
> + error_get_pretty(local_err));
> + error_free(local_err);
> + data->s->file = NULL;
> + migrate_fd_error(data->s);
> + } else {
> + TCPConnectTLSData *tdata =
> + g_new0(TCPConnectTLSData, 1);
> + DPRINTF("migrate connect tls handshake begin\n");
> + tdata->s = data->s;
> + tdata->ioc = sioc;
> + object_ref(OBJECT(sioc));
> + qio_channel_tls_handshake(tioc,
> + tcp_outgoing_migration_tls,
> + tdata,
> + tcp_connect_tls_data_free);
> + }
> + } else {
> + DPRINTF("migrate connect success\n");
> + data->s->file = qemu_fopen_channel_output(sioc);
> + migrate_fd_connect(data->s);
> + }
> }
> object_unref(src);
> }
> @@ -77,21 +248,56 @@ void tcp_start_outgoing_migration(MigrationState *s,
> {
> SocketAddress *saddr = tcp_build_address(host_port, errp);
> QIOChannelSocket *sioc;
> + Error *local_err = NULL;
> + QCryptoTLSCreds *creds;
> + TCPConnectData *data;
>
> if (!saddr) {
> return;
> }
>
> + creds = tcp_get_tls_creds(host_port, false, errp);
> + if (local_err) {
> + error_propagate(errp, local_err);
> + qapi_free_SocketAddress(saddr);
> + return;
> + }
> +
> + data = g_new0(TCPConnectData, 1);
> + data->s = s;
> + if (creds) {
> + data->hostname = g_strdup(saddr->inet->host);
> + data->tlscreds = creds;
> + }
> +
> sioc = qio_channel_socket_new();
> qio_channel_socket_connect_async(sioc,
> saddr,
> tcp_outgoing_migration,
> - s,
> - NULL);
> + data,
> + tcp_connect_data_free);
> qapi_free_SocketAddress(saddr);
> }
>
>
> +static void tcp_incoming_migration_tls(Object *src,
> + Error *err,
> + gpointer opaque)
> +{
> + QIOChannel *ioc = QIO_CHANNEL(src);
> +
> + if (err) {
> + DPRINTF("migrate listen TLS error: %s\n", error_get_pretty(err));
> + object_unref(OBJECT(ioc));
> + } else {
> + DPRINTF("migrate listen success\n");
> + QEMUFile *f = qemu_fopen_channel_input(ioc);
> +
> + process_incoming_migration(f);
> + }
> +}
> +
> +
> static gboolean tcp_accept_incoming_migration(QIOChannel *ioc,
> GIOCondition condition,
> gpointer opaque)
> @@ -99,6 +305,7 @@ static gboolean tcp_accept_incoming_migration(QIOChannel *ioc,
> QEMUFile *f;
> QIOChannelSocket *cioc;
> Error *err = NULL;
> + TCPListenData *data = opaque;
>
> cioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc),
> &err);
> @@ -108,16 +315,38 @@ static gboolean tcp_accept_incoming_migration(QIOChannel *ioc,
> goto out;
> }
>
> - DPRINTF("accepted migration\n");
> + if (data->tlscreds) {
> + DPRINTF("Starting client TLS\n");
> + QIOChannelTLS *tioc = qio_channel_tls_new_server(
> + QIO_CHANNEL(cioc), data->tlscreds,
> + NULL, /* XXX pass ACL name */
> + &err);
> + object_unref(OBJECT(cioc));
> + if (!tioc) {
> + DPRINTF("migrate tls setup error: %s\n",
> + error_get_pretty(err));
> + error_free(err);
> + goto out;
> + } else {
> + DPRINTF("migrate connect tls handshake begin\n");
> + qio_channel_tls_handshake(tioc,
> + tcp_incoming_migration_tls,
> + NULL,
> + NULL);
> + }
> + } else {
> + DPRINTF("accepted migration\n");
>
> - f = qemu_fopen_channel_input(QIO_CHANNEL(cioc));
> - object_unref(OBJECT(cioc));
> + f = qemu_fopen_channel_input(QIO_CHANNEL(cioc));
> + object_unref(OBJECT(cioc));
>
> - process_incoming_migration(f);
> + process_incoming_migration(f);
> + }
>
> out:
> /* Close listening socket as its no longer needed */
> qio_channel_close(ioc, NULL);
> + object_unref(OBJECT(ioc));
> return FALSE;
> }
>
> @@ -126,23 +355,39 @@ void tcp_start_incoming_migration(const char *host_port, Error **errp)
> {
> SocketAddress *saddr = tcp_build_address(host_port, errp);
> QIOChannelSocket *listen_ioc;
> + TCPListenData *data;
> + Error *local_err = NULL;
> + QCryptoTLSCreds *creds;
>
> if (!saddr) {
> return;
> }
>
> + creds = tcp_get_tls_creds(host_port, true, &local_err);
> + if (local_err) {
> + error_propagate(errp, local_err);
> + qapi_free_SocketAddress(saddr);
> + return;
> + }
> +
> listen_ioc = qio_channel_socket_new();
> if (qio_channel_socket_listen_sync(listen_ioc, saddr, errp) < 0) {
> object_unref(OBJECT(listen_ioc));
> qapi_free_SocketAddress(saddr);
> + object_unref(OBJECT(creds));
> return;
> }
>
> + data = g_new0(TCPListenData, 1);
> + if (creds) {
> + data->tlscreds = creds;
> + }
> +
> qio_channel_add_watch(QIO_CHANNEL(listen_ioc),
> G_IO_IN,
> tcp_accept_incoming_migration,
> - listen_ioc,
> - (GDestroyNotify)object_unref);
> + data,
> + tcp_listen_data_free);
>
> qapi_free_SocketAddress(saddr);
> }
> diff --git a/qemu-options.hx b/qemu-options.hx
> index 6acd400..c1bf4a7 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -3299,7 +3299,7 @@ Set TB size.
> ETEXI
>
> DEF("incoming", HAS_ARG, QEMU_OPTION_incoming, \
> - "-incoming tcp:[host]:port[,to=maxport][,ipv4][,ipv6]\n" \
> + "-incoming tcp:[host]:port[,to=maxport][,ipv4][,ipv6][,tls-creds=ID]\n" \
> "-incoming rdma:host:port[,ipv4][,ipv6]\n" \
> "-incoming unix:socketpath\n" \
> " prepare for incoming migration, listen on\n" \
> @@ -3312,11 +3312,14 @@ DEF("incoming", HAS_ARG, QEMU_OPTION_incoming, \
> " wait for the URI to be specified via migrate_incoming\n",
> QEMU_ARCH_ALL)
> STEXI
> -@item -incoming tcp:[@var{host}]:@var{port}[,to=@var{maxport}][,ipv4][,ipv6]
> +@item -incoming tcp:[@var{host}]:@var{port}[,to=@var{maxport}][,ipv4][,ipv6][,tls-creds=ID]
> @itemx -incoming rdma:@var{host}:@var{port}[,ipv4][,ipv6]
> @findex -incoming
> Prepare for incoming migration, listen on a given tcp port.
>
> +If the @var{tls-creds} parameter is specified, it should refer to the ID
> +of a TLS credentials object previously created with @var{-object}.
> +
> @item -incoming unix:@var{socketpath}
> Prepare for incoming migration, listen on a given unix socket.
>
> --
> 2.4.3
>
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
next prev parent reply other threads:[~2015-09-07 16:23 UTC|newest]
Thread overview: 57+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 01/46] sockets: add helpers for creating SocketAddress from a socket Daniel P. Berrange
2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 02/46] sockets: move qapi_copy_SocketAddress into qemu-sockets.c Daniel P. Berrange
2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 03/46] sockets: allow port to be NULL when listening on IP address Daniel P. Berrange
2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 04/46] osdep: add qemu_fork() wrapper for safely handling signals Daniel P. Berrange
2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 05/46] coroutine: move into libqemuutil.a library Daniel P. Berrange
2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 06/46] io: add abstract QIOChannel classes Daniel P. Berrange
2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 07/46] io: add helper module for creating watches on FDs Daniel P. Berrange
2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 08/46] io: pull Buffer code out of VNC module Daniel P. Berrange
2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 09/46] io: add QIOTask class for async operations Daniel P. Berrange
2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 10/46] io: add QIOChannelSocket class Daniel P. Berrange
2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 11/46] io: add QIOChannelFile class Daniel P. Berrange
2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 12/46] io: add QIOChannelTLS class Daniel P. Berrange
2015-09-07 15:31 ` Dr. David Alan Gilbert
2015-09-07 15:41 ` Daniel P. Berrange
2015-09-07 15:51 ` Dr. David Alan Gilbert
2015-09-07 16:04 ` Daniel P. Berrange
2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 13/46] io: add QIOChannelWebsock class Daniel P. Berrange
2015-09-07 15:44 ` Dr. David Alan Gilbert
2015-09-07 15:50 ` Daniel P. Berrange
2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 14/46] io: add QIOChannelCommand class Daniel P. Berrange
2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 15/46] ui: convert VNC startup code to use SocketAddress Daniel P. Berrange
2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 16/46] ui: convert VNC server to use QIOChannelSocket Daniel P. Berrange
2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 17/46] ui: convert VNC server to use QIOChannelTLS Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 18/46] ui: convert VNC server to use QIOChannelWebsock Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 19/46] char: remove fixed length filename allocation Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 20/46] char: convert from GIOChannel to QIOChannel Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 21/46] char: don't assume telnet initialization will not block Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 22/46] char: introduce support for TLS encrypted TCP chardev backend Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 23/46] nbd: convert to use the QAPI SocketAddress object Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 24/46] qemu-nbd: " Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 25/46] sockets: remove use of QemuOpts from header file Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 26/46] sockets: remove use of QemuOpts from socket_listen Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 27/46] sockets: remove use of QemuOpts from socket_connect Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 28/46] sockets: remove use of QemuOpts from socket_dgram Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 29/46] migration: remove use of qemu_bufopen from vmstate tests Daniel P. Berrange
2015-09-07 16:08 ` Dr. David Alan Gilbert
2015-09-07 16:17 ` Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 30/46] migration: remove memory buffer based QEMUFile backend Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 31/46] migration: move definition of struct QEMUFile back into qemu-file.c Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 32/46] migration: split migration hooks out of QEMUFileOps Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 33/46] migration: ensure qemu_fflush() always writes full data amount Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 34/46] migration: introduce qemu_fset_blocking function on QEMUFile Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 35/46] migration: force QEMUFile to blocking mode for outgoing migration Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 36/46] migration: introduce a new QEMUFile impl based on QIOChannel Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 37/46] migration: convert unix socket protocol to use QIOChannel Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 38/46] migration: convert tcp " Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 39/46] migration: convert fd " Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 40/46] migration: convert exec " Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 41/46] migration: convert RDMA to use QIOChannel interface Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 42/46] migration: convert savevm to use QIOChannel for writing to files Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 43/46] migration: delete QEMUFile sockets implementation Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 44/46] migration: delete QEMUFile stdio implementation Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 45/46] migration: support TLS encryption with TCP migration backend Daniel P. Berrange
2015-09-07 16:23 ` Dr. David Alan Gilbert [this message]
2015-09-07 16:29 ` Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 46/46] migration: remove support for non-iovec based write handlers Daniel P. Berrange
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20150907162301.GI2337@work-vm \
--to=dgilbert@redhat.com \
--cc=amit.shah@redhat.com \
--cc=berrange@redhat.com \
--cc=kraxel@redhat.com \
--cc=pbonzini@redhat.com \
--cc=qemu-devel@nongnu.org \
--cc=quintela@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).