Elasticsearch supports two realm types for directory-based authentication: ldap and active_directory. Both connect to LDAP-compatible servers but differ in defaults and behavior. Most authentication failures trace back to connection issues, incorrect bind credentials, misconfigured search bases, or TLS certificate problems.
LDAP vs Active Directory Realm Types
The ldap realm works with any LDAP v3 compatible directory (OpenLDAP, 389 Directory Server, etc.) and supports two authentication modes: user DN templates and user search. The active_directory realm targets Microsoft AD specifically. It only supports user search mode, and uses AD-specific defaults like querying the tokenGroups attribute for group membership.
Choose active_directory when connecting to Microsoft AD. It handles UPN (user@domain) authentication, domain controller discovery via DNS SRV records (when domain_name is set), and AD-specific group resolution out of the box. Use ldap for everything else.
# Active Directory realm
xpack.security.authc.realms.active_directory.ad1:
order: 1
domain_name: corp.example.com
url: "ldaps://dc1.corp.example.com:636"
# LDAP realm
xpack.security.authc.realms.ldap.ldap1:
order: 2
url: "ldaps://ldap.example.com:636"
user_search.base_dn: "ou=users,dc=example,dc=com"
bind_dn: "cn=elasticsearch,ou=services,dc=example,dc=com"
group_search.base_dn: "ou=groups,dc=example,dc=com"
The active_directory realm requires domain_name. If the realm cannot resolve the domain via DNS, or the domain controllers are unreachable, authentication fails immediately. You can override automatic discovery by explicitly setting the url parameter.
Connection Errors and TLS Handshake Failures
The most common initial error is Elasticsearch being unable to reach the LDAP server. The url setting must specify the protocol (ldap:// or ldaps://), hostname, and port. Standard LDAP uses port 389, LDAPS uses 636. If the server is unreachable, connection timeout errors appear in the log. Verify connectivity from the Elasticsearch node using ldapsearch before investigating configuration.
TLS handshake failures occur when Elasticsearch cannot verify the LDAP server's certificate. By default, it performs full certificate verification (hostname check and CA chain validation). You must provide the CA certificate that signed the LDAP server's certificate:
xpack.security.authc.realms.ldap.ldap1:
ssl.certificate_authorities: ["/etc/elasticsearch/certs/ldap-ca.pem"]
ssl.verification_mode: full
The ssl.verification_mode accepts full (default - verifies chain and hostname), certificate (verifies chain, skips hostname), and none (disables verification, not recommended). If you see SSLPeerUnverifiedException or "unable to find valid certification path," the CA certificate is missing, wrong, or incomplete. Hostname mismatch errors mean the certificate's SAN does not include the hostname in the url setting - fix the certificate or set ssl.verification_mode: certificate.
Bind DN and User Search Configuration
The ldap realm supports two modes: DN templates and user search. In template mode, Elasticsearch constructs the user's DN from a template and attempts a direct bind:
user_dn_templates:
- "uid={0},ou=users,dc=example,dc=com"
- "uid={0},ou=admins,dc=example,dc=com"
The {0} placeholder is replaced with the username. Elasticsearch tries each template in order until a bind succeeds. No bind_dn is needed since the authenticating user's own credentials are used. This mode is simple but brittle - it fails if user DNs do not follow a predictable pattern.
User search mode is more flexible. It requires a bind_dn (a service account that can search the directory) and the service account password stored in the Elasticsearch keystore:
bin/elasticsearch-keystore add \
xpack.security.authc.realms.ldap.ldap1.secure_bind_password
If the bind DN or password is wrong, every authentication attempt fails with resultCode=49 (invalid credentials) in the log. This error refers to the service account bind, not the end user. Double-check the bind DN format - some LDAP servers expect a full DN like cn=elasticsearch,ou=services,dc=example,dc=com, while AD often accepts user@domain.com or DOMAIN\user.
The user_search.base_dn limits the search scope. If users exist in multiple OUs, set the base DN to a common ancestor. The user_search.filter defaults to (uid={0}) for LDAP realms and (sAMAccountName={0}) for Active Directory. Override it if your directory uses a different attribute for login names: user_search.filter: "(mail={0})".
Group Search and Role Mapping Issues
Group resolution determines which Elasticsearch roles a user receives. The ldap realm uses group_search.base_dn and group_search.filter to find groups. The default filter matches member attributes against the user's DN. If your directory uses uniqueMember or another attribute, override it:
group_search.base_dn: "ou=groups,dc=example,dc=com"
group_search.filter: "(uniqueMember={0})"
group_search.scope: sub
The {0} placeholder is replaced with the user's DN, not the username - confusing these is a common mistake. If group search returns empty results, verify the filter with ldapsearch first. Check group_search.scope too - default is sub (subtree search). Set it to one_level if groups exist only directly under the base DN.
The active_directory realm resolves groups differently. It reads the tokenGroups attribute from the user's AD entry, which is a binary attribute containing SIDs of all groups (including nested groups). This bypasses the need for recursive group search. If group resolution fails with the AD realm, verify that the bind user has permission to read the tokenGroups attribute.
After groups are resolved, you need role mappings to connect LDAP groups to Elasticsearch roles. Create these via the role mapping API or in role_mapping.yml. If users authenticate but have no permissions, the issue is almost always missing or incorrect role mappings, not the LDAP configuration itself.
Connection Pooling and Debugging
When using user search mode with a bind DN, Elasticsearch maintains a connection pool to the LDAP server. The user_search.pool.enabled setting (default true with a bind DN) controls this. Key pool settings: user_search.pool.size (default 20), user_search.pool.initial_size (default 0), and user_search.pool.health_check.enabled (default true). Pool exhaustion causes authentication timeouts under load - increase user_search.pool.size if you see intermittent failures during peak traffic.
The load_balance.type setting controls request distribution across multiple LDAP servers: failover (default), dns_failover, round_robin, or dns_round_robin.
For debugging, enable TRACE logging on the LDAP authentication package:
PUT /_cluster/settings
{
"transient": {
"logger.org.elasticsearch.xpack.security.authc.ldap": "TRACE"
}
}
This logs every LDAP operation: bind attempts, search requests and their results, group lookups, and connection pool activity. Look for the resultCode in error messages - 49 means invalid credentials, 32 means the base DN does not exist, 10 means a referral was returned (the search base may be in a different partition), and 4 means the size limit was exceeded. Disable TRACE logging after debugging since it logs user DNs and search parameters on every authentication request.