[Libvir] Certificate management APIs ?

I now have the QEMU backend working with full wire encryption using the TLS protocol, and so ready to start thinking about various authentication related issues. - The client needs to have the certificate of the CA in order to validate the signature on the remote server's certificate (aka, just like web browsers need a set of CA certificates) Currently I just have it loading 'cert.pem' out of the current working directory of the app using libvirt. Obviously this is not a real solution. - The client technically should have a certificate revocation list too. Again I currently load this out of current working dir - The server end can optionally require the client to present a certificate too, as its means of authenticating the client. This obviously requires a way of specifying the client certificate and secret key in libvirt when opening the connection. - The first time it connects to a server, the client validates the server's certificate against the CA certificate, and remote hostname. Web browsers will also typically display the certificate to the user and ask them to allow one-time, allow permanently, or reject it. This would require a way to pass the certificate back to the client app during the virConnectOpen call. The above is all related to x509 certificates. So we basically have 5 types of file we need to store / provide to libvirt when opening a connection: - Certificate Authority's certificate - Certificate Authority's certificate revocation list - Client secret key - Client certificate - Trusted server certificates And some form of callback to let the client app validate the server certificate. As a rough stab at an API my thoughts are: typedef enum { VIR_CERT_CA_CERT, VIR_CERT_CA_CRL, VIR_CERT_CLIENT_KEY, VIR_CERT_CLIENT_CERT, } virConnectCertDataType; /** * dataType: one of the virConnectCertDataType constants * data: base64, PEM encoded certificate/key data */ int virConnectAddCertData(int dataType, const char *data); /** * dataType: one of the virConnectCertDataType constants * dataFile: file containing base64, PEM encoded certificate/key data */ int virConnectAddCertDataFile(int dataType, const char *dataFile); Virt-manager would use the latter API to load in its client certificate, CA certs, etc to libvirt prior to opening a connection to a TLS enabled server. This means libvirt doesn't have to make the policy on where to store its client cert data. On the otherhand, we probably want the files to be in a common location for virsh & virt-manager so both can use the same data. On yet another hand, virt-manager will also likely end up using TLS to connect to VNC, so needs access to the files independant on libvirt / virsh for that. So we should probably have these flexible APIs, but recommend that all apps use a pre-defined directory unless they have a particular need not to, eg $HOME/.libvirt/tls/ | +- ca | | | +- cert.pem | +- ca-crl.pem | +- client | | | +- cert.pem | +- key.pem | +- server | +- trusted.pem /** * hostname: canonical name of remote host providing certiifcate * cert: base64, PEM encoded certificate data * * Invoked to let client application decide whether to accept a * server certificate. The certificate wil already have been * validated against the CA cert, and its expiration date, etc * checked. This callback is intended to allow the end user to * accept/reject the certificate. The trusted flag indicates * whether the server is present in the list of known servers. */ typedef int (*virConnectCertificateValidator)(const char *hostname, const char *cert); int virConnectSetCertificateValidator(virConnectCertificateValidator cb) The applications like virt-manager would probably want to present the certificate details to the user for validation, also optionally maintaining a list of previously trusted server certs. Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

Hi Dan, On Wed, 2007-01-10 at 23:59 +0000, Daniel P. Berrange wrote:
I now have the QEMU backend working with full wire encryption using the TLS protocol, and so ready to start thinking about various authentication related issues.
If only we had decent SSH infrastructure. I'd love if apps could easily use a library to request an SSH connection to a host, authenticate and then apps could request tunnels over that shared per-user connection.
- The client needs to have the certificate of the CA in order to validate the signature on the remote server's certificate (aka, just like web browsers need a set of CA certificates)
Currently I just have it loading 'cert.pem' out of the current working directory of the app using libvirt. Obviously this is not a real solution.
Personally, I'd start with the assumption that essentially no-one will install a CA signed cert for the libvirt server. So, first and foremost, the client needs to be present self-signed certs to the user. As for the CA certs, we should use /etc/pki/tls/cert.pem by default, right? And perhaps allow users and admins to install other CA certs just for libvirt.
- The server end can optionally require the client to present a certificate too, as its means of authenticating the client.
This obviously requires a way of specifying the client certificate and secret key in libvirt when opening the connection.
For the networks stuff, I think the server will also need to be able to connect to (and authenticate and be authenticated by) other servers. Also, as much as it sucks, I think we'll want password based authentication as well as certificate based authentication. Very few people will copy the client cert over to the server, I think.
As a rough stab at an API my thoughts are:
typedef enum { VIR_CERT_CA_CERT, VIR_CERT_CA_CRL, VIR_CERT_CLIENT_KEY, VIR_CERT_CLIENT_CERT, } virConnectCertDataType;
/** * dataType: one of the virConnectCertDataType constants * data: base64, PEM encoded certificate/key data */ int virConnectAddCertData(int dataType, const char *data);
/** * dataType: one of the virConnectCertDataType constants * dataFile: file containing base64, PEM encoded certificate/key data */ int virConnectAddCertDataFile(int dataType, const char *dataFile);
Seems reasonable, I can't think of anything better.
Virt-manager would use the latter API to load in its client certificate, CA certs, etc to libvirt prior to opening a connection to a TLS enabled server. This means libvirt doesn't have to make the policy on where to store its client cert data. On the otherhand, we probably want the files to be in a common location for virsh & virt-manager so both can use the same data. On yet another hand, virt-manager will also likely end up using TLS to connect to VNC, so needs access to the files independant on libvirt / virsh for that. So we should probably have these flexible APIs, but recommend that all apps use a pre-defined directory unless they have a particular need not to, eg
$HOME/.libvirt/tls/ | +- ca | | | +- cert.pem | +- ca-crl.pem | +- client | | | +- cert.pem | +- key.pem | +- server | +- trusted.pem
Not entirely unreasonable, but I think I'd expect it to be in ~/.virt-manager and have a ~/.virshrc which you could point at it. If it was ~/.libvirt, then I'd expect virt-manager to access the certs through e.g. virConnectGetCertData() rather than making the directory structure part of the API. One final point on all that - "uggh".
/** * hostname: canonical name of remote host providing certiifcate * cert: base64, PEM encoded certificate data * * Invoked to let client application decide whether to accept a * server certificate. The certificate wil already have been * validated against the CA cert, and its expiration date, etc * checked. This callback is intended to allow the end user to * accept/reject the certificate. The trusted flag indicates * whether the server is present in the list of known servers. */ typedef int (*virConnectCertificateValidator)(const char *hostname, const char *cert);
int virConnectSetCertificateValidator(virConnectCertificateValidator cb)
The applications like virt-manager would probably want to present the certificate details to the user for validation, also optionally maintaining a list of previously trusted server certs.
The callback approach seems fine. I'd think about adding a virConnectCertificate structure whose contents are only available through accessor functions and passing that to the callback, though. For future expansion. Cheers, Mark.

On Mon, Jan 15, 2007 at 08:44:22PM +0000, Mark McLoughlin wrote:
Hi Dan,
On Wed, 2007-01-10 at 23:59 +0000, Daniel P. Berrange wrote:
I now have the QEMU backend working with full wire encryption using the TLS protocol, and so ready to start thinking about various authentication related issues.
If only we had decent SSH infrastructure. I'd love if apps could easily use a library to request an SSH connection to a host, authenticate and then apps could request tunnels over that shared per-user connection.
The actual TLS code in the server daemon & client backend was soo trivial that I actually quite like it from that POV. Its the external administrative bits where SSH wins I guess, by virtue of fact that most people already have it widely deployed. Then again I'm far from certain everyone neccessarily wants application remote access of this kind tied into the general SSH shell service. While you can restrict what users can run by using authorized_keys entries & and enumerating particular commands, this then starts to increase the administrative complexity to point where there's less differentiation between SSH & TLS setup.
- The client needs to have the certificate of the CA in order to validate the signature on the remote server's certificate (aka, just like web browsers need a set of CA certificates)
Currently I just have it loading 'cert.pem' out of the current working directory of the app using libvirt. Obviously this is not a real solution.
Personally, I'd start with the assumption that essentially no-one will install a CA signed cert for the libvirt server. So, first and foremost, the client needs to be present self-signed certs to the user.
As for the CA certs, we should use /etc/pki/tls/cert.pem by default, right? And perhaps allow users and admins to install other CA certs just for libvirt.
Yeah, as Rich suggested, we should definitely try to use the appropriate system directories for storing certs & CA certs if there's something sane we can do here. For client apps we do still need a $HOME/.XXXX directory since we need to be able to run clients without root privileges.
- The server end can optionally require the client to present a certificate too, as its means of authenticating the client.
This obviously requires a way of specifying the client certificate and secret key in libvirt when opening the connection.
For the networks stuff, I think the server will also need to be able to connect to (and authenticate and be authenticated by) other servers.
Good point.
Also, as much as it sucks, I think we'll want password based authentication as well as certificate based authentication. Very few people will copy the client cert over to the server, I think.
I'd really like to just have everyone use mutual certificate authentication, but I fear there is a class of people who just like passwords, or tieing into a kerberos authentication system. As long as we don't lock ourselves out of adding in extra auth at a later date though, I think I'd like to stick with just certificates for the initial implementation.
$HOME/.libvirt/tls/ | +- ca | | | +- cert.pem | +- ca-crl.pem | +- client | | | +- cert.pem | +- key.pem | +- server | +- trusted.pem
Not entirely unreasonable, but I think I'd expect it to be in ~/.virt-manager and have a ~/.virshrc which you could point at it.
If it was ~/.libvirt, then I'd expect virt-manager to access the certs through e.g. virConnectGetCertData() rather than making the directory structure part of the API.
One final point on all that - "uggh".
I hear your pain. Perhaps we should design it so that it does use ~/.libvirt at the client end and have the backend drivers just 'do the right thing' loading the files as needed to avoid requiring every user of libvirt to know about these APIs, except for an (optional) virConnectSetCertificateValidator one ?
typedef int (*virConnectCertificateValidator)(const char *hostname, const char *cert);
int virConnectSetCertificateValidator(virConnectCertificateValidator cb)
The applications like virt-manager would probably want to present the certificate details to the user for validation, also optionally maintaining a list of previously trusted server certs.
The callback approach seems fine. I'd think about adding a virConnectCertificate structure whose contents are only available through accessor functions and passing that to the callback, though. For future expansion.
Regards, Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|
participants (2)
-
Daniel P. Berrange
-
Mark McLoughlin