This lesson configures TLS on a RHEL-family OpenLDAP server:
- a private CA and SAN-signed server certificate
cn=configTLS settings (olcTLS*)- StartTLS on port 389 and LDAPS on port 636
- CA trust on RHEL-family clients
olcSecurity: simple_bind=128so 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.
This lesson configures server TLS, client trust, and rejection of unprotected simple password binds. It does not cover:
olcAccessACL 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
ExecStartlistener URLs
RHEL and vendor support
- Red Hat deprecated
openldap-serversin RHEL 7.4 and does not ship it as a supported BaseOS or AppStream directory service in later releases. - On RHEL, installing
slapdfrom 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
slapdcame 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:
cat /etc/os-releaseSample 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:
rpm -q openldap openldap-clients openldap-serversSample 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_64The 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:
slapd -VV 2>&1 | headSample output:
@(#) $OpenLDAP: slapd 2.6.10 (Jan 28 2026 00:00:00) $
openldapCapture the effective listener URLs with systemctl before editing firewall rules or a systemd override:
sudo systemctl cat slapdSample 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-ZZso 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
olcSecuritywithsimple_bind=128so 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.comin this lab) - include the host-only IP address clients will use (
192.168.56.108here—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:
192.168.56.108 ldap-server.example.com ldap-server
192.168.56.109 ldap-client.example.com ldap-clientConfirm hostname resolution, slapd status, and SELinux mode:
hostname -fSample output:
ldap-server.example.comgetent hosts ldap-server.example.comSample output:
192.168.56.108 ldap-server.example.com ldap-serversystemctl is-active slapdSample output:
activegetenforceSample output:
EnforcingCreate 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:
sudo dnf install -y opensslOpen a root shell for CA work
sudo -iumask 077A restrictive umask keeps newly generated private keys and config files readable only to root.
install -d -m 700 /root/openldap-cacd /root/openldap-caCreate the lab CA
Write the CA OpenSSL configuration as ca.cnf:
[ 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,issueropenssl genrsa -out example-ldap-ca.key 4096openssl req -x509 -new -key example-ldap-ca.key -sha256 -days 3650 -out example-ldap-ca.crt -config ca.cnfchmod 600 example-ldap-ca.keyGenerate the OpenLDAP server key and CSR
Create ldap-server.cnf with DNS and IP SAN entries:
[ 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.108openssl genrsa -out ldap-server.example.com.key 2048openssl req -new -key ldap-server.example.com.key -out ldap-server.example.com.csr -config ldap-server.cnfOpenLDAP expects a readable, unencrypted PEM private key at service start—filesystem permissions protect the service key.
Sign the server certificate
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.cnfSample output:
Certificate request self-signature ok
subject=CN=ldap-server.example.com, O=Example OrganizationValidate the certificate before installation
openssl x509 -in ldap-server.example.com.crt -noout -subject -issuer -datesopenssl x509 -in ldap-server.example.com.crt -noout -ext subjectAltNameopenssl verify -purpose sslserver -verify_hostname ldap-server.example.com -CAfile example-ldap-ca.crt ldap-server.example.com.crtSample output:
ldap-server.example.com.crt: OKopenssl verify -purpose sslserver -verify_ip 192.168.56.108 -CAfile example-ldap-ca.crt ldap-server.example.com.crtSample output:
ldap-server.example.com.crt: OKCompare 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:
openssl x509 -in ldap-server.example.com.crt -pubkey -noout | openssl pkey -pubin -outform pem | sha256sumopenssl pkey -in ldap-server.example.com.key -pubout -outform pem | sha256sumBoth 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:
cat ldap-server.example.com.crt issuing-intermediate-ca.crt > ldap-server.example.com-fullchain.crtFor the CA trust bundle, certificate order is not significant. Include the trusted intermediate and root CA certificates:
cat issuing-intermediate-ca.crt root-ca.crt > example-ldap-ca-chain.crtThen configure both attributes:
replace: olcTLSCertificateFile
olcTLSCertificateFile: /etc/openldap/certs/ldap-server.example.com-fullchain.crt
-
replace: olcTLSCACertificateFile
olcTLSCACertificateFile: /etc/openldap/certs/example-ldap-ca-chain.crtolcTLSCertificateFile 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) — whatslapdtrusts, particularly when verifying client certificates during mutual TLS
For a directly root-signed lab certificate, the single-file setup above is enough.
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/
/etc/openldap/certs/
├── example-ldap-ca.crt
├── ldap-server.example.com.crt
└── ldap-server.example.com.keycp example-ldap-ca.crt /etc/openldap/certs/cp ldap-server.example.com.crt /etc/openldap/certs/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 |
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.keychmod 644 /etc/openldap/certs/example-ldap-ca.crt /etc/openldap/certs/ldap-server.example.com.crtchmod 640 /etc/openldap/certs/ldap-server.example.com.keymatchpathcon /etc/openldap/certs/ldap-server.example.com.keySample output:
/etc/openldap/certs/ldap-server.example.com.key system_u:object_r:slapd_cert_t:s0restorecon -Rv /etc/openldap/certsls -lZ /etc/openldap/certsThe slapd_cert_t label is what the confined slapd service expects on certificate files.
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:
exitConfigure OpenLDAP TLS Through cn=config
Inspect current TLS settings
Check existing olcTLS* values before applying changes:
sudo ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b cn=config -s base olcTLSCACertificateFile olcTLSCertificateFile olcTLSCertificateKeyFile olcTLSProtocolMin olcTLSVerifyClientCreate the TLS LDIF
Create configure-tls.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.3olcTLSCertificateFile and olcTLSCACertificateFile serve different roles:
olcTLSCertificateFile— server leaf certificate; when an intermediate CA issued the certificate, include the leaf followed by intermediate certificates thatslapdshould present to clientsolcTLSCertificateKeyFile— private key matching the server certificateolcTLSCACertificateFile— CA certificates trusted byslapd, 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.3means TLS 1.2 (3.(x+1)represents TLS 1.x) - the directive is ignored when
slapdis linked against GnuTLS
The slapd-config manual documents this mapping.
To see which TLS library your build uses:
ldd /usr/sbin/slapd | grep -E 'libssl|libgnutls'Sample 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.
sudo ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f configure-tls.ldifcn=config changes through LDAPI normally take effect without a restart. Restart slapd here so the running process reloads the certificate files from disk.
sudo ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b cn=config -s base olcTLSCACertificateFile olcTLSCertificateFile olcTLSCertificateKeyFile olcTLSProtocolMinRestart 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:
sudo systemctl restart slapdsudo systemctl status slapd --no-pagersudo journalctl -u slapd -n 30 --no-pagerTrust 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:
sudo firewall-cmd --get-active-zonesSample output:
public (default)
interfaces: enp0s3 enp0s8sudo firewall-cmd --query-service=ldapSample output:
yesWhen the query returns no, enable ldap on the active zone. For the default public zone:
sudo firewall-cmd --permanent --add-service=ldapWhen the interface uses a named zone explicitly:
sudo firewall-cmd --zone=public --permanent --add-service=ldapsudo firewall-cmd --reloadsudo firewall-cmd --query-service=ldapOpen 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
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/nullSample 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
DONEThe 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:
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/nullConfigure 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:
sudo install -m 0644 /etc/openldap/certs/example-ldap-ca.crt /etc/pki/ca-trust/source/anchors/example-ldap-ca.crtsudo update-ca-trust extractSet /etc/openldap/ldap.conf on the LDAP server to:
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 demandTLS_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 chainTLS_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:
sudo dnf install -y openldap-clients ca-certificatesFrom the LDAP server, copy the public CA file with scp over SSH; the ssh command covers secure copy and remote login options:
scp /etc/openldap/certs/example-ldap-ca.crt [email protected]:/tmp/On the client:
sudo install -m 0644 /tmp/example-ldap-ca.crt /etc/pki/ca-trust/source/anchors/example-ldap-ca.crtsudo update-ca-trust extractUse 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
ldapsearch -x -H ldap://ldap-server.example.com -ZZ -b "" -s base -LLL namingContexts supportedLDAPVersionSample output:
dn:
namingContexts: dc=example,dc=com
supportedLDAPVersion: 3ldapsearch -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 slapdforldaps:/// - add a systemd override when the listener is missing
- confirm port 636 with
ss - open the
ldapsfirewalld service - run the handshake and
ldapsearchtests
Inspect whether ldaps:/// is in the effective service command:
sudo systemctl cat slapdSample 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:
sudo systemctl edit slapd[Service]
ExecStart=
ExecStart=/usr/sbin/slapd -u ldap -h "ldap:/// ldaps:/// ldapi:///"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.
sudo systemctl daemon-reloadsudo systemctl restart slapdsudo journalctl -u slapd -n 30 --no-pagerConfirm port 636 is listening with ss:
sudo ss -ltnp | grep slapdSample 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:
sudo firewall-cmd --permanent --add-service=ldapssudo firewall-cmd --reloadsudo firewall-cmd --query-service=ldapsSample output:
yesTest the LDAPS handshake:
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/nullldapsearch -x -H ldaps://ldap-server.example.com -b "" -s base -LLL namingContexts supportedLDAPVersionSample 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
sudo ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b cn=config -s base olcSecurityWhen 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 addssimple_bind=128to the current string
Empty configuration:
dn: cn=config
changetype: modify
add: olcSecurity
olcSecurity: simple_bind=128Existing configuration example when the query returns olcSecurity: ssf=1 update_ssf=128:
dn: cn=config
changetype: modify
replace: olcSecurity
olcSecurity: ssf=1 update_ssf=128 simple_bind=128OpenLDAP 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:
umask 077
vi configure-security.ldifsudo ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f configure-security.ldifsimple_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:
slapdrejects 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 useldaps://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.
ldapwhoami -x -H ldap://127.0.0.1 -D "uid=tls-negative-test,dc=example,dc=com" -w 'NOT-A-REAL-PASSWORD'Sample output:
ldap_bind: Confidentiality required (13)
additional info: confidentiality requiredThe successful tests below use -W because StartTLS and LDAPS negotiate encryption before the client sends the real password.
ldapwhoami -x -H ldap://ldap-server.example.com -ZZ -D "cn=admin,dc=example,dc=com" -Wldapwhoami -x -H ldaps://ldap-server.example.com -D "cn=admin,dc=example,dc=com" -WRenew 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:
openssl verify -purpose sslserver -verify_hostname ldap-server.example.com -CAfile example-ldap-ca.crt new-server.crtFor a certificate signed by an intermediate CA:
openssl verify -purpose sslserver -verify_hostname ldap-server.example.com -CAfile root-ca.crt -untrusted intermediate-ca-chain.crt new-server.crtWhen reusing the existing key
Compare public-key hashes to confirm the renewed certificate matches the deployed key:
openssl x509 -in new-server.crt -pubkey -noout | openssl pkey -pubin -outform pem | sha256sumsudo openssl pkey -in /etc/openldap/certs/ldap-server.example.com.key -pubout -outform pem | sha256sumWhen the hashes match, install only the renewed certificate:
sudo install -o root -g ldap -m 0644 new-server.crt /etc/openldap/certs/ldap-server.example.com.crt.newsudo mv /etc/openldap/certs/ldap-server.example.com.crt.new /etc/openldap/certs/ldap-server.example.com.crtWhen using a new key
Install both files to .new paths first:
sudo install -o root -g ldap -m 0644 new-server.crt /etc/openldap/certs/ldap-server.example.com.crt.newsudo install -o root -g ldap -m 0640 new-server.key /etc/openldap/certs/ldap-server.example.com.key.newVerify the .new certificate and key match before replacing the active files:
openssl x509 -in /etc/openldap/certs/ldap-server.example.com.crt.new -pubkey -noout | openssl pkey -pubin -outform pem | sha256sumsudo openssl pkey -in /etc/openldap/certs/ldap-server.example.com.key.new -pubout -outform pem | sha256sumWhen the hashes match, replace both active files:
sudo mv /etc/openldap/certs/ldap-server.example.com.crt.new /etc/openldap/certs/ldap-server.example.com.crtsudo mv /etc/openldap/certs/ldap-server.example.com.key.new /etc/openldap/certs/ldap-server.example.com.keyFinish 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
ldapsearchtests over StartTLS and LDAPS
openssl x509 -in /etc/openldap/certs/ldap-server.example.com.crt -noout -subject -issuer -datessudo restorecon -Rv /etc/openldap/certssudo systemctl restart slapdopenssl 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/nullopenssl 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/nullldapsearch -x -ZZ -H ldap://ldap-server.example.com -b "" -s base -LLL namingContextsldapsearch -x -H ldaps://ldap-server.example.com -b "" -s base -LLL namingContextsSample output from either ldapsearch:
dn:
namingContexts: dc=example,dc=comThese 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:
update-crypto-policies --showDEFAULT is normal on current RHEL-family releases. A stricter policy such as FIPS or FUTURE can block older clients.
Sample output:
DEFAULTAfter 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
- OpenLDAP 2.6 Administrator's Guide — official configuration reference
- Configuring slapd TLS —
olcTLS*attributes and client verification - OpenLDAP security considerations — transport security, SSF, and simple-bind requirements
- ldap.conf(5) — client TLS settings
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.

