=head1 NAME

nbdkit-tls - authentication and encryption of NBD connections
(sometimes called "SSL")

=head1 SYNOPSIS

 nbdkit [--tls=off|on|require]
        [--tls-certificates=/path/to/certificates]
        [--tls-psk=/path/to/pskfile]
        [--tls-verify-peer]
        PLUGIN [...]

=head1 DESCRIPTION

TLS (authentication and encryption, sometimes incorrectly called
"SSL") is supported if nbdkit was compiled with GnuTLS.  This allows
the server to verify that the client is allowed access, and to encrypt
the contents of the protocol in transit over the network.

TLS can be disabled or enabled by specifying either I<--tls=off> or
I<--tls=on>.  With I<--tls=off>, if a client tries to use TLS to
connect, it will be rejected by the server (in other words, as if the
server doesn't support TLS).

I<--tls=on> means that the client may choose to connect either with or
without TLS.

I<--tls=require> enables TLS B<and> rejects all non-TLS connection
attempts.  This prevents downgrade attacks where a malicious proxy
pretends not to support TLS in order to force either the client or
server to communicate in plaintext.

=head2 Example

If certificates have been set up correctly then you should be able to
start a TLS server by doing:

 nbdkit --tls=require memory 1G

and connect to it by doing:

 nbdinfo nbds://localhost

If certificates are in a non-standard directory and you have
S<libnbd E<ge> 1.10>:

 nbdkit --tls=require --tls-certificates=/certs memory 1G
 nbdinfo nbds://localhost?tls-certificates=/certs

=head2 TLS with X.509 certificates

When nbdkit starts up, it loads TLS certificates from some built-in
paths, or from the directory specified by the I<--tls-certificates>
option.

In this directory, nbdkit expects to find several files:

=over 4

=item F<ca-cert.pem>

The Certificate Authority certificate.

=item F<server-cert.pem>

The server certificate.

=item F<server-key.pem>

The server private key.

=item F<ca-crl.pem>

(Optional) The certificate revocation list.

=back

=head3 Setting up the Certificate Authority

This step only needs to be done once per organization.  It may be that
your organization already has a CA.

 $ certtool --generate-privkey > ca-key.pem
 $ chmod 0600 ca-key.pem

The F<ca-key.pem> file is the CA private key and is I<extremely>
sensitive data.  With possession of this key, anyone can create
certificates pretending to be your organization!

To create the CA certificate file:

 $ cat > ca.info <<EOF
 cn = Name of your organization
 ca
 cert_signing_key
 EOF
 $ certtool --generate-self-signed \
            --load-privkey ca-key.pem \
            --template ca.info \
            --outfile ca-cert.pem

=head3 Issuing a server certificate for the nbdkit server

Each nbdkit server (or host) needs a secret key and certificate.

 $ certtool --generate-privkey > server-key.pem
 $ chmod 0600 server-key.pem

The server key file is sensitive.  Setting the mode to C<0600> helps
to prevent other users on the same machine from reading it.

The common name (C<cn> below) field must be the fully qualified
hostname that the client connects to.  However most clients and
servers including nbdkit support the Subject Alternative Name
extension (S<RFC 2818>) which uses the C<dns_name> and C<ip_address>
fields and deprecates C<cn>.

 $ cat > server.info <<EOF
 organization = Name of your organization
 cn = nbd-server.example.com
 dns_name = nbd-server
 dns_name = nbd-server.example.com
 ip_address = 10.0.1.2
 ip_address = 2001:24::92
 tls_www_server
 encryption_key
 signing_key
 EOF
 $ certtool --generate-certificate \
            --load-ca-certificate ca-cert.pem \
            --load-ca-privkey ca-key.pem \
            --load-privkey server-key.pem \
            --template server.info \
            --outfile server-cert.pem

=head3 Issuing and checking client certificates

B<Note:> You don't need to create client certificates unless you want
to check and limit which clients can connect to nbdkit.  B<nbdkit does
not check client certificates> unless you specify the
I<--tls-verify-peer> option on the command line.  There are other
methods for limiting access to nbdkit including
L<nbdkit-ip-filter(1)>.

For each client you should generate a private key and a client
certificate:

 $ certtool --generate-privkey > client-key.pem
 $ chmod 0600 client-key.pem

The client key file is sensitive.

The client DNS name (C<cn> below) is the client's name for information
only.

 $ cat > client.info <<EOF
 country = US
 state = New York
 locality = New York
 organization = Name of your organization
 cn = client.example.com
 tls_www_client
 encryption_key
 signing_key
 EOF
 $ certtool --generate-certificate \
            --load-ca-certificate ca-cert.pem \
            --load-ca-privkey ca-key.pem \
            --load-privkey client-key.pem \
            --template client.info \
            --outfile client-cert.pem

Client certificates do I<not> need to be present anywhere on the
nbdkit host.  You don't need to copy them into nbdkit's TLS
certificates directory.  The security comes from the fact that the
client must present a client certificate signed by the Certificate
Authority, and nbdkit can check this because it has the F<ca-cert.pem>
file.

To enable checking of client certificates, specify the
I<--tls-verify-peer> option on the command line.  Clients which don't
present a valid certificate (eg. not signed, incorrect signature) are
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 Connecting nbd-client to nbdkit with TLS certificates

With the TLS certificates files generated above in the current
directory (C<.>) you can use:

 nbdkit --tls=require --tls-certificates=. --tls-verify-peer memory 1G

 nbd-client /dev/nbd0 \
           -cacertfile ca-cert.pem \
           -certfile client-cert.pem \
           -keyfile client-key.pem

I<--tls-verify-peer> is only required if you want to check the client
certificate.  If you want to allow any client to connect then you can
omit it.

=head2 TLS with Pre-Shared Keys (PSK)

As a simpler alternative to TLS certificates, you may use 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 alice -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 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<nbdinfo(1)> using an NBD URI is:

 nbdinfo 'nbds://alice@localhost?tls-psk-file=/tmp/keys/keys.psk'

An example of connecting using L<qemu-img(1)> is:

 qemu-img info \
   --object tls-creds-psk,id=tls0,dir=/tmp/keys,username=alice,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
and TLS connections will be rejected (as if I<--tls=off> was specified
on the command line).  Also it is impossible to turn on TLS in this
scenario.  You can tell if nbdkit was compiled without GnuTLS support
because C<nbdkit --dump-config> will contain C<tls=no>.

If TLS certificates cannot be loaded either from the built-in path or
from the directory specified by I<--tls-certificates>, then TLS
defaults to disabled.  Turning TLS on will give a warning
(I<--tls=on>) or error (I<--tls=require>) about the missing
certificates.

If TLS certificates can be loaded from the built-in path or from the
I<--tls-certificates> directory, then TLS will by default be enabled
(like I<--tls=on>), but it is not required.  Clients can choose
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).  If you
expect TLS then it is best to specify I<--tls=require>, and if you
want to check client certificates, additionally use the
I<--tls-verify-peer> option.

=head2 Controlling TLS fallback to plaintext

When I<--tls=on> is used, the connection can fall back to plaintext.
You can use L<nbdkit-tls-fallback-filter(1)> to provide safe fallback
content to plaintext connections.  With this filter the underlying
plugin content is only served on secure connections.

Alternatively a plugin may wish to serve different content depending
on whether the client is using TLS.  The function L<nbdkit_is_tls(3)>
can be used during the C<.open> callback for that purpose.

=head2 NBD URIs for TLS

Tools such L<nbdcopy(1)>, L<nbdinfo(1)> and L<nbdsh(1)> (from
L<libnbd(3)>) allow you to use C<nbds://> or C<nbds+unix://> URIs to
connect to nbdkit servers using TLS.

The syntax is fully documented in the NBD URI specification:
L<https://github.com/NetworkBlockDevice/nbd/blob/master/doc/uri.md>.
This section contains an outline.  You can also find further examples
in L<nbd_connect_uri(3)>.

Use the nbdkit I<--print-uri> option to print the URI when nbdkit
starts up.

=over 4

=item B<nbds://>example.com

Connect over TCP with TLS, to C<example.com> port 10809.  If the
server does not support TLS then this will fail.

=item B<nbds+unix:///?socket=>SOCKET

As above, but connect over a Unix domain socket called F<SOCKET>.

=item B<nbds+unix:///?socket=>SOCKETB<&tls-certificates=>DIR

As above, but specify the directory F<DIR> containing TLS certificates
(used by the client to verify the server, and to present client
authentication to the server).  Note this requires S<libnbd E<ge> 1.10>.

=item B<nbds+unix:///?socket=>SOCKETB<&tls-psk-file=>FILENAME

As above, but use TLS with Pre-Shared Keys (PSK), stored in the
secrets file F<FILENAME>.

=item B<nbds+unix://>aliceB<@/?socket=>SOCKETB<&tls-psk-file=>FILENAME

As above, but use C<alice> as the username.

=back

=head2 Default location of certificates

Without I<--tls-certificates> nbdkit and libnbd look in several
locations for certificates.

If nbdkit is started as a non-root user (note this does not include
use of the I<-u> or I<-g> options), nbdkit looks in each of these
paths in turn:

 $HOME/.pki/nbdkit/
 $HOME/.config/pki/nbdkit/

If nbdkit is started as root:

 $sysconfdir/pki/nbdkit/

where $sysconfdir is set when nbdkit is compiled, usually F</etc>.
(Use C<nbdkit --dump-config> and look at the
C<root_tls_certificates_dir> setting to get the actual directory built
into the binary.)

In libnbd the paths are different.  For non-root:

 $HOME/.pki/libnbd/
 $HOME/.config/pki/libnbd/

For root:

 $sysconfdir/pki/libnbd/

In nbdkit you can override these directories by using
I<--tls-certificates=/path/to/certificates>.

In libnbd you can use L<nbd_set_tls_certificates(3)>.
In S<libnbd E<ge> 1.10> you can append
C<&tls-certificates=/path/to/certificates> to URIs.

=head2 Choice of TLS algorithms

TLS has a bewildering choice of algorithms that can be used.  To
enable you to choose a default set of algorithms, there is a configure
setting I<--with-tls-priority>.  This defaults to C<NORMAL> which, to
quote the GnuTLS documentation:

=over 4

"C<NORMAL> means all C<secure> ciphersuites.  The 256-bit ciphers are
included as a fallback only.  The ciphers are sorted by security
margin."

=back

You could also set the TLS priority so that it can be configured from
a file at runtime:

 ./configure --with-tls-priority=@SYSTEM

means use the policy from F</etc/crypto-policies/config>.

 ./configure --with-tls-priority=@NBDKIT,SYSTEM

means use the policy from
F</etc/crypto-policies/local.d/nbdkit.config> and fall back to
F</etc/crypto-policies/config> if the first file does not exist.

More information can be found in L<gnutls_priority_init(3)>.

=head2 Debugging TLS connections

Encrypted connections makes snooping on network traffic with Wireshark
impossible, by design.

The TLS library used by nbdkit, called gnutls, supports the
C<SSLKEYLOGFILE> standard:
L<https://web.archive.org/web/20200118013150/https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format>

Before running nbdkit set the environment variable to point to a log
file (note it will append to the file if it already exists):

 SSLKEYLOGFILE=/tmp/keylog nbdkit [...]

In Wireshark go to Edit → Preferences → Protocols → TLS and set
S<(Pre)-Master-Secret log filename> to the log file name.  Wireshark
should be able to see the unencrypted traffic.  For further
information read:
L<https://wiki.wireshark.org/TLS#using-the-pre-master-secret>

=head1 SEE ALSO

L<nbdkit(1)>,
L<nbdkit-luks-filter(1)>,
L<nbdkit-tls-fallback-filter(1)>,
L<nbdkit_is_tls(3)>,
L<nbdkit_peer_tls_dn(3)>,
L<nbdkit_peer_tls_issuer_dn(3)>,
L<nbdcopy(1)>,
L<nbdfuse(1)>,
L<nbdinfo(1)>,
L<nbdsh(1)>,
L<nbd_connect_uri(3)>,
L<nbd_set_tls(3)>,
L<nbd_set_tls_certificates(3)>,
L<gnutls_priority_init(3)>,
L<psktool(1)>,
L<https://github.com/NetworkBlockDevice/nbd/blob/master/doc/proto.md>,
L<https://github.com/NetworkBlockDevice/nbd/blob/master/doc/uri.md>,
L<https://nbd.sourceforge.io/>.

=head1 AUTHORS

Eric Blake

Richard W.M. Jones

Pino Toscano

=head1 COPYRIGHT

Copyright Red Hat
