Mapping client certificate to user name

apache-2.2certificate

I have Apache set up to require a client certificate. Unfortunately, REMOTE_USER comes across has a nasty, long string from the certificate. This is problematic for applications like Trac that use that for the user name. I've been able to get Apache to map the certificate to a more legible user name using:

RewriteMap certmap txt:/var/www/certmap.txt
RewriteRule .* - [E=REMOTE_USER:${certmap:%{SSL:SSL_CLIENT_S_DN_CN}}]

This works just fine except for the first request. The first request has REMOTE_USER as an empty string but the next action (i.e. click a link) results in the value being set. In Trac, the effect is the user is not initially logged in but are after they do anything.

Is there some way to have REMOTE_USER set initially? Or if that's not possible, could a redirect or two take care of "clicking on a link"?

Edit

Unsurprisingly, IE has to throw a wrench into things. This method just flat out doesn't work in IE (8): the client SSL variables are not available at any point when trying to use the RewriteRule.

Best Answer

I found a solution that doesn't use mod_rewrite or Apache at all for that matter. Unfortunately, it only works with WSGI scripts. While this doesn't solve a potential problem in the future, it does solve the immediate need. The main application that needs this is Trac, which this works just fine for.

I'm using this WSGI script for Trac:

def application(environ, start_response):
    common_name = environ['SSL_CLIENT_S_DN_CN']
    user_mapped = False
    file = open('/var/www/cert-map.txt')
    for line in file:
        if not line or line.startswith('#'):
            continue
        parts = line.split()
        if len(parts) < 2:
            continue
        if common_name == parts[0]:
            environ['REMOTE_USER'] = parts[1]
            user_mapped = True
            break
    file.close()
    if not user_mapped:
        environ['REMOTE_USER'] = common_name
    return trac.web.main.dispatch_request(environ, start_response)

Essentially, it's setting the value of REMOTE_USER before Trac does its thing.