Keycloak Active Directory over LDAPS

Tested on Red Hat Enterprise Linux 10.2 (Coughlan)
Windows Server 2025 AD DC — WIN-8S0K67EP5EO.lab.example (192.168.56.128)
Package quay.io/keycloak/keycloak:26.7.3
Applies to Keycloak 26.x with Microsoft Active Directory user federation
Privilege Keycloak realm admin; AD bind account (keycloak-bind in the lab)
Lab environment Keycloak on http://127.0.0.1:8080/ with DC PEM in truststore-paths — Install Keycloak with Podman
Prerequisite — Integrate Keycloak with Active Directory (bind account and realm golinuxcloud)
Scope Switch an existing Active Directory federation provider from StartTLS to LDAPS on port 636: confirm LDAPS on the DC, export or trust the DC TLS certificate, Keycloak ldaps:// settings (StartTLS off), host-side ldapsearch, and Test connection. Does not cover Windows CA issuance, LDAP channel binding policy, or generic non-AD LDAPS → Configure LDAPS in Keycloak. PKIX and hostname errors → Fix Keycloak LDAPS certificate errors.
Related guides Integrate Keycloak with Active Directory
Configure LDAPS in Keycloak
Fix Keycloak LDAPS certificate errors
Keycloak AD users not syncing
Keycloak tutorial
Short on time?Beta Get the gist of this 10-minute read in seconds, then ask follow-up questions

Microsoft Active Directory can reject simple LDAP binds that are sent over an unencrypted connection. Keycloak can protect the LDAP session in two common ways:

  • StartTLS on port 389, where LDAP connects first and then upgrades to TLS.
  • LDAPS on port 636, where TLS is established before LDAP traffic is exchanged.

This guide uses LDAPS with the existing Active Directory federation provider lab-ad so the bind credentials and directory traffic are protected by TLS from the start of the connection. LDAP signing and channel binding are separate AD security mechanisms; this walkthrough focuses on protecting Keycloak's LDAP simple bind with TLS.

You already need a bind account, realm golinuxcloud, and DC certificate trust—the same foundation as Integrate Keycloak with Active Directory. The steps below focus on port 636, the ldaps:// connection URL, and checks you can run before you click Test connection.


Keycloak AD LDAPS: Quick Answer

  1. Confirm the domain controller accepts TLS on port 636.
  2. Export or trust the DC TLS certificate (lab: leaf PEM in truststore-paths).
  3. Set Connection URL to ldaps://<dc-fqdn>:636. Use the FQDN listed in the certificate's DNS Subject Alternative Name (SAN); older certificates may rely on the subject/CN when no suitable SAN is present.
  4. Turn StartTLS Off — LDAPS is already encrypted.
  5. Run Test connection and Test authentication on the Active Directory provider.
  6. Confirm the same bind and search with ldapsearch against ldaps:// on the Keycloak host.

For OpenLDAP or vendor-neutral LDAPS mechanics, see Configure LDAPS in Keycloak. When Keycloak logs PKIX path building failed or hostname errors while ldapsearch works, use Fix Keycloak LDAPS certificate errors.

What changes when you switch from StartTLS to LDAPS?

The LDAP search base, bind account, user mappings, and passwords do not change.

Only the transport settings change:

StartTLS LDAPS
ldap://dc.example.com:389 ldaps://dc.example.com:636
StartTLS = On StartTLS = Off
TLS begins after the LDAP connection starts TLS is established before LDAP traffic
Port 389 must be reachable Port 636 must be reachable

Both modes still require Keycloak to trust the certificate presented by the domain controller.


Why Keycloak Active Directory connections need TLS

Hardened Active Directory environments commonly require LDAP sessions to use appropriate integrity or TLS protection. For a simple bind from Keycloak, StartTLS or LDAPS provides the encrypted transport needed to avoid sending credentials over cleartext LDAP. A cleartext bind on port 389 without TLS can still return Strong(er) authentication required even when the bind DN and password are correct.

Keycloak supports two common patterns against AD:

Transport Port When to choose it
StartTLS 389 LDAP begins cleartext, then upgrades to TLS; Connection pooling must stay off in this provider configuration
LDAPS 636 TLS is established before any LDAP protocol data is exchanged; uses a dedicated TLS port (636/tcp), which can make firewall rules more explicit

Both require Keycloak to trust the certificate the DC presents during TLS. In this lab, the certificate presented by the LDAPS listener on port 636 is also valid for the LDAP TLS connection used by StartTLS. The exact certificate selected by a production domain controller depends on the certificates installed and eligible for LDAPS.

Active Directory StartTLS on port 389 versus LDAPS on port 636 for Keycloak

Microsoft documents LDAPS certificate requirements in Configure certificates for LDAP over SSL in Active Directory Domain Services.


Lab layout

This guide assumes the existing lab-ad provider already works with StartTLS from Integrate Keycloak with Active Directory. The goal is only to change the LDAP transport to LDAPS; user search settings, bind account, mappers, and synchronization behavior stay the same.

Before you start:

  • The AD bind account (keycloak-bind) already works against the domain.
  • The domain controller has a valid LDAPS server certificate and listens on TCP 636.
  • The Keycloak host can resolve and reach the DC on 636.
  • The Keycloak container can also resolve and reach the DC (same FQDN as Connection URL).
  • The certificate or CA PEM is mounted into the container and loaded through truststore-paths.
  • ldapsearch checks in this guide run on the RHEL host, not inside the Keycloak container.
  • The FQDN in Connection URL must match a DNS name in the certificate SAN (or subject/CN on older certs).
  • Users DN, bind DN, LDAP filters, and mappers are already correct; you are switching transport only.
Role Value
Keycloak http://127.0.0.1:8080, realm golinuxcloud
Domain controller win-8s0k67ep5eo.lab.example (192.168.56.128)
LDAP bind keycloak-bind@lab.example / CN=Keycloak Bind,CN=Users,DC=lab,DC=example
Federation provider lab-ad (Active Directory vendor)
Trust PEM on host ~/keycloak-ad-lab/trust/lab-dc.pem

truststore-paths tells Keycloak which CA or certificate files it should trust when opening TLS connections to systems such as Active Directory. Mount the PEM into the container and set truststore-paths in keycloak.conf, then restart Keycloak after you change trust material, as shown in Integrate Keycloak with Active Directory.

If port 636 does not complete a TLS handshake, configure LDAPS on the domain controller before you change Keycloak. If Podman cannot look up win-8s0k67ep5eo.lab.example from inside the container, add --add-host win-8s0k67ep5eo.lab.example:192.168.56.128 to podman run, or use DNS that publishes the domain controller name.


Retrieve the Active Directory LDAPS certificate

First confirm that the DC is listening with TLS on port 636:

bash
openssl s_client -connect win-8s0k67ep5eo.lab.example:636 -servername win-8s0k67ep5eo.lab.example </dev/null

A successful TLS handshake confirms that the domain controller is accepting TLS connections on port 636 and presenting a certificate. The ldapsearch test later in this guide confirms that the certificate is also trusted by the LDAP client. Press Ctrl+C after you see the certificate details if the session stays open.

Export the leaf certificate the lab trusts directly:

bash
mkdir -p ~/keycloak-ad-lab/trust
openssl s_client -connect win-8s0k67ep5eo.lab.example:636 -servername win-8s0k67ep5eo.lab.example </dev/null 2>/dev/null \
  | openssl x509 -outform PEM > ~/keycloak-ad-lab/trust/lab-dc.pem

This lab trusts the DC certificate directly because it is a small isolated environment. In a managed AD environment, prefer trusting the issuing CA or root chain so certificate renewal does not require replacing a pinned DC leaf certificate in Keycloak.

LDAPS encrypts the connection, but Keycloak still has to verify that it is talking to the expected domain controller. It does that by validating the certificate the DC presents against Keycloak's trusted certificates.

Check the certificate identity before using the hostname in Keycloak:

bash
openssl x509 -in ~/keycloak-ad-lab/trust/lab-dc.pem -noout -subject -ext subjectAltName

Sample output:

output
subject=CN=win-8s0k67ep5eo.lab.example
X509v3 Subject Alternative Name:
    DNS:win-8s0k67ep5eo.lab.example

The FQDN in Connection URL should match a DNS name in the certificate's SAN, or the certificate subject when applicable. Connecting by IP alone often breaks hostname verification even when the certificate is in the truststore.

Domain controller LDAPS certificate showing Subject and DNS SAN win-8s0k67ep5eo.lab.example


Verify LDAPS with ldapsearch before Keycloak

Run ldapsearch on the RHEL host where Keycloak runs, not inside the container. Point OpenLDAP clients at the same PEM:

bash
LDAPTLS_CACERT=~/keycloak-ad-lab/trust/lab-dc.pem ldapsearch -x \
  -H ldaps://win-8s0k67ep5eo.lab.example:636 \
  -D "keycloak-bind@lab.example" \
  -W \
  -b "DC=lab,DC=example" -s sub "(sAMAccountName=jane)" sAMAccountName

-W prompts for the bind password without placing it in the command line or an environment variable.

Sample output:

output
dn: CN=Jane Admin,CN=Users,DC=lab,DC=example
sAMAccountName: jane
# numEntries: 1

A successful host-side search confirms the bind credentials, host DNS, host network path to 636, and OpenLDAP client certificate trust. Keycloak can still fail if the container cannot resolve or reach the DC, or if Keycloak has not loaded the same trust material. If this command fails, fix TLS or networking on the host before you change Keycloak.

Do not pass -ZZ with ldaps:// — -ZZ is for StartTLS on ldap://. LDAPS already negotiates TLS on connect.


Configure Keycloak for Active Directory LDAPS

Open User federation → provider lab-ad in realm golinuxcloud. Update transport settings:

Setting LDAPS lab value
Connection URL ldaps://win-8s0k67ep5eo.lab.example:636
StartTLS Off
Connection pooling On — LDAPS can use pooled SSL connections; StartTLS required pooling off in this provider configuration
Bind DN, Users DN, Vendor Unchanged from the StartTLS setup

With ldaps://, TLS is already established when the connection opens, so enabling StartTLS would try to apply a second TLS-upgrade mechanism that belongs to plain ldap://.

Keycloak Active Directory provider using ldaps Connection URL with StartTLS disabled

Save the provider, then run Test connection and Test authentication. Both should succeed when truststore-paths includes the DC trust material, the container resolves the DC FQDN, and the connection URL hostname matches the certificate.

Keycloak lab-ad Test connection success after LDAPS configuration

Run Sync all users if you changed transport on an existing provider; user counts should match the prior StartTLS sync when Users DN and filters are unchanged. If sync returns zero users, return to Keycloak AD users not syncing — that is a search-scope problem, not LDAPS-specific.


StartTLS vs LDAPS for Keycloak Active Directory

You normally configure one TLS mode per Keycloak LDAP provider, not both at once.

In this lab, the same lab-dc.pem trust file works for both modes because both sessions use the DC's LDAP TLS certificate. Firewall rules differ: StartTLS needs 389/tcp reachable; LDAPS needs 636/tcp.


Troubleshoot Keycloak AD LDAPS

Symptom Likely cause What to check
PKIX path building failed in Keycloak DC cert or CA chain missing from truststore truststore-paths, container mount, Keycloak restart
ldapsearch works, Keycloak fails Wrong URL scheme, StartTLS left on, or container DNS ldaps:// with StartTLS off; --add-host or DNS; see Keycloak LDAPS certificate errors
FQDN works but IP fails, or hostname verification errors Connection URL does not match certificate SAN/CN Use the DC FQDN listed in the certificate SAN
Can't contact LDAP server on 636 Firewall or LDAPS disabled on DC openssl s_client -connect dc:636; open 636/tcp
UnknownHost in Keycloak logs Container cannot resolve DC FQDN --add-host on Podman, or corporate DNS from the Keycloak pod/network
Cleartext bind error on 389 Simple bind sent without TLS Use StartTLS or LDAPS — not cleartext ldap:// without TLS

Summary

LDAPS on port 636 establishes TLS before LDAP protocol data flows between Keycloak and Active Directory. Confirm LDAPS on the DC, trust the DC TLS certificate (or issuing CA in production), set Connection URL to ldaps://<dc-fqdn>:636, and disable StartTLS on the provider. A host-side ldapsearch against ldaps:// validates bind and trust from the Keycloak host; Test connection still depends on container DNS and Keycloak's truststore.

StartTLS on port 389 remains a valid AD pattern when you prefer that transport; choose one mode per provider and align firewall rules with the port you use. For generic LDAPS setup outside AD-specific defaults, continue with Configure LDAPS in Keycloak.


References


Frequently Asked Questions

Is LDAPS required for Keycloak and Active Directory?

Not always. Many labs use StartTLS on port 389 with the same DC certificate trust. Production AD environments commonly require LDAP clients to use secure session protection. For Keycloak simple binds, StartTLS on 389 or LDAPS on 636 provides TLS encryption and server authentication.

Why fetch the certificate from port 636 if I use StartTLS on 389?

In this lab, the LDAPS listener presents the same DC certificate used for LDAP TLS. Fetching it from port 636 is a convenient way to inspect or export the certificate before using either LDAPS or StartTLS.

Can I use ldaps:// with an IP address in Connection URL?

Avoid it when possible. AD certificates are issued for the DC hostname. Keycloak and the JVM verify the hostname in the URL against the certificate; use the FQDN that appears in the cert subject or SAN.

Should StartTLS stay enabled for LDAPS?

No. StartTLS upgrades a cleartext ldap:// session. LDAPS (ldaps://) is already encrypted. Enable only one model per provider.

Finished reading?Beta Get the key takeaways in seconds, then ask follow-up questions
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with 15+ years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)