Home
Got Linux ?

Blah blah blah... Mostly technical thoughts, rants and gibberish


Apache SSL client authentication vs LDAP authorization

[2018.08.07]

I’ve been netsearching a way to make SSL client authentication (SSLVerifyClient require) play nicely along LDAP authorization (e.g. Require ldap-group ...)… to no avail.

In order for basic (LDAP) authorization to kick in, one MUST configure the SSL stack to fake basic authentication: SSLOptions +FakeBasicAuth

Unfortunately, once a user has been successfully authenticated using his SSL certificate (with no password communicated what-so-ever), FakeBasicAuth will take over with two gotchas:

  1. The “authenticated” username will be set to the SSL certificate’s subject Distinguished Name (DN)

  2. The “authenticated” password will be set to “password” (literally)

Which both don’t make much sense to any existing LDAP directory per-se.

But the FOSS world being such a marvelous environment - this time thanks to OpenLDAP - there is a rather (very!) elegant solution to the problem at hand!

For the sake of the explanation, we’ll assume the SSL client certificates have a DN that includes a Common Name (CN) that matches the LDAP directory’s existing users' UID; example given: /C=CH/O=Example Corp./OU=example.org/CN=username

NOTE: from Apache >= 2.5, thanks to the SSLUserName parameter, any part of the certificate’s subject or Subject Alternative Name (SAN) may be used; but we’re not there yet (since I’m among those fossilized Debian/Stable retarted sysadmins).

OpenLDAP virtual view for Apache SSL client vs LDAP users authentication

Since our problem stems from a dummy (constant) password and a username that must be massaged into something meaningful to our LDAP directory, let’s create a virtual view on top of the existing posixAccounts, with some nifty rewrite rules, thanks to OpenLDAP relay and rwm overlays;
in /etc/ldap/slapd.apacheSslClients-overlay.conf, to be included in /etc/ldap/slapd.conf before the definition of the dc=example,dc=org database:

## Database: ou=apacheSslClients,dc=example,dc=org (RELAY/RWM)

#  REF:  https://httpd.apache.org/docs/2.4/mod/mod_auth_basic.html#authbasicfake
#  NOTE: The Apache SSL Clients virtual container masquerades the users
#        directory (ou=users,dc=example,dc=org) for the purposes of certificate-
#        based authentication, by stripping all unnecessary attributes
#        and mapping the 'userPassword' to the dummy "password".

#  !!! WARNING !!! WARNING !!! WARNING !!! WARNING !!! WARNING !!! WARNING !!!
#  Do NOT make this database a sub-directory of the existing users directory!
#  We do NOT want the dummy "password" to be used for authentication purposes
#  other than for Apache SSL (inherently-authenticated) clients!
#  !!! WARNING !!! WARNING !!! WARNING !!! WARNING !!! WARNING !!! WARNING !!!

# Database
database relay
suffix "ou=apacheSslClients,dc=example,dc=org"

# Features
readonly on
lastmod off

# Rewriting (mapping) rules
overlay rwm
rwm-rewriteEngine on

# ... attributes (client<->server)
rwm-map objectclass simpleSecurityObject posixAccount
rwm-map objectclass *
rwm-map attribute uid *
rwm-map attribute userPassword *
rwm-map attribute *

# ... default client->server requests
rwm-rewriteContext default
rwm-rewriteRule "^uid=[^,]*cn=([^,/]*)[^,]*,ou=apacheSslClients,dc=example,dc=org$" "uid=$1,ou=users,dc=example,dc=org" ":@"
rwm-rewriteRule "^(.+,)?ou=apacheSslClients,dc=example,dc=org$" "$1ou=users,dc=example,dc=org" ":@"
rwm-rewriteContext searchFilter
rwm-rewriteRule "^(.*)\\(uid=[^)]*cn=([^/)]*)[^)]*\\)(.*)$" "$1(uid=$2)$3" ":@"

# ... bind client->server requests
rwm-rewriteContext bindDN
rwm-rewriteRule "^uid=[^,]+,ou=apacheSslClients,dc=example,dc=org$" "cn=apacheSslClient,dc=example,dc=org" ":@"
rwm-rewriteRule ".*ou=apacheSslClients,dc=example,dc=org$" "" "#"

# ... all server->client responses
rwm-rewriteContext searchEntryDN
rwm-rewriteRule "^(.+,)?ou=users,dc=example,dc=org$" "$1ou=apacheSslClients,dc=example,dc=org" ":@"
rwm-rewriteContext searchAttrDN alias searchEntryDN
rwm-rewriteContext matchedDN alias searchEntryDN


## ACL

# -> Protect passwords
access to attrs=userPassword
 by * none

# -> Simplistic default (allow anonymous search from Apache)
access to *
 by * read

The rwm magic here is four-fold:

As mentioned, the cn=apacheSslClient,dc=example,dc=org dummy Apache user/password needs to be created; as per following LDIF:

dn: cn=apacheSslClient,dc=example,dc=org
changetype: add
cn: apacheSslClient
objectClass: simpleSecurityObject
userPassword: password

OpenLDAP virtual view for Apache SSL client vs LDAP group authorization

Now that LDAP authentication has been taken care of, let’s deal with LDAP group-based authorization. Once again, we’ll use a virtual view on top of the existing groupOfNames;
in /etc/ldap/slapd.apacheSslGroups-overlay.conf, to be included in /etc/ldap/slapd.conf before the definition of the dc=example,dc=org database:

## Database: ou=apacheSslGroups,dc=example,dc=org (RELAY/RWM)

#  NOTE: The Apache SSL Groups virtual container masquerades the groups
#        directory (ou=groups,dc=example,dc=org) for the purposes of certificate-
#        based authentication, by stripping all unnecessary attributes and
#        mapping the group members to the Apache SSL Clients.

# Database
database relay
suffix "ou=apacheSslGroups,dc=example,dc=org"

# Features
readonly on
lastmod off

# Rewriting (mapping) rules
overlay rwm
rwm-rewriteEngine on

# ... attributes (client<->server)
rwm-map objectclass groupOfNames *
rwm-map objectclass *
rwm-map attribute cn *
rwm-map attribute member *
rwm-map attribute *

# ... default client->server requests 
rwm-rewriteContext default
rwm-rewriteRule "^(.+,)?ou=apacheSslGroups,dc=example,dc=org$" "$1ou=groups,dc=example,dc=org" ":@"
rwm-rewriteContext compareAttrDN
rwm-rewriteRule "^uid=([^,]*),.*$" "uid=$1,ou=users,dc=example,dc=org" ":@"

# ... all server->client responses
rwm-rewriteContext searchEntryDN
rwm-rewriteRule "^(.+,)?ou=groups,dc=example,dc=org$" "$1ou=apacheSslGroups,dc=example,dc=org" ":@"
rwm-rewriteContext matchedDN alias searchEntryDN
rwm-rewriteContext searchAttrDN
rwm-rewriteRule "^uid=([^,]*),.*$" "uid=$1,ou=apacheSslClients,dc=example,dc=org" ":@"


## ACL

# -> Simplistic default (allow anonymous search from Apache)
access to *
 by * read

The rwm magic is still the same, with the addition of compareAttrDN and searchAttrDN massaging, rewriting/mapping the groupOfNames’s members from/to Apache ou=apacheSslClients,dc=example,dc=org SSL clients.
NOTE: This magic works only with groupOfNames, which members are DNs, and will NOT work with posixGroups; this is a limitation of rwm.
NOTE(2): Apache >= 2.5 SSLUserName parameter may allow to get rid of this view, allowing to match the authenticated SSL client directly to existing posixGroups.

Configuring Apache

SSL client authentication versus LDAP authorization is therefrom straight-forward:

# Users SSL (certificate-based) authentication
SSLRequireSSL
SSLVerifyClient require
SSLOptions +FakeBasicAuth +StdEnvVars
# (ignored until Apache 2.5)
SSLUserName SSL_CLIENT_S_DN_CN

# Users (dummy) LDAP "authentication"
AuthType Basic
AuthName "Access Restricted - SSL certificate-based authentication"
AuthBasicProvider ldap
AuthLDAPURL ldaps://ldap.example.org/ou=apacheSslClients,dc=example,dc=org?uid?one?(objectClass=simpleSecurityObject)
AuthLDAPGroupAttribute member
AuthLDAPGroupAttributeIsDN On

# Authorization
Require ldap-group cn=somegroup,ou=apacheSslGroups,dc=example,dc=org