Connecting to LDAPS with self signed cert using Python

posted on 2013-08-24

To up the security, we run the LDAP connections that go over the network using LDAPS. As it's not a public LDAP and users never directly connect to it, a self-signed certificate is good enough. We can just import he certificate into the trusted chain of each of the server that use LDAP.

One server, however, does not have an LDAP module but needs a flat configuration file with users: enter a simple script Python script to create it.

The set-up to connect is simple:

import ldap
l = ldap.initialize('ldaps://ldap.example.com')
l.simple_bind_s("cn=admin,dc=example,dc=com", "super secret")

Now if we want to accept any cert we get, we have to set an LDAP option. The ldap connection is lazily initialized at the simple_bind_s, which makes the stack a bit weird. But without the self-signed certificate imported, the error will be something vague like:

ldap.SERVER_DOWN: {'info': '(unknown error code)', 'desc': "Can't contact LDAP server"}

Well, if you run into the unknown error code when working with LDAPS and a self-signed cert, you probably have the same problem as I did. One solution should be to import the certificate, but running into difficulties there, I decided to ignore the certificate for now. Do this on the ldap library (not the connection) like so:

ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)

I ended up combining this into a simple script to read all the users from the LDAP directory using the LDAPS connection:

#!/usr/bin/python2
# -*- coding: utf-8 -*-
import sys
import getpass
import ldap

def getLdapUsers():
    ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)

    l = ldap.initialize('ldaps://ldap.example.com')
    try:
        password = getpass.getpass("Please enter the ldap admin password: ")
        l.simple_bind_s("cn=admin,dc=example,dc=com", password)

        baseDN = "ou=people,o=example,dc=example,dc=com"
        searchScope = ldap.SCOPE_SUBTREE
        searchFilter = "cn=*"
        users = {}
        ldap_result_id = l.search(baseDN, searchScope, searchFilter)
        while 1:
            rType, rData = l.result(ldap_result_id, 0)
            if (rData == []):
                break
            else:
                if rType == ldap.RES_SEARCH_ENTRY:
                    cn = rData[0][0]
                    data = rData[0][1]

                    #Flatten, just for more easy access
                    for (k, v) in data.items():
                        if len(v) == 1:
                            data[k] = v[0]

                    uid = data["uid"]
                    users[cn] = data
        return users;
    except ldap.LDAPError, e:
        print e
    finally:
        l.unbind_s()
    return 0

def main():
    ldapUsers = getLdapUsers()
    print(ldapUsers)

if __name__ == "__main__":
    sys.exit(main())

Most of the code when loading will do some parsing of the returned LDAP data. Happy hacking!