Connecting to an LDAP Store with Python over SSL

The trials and tribulations of connecting to an LDAP directory server using python-ldap over OpenSSL

I had the need to connect to an LDAP directory server to authenticate users for access to a custom CMS built on top of Django.  So I installed OpenLDAP and the python-ldap module, along with the OpenLDAP utilties and a few other tid bits that everything needed to function properly.  After everything was in place, I created a simple test script to determine if I could connect to the LDAP server through the firewall, which had recently been opened up to allow access to it from the development server.

import ldap

AUTH_LDAP_SERVER = 'example.com'
AUTH_LDAP_BASE_USER = "cn=xxx, o=XXX"
AUTH_LDAP_BASE_PASS = "password"

username = 'test'
base = "o=TEST"
scope = ldap.SCOPE_SUBTREE
filter = "(&(objectclass=person) (cn=%s))" % username
ret = ['dn']

l = ldap.initialize('ldaps://example.com:636')
l.protocol_version = ldap.VERSION3
l.simple_bind_s(AUTH_LDAP_BASE_USER,AUTH_LDAP_BASE_PASS)

result_id = l.search(base, scope, filter, ret)

print result_id

Unfortunately, the test failed from development with the following error:

ldap.SERVER_DOWN: {'desc': "Can't contact LDAP server"}

I tried the test from our production server, which I knew could connect to the LDAP server, and it worked perfectly well.  So there was something wrong with the handshake between development and the LDAP server.  I fired up the ldapsearch utilitiy with the following command:

ldapsearch -v -d5 -H ldaps://example.com:636 -D "cn=xxx, o=XXX" 
-w password

and it returned the telltale sign of why the handshake was failing:

TLS: peer cert untrusted or revoked (0x42)

In order to take a look at the certificate from command line, I executed the following:

openssl s_client -connect example.com:636

which showed me that the certificate was self-signed, and might be the source of the problem.  Using gnutls-cli:

gnutls-cli --print-cert -p 636 example.com

I saw similar results, again self-signed, but a self-signed cert should not present a problem, so I copied the contents of the cert into a .crt file and included it in the ldap.conf file, like so:

TLS_CACERT /etc/ssl/certs/mycert.crt

I restarted slapd, but again I was unable to connect to the LDAP server.  I also tried to add the collection of certs from openssl:

TLS_CACERT      /etc/ssl/certs/ca-certificates.crt

and even added my cert to it, but to no avail.  after googling around for a while, it turns out that this might be a bug in the debian OpenLDAP code.  By adding:

TLS_REQCERT allow

to the ldap.conf file, I was finally able to connect.  This is, however, an insecure connection, as it ignores the certificate verification errors and continues on with the connection, so I decided to read through the entire bug thread, and found out that I needed to include the entire certificate chain in the .crt file.  As noted in the OpenLDAP admin guide:

"If the signing CA was not a top-level (root) CA, certificates for the entire sequence of CA's from the signing CA to the top-level CA should be present. Multiple certificates are simply appended to the file; the order is not significant."

So I added the other certificate that was revealed in the openssl connect command, and now I can connect without a problem.

See this thread for details

0 Comments, 0 trackbacks (Trackback URL)

0 responses to Connecting to an LDAP Store with Python over SSL

Leave a Comment
  1. (required)
  2. Ignore this field:
  3. Don't put anything in this field:
    Don't put anything here:
  4. Leave this empty:
    (required)
  5. Your email is not publically displayed.