Well, really add parameters to pass on to libnbd which does all the
heavy lifting :)
Signed-off-by: Eric Blake <eblake(a)redhat.com>
---
plugins/nbd/nbdkit-nbd-plugin.pod | 91 ++++++++++++++++++++++---------
plugins/nbd/nbd.c | 52 +++++++++++++++++-
TODO | 13 +----
3 files changed, 117 insertions(+), 39 deletions(-)
diff --git a/plugins/nbd/nbdkit-nbd-plugin.pod b/plugins/nbd/nbdkit-nbd-plugin.pod
index c78fdca..38688a3 100644
--- a/plugins/nbd/nbdkit-nbd-plugin.pod
+++ b/plugins/nbd/nbdkit-nbd-plugin.pod
@@ -5,7 +5,8 @@ nbdkit-nbd-plugin - nbdkit nbd plugin
=head1 SYNOPSIS
nbdkit nbd { socket=SOCKNAME | hostname=HOST [port=PORT] | [uri=]URI }
- [export=NAME] [retry=N] [shared=BOOL]
+ [export=NAME] [retry=N] [shared=BOOL] [tls=MODE] [tls-certificates=DIR]
+ [tls-verify=BOOL] [tls-username=NAME] [tls-psk=FILE]
=head1 DESCRIPTION
@@ -20,11 +21,6 @@ original server lacks it). Use of this plugin along with nbdkit
filters (adding I<--filter> to the nbdkit command line) makes it
possible to apply any nbdkit filter to any other NBD server.
-For now, this is limited to connecting to another NBD server over an
-unencrypted connection; if the data is sensitive, it is better to
-stick to a Unix socket rather than transmitting plaintext over TCP. It
-is feasible that future additions will support encryption.
-
=head1 PARAMETERS
One of B<socket>, B<hostname> or B<uri> must be provided to designate
@@ -32,6 +28,9 @@ the server. The server can speak either new or old style
protocol. C<uri=> is a magic config key and may be omitted in most
cases. See L<nbdkit(1)/Magic parameters>.
+The following parameters are available whether or not the plugin was
+compiled against libnbd:
+
=over 4
=item B<socket=>SOCKNAME
@@ -47,16 +46,6 @@ Connect to the NBD server at the given remote C<HOST> using a TCP
socket.
When B<hostname> is supplied, use B<PORT> instead of the default port
10809.
-=item B<uri=>URI
-
-When B<uri> is supplied, decode B<URI> to determine the address to
-connect to. A URI can specify a TCP connection (such as
-C<nbd://localhost:10809>) or a Unix socket (such as
-C<nbd+unix:///?socket=/path/to/sock>). The B<uri> parameter is only
-available when the plugin was compiled against libnbd, although
-whether it works also depends on whether libnbd was compiled against
-libxml2.
-
=item B<export=>NAME
If this parameter is given, and the server speaks new style protocol,
@@ -82,6 +71,55 @@ nbdkit will share that single connection.
=back
+The following parameters are only available if the plugin was compiled
+against libnbd:
+
+=over 4
+
+=item B<uri=>URI
+
+When B<uri> is supplied, decode B<URI> to determine the address to
+connect to. A URI can specify a TCP connection (such as
+C<nbd://localhost:10809>) or a Unix socket (such as
+C<nbd+unix:///?socket=/path/to/sock>). However, whether the B<uri>
+parameter works depends on whether libnbd was compiled against
+libxml2.
+
+=item B<tls=>MODE
+
+Selects which TLS mode to use with the server. If no other tls option
+is present, this defaults to C<off>, where the client does not attempt
+encryption (and may be rejected by a server that requires it). If
+omitted but another tls option is present, this defaults to C<on>,
+where the client opportunistically attempts a TLS handshake, but will
+continue running unencrypted if the server does not support
+encryption. If set to C<require>, this requires an encrypted
+connection to the server.
+
+=item B<tls-certificates=>DIR
+
+This specifies the directory containing X.509 client certificates to
+present to the server.
+
+=item B<tls-verify=>BOOL
+
+Setting this to true disables server name verification, which opens
+you to potential Man-in-the-Middle (MITM) attacks, but allows for a
+simpler setup for distributing certificates.
+
+=item B<tls-username=>NAME
+
+If provided, this overrides the user name to present to the server
+alongside the certificate.
+
+=item B<tls-psk=>FILE
+
+If provided, this is the filename containing the Pre-Shared Keys (PSK)
+to present to the server. While this is easier to set up than X.509,
+it requires that the PSK file be transmitted over a secure channel.
+
+=back
+
=head1 EXAMPLES
Expose the contents of an export served by an old style server over a
@@ -93,9 +131,9 @@ that the old server exits.
nbdkit --exit-with-parent --tls=require nbd socket=$sock &
exec /path/to/oldserver --socket=$sock )
- ┌────────────┐ ┌────────┐ ┌────────────┐
- │ new client │ ────────▶│ nbdkit │ ────────▶│ old server │
- └────────────┘ TCP └────────┘ Unix └────────────┘
+ ┌────────────┐ TLS ┌────────┐ plaintext ┌────────────┐
+ │ new client │ ────────▶│ nbdkit │ ───────────▶│ old server │
+ └────────────┘ TCP └────────┘ Unix └────────────┘
Combine nbdkit's partition filter with qemu-nbd's ability to visit
qcow2 files (nbdkit does not have a native qcow2 plugin), performing
@@ -110,19 +148,20 @@ utilize a 5-second retry to give qemu-nbd time to create the
socket:
exec qemu-nbd -k $sock -f qcow2 /path/to/image.qcow2 )
Conversely, expose the contents of export I<foo> from a new style
-server with unencrypted data to a client that can only consume
+server with encrypted data to a client that can only consume
unencrypted old style. Use I<--run> to clean up nbdkit at the time the
-client exits.
+client exits. In general, note that it is best to keep the plaintext
+connection limited to a Unix socket on the local machine.
- nbdkit -U - -o nbd
hostname=example.com export=foo \
+ nbdkit -U - -o nbd
hostname=example.com export=foo tls=require \
--run '/path/to/oldclient --socket=$unixsocket'
- ┌────────────┐ ┌────────┐ ┌────────────┐
- │ old client │ ────────▶│ nbdkit │ ────────▶│ new server │
- └────────────┘ Unix └────────┘ TCP └────────────┘
+ ┌────────────┐ plaintext ┌────────┐ TLS ┌────────────┐
+ │ old client │ ───────────▶│ nbdkit │ ────────▶│ new server │
+ └────────────┘ Unix └────────┘ TCP └────────────┘
Look for the C<libnbd_version> line to learn if the nbd plugin was
-compiled against libnbd for C<uri> support:
+compiled against libnbd for C<uri> and TLS support:
nbdkit --dump-plugin nbd
diff --git a/plugins/nbd/nbd.c b/plugins/nbd/nbd.c
index 2268bd0..02cc86d 100644
--- a/plugins/nbd/nbd.c
+++ b/plugins/nbd/nbd.c
@@ -100,6 +100,13 @@ static unsigned long retry;
static bool shared;
static struct handle *shared_handle;
+/* Control TLS settings */
+static int tls = -1;
+static const char *tls_certificates;
+static int tls_verify = -1;
+static const char *tls_username;
+static const char *tls_psk;
+
static struct handle *nbdplug_open_handle (int readonly);
static void nbdplug_close_handle (struct handle *h);
@@ -114,7 +121,8 @@ nbdplug_unload (void)
/* Called for each key=value passed on the command line. This plugin
* accepts socket=<sockname>, hostname=<hostname>/port=<port>, or
* [uri=]<uri> (exactly one connection required), and optional
- * parameters export=<name>, retry=<n> and shared=<bool>.
+ * parameters export=<name>, retry=<n>, shared=<bool> and various
+ * tls settings.
*/
static int
nbdplug_config (const char *key, const char *value)
@@ -151,6 +159,29 @@ nbdplug_config (const char *key, const char *value)
return -1;
shared = r;
}
+ else if (strcmp (key, "tls") == 0) {
+ if (strcasecmp (optarg, "require") == 0 ||
+ strcasecmp (optarg, "required") == 0 ||
+ strcasecmp (optarg, "force") == 0)
+ tls = 2;
+ else {
+ tls = nbdkit_parse_bool (optarg);
+ if (tls == -1)
+ exit (EXIT_FAILURE);
+ }
+ }
+ else if (strcmp (key, "tls-certificates") == 0)
+ tls_certificates = value;
+ else if (strcmp (key, "tls-verify") == 0) {
+ r = nbdkit_parse_bool (value);
+ if (r == -1)
+ return -1;
+ tls_verify = r;
+ }
+ else if (strcmp (key, "tls-username") == 0)
+ tls_username = value;
+ else if (strcmp (key, "tls-psk") == 0)
+ tls_psk = value;
else {
nbdkit_error ("unknown parameter '%s'", key);
return -1;
@@ -195,6 +226,9 @@ nbdplug_config_complete (void)
if (!export)
export = "";
+ if (tls == -1)
+ tls = tls_certificates || tls_verify >= 0 || tls_username || tls_psk;
+
if (shared && (shared_handle = nbdplug_open_handle (false)) == NULL)
return -1;
return 0;
@@ -209,6 +243,11 @@ nbdplug_config_complete (void)
"retry=<N> Retry connection up to N seconds (default
0).\n" \
"shared=<BOOL> True to share one server connection among all
clients,\n" \
" rather than a connection per client (default
false).\n" \
+ "tls=<MODE> How to use TLS; one of 'off', 'on',
or 'require'.\n" \
+ "tls-certificates=<DIR> Directory containing files for X.509
certificates.\n" \
+ "tls-verify=<BOOL> True (default for X.509) to validate server.\n"
\
+ "tls-username=<NAME> Override username presented in X.509 TLS.\n" \
+ "tls-psk=<FILE> File containing Pre-Shared Key for TLS.\n" \
static void
nbdplug_dump_plugin (void)
@@ -409,6 +448,17 @@ nbdplug_open_handle (int readonly)
goto err;
if (nbd_add_meta_context (h->nbd, "base:allocation") == -1)
goto err;
+ if (nbd_set_tls (h->nbd, tls) == -1)
+ goto err;
+ if (tls_certificates &&
+ nbd_set_tls_certificates (h->nbd, tls_certificates) == -1)
+ goto err;
+ if (tls_verify >= 0 && nbd_set_tls_verify_peer (h->nbd, tls_verify) ==
-1)
+ goto err;
+ if (tls_username && nbd_set_tls_username (h->nbd, tls_username) == -1)
+ goto err;
+ if (tls_psk && nbd_set_tls_psk_file (h->nbd, tls_psk) == -1)
+ goto err;
if (uri)
r = nbd_connect_uri (h->nbd, uri);
else if (sockname)
diff --git a/TODO b/TODO
index b9ddb1e..332400b 100644
--- a/TODO
+++ b/TODO
@@ -90,13 +90,6 @@ qemu-nbd for these use cases.
https://lists.gnu.org/archive/html/qemu-devel/2017-11/msg02971.html
is a partial solution but it needs cleaning up.
-nbdkit-nbd-plugin could use enhancements:
-
-* Enable client-side TLS (right now, the nbd plugin allows us to
- support an encrypted client connecting to a plain server; but we
- would need TLS to support a plain client connecting to an encrypted
- server).
-
nbdkit-floppy-plugin:
* Add boot sector support. In theory this is easy (eg. using
@@ -159,11 +152,7 @@ Filters allow certain types of composition, but others would not be
possible, for example RAIDing over multiple nbd sources. Because the
plugin API limits us to loading a single plugin to the server, the
best way to do this (and the most robust) is to compose multiple
-nbdkit processes.
-
-The nbd plugin (plugins/nbd) already contains an NBD client, so we
-could factor this client out and make it available to other plugins to
-use.
+nbdkit processes. Perhaps libnbd will prove useful for this purpose.
Build-related
-------------
--
2.20.1