How to setup Access logging Overlay for OpenLDAP Server

How to setup Access logging Overlay for OpenLDAP Server

Here in this article we will see a feature of slapd known as overlay which can be used modify the behaviour of the server. We will be implementing the accesslog overlay in slapd which will help us in capturing the request received by slapd and logged into a log database.

Test Environment

Ubuntu 22.04

What is OpenLDAP Overlay

An overlay is a chunk of code that can modify the behaviour of the slapd. The overlay is given an opportunity to perform processing on the request before any information is retrieved from the underlying database.

Slapd is compiled to dynamically load modules that are loaded when slapd starts, and almost all overlays are implemented as modules. The access logging overlay is one of the overlay which is used to record information about directory access and utilization.

If you are interested in watching the video. Here is the YouTube video on the same step by step procedure outlined below.

Procedure

Step1: Build and Install OpenLDAP with accesslog overlay

As a first step we will configure the OpenLDAP configure script with “–enable-accesslog” to enable accesslog overlay feature, then compile and install OpenLDAP package.

ubadmin@ubscratch:~/openldap-2.6.7$ ./configure --enable-accesslog
ubadmin@ubscratch:~/openldap-2.6.7$ make depend
ubadmin@ubscratch:~/openldap-2.6.7$ make
ubadmin@ubscratch:~/openldap-2.6.7$ sudo make install

Step2: Create database configuration file with access logging overlay

Here we are going to update our slapd configuration file. We are adding the following section “LMDB database overlay definition” were in we include the overlay directives for the primary mdb database with suffix “dc=stack, dc=com”.

The accesslog overlay directive should appear after the olcOverlay directive. Here are the details of the directive that we used in our configuration.

  • olcAccessLogDB – Specify the suffix of a database to be used for storing the log records
  • olcAccessLogOps – Specify which types of operations to log
  • olcAccessLogOld – Specify a filter for matching against Deleted and Modified entries

Once the primary database is configured with the overlay directive, we are adding the following section “LMDB logging database” to create the logging database with suffix “cn=log” and logging database directory “/usr/local/var/openldap-data-log”. This is the location where all the logging will be stored on the filesystem. Also please note on the index “reqStart” that we are creating for this database.

We also enabling rootDN and password for the log database access with access control restriction.

ubadmin@ubscratch:~/openldap-2.6.7$ cat ~/testldapconfig/slapd.overlay.ldif 
#
# See slapd-config(5) for details on configuration options.
# This file should NOT be world readable.
#
dn: cn=config
objectClass: olcGlobal
cn: config
#
#
# Define global ACLs to disable default read access.
#
olcArgsFile: /usr/local/var/run/slapd.args
olcPidFile: /usr/local/var/run/slapd.pid
#
# Do not enable referrals until AFTER you have a working directory
# service AND an understanding of referrals.
#olcReferral:	ldap://root.openldap.org
#
# Sample security restrictions
#	Require integrity protection (prevent hijacking)
#	Require 112-bit (3DES or better) encryption for updates
#	Require 64-bit encryption for simple bind
#olcSecurity: ssf=1 update_ssf=112 simple_bind=64
#######################################################################
# TLS
#######################################################################
olcTLSCACertificateFile: /usr/local/etc/openldap/tls/ca.pem
olcTLSCertificateFile: /usr/local/etc/openldap/tls/server-crt.pem
olcTLSCertificateKeyFile: /usr/local/etc/openldap/tls/server-key.pem
#######################################################################
# SASL configuration
#######################################################################
#olcSaslRealm: stack.com
#olcAuthzRegexp: "^uid=([^,]+).*,cn=auth$" "uid=$1,ou=Users,dc=stack,dc=com"
olcAuthzRegexp: "^uid=([^,]+).*,cn=auth$" "ldap:///dc=stack,dc=com??sub?(uid=$1)"

#
# Load dynamic backend modules:
#
dn: cn=module,cn=config
objectClass: olcModuleList
cn: module
olcModulepath:	/usr/local/libexec/openldap
olcModuleload:	back_mdb.la
#olcModuleload:	back_ldap.la
#olcModuleload:	back_passwd.la

dn: cn=schema,cn=config
objectClass: olcSchemaConfig
cn: schema

include: file:///usr/local/etc/openldap/schema/core.ldif
include: file:///usr/local/etc/openldap/schema/cosine.ldif
include: file:///usr/local/etc/openldap/schema/inetorgperson.ldif

# Frontend settings
#
dn: olcDatabase=frontend,cn=config
objectClass: olcDatabaseConfig
objectClass: olcFrontendConfig
olcDatabase: frontend
#
# Sample global access control policy:
#	Root DSE: allow anyone to read it
#	Subschema (sub)entry DSE: allow anyone to read it
#	Other DSEs:
#		Allow self write access
#		Allow authenticated users read access
#		Allow anonymous users to authenticate
#
#olcAccess: to dn.base="" by * read
#olcAccess: to dn.base="cn=Subschema" by * read
#olcAccess: to *
#	by self write
#	by users read
#	by anonymous auth
#
# if no access controls are present, the default policy
# allows anyone and everyone to read anything but restricts
# updates to rootdn.  (e.g., "access to * by * read")
#
# rootdn can always read and write EVERYTHING!
#

#######################################################################
# LMDB database definitions
#######################################################################
#
dn: olcDatabase=mdb,cn=config
objectClass: olcDatabaseConfig
objectClass: olcMdbConfig
olcDatabase: mdb
olcDbMaxSize: 1073741824
olcSuffix: dc=stack,dc=com
olcRootDN: cn=Manager,dc=stack,dc=com
# Cleartext passwords, especially for the rootdn, should
# be avoided.  See slappasswd(8) and slapd-config(5) for details.
# Use of strong authentication encouraged.
olcRootPW: secret
# The database directory MUST exist prior to running slapd AND 
# should only be accessible by the slapd and slap tools.
# Mode 700 recommended.
olcDbDirectory:	/usr/local/var/openldap-data-com
# Indices to maintain
olcDbIndex: objectClass eq
olcDbIndex: uid pres,eq
olcDbIndex: cn,sn pres,eq,approx,sub
olcAccess: to attrs=uid
  by anonymous read
  by users read
olcAccess: to attrs=userPassword
  by self write
  by anonymous auth
  by dn.base="cn=Admin,dc=stack,dc=com" write
  by * none
olcAccess: to *
  by self write
  by dn.base="cn=Admin,dc=stack,dc=com" write
  by * read

#######################################################################
# LMDB database overlay definition
#######################################################################
dn: olcOverlay=accesslog,olcDatabase={1}mdb,cn=config
objectClass: olcOverlayConfig
objectClass: olcAccessLogConfig
olcOverlay: accesslog
olcAccessLogDB: cn=log
olcAccessLogOps: writes
olcAccessLogOps: reads
olcAccessLogOld: (objectClass=person)

#######################################################################
# LMDB logging database
#######################################################################
dn: olcDatabase=mdb,cn=config
objectClass: olcDatabaseConfig
objectClass: olcMdbConfig
olcDatabase: mdb
olcDbMaxSize: 1073741824
olcSuffix: cn=log
olcRootDN: cn=Manager,cn=log
# Cleartext passwords, especially for the rootdn, should
# be avoided.  See slappasswd(8) and slapd-config(5) for details.
# Use of strong authentication encouraged.
olcRootPW: secret
# The database directory MUST exist prior to running slapd AND 
# should only be accessible by the slapd and slap tools.
# Mode 700 recommended.
olcDbDirectory: /usr/local/var/openldap-data-log
olcDbIndex: reqStart eq
olcAccess: to *
  by * read

dn: olcDatabase=monitor,cn=config
objectClass: olcDatabaseConfig
olcDatabase: monitor
olcRootDN: cn=config
olcMonitoring: FALSE

Validate the database configuration ldif file

ubadmin@ubscratch:~/openldap-2.6.7$ sudo /usr/local/sbin/slapadd -v -u -c -n 0 -F /usr/local/etc/slapd.d -l ~/testldapconfig/slapd.overlay.ldif 
added: "cn=config" (00000000)
added: "cn=module{0},cn=config" (00000000)
added: "cn=schema,cn=config" (00000000)
added: "cn={0}core,cn=schema,cn=config" (00000000)
added: "cn={1}cosine,cn=schema,cn=config" (00000000)
added: "cn={2}inetorgperson,cn=schema,cn=config" (00000000)
added: "olcDatabase={-1}frontend,cn=config" (00000000)
added: "olcDatabase={1}mdb,cn=config" (00000000)
added: "olcOverlay={0}accesslog,olcDatabase={1}mdb,cn=config" (00000000)
added: "olcDatabase={2}mdb,cn=config" (00000000)
added: "olcDatabase={3}monitor,cn=config" (00000000)
Closing DB...

Apply the database configuration ldif file

ubadmin@ubscratch:~/openldap-2.6.7$ sudo /usr/local/sbin/slapadd -v -n 0 -F /usr/local/etc/slapd.d -l ~/testldapconfig/slapd.overlay.ldif 

Verify overlay access is configured for the mdb database

ubadmin@ubscratch:~/openldap-2.6.7$ sudo cat /usr/local/etc/slapd.d/cn\=config/olcDatabase={1}mdb/olcOverlay={0}accesslog.ldif
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
# CRC32 a6d67774
dn: olcOverlay={0}accesslog
objectClass: olcOverlayConfig
objectClass: olcAccessLogConfig
olcOverlay: {0}accesslog
olcAccessLogDB: cn=log
olcAccessLogOps: all
olcAccessLogOld: (objectClass=person)
structuralObjectClass: olcAccessLogConfig
entryUUID: 8305d2ba-9cdc-103e-96b5-0f297ce8ae6f
creatorsName: cn=config
createTimestamp: 20240502143213Z
entryCSN: 20240502143213.599574Z#000000#000#000000
modifiersName: cn=config
modifyTimestamp: 20240502143213Z

Step3: Add database entries using slapadd

Here are we going to load data into our directory tree using LDIF file as shown below. First we are doing a dry-run to understand what entries would be added and to know if there are any issues with the LDIF file.

LDIF Data

ubadmin@ubscratch:~/openldap-2.6.7$ cat /home/ubadmin/ldapfiles/stack.com.ldif
dn: dc=stack,dc=com
objectclass: dcObject
objectclass: organization
o: stack company
dc: stack

dn: cn=Manager,dc=stack,dc=com
objectclass: organizationalRole
cn: Manager

dn: cn=Admin,dc=stack,dc=com
objectclass: organizationalRole
cn: Admin

dn: ou=devops,dc=stack,dc=com
objectclass: organizationalUnit
ou: devops

dn: cn=mark,ou=devops,dc=stack,dc=com
objectclass: person
cn: mark
sn: m
userPassword: mark@1234

dn: cn=bob,ou=devops,dc=stack,dc=com
objectclass: person
cn: bob
sn: b
userPassword: bob@1234

dn: cn=alice,ou=devops,dc=stack,dc=com
objectclass: person
cn: alice
sn: a
userPassword: alice@1234

Validate the LDIF data

ubadmin@ubscratch:~/openldap-2.6.7$ sudo /usr/local/sbin/slapadd -v -u -c -F /usr/local/etc/slapd.d -l /home/ubadmin/ldapfiles/stack.com.ldif
added: "dc=stack,dc=com" (00000000)
added: "cn=Manager,dc=stack,dc=com" (00000000)
added: "cn=Admin,dc=stack,dc=com" (00000000)
added: "ou=devops,dc=stack,dc=com" (00000000)
added: "cn=mark,ou=devops,dc=stack,dc=com" (00000000)
added: "cn=bob,ou=devops,dc=stack,dc=com" (00000000)
added: "cn=alice,ou=devops,dc=stack,dc=com" (00000000)
Closing DB...

Once the dry-run is successful, we can load the data into directory tree without “-u” and “-c” option as shown below.

ubadmin@ubscratch:~/openldap-2.6.7$ sudo /usr/local/sbin/slapadd -v -F /usr/local/etc/slapd.d -l /home/ubadmin/ldapfiles/stack.com.ldif

Step4: Start LDAP service

Now we have our LMDB primary database configured with accesslog overlay directive and have loaded the directory tree data. It’s time to start up the LDAP service as shown below.

ubadmin@ubscratch:~/openldap-2.6.7$ sudo /usr/local/libexec/slapd -F /usr/local/etc/slapd.d

Step5: Validate LDAP service

Verify you are able to use the ldapsearch to search for an entry with “cn=bob” in the base “dc=stack,dc=com” with bind DN and password of bob as shown below. If you get a response means that the LDAP service is working as per our expection.

ubadmin@ubscratch:~/openldap-2.6.7$ ldapsearch -x -b 'dc=stack,dc=com' -D 'cn=bob,ou=devops,dc=stack,dc=com' '(cn=bob)' -W

Step6: Validate access log database

Now its time to validate the logs in the “cn=log” database suffix as shown below. You can see from the output that it has captured all the events related to the search that we carried out in our previous step.

The search for “cn=bob” entry is basically a three step process that is carried out in LDAP. First it does a bind with the bindDN, then does a search for entry in the directory tree and finally it unbinds the user.

The “reqType” in each of the log entry below explains the same.

ubadmin@ubscratch:~/testldapconfig$ ldapsearch -x -b cn=log
# extended LDIF
#
# LDAPv3
# base <cn=log> with scope subtree
# filter: (objectclass=*)
# requesting: ALL
#

# log
dn: cn=log
objectClass: auditContainer
cn: log

# 20240502145900.000000Z, log
dn: reqStart=20240502145900.000000Z,cn=log
objectClass: auditBind
reqStart: 20240502145900.000000Z
reqEnd: 20240502145900.000001Z
reqType: bind
reqSession: 1000
reqAuthzID:
reqDN: cn=bob,ou=devops,dc=stack,dc=com
reqResult: 0
reqVersion: 3
reqMethod: SIMPLE

# 20240502145900.000002Z, log
dn: reqStart=20240502145900.000002Z,cn=log
objectClass: auditSearch
reqStart: 20240502145900.000002Z
reqEnd: 20240502145900.000003Z
reqType: search
reqSession: 1000
reqAuthzID: cn=bob,ou=devops,dc=stack,dc=com
reqDN: dc=stack,dc=com
reqResult: 0
reqScope: sub
reqDerefAliases: never
reqAttrsOnly: FALSE
reqFilter: (cn=bob)
reqEntries: 1
reqTimeLimit: 3600
reqSizeLimit: 500

# 20240502145900.000004Z, log
dn: reqStart=20240502145900.000004Z,cn=log
objectClass: auditObject
reqStart: 20240502145900.000004Z
reqEnd: 20240502145900.000005Z
reqType: unbind
reqSession: 1000
reqAuthzID: cn=bob,ou=devops,dc=stack,dc=com

# search result
search: 2
result: 0 Success

# numResponses: 5
# numEntries: 4

Hope you enjoyed reading this article. Thank you..