329 lines
13 KiB
ReStructuredText
329 lines
13 KiB
ReStructuredText
.. _network_005ftls:
|
|
|
|
TLS setup for network services
|
|
------------------------------
|
|
|
|
Almost all network services in QEMU have the ability to use TLS for
|
|
session data encryption, along with x509 certificates for simple client
|
|
authentication. What follows is a description of how to generate
|
|
certificates suitable for usage with QEMU, and applies to the VNC
|
|
server, character devices with the TCP backend, NBD server and client,
|
|
and migration server and client.
|
|
|
|
At a high level, QEMU requires certificates and private keys to be
|
|
provided in PEM format. Aside from the core fields, the certificates
|
|
should include various extension data sets, including v3 basic
|
|
constraints data, key purpose, key usage and subject alt name.
|
|
|
|
The GnuTLS package includes a command called ``certtool`` which can be
|
|
used to easily generate certificates and keys in the required format
|
|
with expected data present. Alternatively a certificate management
|
|
service may be used.
|
|
|
|
At a minimum it is necessary to setup a certificate authority, and issue
|
|
certificates to each server. If using x509 certificates for
|
|
authentication, then each client will also need to be issued a
|
|
certificate.
|
|
|
|
Assuming that the QEMU network services will only ever be exposed to
|
|
clients on a private intranet, there is no need to use a commercial
|
|
certificate authority to create certificates. A self-signed CA is
|
|
sufficient, and in fact likely to be more secure since it removes the
|
|
ability of malicious 3rd parties to trick the CA into mis-issuing certs
|
|
for impersonating your services. The only likely exception where a
|
|
commercial CA might be desirable is if enabling the VNC websockets
|
|
server and exposing it directly to remote browser clients. In such a
|
|
case it might be useful to use a commercial CA to avoid needing to
|
|
install custom CA certs in the web browsers.
|
|
|
|
The recommendation is for the server to keep its certificates in either
|
|
``/etc/pki/qemu`` or for unprivileged users in ``$HOME/.pki/qemu``.
|
|
|
|
.. _tls_005fgenerate_005fca:
|
|
|
|
Setup the Certificate Authority
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
This step only needs to be performed once per organization /
|
|
organizational unit. First the CA needs a private key. This key must be
|
|
kept VERY secret and secure. If this key is compromised the entire trust
|
|
chain of the certificates issued with it is lost.
|
|
|
|
::
|
|
|
|
# certtool --generate-privkey > ca-key.pem
|
|
|
|
To generate a self-signed certificate requires one core piece of
|
|
information, the name of the organization. A template file ``ca.info``
|
|
should be populated with the desired data to avoid having to deal with
|
|
interactive prompts from certtool::
|
|
|
|
# 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
|
|
|
|
The ``ca`` keyword in the template sets the v3 basic constraints
|
|
extension to indicate this certificate is for a CA, while
|
|
``cert_signing_key`` sets the key usage extension to indicate this will
|
|
be used for signing other keys. The generated ``ca-cert.pem`` file
|
|
should be copied to all servers and clients wishing to utilize TLS
|
|
support in the VNC server. The ``ca-key.pem`` must not be
|
|
disclosed/copied anywhere except the host responsible for issuing
|
|
certificates.
|
|
|
|
.. _tls_005fgenerate_005fserver:
|
|
|
|
Issuing server certificates
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Each server (or host) needs to be issued with a key and certificate.
|
|
When connecting the certificate is sent to the client which validates it
|
|
against the CA certificate. The core pieces of information for a server
|
|
certificate are the hostnames and/or IP addresses that will be used by
|
|
clients when connecting. The hostname / IP address that the client
|
|
specifies when connecting will be validated against the hostname(s) and
|
|
IP address(es) recorded in the server certificate, and if no match is
|
|
found the client will close the connection.
|
|
|
|
Thus it is recommended that the server certificate include both the
|
|
fully qualified and unqualified hostnames. If the server will have
|
|
permanently assigned IP address(es), and clients are likely to use them
|
|
when connecting, they may also be included in the certificate. Both IPv4
|
|
and IPv6 addresses are supported. Historically certificates only
|
|
included 1 hostname in the ``CN`` field, however, usage of this field
|
|
for validation is now deprecated. Instead modern TLS clients will
|
|
validate against the Subject Alt Name extension data, which allows for
|
|
multiple entries. In the future usage of the ``CN`` field may be
|
|
discontinued entirely, so providing SAN extension data is strongly
|
|
recommended.
|
|
|
|
On the host holding the CA, create template files containing the
|
|
information for each server, and use it to issue server certificates.
|
|
|
|
::
|
|
|
|
# cat > server-hostNNN.info <<EOF
|
|
organization = Name of your organization
|
|
cn = hostNNN.foo.example.com
|
|
dns_name = hostNNN
|
|
dns_name = hostNNN.foo.example.com
|
|
ip_address = 10.0.1.87
|
|
ip_address = 192.8.0.92
|
|
ip_address = 2620:0:cafe::87
|
|
ip_address = 2001:24::92
|
|
tls_www_server
|
|
encryption_key
|
|
signing_key
|
|
EOF
|
|
# certtool --generate-privkey > server-hostNNN-key.pem
|
|
# certtool --generate-certificate \
|
|
--load-ca-certificate ca-cert.pem \
|
|
--load-ca-privkey ca-key.pem \
|
|
--load-privkey server-hostNNN-key.pem \
|
|
--template server-hostNNN.info \
|
|
--outfile server-hostNNN-cert.pem
|
|
|
|
The ``dns_name`` and ``ip_address`` fields in the template are setting
|
|
the subject alt name extension data. The ``tls_www_server`` keyword is
|
|
the key purpose extension to indicate this certificate is intended for
|
|
usage in a web server. Although QEMU network services are not in fact
|
|
HTTP servers (except for VNC websockets), setting this key purpose is
|
|
still recommended. The ``encryption_key`` and ``signing_key`` keyword is
|
|
the key usage extension to indicate this certificate is intended for
|
|
usage in the data session.
|
|
|
|
The ``server-hostNNN-key.pem`` and ``server-hostNNN-cert.pem`` files
|
|
should now be securely copied to the server for which they were
|
|
generated, and renamed to ``server-key.pem`` and ``server-cert.pem``
|
|
when added to the ``/etc/pki/qemu`` directory on the target host. The
|
|
``server-key.pem`` file is security sensitive and should be kept
|
|
protected with file mode 0600 to prevent disclosure.
|
|
|
|
.. _tls_005fgenerate_005fclient:
|
|
|
|
Issuing client certificates
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
The QEMU x509 TLS credential setup defaults to enabling client
|
|
verification using certificates, providing a simple authentication
|
|
mechanism. If this default is used, each client also needs to be issued
|
|
a certificate. The client certificate contains enough metadata to
|
|
uniquely identify the client with the scope of the certificate
|
|
authority. The client certificate would typically include fields for
|
|
organization, state, city, building, etc.
|
|
|
|
Once again on the host holding the CA, create template files containing
|
|
the information for each client, and use it to issue client
|
|
certificates.
|
|
|
|
::
|
|
|
|
# cat > client-hostNNN.info <<EOF
|
|
country = GB
|
|
state = London
|
|
locality = City Of London
|
|
organization = Name of your organization
|
|
cn = hostNNN.foo.example.com
|
|
tls_www_client
|
|
encryption_key
|
|
signing_key
|
|
EOF
|
|
# certtool --generate-privkey > client-hostNNN-key.pem
|
|
# certtool --generate-certificate \
|
|
--load-ca-certificate ca-cert.pem \
|
|
--load-ca-privkey ca-key.pem \
|
|
--load-privkey client-hostNNN-key.pem \
|
|
--template client-hostNNN.info \
|
|
--outfile client-hostNNN-cert.pem
|
|
|
|
The subject alt name extension data is not required for clients, so the
|
|
the ``dns_name`` and ``ip_address`` fields are not included. The
|
|
``tls_www_client`` keyword is the key purpose extension to indicate this
|
|
certificate is intended for usage in a web client. Although QEMU network
|
|
clients are not in fact HTTP clients, setting this key purpose is still
|
|
recommended. The ``encryption_key`` and ``signing_key`` keyword is the
|
|
key usage extension to indicate this certificate is intended for usage
|
|
in the data session.
|
|
|
|
The ``client-hostNNN-key.pem`` and ``client-hostNNN-cert.pem`` files
|
|
should now be securely copied to the client for which they were
|
|
generated, and renamed to ``client-key.pem`` and ``client-cert.pem``
|
|
when added to the ``/etc/pki/qemu`` directory on the target host. The
|
|
``client-key.pem`` file is security sensitive and should be kept
|
|
protected with file mode 0600 to prevent disclosure.
|
|
|
|
If a single host is going to be using TLS in both a client and server
|
|
role, it is possible to create a single certificate to cover both roles.
|
|
This would be quite common for the migration and NBD services, where a
|
|
QEMU process will be started by accepting a TLS protected incoming
|
|
migration, and later itself be migrated out to another host. To generate
|
|
a single certificate, simply include the template data from both the
|
|
client and server instructions in one.
|
|
|
|
::
|
|
|
|
# cat > both-hostNNN.info <<EOF
|
|
country = GB
|
|
state = London
|
|
locality = City Of London
|
|
organization = Name of your organization
|
|
cn = hostNNN.foo.example.com
|
|
dns_name = hostNNN
|
|
dns_name = hostNNN.foo.example.com
|
|
ip_address = 10.0.1.87
|
|
ip_address = 192.8.0.92
|
|
ip_address = 2620:0:cafe::87
|
|
ip_address = 2001:24::92
|
|
tls_www_server
|
|
tls_www_client
|
|
encryption_key
|
|
signing_key
|
|
EOF
|
|
# certtool --generate-privkey > both-hostNNN-key.pem
|
|
# certtool --generate-certificate \
|
|
--load-ca-certificate ca-cert.pem \
|
|
--load-ca-privkey ca-key.pem \
|
|
--load-privkey both-hostNNN-key.pem \
|
|
--template both-hostNNN.info \
|
|
--outfile both-hostNNN-cert.pem
|
|
|
|
When copying the PEM files to the target host, save them twice, once as
|
|
``server-cert.pem`` and ``server-key.pem``, and again as
|
|
``client-cert.pem`` and ``client-key.pem``.
|
|
|
|
.. _tls_005fcreds_005fsetup:
|
|
|
|
TLS x509 credential configuration
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
QEMU has a standard mechanism for loading x509 credentials that will be
|
|
used for network services and clients. It requires specifying the
|
|
``tls-creds-x509`` class name to the ``--object`` command line argument
|
|
for the system emulators. Each set of credentials loaded should be given
|
|
a unique string identifier via the ``id`` parameter. A single set of TLS
|
|
credentials can be used for multiple network backends, so VNC,
|
|
migration, NBD, character devices can all share the same credentials.
|
|
Note, however, that credentials for use in a client endpoint must be
|
|
loaded separately from those used in a server endpoint.
|
|
|
|
When specifying the object, the ``dir`` parameters specifies which
|
|
directory contains the credential files. This directory is expected to
|
|
contain files with the names mentioned previously, ``ca-cert.pem``,
|
|
``server-key.pem``, ``server-cert.pem``, ``client-key.pem`` and
|
|
``client-cert.pem`` as appropriate. It is also possible to include a set
|
|
of pre-generated Diffie-Hellman (DH) parameters in a file
|
|
``dh-params.pem``, which can be created using the
|
|
``certtool --generate-dh-params`` command. If omitted, QEMU will
|
|
dynamically generate DH parameters when loading the credentials.
|
|
|
|
The ``endpoint`` parameter indicates whether the credentials will be
|
|
used for a network client or server, and determines which PEM files are
|
|
loaded.
|
|
|
|
The ``verify`` parameter determines whether x509 certificate validation
|
|
should be performed. This defaults to enabled, meaning clients will
|
|
always validate the server hostname against the certificate subject alt
|
|
name fields and/or CN field. It also means that servers will request
|
|
that clients provide a certificate and validate them. Verification
|
|
should never be turned off for client endpoints, however, it may be
|
|
turned off for server endpoints if an alternative mechanism is used to
|
|
authenticate clients. For example, the VNC server can use SASL to
|
|
authenticate clients instead.
|
|
|
|
To load server credentials with client certificate validation enabled
|
|
|
|
.. parsed-literal::
|
|
|
|
|qemu_system| -object tls-creds-x509,id=tls0,dir=/etc/pki/qemu,endpoint=server
|
|
|
|
while to load client credentials use
|
|
|
|
.. parsed-literal::
|
|
|
|
|qemu_system| -object tls-creds-x509,id=tls0,dir=/etc/pki/qemu,endpoint=client
|
|
|
|
Network services which support TLS will all have a ``tls-creds``
|
|
parameter which expects the ID of the TLS credentials object. For
|
|
example with VNC:
|
|
|
|
.. parsed-literal::
|
|
|
|
|qemu_system| -vnc 0.0.0.0:0,tls-creds=tls0
|
|
|
|
.. _tls_005fpsk:
|
|
|
|
TLS Pre-Shared Keys (PSK)
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Instead of using certificates, you may also use TLS Pre-Shared Keys
|
|
(TLS-PSK). This can be simpler to set up than certificates but is less
|
|
scalable.
|
|
|
|
Use the GnuTLS ``psktool`` program to generate a ``keys.psk`` file
|
|
containing one or more usernames and random keys::
|
|
|
|
mkdir -m 0700 /tmp/keys
|
|
psktool -u rich -p /tmp/keys/keys.psk
|
|
|
|
TLS-enabled servers such as ``qemu-nbd`` can use this directory like so::
|
|
|
|
qemu-nbd \
|
|
-t -x / \
|
|
--object tls-creds-psk,id=tls0,endpoint=server,dir=/tmp/keys \
|
|
--tls-creds tls0 \
|
|
image.qcow2
|
|
|
|
When connecting from a qemu-based client you must specify the directory
|
|
containing ``keys.psk`` and an optional username (defaults to "qemu")::
|
|
|
|
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=/
|