How to secure communication between OpenLDAP server and client using TLS

How to secure communication between OpenLDAP server and client using TLS

openldap_tls_setup

Here in this article we will try to secure the OpenLDAP server using the TLS certificates for a secure and encrypted communication between OpenLDAP server and the Client application. Also we will see the different between StartTLS and LDAPS methods of securing the channel.

Test Environment

Ubuntu 22.04 Desktop

Using TLS

OpenLDAP clients and servers are capable of using the Transport Layer Security (TLS) framework to provide integrity and confidentiality protections and to support LDAP authentication using the SASL EXTERNAL mechanism. TLS uses X.509 certificates to carry client and server identities. All servers are required to have valid certificates, whereas client certificates are optional.

For more details on TLS directives that can be applied. Please follow OpenLDAP – Using TLS documentation.

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 TLS enabled

In this step we first are installing the “libssl-dev” package. It contains development libraries, header files, and manpages for libssl and libcrypto.

ubadmin@ubscratch:~/openldap-2.6.7$ sudo apt-get install libssl-dev

Once the “libssl-dev” package is installed. You can follow Step1 and Step2 of my previous blog on “How to setup a standalone OpenLDAP service on Ubuntu 22.04” to download and extract the OpenLDAP package.

Then we can configure the OpenLDAP configure script “–with-tls=openssl” to support SSL/TLS secure communication, compile and install OpenLDAP package.

ubadmin@ubscratch:~/openldap-2.6.7$ ./configure --with-tls=openssl
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: Generate TLS Certificate Key pair

For configure our OpenLDAP server and Client to communication on secure TLS channel, we need to generate SSL certificate key pair signed by a CA. Here we are going to install “sscg” package which will help us in generate server certificate, server private key and certificate authority certificate in PEM format.

These certificates are then copied to a paricular location and provided the required permissions and ownership so that they are accessible by the slapd daemon.

ubadmin@ubscratch:~/openldap-2.6.7$ sudo apt-get install sscg
/usr/bin/sscg --lifetime=365 --country=IN --state=Maharashtra --locality=Mumbai --organization="Stack Inc." --organizational-unit="Stack" --hostname="ubscratch.stack.com" --subject-alt-name www.stack.com ubscratch.stack.com --key-strength=2048 --hash-alg="sha256" --ca-file=singer.pem --cert-key-file=server-key.pem --cert-file=server-crt.pem --cert-mode=0644 --cert-key-mode=0600

Copy certificate to the following location and provide the required permissions and ownership.

ubadmin@ubscratch:~/openldap-2.6.7$ sudo mkdir -p /usr/local/etc/openldap/tls
ubadmin@ubscratch:~/openldap-2.6.7$ sudo cp -pr server-* /usr/local/etc/openldap/tls/
ubadmin@ubscratch:~/openldap-2.6.7$ sudo cp -pr singer.pem /usr/local/etc/openldap/tls/ca.pem

ubadmin@ubscratch:~/openldap-2.6.7$ sudo chown -R root:root /usr/local/etc/openldap/tls
ubadmin@ubscratch:~/openldap-2.6.7$ sudo chmod -R 755 /usr/local/etc/openldap/tls
ubadmin@ubscratch:~/openldap-2.6.7$ sudo chmod 400 /usr/local/etc/openldap/tls/server-crt.pem
ubadmin@ubscratch:~/openldap-2.6.7$ sudo chmod 400 /usr/local/etc/openldap/tls/server-key.pem

ubadmin@ubscratch:~/openldap-2.6.7$ sudo ls -ltr /usr/local/etc/openldap/tls
total 12
-r-------- 1 root root 1704 Apr 20 13:49 server-key.pem
-r-------- 1 root root 1801 Apr 20 13:49 server-crt.pem
-rwxr-xr-x 1 root root 2199 Apr 20 13:49 ca.pem

Step3: Configure StartTLS in SLDAP configuration database

Now, its time to update our slapd database configuration file with the SSL/TLS directive in the global configration (ie. cn=config). We need to add the following three directives (ie. olcTLSCACertificateFile, olcTLSCertificateFile, olcTLSCertificateKeyFile) pointing the location of the respective PEM files.

ubadmin@ubscratch:~/openldap-2.6.7$ sudo cat /home/ubadmin/testldapconfig/slapd.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

#
# 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

# 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
# Indices to maintain
olcDbIndex: objectClass eq

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

Step4: Import configuration database

Once our slapd database configuration file is updated with the TLS setting its time to import the configuration database as shown below using slapadd.

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

As you can see the “cn=config” global configuration is updated with the TLS settings as shown below.

ubadmin@ubscratch:~/openldap-2.6.7$ sudo cat /usr/local/etc/slapd.d/cn\=config.ldif 
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
# CRC32 0043d4c7
dn: cn=config
objectClass: olcGlobal
cn: config
olcArgsFile: /usr/local/var/run/slapd.args
olcPidFile: /usr/local/var/run/slapd.pid
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
structuralObjectClass: olcGlobal
entryUUID: 56e5adee-9346-103e-84c3-e3c1aeadfaca
creatorsName: cn=config
createTimestamp: 20240420094434Z
entryCSN: 20240420094434.594694Z#000000#000#000000
modifiersName: cn=config
modifyTimestamp: 20240420094434Z

Step5: Configure Client TLS

Once the server side settings are completed, we need to ensure that the client from which we want to communicate with server also needs to have the openssl and openssl-clients package installed. Ensure to update the configuration file “/usr/local/etc/openldap/ldap.conf” used by the ldap client utilties to update with the CA certificate file for the server with “TLS_CACERT” directive as shown below.

ubadmin@ubscratch:~/openldap-2.6.7$ sudo cat /usr/local/etc/openldap/ldap.conf
#
# LDAP Defaults
#

# See ldap.conf(5) for details
# This file should be world readable but not world writable.

#BASE	dc=example,dc=com
#URI	ldap://ldap.example.com ldap://ldap-provider.example.com:666

#SIZELIMIT	12
#TIMELIMIT	15
#DEREF		never

TLS_CACERT /usr/local/etc/openldap/tls/ca.pem

Step6: Start LDAP daemon

Now, its time to start the slapd daemon on the server and verify that its up and running.

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

Step7: Create LDIF database entries file

Once the slapd daemon service is up and running we can import data into the LDAP directory tree using an ldif file as shown below.

ubadmin@ubscratch:~/openldap-2.6.7$ cat ~/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

NOTE: userPassword attribute is not protected using access control setting here. But this is not recommended for production.

Step8: Add entries to LDAP directory

Now its time to add the entries to the LDAP directory tree using the ldapadd utility as shown below.

ubadmin@ubscratch:~/openldap-2.6.7$ sudo ldapadd -x -D "cn=Manager,dc=stack,dc=com" -W -f ~/ldapfiles/stack.com.ldif 
Enter LDAP Password: 
adding new entry "dc=stack,dc=com"

adding new entry "cn=Manager,dc=stack,dc=com"

adding new entry "cn=Admin,dc=stack,dc=com"

adding new entry "ou=devops,dc=stack,dc=com"

adding new entry "cn=mark,ou=devops,dc=stack,dc=com"

adding new entry "cn=bob,ou=devops,dc=stack,dc=com"

adding new entry "cn=alice,ou=devops,dc=stack,dc=com"

Step9: Search LDAP directory

Its time for us to now search for LDAP directory tree entries using the STARTTLS method. In this method the LDAP server listen on default port 389 but we are passing the option “-ZZ” in this request. This option issue StartTLS (Transport Layer Security) extended operation. If you use -ZZ, the command will require the operation to be successful.

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 -ZZ

We can also enable debugging for this ldap query by passing “-d 255” option to make sure that the TLS handshake is taking place between client and server. This will also help you in troubleshooting TLS issues in case there are any issues.

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 -ZZ -d 255

Step10: Enable LDAP and LDAPS Protocols

In this step first, we are stopping the LDAP service which is running on the default 389 port.

ubadmin@ubscratch:~/openldap-2.6.7$ sudo kill -2 `cat /usr/local/var/run/slapd.pid`

Now, we are starting the LDAP service to listen on both ldap and ldaps protocol (ie. 389 and 636 default ports).

ubadmin@ubscratch:~/openldap-2.6.7$ sudo /usr/local/libexec/slapd -F /usr/local/etc/slapd.d -h "ldap://ubscratch.stack.com:389/ ldaps://ubscratch.stack.com:636/"

Now this configuration enables our LDAP server to support ldap, ldap with starttls and ldaps secure communication.

We can search for LDAP directory entires as shown below using different methods.

LDAP search on insecure channel

Here we are using the ldap protocol in the ldap host option “-H” with the default “389” port.

ubadmin@ubscratch:~/openldap-2.6.7$ ldapsearch -H "ldap://ubscratch.stack.com:389" -x -b 'dc=stack,dc=com' -D 'cn=bob,ou=devops,dc=stack,dc=com' '(cn=bob)' -W
Enter LDAP Password: 
# extended LDIF
#
# LDAPv3
# base <dc=stack,dc=com> with scope subtree
# filter: (cn=bob)
# requesting: ALL
#

# bob, devops, stack.com
dn: cn=bob,ou=devops,dc=stack,dc=com
objectClass: person
cn: bob
sn: b
userPassword:: Ym9iQDEyMzQ=

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1

LDAP search with StartTLS on insecure channel

Here we are using the ldap protocol in the ldap host option “-H” with the default “389” port and enable StartTLS encryption using “-ZZ” option.

ubadmin@ubscratch:~/openldap-2.6.7$ ldapsearch -H "ldap://ubscratch.stack.com:389" -x -b 'dc=stack,dc=com' -D 'cn=bob,ou=devops,dc=stack,dc=com' '(cn=bob)' -W -ZZ
Enter LDAP Password: 
# extended LDIF
#
# LDAPv3
# base <dc=stack,dc=com> with scope subtree
# filter: (cn=bob)
# requesting: ALL
#

# bob, devops, stack.com
dn: cn=bob,ou=devops,dc=stack,dc=com
objectClass: person
cn: bob
sn: b
userPassword:: Ym9iQDEyMzQ=

# search result
search: 3
result: 0 Success

# numResponses: 2
# numEntries: 1

LDAP search with StartTLS on secure channel – fails as TLS is already started

Here as we are using the LDAPS protocol already, we can use “-ZZ” option as TLS is already started.

$ ldapsearch -H "ldaps://ubscratch.stack.com:636" -x -b 'dc=stack,dc=com' -D 'cn=bob,ou=devops,dc=stack,dc=com' '(cn=bob)' -W -ZZ
ldap_start_tls: Operations error (1)
	additional info: TLS already started

LDAP search on secure channel

Here we are using the ldaps protocol in the ldap host option “-H” with the default “636” port.

ubadmin@ubscratch:~/openldap-2.6.7$ ldapsearch -H "ldaps://ubscratch.stack.com:636" -x -b 'dc=stack,dc=com' -D 'cn=bob,ou=devops,dc=stack,dc=com' '(cn=bob)' -W
Enter LDAP Password: 
# extended LDIF
#
# LDAPv3
# base <dc=stack,dc=com> with scope subtree
# filter: (cn=bob)
# requesting: ALL
#

# bob, devops, stack.com
dn: cn=bob,ou=devops,dc=stack,dc=com
objectClass: person
cn: bob
sn: b
userPassword:: Ym9iQDEyMzQ=

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1

Hope you enjoyed reading this article. Thank you..