Thursday, 1 August 2013

OpenLDAP ldap backend: SASL authentication

The problem

The OpenLDAP ldap backend acts as a reverse proxy to a LDAP server containing real data. The simple binds are simply verbatim transferred to the data server and users don't even feel there is a proxy in-between.
The problem is with SASL. With a SASL bind the proxy doesn't have a password to forward to the data server. Even if proxy authenticates user with GSSAPI or EXTERNAL, proxy obtains a dn but no password. So it has to perform an anonymous bind which is different from an authenticated one. This behaviour is warned by the log line:
ldap_back_dobind_int: DN="cn=admin,dc=example,dc=org" without creds, binding anonymously

Overview

The solution uses the id_assert directive in the ldap badabase section on the proxy in order to bind to the data server with a defined principal who then proxies to the original client identity thanks to the authzTo attribute.
The OpenLDAP need three modifies:
  • id_assert section in ldap database (proxy server)
  • creation of the id_assert principal with the authzTo attribute (data server)
  • enable authzPolicy to in cn=config (data server)

ldifs needed

To enable authzPolicy on the data server:
dn: cn=config
changetype: modify
replace: olcAuthzPolicy
olcAuthzPolicy: to
This is necessary because OpenLDAP disregards authzTo attribute in a entry unless the global olcAuthzPolicy is set.
To enable id_assert on ldap database on proxy server:
dn: olcDatabase={2}ldap,cn=config
changetype: modify
replace: olcDbIDAssertAuthzFrom
olcDbIDAssertAuthzFrom: "dn:*"
-
replace: olcDbIDAssertBind
olcDbIDAssertBind: mode=self bindmethod=simple binddn="cn=proxy,ou=agents,dc=example,dc=org" credentials=KHg7VvVD
The id_assert is in effect only for authenticated users to avoid using a privileged bind for anonymous users.
The proxy user is defined as follow:
dn: cn=proxy,ou=agents,dc=example,dc=org
objectClass: simpleSecurityObject
objectClass: organizationalRole
cn: proxy
description: principal for proxy ldap
userPassword: {SSHA}LCLFm/lhsZqr4ltuGoyrr/zKGZgJSZ3c
authzTo: {0}dn.regex:^uid=[^,]*,ou=people,dc=example,dc=org$
authzTo: {1}dn.regex:^cn=[^,]*,ou=agents,dc=example,dc=org$
view raw proxy.ldif hosted with ❤ by GitHub

Check log entries

This is a log entry for the user "a_user" bound with GSSAPI requesting his own entry:
Log on proxy:
Aug 1 10:36:59 proxy slapd[23946]: conn=1057 fd=19 ACCEPT from PATH=/var/run/slapd/ldapi (PATH=/var/run/slapd/ldapi)
Aug 1 10:36:59 proxy slapd[23946]: conn=1057 op=0 SRCH base="" scope=0 deref=0 filter="(objectClass=*)"
Aug 1 10:36:59 proxy slapd[23946]: conn=1057 op=0 SRCH attr=supportedSASLMechanisms
Aug 1 10:36:59 proxy slapd[23946]: conn=1057 op=0 ENTRY dn=""
Aug 1 10:36:59 proxy slapd[23946]: conn=1057 op=0 SEARCH RESULT tag=101 err=0 nentries=1 text=
Aug 1 10:36:59 proxy slapd[23946]: conn=1057 op=1 BIND dn="" method=163
Aug 1 10:36:59 proxy slapd[23946]: conn=1057 op=1 RESULT tag=97 err=14 text=SASL(0): successful result: security flags do not match required
Aug 1 10:36:59 proxy slapd[23946]: conn=1057 op=2 BIND dn="" method=163
Aug 1 10:36:59 proxy slapd[23946]: conn=1057 op=2 RESULT tag=97 err=14 text=SASL(0): successful result: security flags do not match required
Aug 1 10:36:59 proxy slapd[23946]: conn=1057 op=3 BIND dn="" method=163
Aug 1 10:36:59 proxy slapd[23946]: conn=1057 op=3 BIND authcid="a_user" authzid="a_user"
Aug 1 10:36:59 proxy slapd[23946]: conn=1057 op=3 BIND dn="uid=a_user,ou=people,dc=example,dc=org" mech=GSSAPI sasl_ssf=56 ssf=71
Aug 1 10:36:59 proxy slapd[23946]: conn=1057 op=3 RESULT tag=97 err=0 text=
Aug 1 10:36:59 proxy slapd[23946]: conn=1057 op=4 SRCH base="dc=example,dc=org" scope=2 deref=0 filter="(uid=a_user)"
Aug 1 10:36:59 proxy slapd[23946]: conn=1057 op=4 ENTRY dn="uid=a_user,ou=people,dc=example,dc=org"
Aug 1 10:36:59 proxy slapd[23946]: conn=1057 op=4 SEARCH RESULT tag=101 err=0 nentries=1 text=
Aug 1 10:36:59 proxy slapd[23946]: conn=1057 op=5 UNBIND
Aug 1 10:36:59 proxy slapd[23946]: conn=1057 fd=19 closed
view raw proxy.log hosted with ❤ by GitHub

Log on data server:
Aug 1 10:36:44 openldap slapd[19002]: conn=1662 fd=22 ACCEPT from IP=10.0.0.1:43682 (IP=0.0.0.0:389)
Aug 1 10:36:44 openldap slapd[19002]: conn=1662 op=0 EXT oid=1.3.6.1.4.1.1466.20037
Aug 1 10:36:44 openldap slapd[19002]: conn=1662 op=0 STARTTLS
Aug 1 10:36:44 openldap slapd[19002]: conn=1662 op=0 RESULT oid= err=0 text=
Aug 1 10:36:44 openldap slapd[19002]: conn=1662 fd=22 TLS established tls_ssf=128 ssf=128
Aug 1 10:36:44 openldap slapd[19002]: conn=1662 op=1 BIND dn="cn=proxy,ou=agents,dc=example,dc=org" method=128
Aug 1 10:36:44 openldap slapd[19002]: conn=1662 op=1 BIND dn="cn=proxy,ou=agents,dc=example,dc=org" mech=SIMPLE ssf=0
Aug 1 10:36:44 openldap slapd[19002]: conn=1662 op=1 RESULT tag=97 err=0 text=
Aug 1 10:36:44 openldap slapd[19002]: conn=1662 op=2 PROXYAUTHZ dn="uid=a_user,ou=people,dc=example,dc=org"
Aug 1 10:36:44 openldap slapd[19002]: conn=1662 op=2 SRCH base="dc=example,dc=org" scope=2 deref=0 filter="(uid=a_user)"
Aug 1 10:36:44 openldap slapd[19002]: conn=1662 op=2 SEARCH RESULT tag=101 err=0 nentries=1 text=
view raw openldap.log hosted with ❤ by GitHub
Note bind is performed by cn=proxy,ou=agents,dc=example,dc=org but then authorization is performed on behalf to "a_user".