Configure OpenLDAP TLS on RHEL-Based Linux

Configure OpenLDAP TLS on RHEL, Rocky Linux, AlmaLinux, Oracle Linux, and CentOS Stream using a private CA, StartTLS, LDAPS, and cn=config.

Published

Updated

Read time 20 min read

Reviewed byDeepak Prasad

OpenLDAP StartTLS and LDAPS configuration on RHEL-based Linux

This lesson configures TLS on a RHEL-family OpenLDAP server:

  • a private CA and SAN-signed server certificate
  • cn=config TLS settings (olcTLS*)
  • StartTLS on port 389 and LDAPS on port 636
  • CA trust on RHEL-family clients
  • olcSecurity: simple_bind=128 so simple password binds require an SSF of at least 128

OpenLDAP is still commonly described as LDAP over SSL, but current deployments use TLS. Both StartTLS and LDAPS use TLS; they differ mainly in how and when the encrypted connection is established.

This is the TLS and certificate-management lesson in the OpenLDAP tutorial series.

Complete these lessons first:

The install lesson should already have left slapd running, cn=config reachable over LDAPI, and a directory suffix with an administrator bind DN in place.

Tested on: Rocky Linux 10.2; OpenLDAP 2.6.10 from EPEL 10.2; OpenSSL 3.x; SELinux Enforcing.

IMPORTANT

This lesson configures server TLS, client trust, and rejection of unprotected simple password binds. It does not cover:

  • olcAccess ACL rules
  • SSSD client login
  • replication certificates
  • mutual TLS (client certificate authentication)

RHEL-Family Compatibility and Package Notes

The commands target current Enterprise Linux 9 and 10 with OpenLDAP 2.6. The TLS settings live in cn=config, but these items can differ between distributions and minor releases—check your host before copying paths verbatim:

  • package source and OpenLDAP build
  • certificate paths and service account
  • default ExecStart listener URLs

RHEL and vendor support

  • Red Hat deprecated openldap-servers in RHEL 7.4 and does not ship it as a supported BaseOS or AppStream directory service in later releases.
  • On RHEL, installing slapd from EPEL gives a community-supported deployment, not a Red Hat-supported directory service.
  • When vendor support matters, Red Hat points production customers toward Identity Management or Red Hat Directory Server.

Other RHEL-family distributions

  • Rocky Linux, AlmaLinux, Oracle Linux, and CentOS Stream can follow the same procedure when slapd came from a compatible EPEL or distribution repository.
  • The exact OpenLDAP build differs by EL branch—EPEL 9 and the newer EPEL 10 branch do not necessarily ship the same version.

Before changing certificates or listeners, record the distribution, OpenLDAP version, certificate paths, service account, and listener URLs. These checks establish a baseline if TLS troubleshooting later points at package drift, a missing listener, or the wrong Enterprise Linux branch.

Confirm the installed distribution and minor release:

bash
cat /etc/os-release

Sample output:

output
NAME="Rocky Linux"
VERSION="10.2 (Red Quartz)"
ID="rocky"
ID_LIKE="rhel centos fedora"

The VERSION line shows which Enterprise Linux branch and minor release the host is running.

Query installed OpenLDAP RPMs with rpm -q; the rpm command covers -qa, -qf, and other package checks on RPM-based hosts. Note the EPEL build suffix on openldap-servers:

bash
rpm -q openldap openldap-clients openldap-servers

Sample output:

output
openldap-2.6.10-1.el10.x86_64
openldap-clients-2.6.10-1.el10.x86_64
openldap-servers-2.6.10-1.el10_2.x86_64

The el10_2 suffix on openldap-servers shows the package came from the Enterprise Linux 10.2 EPEL branch.

Check the slapd binary version the running service will use:

bash
slapd -VV 2>&1 | head

Sample output:

output
@(#) $OpenLDAP: slapd 2.6.10 (Jan 28 2026 00:00:00) $
	openldap

Capture the effective listener URLs with systemctl before editing firewall rules or a systemd override:

bash
sudo systemctl cat slapd

Sample output:

output
ExecStart=/usr/sbin/slapd -u ldap -h "ldap:/// ldaps:/// ldapi:///"

The ExecStart line shows whether ldap:///, ldaps:///, and ldapi:/// are already enabled on this host.

The cn=config TLS attributes below work the same way across supported RHEL-family systems; only package versions and listener defaults may differ on your machine.


StartTLS vs LDAPS

Connection URI and command Port Behavior
Plain LDAP ldap://ldap-server.example.com 389 Unencrypted unless upgraded
StartTLS ldap://ldap-server.example.com with -ZZ 389 Starts plain, then requires a TLS upgrade
LDAPS ldaps://ldap-server.example.com 636 TLS begins immediately

StartTLS and LDAPS can share the same certificate and CA. With certificate verification and a modern TLS minimum, both methods protect LDAP traffic.

  • StartTLS — connect on ldap:// port 389, then upgrade with -ZZ so the client cannot continue without TLS
  • LDAPS — connect on ldaps:// port 636; TLS begins immediately

Installing certificates alone does not stop unencrypted simple binds. Follow this order:

  • verify TLS handshakes first
  • then add olcSecurity with simple_bind=128 so simple password binds require an SSF of at least 128

Prerequisites and Certificate Plan

My lab uses this naming plan across certificate files, ldap.conf, and firewall rules. Keep the same pattern on your host so SANs, URIs, and LDIF stay aligned.

Setting Value
LDAP server ldap-server.example.com
Server IP 192.168.56.108 (host-only adapter)
Base DN dc=example,dc=com
Administrator DN cn=admin,dc=example,dc=com
Certificate authority Example Lab LDAP CA
Server certificate SAN DNS:ldap-server.example.com
Optional IP SAN IP:192.168.56.108
StartTLS port 389
LDAPS port 636
SELinux Enforcing

The SAN must list every name clients will use to connect; see OpenSSL Subject Alternative Name when hostname or IP coverage grows beyond this lab:

  • include the server hostname (ldap-server.example.com in this lab)
  • include the host-only IP address clients will use (192.168.56.108 here—not a NAT address)
  • do not rely on the certificate Common Name alone—clients validate against SAN entries

Map both lab hosts on the host-only network in /etc/hosts before generating certificates:

text
192.168.56.108 ldap-server.example.com ldap-server
192.168.56.109 ldap-client.example.com ldap-client

Confirm hostname resolution, slapd status, and SELinux mode:

bash
hostname -f

Sample output:

output
ldap-server.example.com
bash
getent hosts ldap-server.example.com

Sample output:

output
192.168.56.108    ldap-server.example.com ldap-server
bash
systemctl is-active slapd

Sample output:

output
active
bash
getenforce

Sample output:

output
Enforcing

Create a Private CA and OpenLDAP Server Certificate

This lab uses a private CA-signed server certificate—not a standalone self-signed leaf without a SAN. Certificate generation uses the OpenSSL CLI throughout; the OpenSSL reference covers req, x509, and verify beyond this walkthrough.

  • Keep CA working files in /root/openldap-ca/.
  • Never place the CA private key under /etc/openldap/certs/.
  • Certificate generation needs a root shell because the CA directory lives under /root.

Install OpenSSL

Install the OpenSSL package with sudo and dnf on this lab host:

bash
sudo dnf install -y openssl

Open a root shell for CA work

bash
sudo -i
bash
umask 077

A restrictive umask keeps newly generated private keys and config files readable only to root.

bash
install -d -m 700 /root/openldap-ca
bash
cd /root/openldap-ca

Create the lab CA

Write the CA OpenSSL configuration as ca.cnf:

text
[ req ]
prompt = no
default_md = sha256
distinguished_name = dn
x509_extensions = v3_ca

[ dn ]
CN = Example Lab LDAP CA
O = Example Organization

[ v3_ca ]
basicConstraints = critical,CA:TRUE
keyUsage = critical,keyCertSign,cRLSign
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
bash
openssl genrsa -out example-ldap-ca.key 4096
bash
openssl req -x509 -new -key example-ldap-ca.key -sha256 -days 3650 -out example-ldap-ca.crt -config ca.cnf
bash
chmod 600 example-ldap-ca.key

Generate the OpenLDAP server key and CSR

Create ldap-server.cnf with DNS and IP SAN entries:

text
[ req ]
prompt = no
default_md = sha256
distinguished_name = dn
req_extensions = v3_req

[ dn ]
CN = ldap-server.example.com
O = Example Organization

[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = digitalSignature,keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[ alt_names ]
DNS.1 = ldap-server.example.com
IP.1 = 192.168.56.108
bash
openssl genrsa -out ldap-server.example.com.key 2048
bash
openssl req -new -key ldap-server.example.com.key -out ldap-server.example.com.csr -config ldap-server.cnf

OpenLDAP expects a readable, unencrypted PEM private key at service start—filesystem permissions protect the service key.

Sign the server certificate

bash
openssl x509 -req -in ldap-server.example.com.csr -CA example-ldap-ca.crt -CAkey example-ldap-ca.key -CAcreateserial -out ldap-server.example.com.crt -days 825 -sha256 -extensions v3_req -extfile ldap-server.cnf

Sample output:

output
Certificate request self-signature ok
subject=CN=ldap-server.example.com, O=Example Organization

Validate the certificate before installation

bash
openssl x509 -in ldap-server.example.com.crt -noout -subject -issuer -dates
bash
openssl x509 -in ldap-server.example.com.crt -noout -ext subjectAltName
bash
openssl verify -purpose sslserver -verify_hostname ldap-server.example.com -CAfile example-ldap-ca.crt ldap-server.example.com.crt

Sample output:

output
ldap-server.example.com.crt: OK
bash
openssl verify -purpose sslserver -verify_ip 192.168.56.108 -CAfile example-ldap-ca.crt ldap-server.example.com.crt

Sample output:

output
ldap-server.example.com.crt: OK

Compare public-key hashes to confirm the certificate matches the private key; see verify a certificate matches its private key when renewal or key reuse is in play:

bash
openssl x509 -in ldap-server.example.com.crt -pubkey -noout | openssl pkey -pubin -outform pem | sha256sum
bash
openssl pkey -in ldap-server.example.com.key -pubout -outform pem | sha256sum

Both SHA-256 hashes should match.

When the server certificate uses an intermediate CA

In this lab the root CA signs the server certificate directly, so a single leaf file at olcTLSCertificateFile and example-ldap-ca.crt at olcTLSCACertificateFile are sufficient.

For enterprise PKI, separate what slapd presents from what it trusts. The OpenSSL certificate chain walkthrough covers leaf, intermediate, and root file layout in more depth:

Server certificate file (olcTLSCertificateFile)

  • leaf server certificate
  • issuing intermediate CA
  • any higher intermediate CA

Server CA trust file (olcTLSCACertificateFile)

  • trusted intermediate and root CA certificates

Build the server presentation chain with the leaf first, followed by intermediate certificates. Do not normally append the root CA to the chain presented by the server:

bash
cat ldap-server.example.com.crt issuing-intermediate-ca.crt > ldap-server.example.com-fullchain.crt

For the CA trust bundle, certificate order is not significant. Include the trusted intermediate and root CA certificates:

bash
cat issuing-intermediate-ca.crt root-ca.crt > example-ldap-ca-chain.crt

Then configure both attributes:

ldif
replace: olcTLSCertificateFile
olcTLSCertificateFile: /etc/openldap/certs/ldap-server.example.com-fullchain.crt
-
replace: olcTLSCACertificateFile
olcTLSCACertificateFile: /etc/openldap/certs/example-ldap-ca-chain.crt

olcTLSCertificateFile contains the server leaf certificate and any intermediate certificates that slapd should present to clients. olcTLSCACertificateFile holds CA certificates trusted by slapd. Under OpenSSL builds, the slapd-config manual allows intermediates in olcTLSCertificateFile rather than relying on olcTLSCACertificateFile for chain presentation.

  • Presentation chain (olcTLSCertificateFile) — what clients receive during the TLS handshake
  • CA trust bundle (olcTLSCACertificateFile) — what slapd trusts, particularly when verifying client certificates during mutual TLS

For a directly root-signed lab certificate, the single-file setup above is enough.

NOTE
In production, submit the CSR to your organization's CA and continue with the returned server certificate and CA chain. This article does not cover Let's Encrypt, ACME, or enterprise PKI automation.

Install the OpenLDAP Certificate and Private Key

Deploy the certificate, public CA certificate, and server private key under /etc/openldap/certs/:

  • matches the SELinux OpenLDAP policy on current RHEL-family packages
  • keeps service TLS material separate from CA working files under /root/openldap-ca/
text
/etc/openldap/certs/
├── example-ldap-ca.crt
├── ldap-server.example.com.crt
└── ldap-server.example.com.key
bash
cp example-ldap-ca.crt /etc/openldap/certs/
bash
cp ldap-server.example.com.crt /etc/openldap/certs/
bash
cp ldap-server.example.com.key /etc/openldap/certs/

Set ownership, permissions, and SELinux labels

The confined slapd process runs as the ldap user and must read the server private key at startup. Apply the ownership from the table with chown; public certificates stay world-readable and the key stays group-readable by ldap only.

File Owner Mode
CA certificate root:ldap 0644
Server certificate root:ldap 0644
Server private key root:ldap 0640
bash
chown root:ldap /etc/openldap/certs/example-ldap-ca.crt /etc/openldap/certs/ldap-server.example.com.crt /etc/openldap/certs/ldap-server.example.com.key
bash
chmod 644 /etc/openldap/certs/example-ldap-ca.crt /etc/openldap/certs/ldap-server.example.com.crt
bash
chmod 640 /etc/openldap/certs/ldap-server.example.com.key
bash
matchpathcon /etc/openldap/certs/ldap-server.example.com.key

Sample output:

output
/etc/openldap/certs/ldap-server.example.com.key    system_u:object_r:slapd_cert_t:s0
bash
restorecon -Rv /etc/openldap/certs
bash
ls -lZ /etc/openldap/certs

The slapd_cert_t label is what the confined slapd service expects on certificate files.

bash
sudo -u ldap test -r /etc/openldap/certs/ldap-server.example.com.key && echo "Private key is readable by slapd"

Leave the root shell after the files are in place:

bash
exit

Configure OpenLDAP TLS Through cn=config

Inspect current TLS settings

Check existing olcTLS* values before applying changes:

bash
sudo ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b cn=config -s base olcTLSCACertificateFile olcTLSCertificateFile olcTLSCertificateKeyFile olcTLSProtocolMin olcTLSVerifyClient

Create the TLS LDIF

Create configure-tls.ldif:

ldif
dn: cn=config
changetype: modify
replace: olcTLSCACertificateFile
olcTLSCACertificateFile: /etc/openldap/certs/example-ldap-ca.crt
-
replace: olcTLSCertificateFile
olcTLSCertificateFile: /etc/openldap/certs/ldap-server.example.com.crt
-
replace: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /etc/openldap/certs/ldap-server.example.com.key
-
replace: olcTLSProtocolMin
olcTLSProtocolMin: 3.3

olcTLSCertificateFile and olcTLSCACertificateFile serve different roles:

  • olcTLSCertificateFile — server leaf certificate; when an intermediate CA issued the certificate, include the leaf followed by intermediate certificates that slapd should present to clients
  • olcTLSCertificateKeyFile — private key matching the server certificate
  • olcTLSCACertificateFile — CA certificates trusted by slapd, mainly for client-certificate verification; distinct from the chain sent to LDAP clients

For a root-signed lab certificate, olcTLSCertificateFile points at the leaf only. Each client must still trust the issuing CA through its own ldap.conf or system trust store.

olcTLSProtocolMin: 3.3 does not use TLS marketing version numbers directly:

  • under OpenSSL, 3.3 means TLS 1.2 (3.(x+1) represents TLS 1.x)
  • the directive is ignored when slapd is linked against GnuTLS

The slapd-config manual documents this mapping.

To see which TLS library your build uses:

bash
ldd /usr/sbin/slapd | grep -E 'libssl|libgnutls'

Sample output:

output
libssl.so.3 => /lib64/libssl.so.3 (0x00007efed88e3000)

RHEL-family crypto policy can enforce an equal or stricter minimum regardless of this OpenLDAP setting.

olcTLSVerifyClient defaults to never on a fresh install. Requiring client certificates and SASL EXTERNAL over TLS is a separate advanced topic.

bash
sudo ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f configure-tls.ldif

cn=config changes through LDAPI normally take effect without a restart. Restart slapd here so the running process reloads the certificate files from disk.

bash
sudo ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b cn=config -s base olcTLSCACertificateFile olcTLSCertificateFile olcTLSCertificateKeyFile olcTLSProtocolMin

Restart and check the service

Restart slapd after TLS file changes, then confirm the unit is active. When the restart fails, journalctl on the slapd unit usually surfaces TLS path, permission, or key-format errors:

bash
sudo systemctl restart slapd
bash
sudo systemctl status slapd --no-pager
bash
sudo journalctl -u slapd -n 30 --no-pager

Trust the CA and Test StartTLS

Verify port 389 for StartTLS

OpenLDAP listeners normally use:

  • TCP 389 — LDAP and StartTLS
  • TCP 636 — LDAPS

Firewall policy controls which networks can reach those ports on RHEL-family hosts. firewalld firewall-cmd manages the ldap and ldaps services in the steps below—confirm the active zone and that ldap is allowed before StartTLS testing:

bash
sudo firewall-cmd --get-active-zones

Sample output:

output
public (default)
  interfaces: enp0s3 enp0s8
bash
sudo firewall-cmd --query-service=ldap

Sample output:

output
yes

When the query returns no, enable ldap on the active zone. For the default public zone:

bash
sudo firewall-cmd --permanent --add-service=ldap

When the interface uses a named zone explicitly:

bash
sudo firewall-cmd --zone=public --permanent --add-service=ldap
bash
sudo firewall-cmd --reload
bash
sudo firewall-cmd --query-service=ldap

Open the ldaps service in the LDAPS section below. The install lesson may already have opened port 389—confirm here rather than assuming the firewall is ready for StartTLS.

Verify with OpenSSL

bash
openssl s_client -starttls ldap -connect ldap-server.example.com:389 -servername ldap-server.example.com -CAfile /etc/openldap/certs/example-ldap-ca.crt -verify_return_error -brief </dev/null

Sample output:

output
CONNECTION ESTABLISHED
Protocol version: TLSv1.3
Ciphersuite: TLS_AES_256_GCM_SHA384
Peer certificate: CN=ldap-server.example.com, O=Example Organization
Verification: OK
DONE

The exact protocol and cipher can differ, but verification must succeed. RHEL-family crypto policy often rejects obsolete TLS versions and weak ciphers—a common cause of failures with legacy LDAP clients.

When the signing CA is not the root, confirm that slapd presents the full chain:

bash
openssl s_client -starttls ldap -connect ldap-server.example.com:389 -servername ldap-server.example.com -CAfile /etc/openldap/certs/example-ldap-ca.crt -verify_return_error -showcerts </dev/null

Configure OpenLDAP client trust on the LDAP server

Install the lab CA into the system trust store so local LDAP tools can validate StartTLS through the OS bundle:

bash
sudo install -m 0644 /etc/openldap/certs/example-ldap-ca.crt /etc/pki/ca-trust/source/anchors/example-ldap-ca.crt
bash
sudo update-ca-trust extract

Set /etc/openldap/ldap.conf on the LDAP server to:

text
URI ldap://ldap-server.example.com/
BASE dc=example,dc=com
TLS_CACERT /etc/pki/tls/certs/ca-bundle.crt
TLS_REQCERT demand
TLS_REQSAN demand

TLS_CACERT points at the rebuilt system bundle, not the copy under /etc/openldap/certs/. After update-ca-trust extract, the lab CA anchor is included in /etc/pki/tls/certs/ca-bundle.crt.

TLS_REQCERT demand and TLS_REQSAN demand tighten client-side validation:

  • TLS_REQCERT demand — validate the issuing CA and certificate chain
  • TLS_REQSAN demand — require the hostname or IP in the LDAP URI to match a SAN entry

Install the CA certificate on a RHEL-family client

Copy only the public CA certificate to each client. Never copy:

  • the CA private key
  • the LDAP server private key

On the client, install LDAP tools and the trust-store package:

bash
sudo dnf install -y openldap-clients ca-certificates

From the LDAP server, copy the public CA file with scp over SSH; the ssh command covers secure copy and remote login options:

bash
scp /etc/openldap/certs/example-ldap-ca.crt [email protected]:/tmp/

On the client:

bash
sudo install -m 0644 /tmp/example-ldap-ca.crt /etc/pki/ca-trust/source/anchors/example-ldap-ca.crt
bash
sudo update-ca-trust extract

Use the trust-anchor directory so update-ca-trust merges the lab CA into the system bundle. Mirror the server-side ldap.conf pattern: reference TLS_CACERT /etc/pki/tls/certs/ca-bundle.crt on each client rather than the temporary /tmp copy.

Test required StartTLS

bash
ldapsearch -x -H ldap://ldap-server.example.com -ZZ -b "" -s base -LLL namingContexts supportedLDAPVersion

Sample output:

output
dn:
namingContexts: dc=example,dc=com
supportedLDAPVersion: 3
bash
ldapsearch -x -H ldap://ldap-server.example.com -ZZ -D "cn=admin,dc=example,dc=com" -W -b "dc=example,dc=com" -LLL "(uid=jdoe)" dn uid

-ZZ upgrades the session to TLS before LDAP messages are sent—the same transport model enforced later by simple_bind=128 and by SSSD clients using StartTLS.


Enable and Test LDAPS on Port 636

The LDAPS section follows this sequence:

  • inspect systemctl cat slapd for ldaps:///
  • add a systemd override when the listener is missing
  • confirm port 636 with ss
  • open the ldaps firewalld service
  • run the handshake and ldapsearch tests

Inspect whether ldaps:/// is in the effective service command:

bash
sudo systemctl cat slapd

Sample output:

output
ExecStart=/usr/sbin/slapd -u ldap -h "ldap:/// ldaps:/// ldapi:///"

When the output includes ldaps:///, continue with the port check and firewall steps below. When ldaps:/// is missing, add a systemd override instead of editing /usr/lib/systemd/system/slapd.service directly. Preserve the complete existing ExecStart command and add ldaps:/// to the listener list:

bash
sudo systemctl edit slapd
text
[Service]
ExecStart=
ExecStart=/usr/sbin/slapd -u ldap -h "ldap:/// ldaps:/// ldapi:///"
WARNING
Do not copy this ExecStart line without comparing it with systemctl cat slapd. A systemd override replaces the complete command. Preserve every existing argument and change only the listener list.
bash
sudo systemctl daemon-reload
bash
sudo systemctl restart slapd
bash
sudo journalctl -u slapd -n 30 --no-pager

Confirm port 636 is listening with ss:

bash
sudo ss -ltnp | grep slapd

Sample output:

output
LISTEN 0      2048         0.0.0.0:389        0.0.0.0:*    users:(("slapd",pid=1336,fd=7))
LISTEN 0      2048         0.0.0.0:636        0.0.0.0:*    users:(("slapd",pid=1336,fd=9))
LISTEN 0      2048            [::]:389           [::]:*    users:(("slapd",pid=1336,fd=8))
LISTEN 0      2048            [::]:636           [::]:*    users:(("slapd",pid=1336,fd=10))

Open the ldaps service in firewalld:

bash
sudo firewall-cmd --permanent --add-service=ldaps
bash
sudo firewall-cmd --reload
bash
sudo firewall-cmd --query-service=ldaps

Sample output:

output
yes

Test the LDAPS handshake:

bash
openssl s_client -connect ldap-server.example.com:636 -servername ldap-server.example.com -CAfile /etc/openldap/certs/example-ldap-ca.crt -verify_return_error -brief </dev/null
bash
ldapsearch -x -H ldaps://ldap-server.example.com -b "" -s base -LLL namingContexts supportedLDAPVersion

Sample output:

output
dn:
namingContexts: dc=example,dc=com
supportedLDAPVersion: 3
Test Expected outcome
ldap://... without -ZZ Connection may still be unencrypted
ldap://... -ZZ Successful StartTLS
ldaps://... Successful implicit TLS
Connect using a hostname absent from SAN Certificate validation failure
Connect without trusting the CA Unknown or untrusted issuer failure

Reject Unprotected Simple Password Binds

Inspect existing security requirements

bash
sudo ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b cn=config -s base olcSecurity

When the query returns no olcSecurity value, use the add: LDIF below. When a value already exists:

  • preserve every existing factor
  • use a single replace: that adds simple_bind=128 to the current string

Empty configuration:

ldif
dn: cn=config
changetype: modify
add: olcSecurity
olcSecurity: simple_bind=128

Existing configuration example when the query returns olcSecurity: ssf=1 update_ssf=128:

ldif
dn: cn=config
changetype: modify
replace: olcSecurity
olcSecurity: ssf=1 update_ssf=128 simple_bind=128

OpenLDAP treats olcSecurity as a set of factors such as ssf, update_ssf, and simple_bind. Replacing the whole value without preserving existing factors can weaken another requirement.

Create the LDIF with a restrictive umask. Use vi when merging simple_bind=128 into an existing olcSecurity value by hand:

bash
umask 077
vi configure-security.ldif
bash
sudo ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f configure-security.ldif

simple_bind=128 requires an overall security strength factor of at least 128 for simple username/password authentication. On this lab server, that SSF is supplied by StartTLS or LDAPS.

Keep these limits in mind:

  • slapd rejects the Bind when the connection SSF is too low, but a misconfigured client may still transmit a password before the rejection
  • clients must require StartTLS (-ZZ) or use ldaps:// so credentials are encrypted before they leave the client
  • OpenLDAP explicitly recommends protecting simple authentication with TLS or another suitable security layer
  • the setting does not close port 389 or force every LDAP operation through TLS
  • anonymous Root DSE queries and other authentication mechanisms may still work over plain LDAP, depending on ACLs and other security settings

Verify the enforcement

Use only a deliberately fake password for the negative test below. Never type a real user or administrator password into an unencrypted ldap:// Bind test.

bash
ldapwhoami -x -H ldap://127.0.0.1 -D "uid=tls-negative-test,dc=example,dc=com" -w 'NOT-A-REAL-PASSWORD'

Sample output:

output
ldap_bind: Confidentiality required (13)
	additional info: confidentiality required

The successful tests below use -W because StartTLS and LDAPS negotiate encryption before the client sends the real password.

bash
ldapwhoami -x -H ldap://ldap-server.example.com -ZZ -D "cn=admin,dc=example,dc=com" -W
bash
ldapwhoami -x -H ldaps://ldap-server.example.com -D "cn=admin,dc=example,dc=com" -W

Renew or Replace the OpenLDAP Certificate

When the server certificate nears expiry:

  • generate a new key and CSR—or reuse the existing key per local policy
  • obtain the renewed certificate
  • validate it before deployment

For a directly root-signed certificate:

bash
openssl verify -purpose sslserver -verify_hostname ldap-server.example.com -CAfile example-ldap-ca.crt new-server.crt

For a certificate signed by an intermediate CA:

bash
openssl verify -purpose sslserver -verify_hostname ldap-server.example.com -CAfile root-ca.crt -untrusted intermediate-ca-chain.crt new-server.crt

When reusing the existing key

Compare public-key hashes to confirm the renewed certificate matches the deployed key:

bash
openssl x509 -in new-server.crt -pubkey -noout | openssl pkey -pubin -outform pem | sha256sum
bash
sudo openssl pkey -in /etc/openldap/certs/ldap-server.example.com.key -pubout -outform pem | sha256sum

When the hashes match, install only the renewed certificate:

bash
sudo install -o root -g ldap -m 0644 new-server.crt /etc/openldap/certs/ldap-server.example.com.crt.new
bash
sudo mv /etc/openldap/certs/ldap-server.example.com.crt.new /etc/openldap/certs/ldap-server.example.com.crt

When using a new key

Install both files to .new paths first:

bash
sudo install -o root -g ldap -m 0644 new-server.crt /etc/openldap/certs/ldap-server.example.com.crt.new
bash
sudo install -o root -g ldap -m 0640 new-server.key /etc/openldap/certs/ldap-server.example.com.key.new

Verify the .new certificate and key match before replacing the active files:

bash
openssl x509 -in /etc/openldap/certs/ldap-server.example.com.crt.new -pubkey -noout | openssl pkey -pubin -outform pem | sha256sum
bash
sudo openssl pkey -in /etc/openldap/certs/ldap-server.example.com.key.new -pubout -outform pem | sha256sum

When the hashes match, replace both active files:

bash
sudo mv /etc/openldap/certs/ldap-server.example.com.crt.new /etc/openldap/certs/ldap-server.example.com.crt
bash
sudo mv /etc/openldap/certs/ldap-server.example.com.key.new /etc/openldap/certs/ldap-server.example.com.key

Finish renewal on the server

After either renewal path:

  • confirm certificate metadata with openssl x509
  • restore SELinux labels with restorecon
  • restart slapd
  • repeat OpenSSL handshake tests on ports 389 and 636
  • repeat LDAP-level ldapsearch tests over StartTLS and LDAPS
bash
openssl x509 -in /etc/openldap/certs/ldap-server.example.com.crt -noout -subject -issuer -dates
bash
sudo restorecon -Rv /etc/openldap/certs
bash
sudo systemctl restart slapd
bash
openssl s_client -starttls ldap -connect ldap-server.example.com:389 -servername ldap-server.example.com -CAfile /etc/openldap/certs/example-ldap-ca.crt -verify_return_error -brief </dev/null
bash
openssl s_client -connect ldap-server.example.com:636 -servername ldap-server.example.com -CAfile /etc/openldap/certs/example-ldap-ca.crt -verify_return_error -brief </dev/null
bash
ldapsearch -x -ZZ -H ldap://ldap-server.example.com -b "" -s base -LLL namingContexts
bash
ldapsearch -x -H ldaps://ldap-server.example.com -b "" -s base -LLL namingContexts

Sample output from either ldapsearch:

output
dn:
namingContexts: dc=example,dc=com

These LDAP searches confirm both TLS negotiation and actual protocol operation—not only the OpenSSL handshake.

Back up the deployed server certificate and private key through your normal configuration-backup process. Store CA material separately and never distribute the CA private key to LDAP servers or clients:

  • deployed server certificate and private key — protected configuration backup
  • CA private key, CA certificate, serial state, and CA configuration — separate encrypted or offline CA backup

Troubleshoot OpenLDAP TLS

Error or symptom Likely cause Verification/fix
ldap_start_tls: Connect error (-11) Server TLS setup, trust, name resolution, or listener failure Run openssl s_client -starttls ldap and inspect journalctl -u slapd
TLS: peer cert untrusted or revoked Client does not trust the issuing CA Run update-ca-trust extract after installing the CA anchor
Hostname does not match certificate URI hostname absent from SAN Reissue the certificate with the correct DNS or IP SAN
ldap_modify: Other (80) when adding olcTLS* Wrong path, unreadable key, mismatched key, or invalid PEM Run sudo -u ldap test -r on the key and compare public-key hashes
TLS: could not use private key Key unreadable, encrypted, malformed, or mismatched Check permissions, PEM format, and matching sha256sum of public keys
StartTLS works but LDAPS fails ldaps:/// missing from the effective ExecStart, port 636 not listening, or ldaps blocked in firewalld Confirm ldaps:/// in systemctl cat slapd, check ss -ltnp, and run firewall-cmd --query-service=ldaps
LDAPS works but StartTLS fails Port 389 or StartTLS negotiation problem Test with openssl s_client -starttls ldap
Works with TLS_REQCERT never only CA trust or SAN is wrong Fix trust and naming—do not disable verification
Permission denied with SELinux Enforcing Wrong certificate label Check ls -lZ, run restorecon, review AVC logs
Legacy clients cannot negotiate TLS Client supports only obsolete protocols or ciphers Upgrade or isolate the client; system-wide crypto policy may reject weak TLS

Show the active system-wide crypto policy when TLS handshake failures mention unsupported protocols or ciphers:

bash
update-crypto-policies --show

DEFAULT is normal on current RHEL-family releases. A stricter policy such as FIPS or FUTURE can block older clients.

Sample output:

output
DEFAULT

After TLS is working on the server and clients, continue with Manage OpenLDAP users and groups with LDIF for POSIX account operations, then wire RHEL-family clients with Configure OpenLDAP Client with SSSD on RHEL-Based Linux.

References


Summary

The steps create a private CA and SAN-signed server certificate, install the files under /etc/openldap/certs/ with correct permissions and SELinux labels, configure TLS through cn=config, distribute CA trust to RHEL-family clients, verify StartTLS on port 389 and LDAPS on port 636, and set olcSecurity: simple_bind=128 so simple password binds require an SSF of at least 128.


Frequently Asked Questions

1. What is the difference between StartTLS and LDAPS?

StartTLS upgrades a plain ldap:// connection on port 389 to TLS after the TCP session opens. LDAPS uses ldaps:// on port 636 and negotiates TLS immediately. Both can use the same server certificate and CA when configured correctly.

2. Should I use StartTLS on port 389 or LDAPS on port 636?

Either works when TLS is enforced for password binds. StartTLS connects on port 389 and upgrades the LDAP session before credentials are sent. LDAPS negotiates TLS immediately after the TCP connection, before any LDAP messages are exchanged. Many Linux clients support both—pick one primary method and document it for your environment.

3. Why does ldapsearch work only with TLS_REQCERT never?

TLS_REQCERT never disables certificate verification. The usual causes are a missing or wrong TLS_CACERT, a hostname that does not match the certificate SAN, or connecting by IP when only a DNS name is in the SAN. Fix trust and naming instead of weakening verification.

4. Does enabling TLS automatically block unencrypted LDAP binds?

No. Configuring certificates only makes TLS available. Setting olcSecurity: simple_bind=128 causes slapd to reject simple password authentication that lacks an SSF of at least 128, but it cannot stop a misconfigured client from transmitting credentials before the rejection. Clients must require StartTLS or use LDAPS before sending passwords.

5. Can I connect using the server IP address?

Yes, but only when the certificate SAN contains that exact IP address and the client performs SAN verification. Otherwise, use the FQDN listed in the DNS SAN.

6. Do I need to restart slapd after renewing its certificate?

Yes. Replace the certificate files, restore ownership, permissions, and SELinux labels, then restart slapd and repeat StartTLS and LDAPS validation before returning the server to production use.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive …