I have a Horizon instance Proof of Concept. It has a way to go to be used in production, but the mechanism works.
This is not a how-to. I’ve written up some of the steps in the past. Instead, this is an attempt to illuminate some of the issues.
The Horizon HTTP instance is set up with S4U2Proxy. Since both Horizon and Keystone are on the same machine, it is both the source and target of the proxy rule; a user connecting via HTTPS gets a service ticket for Horizon, which then requests a delegated service ticket for itself. I’m not seeing any traffic on the KDC when this happens, which leads me to think that the Kerberos library is smart enough to reuse the initial service ticket for the user. However, I’ve also tested S4U2Proxy from HTTPD to Keystone on a remote machine in an earlier set up, and am fairly certain that this will work when Horizon and Keystone are not co-located.
After initial configuration, I did a git clone of the repositories for the projects I needed to modify:
To use this code, I switched to each directory and ran:
sudo pip install -e .
Horizon uses form based authentication. I have not modified this. Longer term, we would need to determine what UI to show based on the authentication mechanism. I would like to be able to disable Form Based authentication for the Kerberos case, as I think passing your Kerberos password over the wire is one of the worst security practices; We should actively discourage it.
Django OpenStack Auth and Keystone Client
Horizon uses a project called django-openstack-auth that communicates with Keystone client. This needs to work with client auth plugins. I’ve hacked this in code, but the right solution is for it to get the auth plugin out of the Django configuration options. Implied here is that django-openstack-auth should be able to use keystoneclient sessions and V3 Keystone authentication.
When a user authenticates to Horizon, they do not set a project. Thus, Horizon does not know what project to pass in the token request. The Token API has some strange behaviour when it comes to token requests without an explicit scope. If the user has a default project set, they get a token scoped to that project. If they do not have a default project set, they get an unscoped token.
Jamie Lennox has some outstanding patches to Keystone client that address the Kerberos use cases. Specifically, if the client gets an unscoped token, there is no service catalog. Since the client makes calls to Keystone based on endpoints in the service catalog, it cannot use an unscoped token. One of Jamie’s patches address this; if there is no service catalog, continue to use the Auth URL to talk to Keystone. This is a bit of an abuse of the Auth URL and really should only be used to get the list of domains or projects for a user. Once the user has this information, they can request a scoped token. This is what Horizon needs to do on behalf of the user.
While Keystone can use Kerberos as an “method” value when creating a token, the current set of plugins did not allow for mapping to the DefaultDomain. There is a plugin for “external” to do that, and I subclassed that for Kerberos. There is an outstanding ticket for removing the “method” value from the plugin implementations, which will reduce the number of classes we need to implement common behavior.
To talk to a Kerberos protected Keystone, the Keystone client needs to use the Kerberos Auth plugin. However, it can only use this to get a token; the auth plugin does not handle other communication. Thus, only the /auth/tokens path should be Kerberos protected. Here is what I am using:
<Location "/keystone/krb"> LogLevel debug WSGIProcessGroup keystone_krb_wsgi NSSRequireSSL </Location> <Location "/keystone/krb/v3/auth/tokens"> AuthType Kerberos AuthName "Kerberos Login" KrbMethodNegotiate on KrbMethodK5Passwd off KrbServiceName HTTP KrbAuthRealms IPA.CLOUDLAB.FREEIPA.ORG Krb5KeyTab /etc/httpd/conf/openstack.keytab KrbSaveCredentials on KrbLocalUserMapping on # defaults off, but to be explicit # Keystone should not be a proxy KrbConstrainedDelegation off Require valid-user NSSRequireSSL </Location> <Location "/dashboard/auth/login/"> LogLevel debug AuthType Kerberos AuthName "Kerberos Login" KrbMethodNegotiate on KrbMethodK5Passwd off KrbServiceName HTTP KrbAuthRealms IPA.CLOUDLAB.FREEIPA.ORG Krb5KeyTab /etc/httpd/conf/openstack.keytab KrbSaveCredentials on KrbLocalUserMapping on KrbConstrainedDelegation on Require valid-user NSSRequireSSL </Location>
To go further, the Kerberos protection should be optional if you wish to allow other authentication methods to be stacked with Kerberos.
This implies that the value:
Should be in the HTTPD conf section for Horizon, but not for Keystone. This does not yet work for me, and I will investigate further.
Secure web communications require cryptography. For authentication, the two HTTP based standards are Client Side Certificates and Kerberos. Of the two, only Kerberos allows for constrained delegation in a standardized way. Making Kerberos part of the standard approach to OpenStack will lead to a more secure OpenStack.