Monday, February 17, 2014

Python and LDAP

I recently found myself working with a custom application similar to nagios. The application checked for services on a regular interval. I needed to add the ability to check and query LDAP server on this interval. The application was already written in python and therefore it made sense to create a python script to connect and query the LDAP server. Like always after a short time googling I found python has a module for communicating with an LDAP server, called python-ldap. More documentation can be found here. However before I dove into creating a python script to accomplish this I needed to setup a test environment. I currently did not have an LDAP server running and therefore naturally wanted to create a Virtual Machine (VM) to host one. Being the lazy computer geek I am, I didn't want to have to create one from the ground up. I discovered turnkey had a prebuilt OpenLDAP VM. I found this to be extremely easy to use. Upon boot the user is prompted to create a couple of passwords and is then bought to the screen show below.
As you can see in the picture the server comes complete with a LDAPadmin web interface, a web shell and a Webmin interface. Perfect utility if you need to quickly test accessing a LDAP server. Now to write that python script. For this I used a Fedora 16 x64 VM I had built for an earlier project. A simple
sudo yum install python-ldap
provided me with the library needed. For this project I was using Python 2.7. First order of business, connecting to the LDAP server.
    l = ldap.open("172.16.133.144")
    l.protocol_version = ldap.VERSION3
It is also important to set the ldap version since there is a difference in searching. In LDAP v2 it is required to do a bind to search where in LDAP v3 a bind is not required. Now that we have a session we need to get a message ID for the operation we want to complete, in this case a search. In order to get the message id we will need to pass the search parameters to the LDAP server. First declare the parameters as variables.
    baseDN = "dc=example,dc=com"
    searchScope = ldap.SCOPE_SUBTREE
    retrieveAttributes = None
    searchFilter = "cn=*"
We are using the default Turnkey LDAP server configuration for simplicity and testing. You would want to change these parameters based on what you actually wanted to search. Here we are setting up to search the example dc and search for everything. Next we pass these parameters to the ldap server to receive a message ID. A message ID is unique to each session and operation. Since we want to search the server we will use the search function and store the returned id.
     ldap_result_id = l.search(baseDN, searchScope, searchFilter, retrieveAttributes)
Now that we have the message ID we can loop through the results of the search. Using the ldap result function we can pull each result which is returned as a tuple. I will first store each result type and result data separately, then check to make sure data isn't empty. If data isn't empty and result type is indeed a search result I will append the data to a list.
    result_set = []
    while 1:
        result_type, result_data = l.result(ldap_result_id, 0)
        if (result_data == []):
            break
        else:
            if result_type == ldap.RES_SEARCH_ENTRY:
                result_set.append(result_data)
    print result_set
I am appending the data to a list only to print it. In a more effective application you could use the returned data to preform what ever purpose your application has. That's it. We have connected to the server and preformed a query. There is no reason to unbind since we used LDAP version 3. If you used version 2 you would want to preform and l.unbind_s(). Below is two sets of code, one is a basic test code with a little error handling built in, and the other does the same task however also takes the server IP, and search parameters as command line arguments. Please if you have any questions, comments or feel I have stated something inaccurate post below. Super basic:
#!/usr/bin/python

import ldap

## first you must open a connection to the server
try:
   print "Connecting"
   l = ldap.open("172.16.133.144")
   l.protocol_version = ldap.VERSION3 
except ldap.LDAPError, e:
   print "Exception caught while trying to connect."
   print e
   exit(-1)

baseDN = "dc=example,dc=com"
searchScope = ldap.SCOPE_SUBTREE
retrieveAttributes = None 
searchFilter = "cn=*"

try:
   ldap_result_id = l.search(baseDN, searchScope, searchFilter, retrieveAttributes)
   result_set = []
   while 1:
    print "Searching..."
    result_type, result_data = l.result(ldap_result_id, 0)
    if (result_data == []):
     break
    else:
     if result_type == ldap.RES_SEARCH_ENTRY:
      result_set.append(result_data)
   print result_set
except ldap.LDAPError, e:
   print "Exception caught while trying to search."
   print e
   exit(-1)

Command line arguments:
#!/usr/bin/python

import ldap
import sys,getopt

def ldapCheck(ip,baseDN,searchFilter):
   ## first you must open a connection to the server
   try:
    print "Connecting"
    l = ldap.open(ip)
    l.protocol_version = ldap.VERSION3 
   except ldap.LDAPError, e:
    print "Exception caught while trying to connect."
    print e
    exit(-1)

   #baseDN = "dc=example,dc=com"
   searchScope = ldap.SCOPE_SUBTREE
   retrieveAttributes = None 
   #searchFilter = "cn=*"

   try:
    ldap_result_id = l.search(baseDN, searchScope, searchFilter, retrieveAttributes)
    result_set = []
    while 1:
     print "Searching..."
     result_type, result_data = l.result(ldap_result_id, 0)
     if (result_data == []):
      break
     else:
      if result_type == ldap.RES_SEARCH_ENTRY:
       result_set.append(result_data)
    print result_set
   except ldap.LDAPError, e:
    print "Exception caught while trying to search."
    print e
    exit(-1)

def main(argv):
   ip = ''
   baseDN = ''
   searchFilter = ''
   try:
      opts, args = getopt.getopt(argv,"hi:b:s:",["ipAddr=","lbaseDN=","lsearchFilter="])
   except getopt.GetoptError,e:
      print 'ldapCheck.py -i  -b  -s '
      print e
      sys.exit(2)
   if not opts:
      print 'ldapCheck.py -i  -b  -s '
      exit(-1)
   for opt, arg in opts:
      if opt == '-h':
         print 'ldapCheck.py -i  -b  -s '
         sys.exit(2)
      elif opt in ("-i", "--ipAddr"):
         ip = arg
      elif opt in ("-b", "--lbaseDN"):
         baseDN = arg
      elif opt in ("-s","--lsearchFilter"):
         searchFilter = arg

   ldapCheck(ip,baseDN,searchFilter)


if __name__ == "__main__":
   main(sys.argv[1:])

No comments:

Post a Comment