HOW-TO: LDAP bind+authenticate using python-ldap

17,835

Solution 1

import ldap
import os
ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
l = ldap.initialize('ldaps://x.x.x.x:636')
l.set_option(ldap.OPT_REFERRALS, 0)
l.set_option(ldap.OPT_PROTOCOL_VERSION, 3)
l.set_option(ldap.OPT_X_TLS_CACERTFILE, os.getcwd()+"/cacert.pem")
l.set_option(ldap.OPT_X_TLS,ldap.OPT_X_TLS_DEMAND)
l.set_option(ldap.OPT_X_TLS_DEMAND, True)
l.set_option(ldap.OPT_DEBUG_LEVEL, 255)
#l.start_tls_s()

Solution 2

On my machine, with python 2.6; I found that /etc/openldap/ldap.conf was used and took precedence over some of the options, even though they are set with ldap.set_option(..., ...).

Depending on your distribution, it may expect certificates to be in /etc/openldap/cacerts rather than /etc/openldap/certs

Here is my approach for debugging this kind of problem.

1. install certs system-wide (openldap)

cp mycert.pem /etc/openldap/certs
cacertdir_rehash /etc/openldap/certs

2. install root certs system-wide (optional)

If you use the same root cert for other uses, you may need:

update-ca-trust enable
cp mycert.pem /etc/pki/ca-trust/source/anchors/
update-ca-trust extract
update-ca-trust check

3. edit /etc/openldap/ldap.conf

Edit the config file:

# /etc/openldap/ldap.conf
base dc=example,dc=com
uri ldaps://ldap.example.com:636/
ssl yes
tls_cacertdir /etc/openldap/certs
pam_password md5

Stop when ldapsearch works properly.

4. write a test script

e.g. test_ldap.py

#!/usr/bin/env python
import ldap, sys
LDAP_SERVER = 'ldaps://ldap.example.com:636'
LDAP_BASE = 'dc=example,dc=com'

try:
    conn = ldap.initialize(LDAP_SERVER)
except ldap.LDAPError, e:
    sys.stderr.write("Fatal Error.n")
    raise

# this may or may not raise an error, e.g. TLS error -8172
items = conn.search_s(LDAP_BASE, ldap.SCOPE_SUBTREE, attrlist=['dn'])

5. use strace

If the above step failed, you can check what is going on by using strace, e.g.

strace -e open test_ldap.py
Share:
17,835
nskalis
Author by

nskalis

Updated on June 05, 2022

Comments

  • nskalis
    nskalis almost 2 years

    Could you please advise on how to troubleshoot the attempt below; am trying to bind to a ldap server, but in vain, when I do

    openssl s_client -CApath /etc/pki/ca-trust/source/anchors/ -connect ...:636
    

    It succeeds :

     Verify return code: 0 (ok)
    

    That is from system-side, but when I try to do a bind via python-ldap module :

    In [1]: import ldap
    
    In [2]: l = ldap.initialize('ldaps://...:636')
    
    In [3]:  ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT,ldap.OPT_X_TLS_ALLOW)
    
    In [4]: l.set_option(ldap.OPT_X_TLS_CACERTFILE,"/etc/pki/ca-trust/source/anchors/cacert.pem")
    
    In [5]: l.set_option(ldap.OPT_X_TLS_NEWCTX,0)
    
    In [6]: l.simple_bind_s("...", "...")
    ---------------------------------------------------------------------------
    SERVER_DOWN                               Traceback (most recent call last)
    <ipython-input-6-a59dae8ba541> in <module>()
    ----> 1 l.simple_bind_s("...", "...")
    
    /usr/lib64/python2.7/site-packages/ldap/ldapobject.pyc in simple_bind_s(self, who, cred, serverctrls, clientctrls)
        205     simple_bind_s([who='' [,cred='']]) -> None
        206     """
    --> 207     msgid = self.simple_bind(who,cred,serverctrls,clientctrls)
        208     resp_type, resp_data, resp_msgid, resp_ctrls = self.result3(msgid,all=1,timeout=self.timeout)
        209     return resp_type, resp_data, resp_msgid, resp_ctrls
    
    /usr/lib64/python2.7/site-packages/ldap/ldapobject.pyc in simple_bind(self, who, cred, serverctrls, clientctrls)
        199     simple_bind([who='' [,cred='']]) -> int
        200     """
    --> 201     return self._ldap_call(self._l.simple_bind,who,cred,RequestControlTuples(serverctrls),RequestControlTuples(clientctrls))
        202 
        203   def simple_bind_s(self,who='',cred='',serverctrls=None,clientctrls=None):
    
    /usr/lib64/python2.7/site-packages/ldap/ldapobject.pyc in _ldap_call(self, func, *args, **kwargs)
         97     try:
         98       try:
    ---> 99         result = func(*args,**kwargs)
        100         if __debug__ and self._trace_level>=2:
        101           if func.__name__!="unbind_ext":
    
    SERVER_DOWN: {'info': 'TLS error -8157:Certificate extension not found.', 'desc': "Can't contact LDAP server"}
    
    
    
    In [1]: import ldap
    
    In [2]: l = ldap.initialize('ldaps://...:636')
    
    In [3]: l.set_option(ldap.OPT_X_TLS_REQUIRE_CERT,ldap.OPT_X_TLS_ALLOW)
    
    In [4]: l.set_option(ldap.OPT_X_TLS_CACERTFILE,"/etc/pki/ca-trust/source/anchors/cacert.pem")
    
    In [5]: l.protocol_version = ldap.VERSION3
    
    In [6]: l.set_option(ldap.OPT_REFERRALS, 0)
    
    In [7]: l.simple_bind_s("...", "...")
    ---------------------------------------------------------------------------
    SERVER_DOWN                               Traceback (most recent call last)
    <ipython-input-7-a59dae8ba541> in <module>()
    ----> 1 l.simple_bind_s("...", "...")
    
    /usr/lib64/python2.7/site-packages/ldap/ldapobject.pyc in simple_bind_s(self, who, cred, serverctrls, clientctrls)
        205     simple_bind_s([who='' [,cred='']]) -> None
        206     """
    --> 207     msgid = self.simple_bind(who,cred,serverctrls,clientctrls)
        208     resp_type, resp_data, resp_msgid, resp_ctrls = self.result3(msgid,all=1,timeout=self.timeout)
        209     return resp_type, resp_data, resp_msgid, resp_ctrls
    
    /usr/lib64/python2.7/site-packages/ldap/ldapobject.pyc in simple_bind(self, who, cred, serverctrls, clientctrls)
        199     simple_bind([who='' [,cred='']]) -> int
        200     """
    --> 201     return self._ldap_call(self._l.simple_bind,who,cred,RequestControlTuples(serverctrls),RequestControlTuples(clientctrls))
        202 
        203   def simple_bind_s(self,who='',cred='',serverctrls=None,clientctrls=None):
    
    /usr/lib64/python2.7/site-packages/ldap/ldapobject.pyc in _ldap_call(self, func, *args, **kwargs)
         97     try:
         98       try:
    ---> 99         result = func(*args,**kwargs)
        100         if __debug__ and self._trace_level>=2:
        101           if func.__name__!="unbind_ext":
    
    SERVER_DOWN: {'info': "TLS error -8172:Peer's certificate issuer has been marked as not trusted by the user.", 'desc': "Can't contact LDAP server"}