qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
To: "Daniel P. Berrange" <berrange@redhat.com>
Cc: Amit Shah <amit.shah@redhat.com>,
	qemu-devel@nongnu.org, Juan Quintela <quintela@redhat.com>
Subject: Re: [Qemu-devel] [PATCH v1 20/22] migration: support TLS encryption with TCP migration backend
Date: Fri, 12 Feb 2016 17:09:52 +0000	[thread overview]
Message-ID: <20160212170951.GG2411@work-vm> (raw)
In-Reply-To: <1452599056-27357-21-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
> 
> 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.

Does that change the option passed or is that just different
in the way the tls-creds are set up?

> 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 ac73977..bef861c 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;
> +

what makes it TCP specific rather than sharing most of this between transports? i.e. should
this work for fd migration ? (rdma is probably not reasonable
giving it's scribbling directly in the other hosts RAM).
Certainly those types dont really look TCP specific.

>  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;
> +    }

At what point does it get saner just to throw host_port into a qemu_opts 
and let it parse it?

> +    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));

error_report ?

> +        data->s->file = NULL;
> +        migrate_fd_error(data->s);
> +    } else {
> +        DPRINTF("migrate connect success\n");

trace_

> +        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_report ?  More below I think - just make sure that
any errors normally get logged.

> +                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->u.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 215d00d..3f16cfc 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -3364,7 +3364,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" \
> @@ -3377,11 +3377,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.5.0
> 
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK

  reply	other threads:[~2016-02-12 17:10 UTC|newest]

Thread overview: 66+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-01-12 11:43 [Qemu-devel] [PATCH v1 00/22] Convert migration to QIOChannel & support TLS Daniel P. Berrange
2016-01-12 11:43 ` [Qemu-devel] [PATCH v1 01/22] s390: use FILE instead of QEMUFile for creating text file Daniel P. Berrange
2016-01-12 11:58   ` Cornelia Huck
2016-01-12 12:01     ` Daniel P. Berrange
2016-01-12 12:05       ` Cornelia Huck
2016-02-12 17:19   ` Dr. David Alan Gilbert
2016-01-12 11:43 ` [Qemu-devel] [PATCH v1 02/22] migration: remove use of qemu_bufopen from vmstate tests Daniel P. Berrange
2016-01-28 17:45   ` Dr. David Alan Gilbert
2016-01-12 11:43 ` [Qemu-devel] [PATCH v1 03/22] migration: ensure qemu_fflush() always writes full data amount Daniel P. Berrange
2016-01-28 17:53   ` Dr. David Alan Gilbert
2016-02-03 13:31     ` Daniel P. Berrange
2016-01-12 11:43 ` [Qemu-devel] [PATCH v1 04/22] migration: split migration hooks out of QEMUFileOps Daniel P. Berrange
2016-01-28 17:57   ` Dr. David Alan Gilbert
2016-01-12 11:43 ` [Qemu-devel] [PATCH v1 05/22] migration: introduce set_blocking function in QEMUFileOps Daniel P. Berrange
2016-01-28 18:00   ` Dr. David Alan Gilbert
2016-01-12 11:44 ` [Qemu-devel] [PATCH v1 06/22] migration: force QEMUFile to blocking mode for outgoing migration Daniel P. Berrange
2016-01-28 18:17   ` Dr. David Alan Gilbert
2016-01-12 11:44 ` [Qemu-devel] [PATCH v1 07/22] migration: introduce a new QEMUFile impl based on QIOChannel Daniel P. Berrange
2016-02-02 17:06   ` Dr. David Alan Gilbert
2016-02-03 13:37     ` Daniel P. Berrange
2016-02-12 17:16       ` Dr. David Alan Gilbert
2016-01-12 11:44 ` [Qemu-devel] [PATCH v1 08/22] migration: convert post-copy to use QIOChannelBuffer Daniel P. Berrange
2016-01-25 19:38   ` Dr. David Alan Gilbert
2016-01-25 22:15     ` Daniel P. Berrange
2016-01-26 18:59       ` Dr. David Alan Gilbert
2016-01-12 11:44 ` [Qemu-devel] [PATCH v1 09/22] migration: convert unix socket protocol to use QIOChannel Daniel P. Berrange
2016-02-02 18:02   ` Dr. David Alan Gilbert
2016-02-03 11:25     ` Daniel P. Berrange
2016-01-12 11:44 ` [Qemu-devel] [PATCH v1 10/22] migration: convert tcp " Daniel P. Berrange
2016-02-02 18:19   ` Dr. David Alan Gilbert
2016-02-03 10:02     ` Daniel P. Berrange
2016-02-03 10:33       ` Dr. David Alan Gilbert
2016-01-12 11:44 ` [Qemu-devel] [PATCH v1 11/22] migration: convert fd " Daniel P. Berrange
2016-02-02 18:46   ` Dr. David Alan Gilbert
2016-02-03 10:05     ` Daniel P. Berrange
2016-02-03 10:29       ` Dr. David Alan Gilbert
2016-02-03 10:39         ` Daniel P. Berrange
2016-01-12 11:44 ` [Qemu-devel] [PATCH v1 12/22] migration: convert exec " Daniel P. Berrange
2016-02-02 18:53   ` Dr. David Alan Gilbert
2016-01-12 11:44 ` [Qemu-devel] [PATCH v1 13/22] migration: convert RDMA to use QIOChannel interface Daniel P. Berrange
2016-02-02 20:01   ` Dr. David Alan Gilbert
2016-02-03 11:37     ` Daniel P. Berrange
2016-02-03 13:23       ` Dr. David Alan Gilbert
2016-02-03 13:25         ` Daniel P. Berrange
2016-01-12 11:44 ` [Qemu-devel] [PATCH v1 14/22] migration: convert savevm to use QIOChannel for writing to files Daniel P. Berrange
2016-02-03  9:52   ` Dr. David Alan Gilbert
2016-01-12 11:44 ` [Qemu-devel] [PATCH v1 15/22] migration: delete QEMUFile buffer implementation Daniel P. Berrange
2016-02-03  9:54   ` Dr. David Alan Gilbert
2016-01-12 11:44 ` [Qemu-devel] [PATCH v1 16/22] migration: delete QEMUSizedBuffer struct Daniel P. Berrange
2016-02-03  9:55   ` Dr. David Alan Gilbert
2016-01-12 11:44 ` [Qemu-devel] [PATCH v1 17/22] migration: delete QEMUFile sockets implementation Daniel P. Berrange
2016-02-03  9:56   ` Dr. David Alan Gilbert
2016-01-12 11:44 ` [Qemu-devel] [PATCH v1 18/22] migration: delete QEMUFile stdio implementation Daniel P. Berrange
2016-02-03  9:58   ` Dr. David Alan Gilbert
2016-01-12 11:44 ` [Qemu-devel] [PATCH v1 19/22] migration: move definition of struct QEMUFile back into qemu-file.c Daniel P. Berrange
2016-02-05 18:32   ` Dr. David Alan Gilbert
2016-01-12 11:44 ` [Qemu-devel] [PATCH v1 20/22] migration: support TLS encryption with TCP migration backend Daniel P. Berrange
2016-02-12 17:09   ` Dr. David Alan Gilbert [this message]
2016-02-12 17:25     ` Daniel P. Berrange
2016-02-15 11:00       ` Daniel P. Berrange
2016-01-12 11:44 ` [Qemu-devel] [PATCH v1 21/22] migration: remove support for non-iovec based write handlers Daniel P. Berrange
2016-02-12 15:48   ` Dr. David Alan Gilbert
2016-01-12 11:44 ` [Qemu-devel] [PATCH v1 22/22] migration: remove qemu_get_fd method from QEMUFile Daniel P. Berrange
2016-02-12 15:51   ` Dr. David Alan Gilbert
2016-01-12 11:59 ` [Qemu-devel] [PATCH v1 00/22] Convert migration to QIOChannel & support TLS Daniel P. Berrange
2016-01-20 18:01 ` 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=20160212170951.GG2411@work-vm \
    --to=dgilbert@redhat.com \
    --cc=amit.shah@redhat.com \
    --cc=berrange@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).