This usage flag was deprecated by RFC8813, such that it is forbidden to be present for certs using ECDSA/ECDH algorithms, and in TLS 1.3 is conceptually obsolete. As such many valid certs will no longer have this key usage flag set, and QEMU should not be rejecting them, as this prevents use of otherwise valid & desirable algorithms. Reviewed-by: Eric Blake <eblake@redhat.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
323 lines
13 KiB
ReStructuredText
323 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
|
|
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 ``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
|
|
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 ``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 ``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
|
|
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=/
|