---
.gitignore | 1 +
docs/nbdkit.pod.in | 42 ++++++++-
nbdkit.in | 2 +-
src/crypto.c | 234 +++++++++++++++++++++++++++++++++++---------------
src/internal.h | 1 +
src/main.c | 8 +-
tests/Makefile.am | 8 ++
tests/make-psk.sh | 54 ++++++++++++
tests/test-tls-psk.sh | 118 +++++++++++++++++++++++++
9 files changed, 394 insertions(+), 74 deletions(-)
diff --git a/.gitignore b/.gitignore
index 5c3d822..c986d76 100644
--- a/.gitignore
+++ b/.gitignore
@@ -52,6 +52,7 @@ Makefile.in
/tests/disk.xz
/tests/ext2.img
/tests/file-data
+/tests/keys.psk
/tests/offset-data
/tests/partition-disk
/tests/pki
diff --git a/docs/nbdkit.pod.in b/docs/nbdkit.pod.in
index 42e6e6b..115db3c 100644
--- a/docs/nbdkit.pod.in
+++ b/docs/nbdkit.pod.in
@@ -11,7 +11,7 @@ nbdkit - A toolkit for creating NBD servers
[--newstyle] [--oldstyle] [-P PIDFILE] [-p PORT] [-r]
[--run CMD] [-s] [--selinux-label LABEL] [-t THREADS]
[--tls=off|on|require] [--tls-certificates /path/to/certificates]
- [--tls-verify-peer]
+ [--tls-psk /path/to/pskfile] [--tls-verify-peer]
[-U SOCKET] [-u USER] [-v] [-V]
PLUGIN [key=value [key=value [...]]]
@@ -288,6 +288,12 @@ support). See L</TLS> below.
Set the path to the TLS certificates directory. If not specified,
some built-in paths are checked. See L</TLS> below for more details.
+=item B<--tls-psk> /path/to/pskfile
+
+Set the path to the pre-shared keys (PSK) file. If used, this
+overrides certificate authentication. There is no built-in path. See
+L</TLS> below for more details.
+
=item B<--tls-verify-peer>
Enables TLS client certificate verification. The default is I<not> to
@@ -757,6 +763,35 @@ denied. Also denied are clients which present a valid certificate
signed by another CA. Also denied are clients with certificates added
to the certificate revocation list (F<ca-crl.pem>).
+=head2 TLS with Pre-Shared Keys (PSK)
+
+As a simpler alternative to TLS certificates, you may used pre-shared
+keys to authenticate clients.
+
+Create a PSK file containing one or more C<username:key> pairs. It is
+easiest to use L<psktool(1)> for this:
+
+ mkdir -m 0700 /tmp/keys
+ psktool -u rich -p /tmp/keys/keys.psk
+
+The PSK file contains the hex-encoded random keys in plaintext. Any
+client which can read this file will be able to connect to the server.
+
+Use the nbdkit I<--tls-psk> option to start the server:
+
+ nbdkit --tls=require --tls-psk=/tmp/keys/keys.psk -e / file file=disk.img
+
+This option overrides X.509 certificate authentication.
+
+Clients must supply one of the usernames in the PSK file and the
+corresponding key in order to connect. An example of connecting using
+L<qemu-img(1)> is:
+
+ qemu-img info \
+ --object tls-creds-psk,id=tls0,dir=/tmp/keys,username=rich,endpoint=client \
+ --image-opts \
+ file.driver=nbd,file.host=localhost,file.port=10809,file.tls-creds=tls0,file.export=/
+
=head2 Default TLS behaviour
If nbdkit was compiled without GnuTLS support, then TLS is disabled
@@ -779,6 +814,10 @@ whether or not to use TLS and whether or not to present
certificates.
TLS client certificates are I<not> checked by default unless you
specify I<--tls-verify-peer>.
+If the I<--tls-psk> option is used then TLS is enabled (but I<not>
+required). To ensure that all clients are authorized you must use
+I<--tls=require>.
+
Each of these defaults is insecure to some extent (including
I<--tls=on> which could be subject to a downgrade attack), so if you
expect TLS then it is best to specify the I<--tls> option that you
@@ -968,6 +1007,7 @@
L<https://en.wikipedia.org/wiki/Fibre_Channel_over_Ethernet>.
L<gnutls_priority_init(3)>,
L<qemu-img(1)>,
+L<psktool(1)>,
L<systemd.socket(5)>.
=head1 AUTHORS
diff --git a/nbdkit.in b/nbdkit.in
index e1cb941..51abcbf 100644
--- a/nbdkit.in
+++ b/nbdkit.in
@@ -70,7 +70,7 @@ while [ $# -gt 0 ]; do
args[$i]="$1"
shift
;;
- -e | --export* | -g | --group | -i | --ip* | -P | --pid* | -p | --port | --run |
--selinux-label | -t | --threads | --tls | --tls-certificates | -U | --unix | -u |
--user)
+ -e | --export* | -g | --group | -i | --ip* | -P | --pid* | -p | --port | --run |
--selinux-label | -t | --threads | --tls | --tls-certificates | --tls-psk | -U | --unix |
-u | --user)
args[$i]="$1"
((++i))
args[$i]="$2"
diff --git a/src/crypto.c b/src/crypto.c
index 23c5c8f..6af3977 100644
--- a/src/crypto.c
+++ b/src/crypto.c
@@ -37,10 +37,12 @@
#include <stdlib.h>
#include <stdarg.h>
#include <stdint.h>
+#include <stdbool.h>
#include <inttypes.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
+#include <limits.h>
#include <errno.h>
#include <sys/types.h>
#include <assert.h>
@@ -51,7 +53,12 @@
#include <gnutls/gnutls.h>
+static int crypto_auth = 0;
+#define CRYPTO_AUTH_CERTIFICATES 1
+#define CRYPTO_AUTH_PSK 2
+
static gnutls_certificate_credentials_t x509_creds;
+static gnutls_psk_server_credentials_t psk_creds;
static void print_gnutls_error (int err, const char *fs, ...)
__attribute__((format (printf, 2, 3)));
@@ -147,23 +154,9 @@ load_certificates (const char *path)
return 1;
}
-/* Initialize crypto. This also handles the command line parameters
- * and loading the server certificate.
- */
-void
-crypto_init (int tls_set_on_cli)
+static int
+start_certificates (void)
{
- int err;
-
- err = gnutls_global_init ();
- if (err < 0) {
- print_gnutls_error (err, "initializing GnuTLS");
- exit (EXIT_FAILURE);
- }
-
- if (tls == 0) /* --tls=off */
- return;
-
/* Try to locate the certificates directory and load them. */
if (tls_certificates_dir == NULL) {
const char *home;
@@ -196,49 +189,119 @@ crypto_init (int tls_set_on_cli)
if (load_certificates (tls_certificates_dir))
goto found_certificates;
}
-
- /* If we get here, we didn't manage to load the certificates. If
- * --tls=require was given on the command line then that's a
- * problem.
- */
- if (tls == 2) { /* --tls=require */
- fprintf (stderr,
- "%s: --tls=require but could not load TLS certificates.\n"
- "Try setting ‘--tls-certificates=/path/to/certificates’ or
read\n"
- "the \"TLS\" section in nbdkit(1).\n",
- program_name);
- exit (EXIT_FAILURE);
- }
-
- /* If --tls=on was given on the command line, warn before we turn
- * TLS off.
- */
- if (tls == 1 && tls_set_on_cli) { /* explicit --tls=on */
- fprintf (stderr,
- "%s: warning: --tls=on but could not load TLS certificates.\n"
- "TLS will be disabled and TLS connections will be rejected.\n"
- "Try setting ‘--tls-certificates=/path/to/certificates’ or
read\n"
- "the \"TLS\" section in nbdkit(1).\n",
- program_name);
- }
-
- tls = 0;
- debug ("TLS disabled: could not load TLS certificates");
- return;
+ return -1;
found_certificates:
#ifdef HAVE_GNUTLS_CERTIFICATE_SET_KNOWN_DH_PARAMS
gnutls_certificate_set_known_dh_params (x509_creds, GNUTLS_SEC_PARAM_MEDIUM);
#endif
+ return 0;
+}
- debug ("TLS enabled");
+static int
+start_psk (void)
+{
+ int err;
+ CLEANUP_FREE char *abs_psk_file = NULL;
+
+ /* Make sure the path to the PSK file is absolute. */
+ abs_psk_file = realpath (tls_psk, NULL);
+ if (abs_psk_file == NULL) {
+ perror (tls_psk);
+ exit (EXIT_FAILURE);
+ }
+
+ err = gnutls_psk_allocate_server_credentials (&psk_creds);
+ if (err < 0) {
+ print_gnutls_error (err, "allocating PSK credentials");
+ exit (EXIT_FAILURE);
+ }
+
+ /* Note that this function makes a copy of the string so it
+ * is safe to free it afterwards.
+ */
+ gnutls_psk_set_server_credentials_file (psk_creds, abs_psk_file);
+
+ return 0;
+}
+
+/* Initialize crypto. This also handles the command line parameters
+ * and loading the server certificate.
+ */
+void
+crypto_init (int tls_set_on_cli)
+{
+ int err, r;
+ const char *what;
+
+ err = gnutls_global_init ();
+ if (err < 0) {
+ print_gnutls_error (err, "initializing GnuTLS");
+ exit (EXIT_FAILURE);
+ }
+
+ if (tls == 0) /* --tls=off */
+ return;
+
+ /* --tls-psk overrides certificates. */
+ if (tls_psk != NULL) {
+ r = start_psk ();
+ what = "Pre-Shared Keys (PSK)";
+ if (r == 0) crypto_auth = CRYPTO_AUTH_PSK;
+ }
+ else {
+ r = start_certificates ();
+ what = "X.509 certificates";
+ if (r == 0) crypto_auth = CRYPTO_AUTH_CERTIFICATES;
+ }
+
+ if (r == 0) {
+ debug ("TLS enabled using: %s", what);
+ }
+ else {
+ /* If we get here, we didn't manage to load the PSK file /
+ * certificates. If --tls=require was given on the command line
+ * then that's a problem.
+ */
+ if (tls == 2) { /* --tls=require */
+ fprintf (stderr,
+ "%s: --tls=require but could not load TLS certificates.\n"
+ "Try setting ‘--tls-certificates=/path/to/certificates’ or
read\n"
+ "the \"TLS\" section in nbdkit(1).\n",
+ program_name);
+ exit (EXIT_FAILURE);
+ }
+
+ /* If --tls=on was given on the command line, warn before we turn
+ * TLS off.
+ */
+ if (tls == 1 && tls_set_on_cli) { /* explicit --tls=on */
+ fprintf (stderr,
+ "%s: warning: --tls=on but could not load TLS certificates.\n"
+ "TLS will be disabled and TLS connections will be rejected.\n"
+ "Try setting ‘--tls-certificates=/path/to/certificates’ or
read\n"
+ "the \"TLS\" section in nbdkit(1).\n",
+ program_name);
+ }
+
+ tls = 0;
+ debug ("TLS disabled: could not load TLS certificates");
+ }
}
void
crypto_free (void)
{
- if (tls > 0)
- gnutls_certificate_free_credentials (x509_creds);
+ if (tls > 0) {
+ switch (crypto_auth) {
+ case CRYPTO_AUTH_CERTIFICATES:
+ gnutls_certificate_free_credentials (x509_creds);
+ break;
+ case CRYPTO_AUTH_PSK:
+ gnutls_psk_free_server_credentials (psk_creds);
+ break;
+ }
+ }
gnutls_global_deinit ();
}
@@ -335,6 +398,7 @@ int
crypto_negotiate_tls (struct connection *conn, int sockin, int sockout)
{
gnutls_session_t *session;
+ CLEANUP_FREE char *priority = NULL;
int err;
/* Create the GnuTLS session. */
@@ -351,33 +415,61 @@ crypto_negotiate_tls (struct connection *conn, int sockin, int
sockout)
return -1;
}
- err = gnutls_priority_set_direct (*session, TLS_PRIORITY, NULL);
- if (err < 0) {
- nbdkit_error ("failed to set TLS session priority to %s: %s",
- TLS_PRIORITY, gnutls_strerror (err));
- goto error;
- }
+ switch (crypto_auth) {
+ case CRYPTO_AUTH_CERTIFICATES:
+ /* Associate the session with the server credentials (key, cert). */
+ err = gnutls_credentials_set (*session, GNUTLS_CRD_CERTIFICATE,
+ x509_creds);
+ if (err < 0) {
+ nbdkit_error ("gnutls_credentials_set: %s", gnutls_strerror (err));
+ goto error;
+ }
- /* Associate the session with the server credentials (key, cert). */
- err = gnutls_credentials_set (*session, GNUTLS_CRD_CERTIFICATE,
- x509_creds);
- if (err < 0) {
- nbdkit_error ("gnutls_credentials_set: %s", gnutls_strerror (err));
- goto error;
- }
-
- /* If verify peer is enabled, tell GnuTLS to request the client
- * certificates. (Note the default is to not request or verify
- * certificates).
- */
- if (tls_verify_peer) {
+ /* If verify peer is enabled, tell GnuTLS to request the client
+ * certificates. (Note the default is to not request or verify
+ * certificates).
+ */
+ if (tls_verify_peer) {
#ifdef HAVE_GNUTLS_SESSION_SET_VERIFY_CERT
- gnutls_certificate_server_set_request (*session, GNUTLS_CERT_REQUEST);
- gnutls_session_set_verify_cert (*session, NULL, 0);
+ gnutls_certificate_server_set_request (*session, GNUTLS_CERT_REQUEST);
+ gnutls_session_set_verify_cert (*session, NULL, 0);
#else
- nbdkit_error ("--tls-verify-peer: GnuTLS >= 3.4.6 is required for this
feature");
- goto error;
+ nbdkit_error ("--tls-verify-peer: GnuTLS >= 3.4.6 is required for this
feature");
+ goto error;
#endif
+ }
+
+ priority = strdup (TLS_PRIORITY);
+ if (priority == NULL) {
+ nbdkit_error ("strdup: %m");
+ goto error;
+ }
+ break;
+
+ case CRYPTO_AUTH_PSK:
+ /* Associate the session with the server PSK credentials. */
+ err = gnutls_credentials_set (*session, GNUTLS_CRD_PSK, psk_creds);
+ if (err < 0) {
+ nbdkit_error ("gnutls_credentials_set: %s", gnutls_strerror (err));
+ goto error;
+ }
+
+ if (asprintf (&priority, "%s:+PSK:+DHE-PSK", TLS_PRIORITY) == -1) {
+ nbdkit_error ("asprintf: %m");
+ goto error;
+ }
+ break;
+
+ default:
+ abort ();
+ }
+
+ assert (priority != NULL);
+ err = gnutls_priority_set_direct (*session, priority, NULL);
+ if (err < 0) {
+ nbdkit_error ("failed to set TLS session priority to %s: %s",
+ priority, gnutls_strerror (err));
+ goto error;
}
/* Set up GnuTLS so it reads and writes on the raw sockets, and set
diff --git a/src/internal.h b/src/internal.h
index ea3155c..ec19841 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -108,6 +108,7 @@ extern int readonly;
extern const char *selinux_label;
extern int tls;
extern const char *tls_certificates_dir;
+extern const char *tls_psk;
extern int tls_verify_peer;
extern char *unixsocket;
extern int verbose;
diff --git a/src/main.c b/src/main.c
index 8d901cf..6524b85 100644
--- a/src/main.c
+++ b/src/main.c
@@ -89,6 +89,7 @@ const char *selinux_label; /* --selinux-label */
int threads; /* -t */
int tls; /* --tls : 0=off 1=on 2=require */
const char *tls_certificates_dir; /* --tls-certificates */
+const char *tls_psk; /* --tls-psk */
int tls_verify_peer; /* --tls-verify-peer */
char *unixsocket; /* -U */
const char *user, *group; /* -u & -g */
@@ -144,6 +145,7 @@ static const struct option long_options[] = {
{ "threads", 1, NULL, 't' },
{ "tls", 1, NULL, 0 },
{ "tls-certificates", 1, NULL, 0 },
+ { "tls-psk", 1, NULL, 0 },
{ "tls-verify-peer", 0, NULL, 0 },
{ "unix", 1, NULL, 'U' },
{ "user", 1, NULL, 'u' },
@@ -161,7 +163,7 @@ usage (void)
" [--newstyle] [--oldstyle] [-P PIDFILE] [-p PORT] [-r]\n"
" [--run CMD] [-s] [--selinux-label LABEL] [-t THREADS]\n"
" [--tls=off|on|require] [--tls-certificates
/path/to/certificates]\n"
- " [--tls-verify-peer]\n"
+ " [--tls-psk /path/to/pskfile] [--tls-verify-peer]\n"
" [-U SOCKET] [-u USER] [-v] [-V]\n"
" PLUGIN [key=value [key=value [...]]]\n"
"\n"
@@ -314,6 +316,10 @@ main (int argc, char *argv[])
tls_certificates_dir = optarg;
break;
}
+ else if (strcmp (long_options[option_index].name, "tls-psk") == 0) {
+ tls_psk = optarg;
+ break;
+ }
else if (strcmp (long_options[option_index].name, "tls-verify-peer") ==
0) {
tls_verify_peer = 1;
break;
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 755fe51..a7a31b9 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -37,6 +37,7 @@ MAINTAINERCLEANFILES =
EXTRA_DIST = \
functions.sh \
make-pki.sh \
+ make-psk.sh \
python-exception.py \
README.tests \
shebang.pl \
@@ -72,6 +73,7 @@ EXTRA_DIST = \
test-start.sh \
test-random-sock.sh \
test-tls.sh \
+ test-tls-psk.sh \
test-version.sh \
test-version-filter.sh \
test-version-plugin.sh \
@@ -112,6 +114,7 @@ TESTS += \
test-captive.sh \
test-random-sock.sh \
test-tls.sh \
+ test-tls-psk.sh \
test-ip.sh \
test-socket-activation \
test-foreground.sh
@@ -171,6 +174,11 @@ check_DATA += pki/.stamp
pki/.stamp: $(srcdir)/make-pki.sh
$(srcdir)/make-pki.sh
+# PSK keys for the TLS-PSK tests.
+check_DATA += keys.psk
+keys.psk: $(srcdir)/make-psk.sh
+ $(srcdir)/make-psk.sh
+
#----------------------------------------------------------------------
# Tests of C plugins or tests which require plugins.
diff --git a/tests/make-psk.sh b/tests/make-psk.sh
new file mode 100755
index 0000000..5e27ea5
--- /dev/null
+++ b/tests/make-psk.sh
@@ -0,0 +1,54 @@
+#!/bin/bash -
+# nbdkit
+# Copyright (C) 2018 Red Hat Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# * Neither the name of Red Hat nor the names of its contributors may be
+# used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+set -e
+
+# This creates the PSK keys for the TLS-PSK tests. However if pkstool
+# doesn't exist, just create an empty directory instead.
+
+if [ ! -f test-tls-psk.sh ]; then
+ echo "$0: script is being run from the wrong directory."
+ echo "Don't try to run this script by hand."
+ exit 1
+fi
+
+rm -f keys.psk
+
+if ! psktool --help >/dev/null 2>&1; then
+ echo "$0: psktool not found, TLS-PSK tests will be skipped."
+ touch keys.psk
+ exit 0
+fi
+
+# Create the keys file.
+psktool -u qemu -p keys.psk
diff --git a/tests/test-tls-psk.sh b/tests/test-tls-psk.sh
new file mode 100755
index 0000000..4f8111a
--- /dev/null
+++ b/tests/test-tls-psk.sh
@@ -0,0 +1,118 @@
+#!/bin/bash -
+# nbdkit
+# Copyright (C) 2018 Red Hat Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# * Neither the name of Red Hat nor the names of its contributors may be
+# used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+set -e
+set -x
+source ./functions.sh
+
+# Don't fail if certain commands aren't available.
+if ! ss --version; then
+ echo "$0: 'ss' command not available"
+ exit 77
+fi
+if ! command -v qemu-img > /dev/null; then
+ echo "$0: 'qemu-img' command not available"
+ exit 77
+fi
+if ! qemu-img --help | grep -- --object; then
+ echo "$0: 'qemu-img' command does not have the --object option"
+ exit 77
+fi
+
+# Does the qemu-img binary support PSK?
+if LANG=C qemu-img info --object tls-creds-psk,id=id disk |&
+ grep -sq "invalid object type: tls-creds-psk"
+then
+ echo "$0: 'qemu-img' command does not support TLS-PSK"
+ exit 77
+fi
+
+# Does the nbdkit binary support TLS?
+if ! nbdkit --dump-config | grep -sq tls=yes; then
+ echo "$0: nbdkit built without TLS support"
+ exit 77
+fi
+
+# Did we create the PSK keys file?
+# Probably 'certtool' is missing.
+if [ ! -s keys.psk ]; then
+ echo "$0: PSK keys file was not created by the test harness"
+ exit 77
+fi
+
+# Unfortunately qemu cannot do TLS over a Unix domain socket (nbdkit
+# probably can, although it is not tested). Find an unused port to
+# listen on.
+for port in `seq 50000 65535`; do
+ if ! ss -ltn | grep -sqE ":$port\b"; then break; fi
+done
+echo picked unused port $port
+
+nbdkit -P tls-psk.pid -p $port -n --tls=require --tls-psk=keys.psk example1
+
+# We may have to wait a short time for the pid file to appear.
+for i in `seq 1 10`; do
+ if test -f tls-psk.pid; then
+ break
+ fi
+ sleep 1
+done
+if ! test -f tls-psk.pid; then
+ echo "$0: PID file was not created"
+ exit 1
+fi
+
+pid="$(cat tls-psk.pid)"
+
+# Kill the process on exit.
+cleanup ()
+{
+ status=$?
+
+ kill $pid
+ rm -f tls-psk.pid tls-psk.out
+
+ exit $status
+}
+trap cleanup INT QUIT TERM EXIT ERR
+
+# Run qemu-img against the server.
+LANG=C \
+qemu-img info \
+ --object "tls-creds-psk,id=tls0,endpoint=client,dir=$PWD" \
+ --image-opts
"file.driver=nbd,file.host=localhost,file.port=$port,file.tls-creds=tls0" >
tls-psk.out
+
+cat tls-psk.out
+
+grep -sq "^file format: raw" tls-psk.out
+grep -sq "^virtual size: 100M" tls-psk.out
--
2.16.2