I have had really good results by upgrading the ldap3 library that ships with the package to the latest version. This is complicated by a change in name of some attributes, and a dependancy on ctypes which also makes it non portable. However I'm seeing a reduction in time of a full query of one directory from 4 hours to 3 minutes.
https://github.com/cannatag/ldap3/archive/v2.5.tar.gz
http://downloads.sourceforge.net/project/ctypes/ctypes/1.0.2/ctypes-1.0.2.tar.gz
On a system with python + python-devel 2.7 (RHEL 7):
$ cd ~
$ tar zxf ctypes-1.0.2.tar.gz
$ cd ctypes-1.0.2
$ python setup.py build
$ tar zxf ldap3-2.5.tar.gz
$ cd ldap3-2.5
$ python setup.py build
$ cd /opt/splunk/etc/apps/
$ tar zxvf ~/splunk-supporting-add-on-for-active-directory_217.tgz
$ rm -fR SA-ldapsearch/bin/packages/ldap3
$ mv ~/ctypes-1.0.2/build/lib.linux-x86_64-2.7/* SA-ldapsearch/bin/packages/
$ mv ~/ldap3-2.5/build/lib/ldap3 SA-ldapsearch/bin/packages/
$ cd SA-ldapsearch
$ patch -p1 < ~/SA-ldapsearch.patch
The patch
$ cat ~/SA-ldapsearch.patch
diff -Naur ./SA-ldapsearch.2.1.7/bin/ldapfetch.py SA-ldapsearch/bin/ldapfetch.py
--- ./SA-ldapsearch.2.1.7/bin/ldapfetch.py 2018-05-29 22:53:10.000000000 +1000
+++ SA-ldapsearch/bin/ldapfetch.py 2018-06-25 14:27:13.465247255 +1000
@@ -64,7 +64,7 @@
"""
configuration = app.Configuration(self, is_expanded=True)
expanded_domain = app.ExpandedString(self.domain)
- search_scope = ldap3.SEARCH_SCOPE_BASE_OBJECT
+ search_scope = ldap3.BASE
search_filter = '(objectClass=*)'
try:
diff -Naur ./SA-ldapsearch.2.1.7/bin/ldapfilter.py SA-ldapsearch/bin/ldapfilter.py
--- ./SA-ldapsearch.2.1.7/bin/ldapfilter.py 2018-05-29 22:53:10.000000000 +1000
+++ SA-ldapsearch/bin/ldapfilter.py 2018-06-25 14:27:13.465247255 +1000
@@ -50,9 +50,9 @@
**Default:** sub.
''',
default='sub', validate=validators.Map(
- base=ldap3.SEARCH_SCOPE_BASE_OBJECT,
- one=ldap3.SEARCH_SCOPE_SINGLE_LEVEL,
- sub=ldap3.SEARCH_SCOPE_WHOLE_SUBTREE
+ base=ldap3.BASE,
+ one=ldap3.LEVEL,
+ sub=ldap3.SUBTREE
))
decode = Option(
diff -Naur ./SA-ldapsearch.2.1.7/bin/ldapgroup.py SA-ldapsearch/bin/ldapgroup.py
--- ./SA-ldapsearch.2.1.7/bin/ldapgroup.py 2018-05-29 22:53:10.000000000 +1000
+++ SA-ldapsearch/bin/ldapgroup.py 2018-06-25 14:27:13.465247255 +1000
@@ -68,7 +68,7 @@
"""
configuration = app.Configuration(self, is_expanded=True)
expanded_domain = app.ExpandedString(self.domain)
- search_scope = ldap3.SEARCH_SCOPE_BASE_OBJECT
+ search_scope = ldap3.BASE
search_filter = '(objectCategory=Group)'
attributes = ['objectSid']
diff -Naur ./SA-ldapsearch.2.1.7/bin/ldapsearch.py SA-ldapsearch/bin/ldapsearch.py
--- ./SA-ldapsearch.2.1.7/bin/ldapsearch.py 2018-05-29 22:53:10.000000000 +1000
+++ SA-ldapsearch/bin/ldapsearch.py 2018-06-25 14:27:13.465247255 +1000
@@ -14,6 +14,7 @@
from time import time
import ldap3
import app
+import datetime
@Configuration(retainsevents=True)
@@ -53,9 +54,9 @@
**Default:** sub.
''',
default='sub', validate=validators.Map(
- base=ldap3.SEARCH_SCOPE_BASE_OBJECT,
- one=ldap3.SEARCH_SCOPE_SINGLE_LEVEL,
- sub=ldap3.SEARCH_SCOPE_WHOLE_SUBTREE
+ base=ldap3.BASE,
+ one=ldap3.LEVEL,
+ sub=ldap3.SUBTREE
))
debug = Option(
@@ -108,13 +109,14 @@
yield LdapSearchCommand._record(
serial_number, time_stamp, connection.server.host, dn, attributes, attribute_names, encoder)
serial_number += 1
+ GeneratingCommand.flush
if self.limit and serial_number == self.limit:
break
pass
pass
- except ldap3.LDAPException as error:
+ except ldap3.core.exceptions.LDAPException as error:
self.error_exit(error, app.get_ldap_error_message(error, configuration))
return
@@ -127,10 +129,14 @@
for name, value in attributes.iteritems():
if isinstance(value, str):
attributes[name] = b64encode(value)
+ elif isinstance(value, datetime.datetime):
+ attributes[name] = str(value)
elif isinstance(value, list):
for i in range(len(value)):
if isinstance(value[i], str):
value[i] = b64encode(value[i])
+ elif isinstance(value[i], datetime.datetime):
+ value[i] = str(value[i])
raw = encoder.encode(attributes)
diff -Naur ./SA-ldapsearch.2.1.7/bin/ldaptestconnection.py SA-ldapsearch/bin/ldaptestconnection.py
--- ./SA-ldapsearch.2.1.7/bin/ldaptestconnection.py 2018-05-29 22:53:10.000000000 +1000
+++ SA-ldapsearch/bin/ldaptestconnection.py 2018-06-25 14:27:13.465247255 +1000
@@ -11,7 +11,7 @@
from collections import Iterable
from itertools import chain
from json import JSONEncoder
-from ldap3 import Connection, SEARCH_SCOPE_BASE_OBJECT
+from ldap3 import Connection, BASE
from ldap3.core.exceptions import LDAPException
from ldapsearch import LdapSearchCommand
from time import time
@@ -55,7 +55,7 @@
servers = configuration.server if isinstance(configuration.server, Iterable) else (configuration.server,)
search_base = configuration.basedn
search_filter = '(objectClass=*)'
- search_scope = SEARCH_SCOPE_BASE_OBJECT
+ search_scope = BASE
attribute_names = 'distinguishedName',
records = []
diff -Naur ./SA-ldapsearch.2.1.7/bin/packages/app/configuration.py SA-ldapsearch/bin/packages/app/configuration.py
--- ./SA-ldapsearch.2.1.7/bin/packages/app/configuration.py 2018-05-29 22:53:10.000000000 +1000
+++ SA-ldapsearch/bin/packages/app/configuration.py 2018-06-25 14:25:40.464586343 +1000
@@ -9,7 +9,7 @@
import ssl
import sys
-from ldap3 import Server, Tls, LDAPSSLConfigurationError, GET_ALL_INFO
+from ldap3 import Server, Tls, core, ALL
from splunklib.searchcommands.validators import Boolean, Integer, List, Map
from splunklib.binding import HTTPError
from splunklib import data
@@ -318,10 +318,8 @@
tls = Tls(
ca_certs_file=ca_cert_file if ca_cert_file else None,
validate=ssl.CERT_REQUIRED if ssl_verify_server_cert else ssl.CERT_NONE,
- version=version,
- use_ssl_context=True if version==ssl.PROTOCOL_SSLv23 else False,
- ssl_no_v2=ssl_no_v2, ssl_no_v3=ssl_no_v3)
- except LDAPSSLConfigurationError as error:
+ version=version)
+ except core.exceptions.LDAPSSLConfigurationError as error:
message = 'SSL configuration issue: {0}'.format(error)
command.error_exit(error, message)
return
@@ -365,7 +363,7 @@
ca_certs_file=ca_cert_file if ca_cert_file else None,
validate=ssl.CERT_REQUIRED if ssl_verify_server_cert else ssl.CERT_NONE,
version=version)
- except LDAPSSLConfigurationError as error:
+ except core.exceptions.LDAPSSLConfigurationError as error:
message = 'SSL configuration issue: {0}'.format(error)
command.error_exit(error, message)
return
@@ -527,7 +525,7 @@
def create_server(hostname):
return Server(
- hostname, int(port), use_ssl, formatter=formatter, get_info=GET_ALL_INFO,
+ hostname, int(port), use_ssl, formatter=formatter, get_info=ALL,
allowed_referral_hosts=[('*', True)], tls=tls)
self.server = create_server(host[0]) if len(host) == 1 else [create_server(h) for h in host]
diff -Naur ./SA-ldapsearch.2.1.7/bin/packages/app/__init__.py SA-ldapsearch/bin/packages/app/__init__.py
--- ./SA-ldapsearch.2.1.7/bin/packages/app/__init__.py 2018-05-29 22:53:10.000000000 +1000
+++ SA-ldapsearch/bin/packages/app/__init__.py 2018-06-25 14:25:40.463586336 +1000
@@ -324,7 +324,7 @@
# Example: The message produced for LDAPInvalidCredentialsResult
error.message = error.message.replace('\0', '')
- if not isinstance(error, ldap3.LDAPInvalidCredentialsResult):
+ if not isinstance(error, ldap3.core.exceptions.LDAPInvalidCredentialsResult):
return error.message
try:
@@ -384,12 +384,12 @@
is_microsoft_active_directory = True
for feature in supported_features:
- if feature.oid == '1.3.6.1.4.1.4203.1.5.1': # This DSA supports "All Operational Attributes" as per RFC 3673
+ if feature[0] == '1.3.6.1.4.1.4203.1.5.1': # This DSA supports "All Operational Attributes" as per RFC 3673
supports_all_operational_attributes = True
break
- if feature.docs != 'MICROSOFT':
+ if feature[3] != 'MICROSOFT':
is_microsoft_active_directory = False
- if feature.oid in _required_features:
+ if feature[0] in _required_features:
required_features_countdown -= 1
if not (is_microsoft_active_directory and required_features_countdown == 0):
... View more