Kerberos, Keystone Client, and S4U2Proxy

Since my eventual goal is to Kerberize Horizon, my next step after getting a CGI solution working was to make use of the Keystone client. Since the Kerberos auth plugin is still a work-in-progress, it required a little tweaking, but not all that much.

My basic strategy was to get a test script working, and to follow that up with a simple WSGI app. Inb order to get the test script, I started with Jose’s incipient patch for a Kerberos Auth plugin. However, this does not quite line up with how the server currently views Kerberos. A bit of a digression is probably in order….

The V3 Auth API requires that a request state the “methods” used, so that a multi-factor authentication could be possible. Since each Factor often requires separate data, there is a section of the request for each of the methods to provide something custom. The standard plugin is the Password on, and for that, a request would have this fragment:

"identity": {
            "methods": [
                "password"
            ],
            "password": {
                "user": {
                    "domain": {
                        "name": "Default"
                    },
                    "name": "admin",
                    "password": "freeipa4all"
                }
            }

For Kerberos, Jose assumed that we would want the same, and made it work with a methond named “kerberos”. I, personally, don’t want the overhead of changing the payload with redundant data. With Kerberos, the most important thing is that the Web Request be allows to Negotiate the mechanism used to authenticate. When useing curl from the command line, this means passing the –negotiate flag. With the Python request-kerberos library, it means passing this to the post request:

requests_auth = requests_kerberos.HTTPKerberosAuth(
            mutual_authentication=requests_kerberos.OPTIONAL)

On the Server side, Kerberos is going to be handled by HTTPD. Yes, it is possible to do this in Eventlet, and Jose has submitted a patch for that, too. But In general, doing Crypto from Python is a bad idea, especially when working with a single threaded web server like Eventlet. However, if we do go with his server side approach, I would like the client to be blissfully ignorant of the server side, and use the same plugin.

Up until recently, the accepted way to set up Keystone with Kerberos was to use the “external” method. This requires no specific data in the request although having it doesn’t hurt anything. Instead, if the environment variable ‘REMOTE_USER’ is specified, the Keystone Server Auth controller understands that the user has already been authenticated.

While we work out which way to go in Juno, I need something which works with the Status Quo. My Keystone server is a modified Packstack install, running out of the RPMs from the RDO equivalent to Icehouse. So I took his back and made the following change:

diff --git a/keystoneclient/contrib/auth/v3/kerberos.py b/keystoneclient/contrib/auth/v3/kerberos.py
index b7f8545..2a0c4ae 100644
--- a/keystoneclient/contrib/auth/v3/kerberos.py
+++ b/keystoneclient/contrib/auth/v3/kerberos.py
@@ -29,7 +29,7 @@ class KerberosMethod(v3.AuthMethod):
                       **kwargs):
         request_kwargs['requests_auth'] = requests_kerberos.HTTPKerberosAuth(
             mutual_authentication=requests_kerberos.OPTIONAL)
-        return 'kerberos', {}
+        return 'external', {}

My script to test is relatively simple, and I’ve added it to Jose’s patch:

import os

from keystoneclient import session
from keystoneclient.contrib.auth.v3 import kerberos

try:
    OS_AUTH_URL = os.environ['OS_AUTH_URL']
except KeyError as e:
    raise SystemExit('%s environment variables not set.' % e.message)

OS_CACERT = os.environ.get('OS_CACERT')
kerb_auth = kerberos.Kerberos(OS_AUTH_URL)
sess=session.Session(kerb_auth, verify=OS_CACERT)
token=sess.get_token()
print (token)

Note that the machine I am running this from is not a registered ipa-client. If it were, I would not need to specify the CA Certificate file to use. Hence, that variable is optional. Before running it, I set:

OS_AUTH_URL=https://ayoungf20packstack.cloudlab.freeipa.org/keystone/krb/v3

Once I had that working, the next step was a WSGI App. In /var/www/cgi-bin/keystone/get_token.py I have

from keystoneclient import session
from keystoneclient.contrib.auth.v3 import kerberos 


def application(environ, start_response):
    status = '200 OK'
    
    OS_AUTH_URL = 'https://ayoungf20packstack.cloudlab.freeipa.org/keystone/krb/v3'
    kerb_auth = kerberos.Kerberos(OS_AUTH_URL)
    sess=session.Session(kerb_auth)
    token=sess.get_token()


    response_headers = [('Content-type', 'application/json'),
                        ('Content-Length', str(len(token)))]

    start_response(status, response_headers)

    return [token]

I added the configurtion to an existing config file in /etc/httpd/conf.d :

WSGIScriptAlias /keystone/token /var/www/cgi-bin/keystone/get_token.py

WSGIDaemonProcess keystone_hello_wsgi user=fedora group=wheel maximum-requests=10000



  WSGIProcessGroup keystone_hello_wsgi
  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

And Now

WSGI App to get  Token  via S4U2Proxy

WSGI App to get Token via S4U2Proxy

Leave a Reply

Your email address will not be published. Required fields are marked *