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
.
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.
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 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 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
qemu-system-x86_64 -object tls-creds-x509,id=tls0,dir=/etc/pki/qemu,endpoint=server
while to load client credentials use
qemu-system-x86_64 -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:
qemu-system-x86_64 -vnc 0.0.0.0:0,tls-creds=tls0