ELK Stack — LDAP and SAML

4 minute read

LDAP and SAML

Enterprise environments rarely manage users directly in Elasticsearch. Instead, they integrate with external identity providers — LDAP (Lightweight Directory Access Protocol), Active Directory, or SAML (Security Assertion Markup Language) for single sign-on.

Elasticsearch supports these through a realm chain — an ordered list of authentication backends that Elasticsearch consults when a user attempts to authenticate.


The Realm Chain

When a user authenticates, Elasticsearch checks each realm in order until one successfully authenticates the user:

Request → native realm → file realm → LDAP realm → SAML realm → reject

Realm order matters — place the most commonly used realm first for performance.

Default realms (always active):

Realm Type Priority
Reserved reserved 0 (internal users like elastic)
Native native Built-in user store (via API)
File file Users defined in users file on disk

Native Realm

The native realm stores users in an internal Elasticsearch index. This is the default realm for users created via the API or Kibana.

# Create a user in the native realm
curl -k -u elastic:password -X POST "https://localhost:9200/_security/user/analyst_1" \
  -H "Content-Type: application/json" -d'
{
  "password": "SecurePassword!",
  "roles": ["viewer"],
  "full_name": "Analyst One"
}'

No additional configuration is needed — the native realm is enabled by default.


File Realm

The file realm stores credentials in flat files on each Elasticsearch node. It is useful for emergency access when the cluster is degraded.

Add users to /etc/elasticsearch/users:

/usr/share/elasticsearch/bin/elasticsearch-users useradd admin_backup \
  -p EmergencyPassword123! \
  -r superuser

File realm users are available immediately without a cluster restart. The files are:

  • /etc/elasticsearch/users — username and hashed password
  • /etc/elasticsearch/users_roles — role assignments

LDAP Realm Configuration

Basic LDAP Configuration

# /etc/elasticsearch/elasticsearch.yml

xpack.security.authc.realms.ldap.ldap1:
  order: 2
  url: "ldaps://ldap.example.com:636"
  bind.dn: "cn=elasticsearch,ou=services,dc=example,dc=com"
  user_search.base_dn: "ou=users,dc=example,dc=com"
  user_search.filter: "(uid={0})"
  group_search.base_dn: "ou=groups,dc=example,dc=com"
  ssl.certificate_authorities: ["/etc/elasticsearch/certs/ldap-ca.crt"]
  unmapped_groups_as_roles: false

Store the bind password in the Elasticsearch keystore:

/usr/share/elasticsearch/bin/elasticsearch-keystore add \
  xpack.security.authc.realms.ldap.ldap1.secure_bind_password

LDAP with User Templates (No Bind DN)

If your LDAP uses a predictable DN pattern, you can skip the bind search:

xpack.security.authc.realms.ldap.ldap1:
  order: 2
  url: "ldaps://ldap.example.com:636"
  user_dn_templates:
    - "uid={0},ou=users,dc=example,dc=com"
    - "uid={0},ou=contractors,dc=example,dc=com"
  group_search.base_dn: "ou=groups,dc=example,dc=com"
  ssl.certificate_authorities: ["/etc/elasticsearch/certs/ldap-ca.crt"]

LDAP with Multiple Servers (Failover)

xpack.security.authc.realms.ldap.ldap1:
  order: 2
  url:
    - "ldaps://ldap-primary.example.com:636"
    - "ldaps://ldap-secondary.example.com:636"
  load_balance.type: "failover"
  bind.dn: "cn=elasticsearch,ou=services,dc=example,dc=com"
  user_search.base_dn: "ou=users,dc=example,dc=com"
  user_search.filter: "(sAMAccountName={0})"
  group_search.base_dn: "ou=groups,dc=example,dc=com"
  ssl.certificate_authorities: ["/etc/elasticsearch/certs/ldap-ca.crt"]

Active Directory Realm

Active Directory has its own dedicated realm type with AD-specific defaults:

# /etc/elasticsearch/elasticsearch.yml

xpack.security.authc.realms.active_directory.ad1:
  order: 3
  url: "ldaps://ad.example.com:636"
  domain_name: "example.com"
  bind.dn: "CN=elasticsearch,OU=Services,DC=example,DC=com"
  ssl.certificate_authorities: ["/etc/elasticsearch/certs/ad-ca.crt"]

Store the bind password:

/usr/share/elasticsearch/bin/elasticsearch-keystore add \
  xpack.security.authc.realms.active_directory.ad1.secure_bind_password

Role Mapping for External Authentication

After configuring LDAP or AD, map external groups to Elasticsearch roles:

# Map LDAP admins group to superuser
curl -k -u elastic:password -X POST "https://localhost:9200/_security/role_mapping/ldap_admins" \
  -H "Content-Type: application/json" -d'
{
  "enabled": true,
  "roles": ["superuser"],
  "rules": {
    "all": [
      { "field": { "realm.name": "ldap1" } },
      { "field": { "groups": "cn=es-admins,ou=groups,dc=example,dc=com" } }
    ]
  }
}'

# Map developers to editor role
curl -k -u elastic:password -X POST "https://localhost:9200/_security/role_mapping/ldap_developers" \
  -H "Content-Type: application/json" -d'
{
  "enabled": true,
  "roles": ["editor"],
  "rules": {
    "all": [
      { "field": { "realm.name": "ldap1" } },
      { "field": { "groups": "cn=developers,ou=groups,dc=example,dc=com" } }
    ]
  }
}'

# Map analysts to a custom viewer role
curl -k -u elastic:password -X POST "https://localhost:9200/_security/role_mapping/ldap_analysts" \
  -H "Content-Type: application/json" -d'
{
  "enabled": true,
  "roles": ["log_viewer", "monitoring_user"],
  "rules": {
    "all": [
      { "field": { "realm.name": "ldap1" } },
      { "field": { "groups": "cn=analysts,ou=groups,dc=example,dc=com" } }
    ]
  }
}'

SAML Realm for SSO

SAML enables single sign-on through identity providers like Okta, Azure AD, or OneLogin.

SAML Configuration in Elasticsearch

# /etc/elasticsearch/elasticsearch.yml

xpack.security.authc.realms.saml.saml1:
  order: 4
  idp.metadata.path: "/etc/elasticsearch/saml/idp-metadata.xml"
  idp.entity_id: "https://idp.example.com/"
  sp.entity_id: "https://kibana.example.com/"
  sp.acs: "https://kibana.example.com/api/security/saml/callback"
  sp.logout: "https://kibana.example.com/logout"
  attributes.principal: "nameid"
  attributes.groups: "groups"
  attributes.name: "displayName"
  attributes.mail: "email"

Downloading IdP Metadata

Most identity providers expose metadata at a URL:

# Download Okta metadata
curl -o /etc/elasticsearch/saml/idp-metadata.xml \
  "https://your-org.okta.com/app/abc123/sso/saml/metadata"

# Download Azure AD metadata
curl -o /etc/elasticsearch/saml/idp-metadata.xml \
  "https://login.microsoftonline.com/<tenant-id>/federationmetadata/2007-06/federationmetadata.xml"

Configuring Kibana for SAML Login

# /etc/kibana/kibana.yml

xpack.security.authc.providers:
  saml.saml1:
    order: 0
    realm: "saml1"
    description: "Log in with SSO"
  basic.basic1:
    order: 1
    description: "Log in with username/password"

This configures Kibana to show both SAML and basic login options. The SAML option appears first.

SAML Role Mapping

curl -k -u elastic:password -X POST "https://localhost:9200/_security/role_mapping/saml_admins" \
  -H "Content-Type: application/json" -d'
{
  "enabled": true,
  "roles": ["kibana_admin", "monitoring_user"],
  "rules": {
    "all": [
      { "field": { "realm.name": "saml1" } },
      { "field": { "groups": "elk-admins" } }
    ]
  }
}'

OIDC Realm Overview

OpenID Connect (OIDC) is an alternative to SAML, commonly used with providers like Google, Auth0, and Keycloak.

# /etc/elasticsearch/elasticsearch.yml

xpack.security.authc.realms.oidc.oidc1:
  order: 5
  rp.client_id: "elasticsearch-client"
  rp.response_type: "code"
  rp.redirect_uri: "https://kibana.example.com/api/security/oidc/callback"
  op.issuer: "https://auth.example.com/"
  op.authorization_endpoint: "https://auth.example.com/authorize"
  op.token_endpoint: "https://auth.example.com/oauth/token"
  op.userinfo_endpoint: "https://auth.example.com/userinfo"
  op.jwkset_path: "https://auth.example.com/.well-known/jwks.json"
  claims.principal: "sub"
  claims.groups: "groups"

Store the client secret:

/usr/share/elasticsearch/bin/elasticsearch-keystore add \
  xpack.security.authc.realms.oidc.oidc1.rp.client_secret

Realm Ordering

The order in which realms are checked affects performance and behavior. Recommended ordering:

# /etc/elasticsearch/elasticsearch.yml

xpack.security.authc.realms:
  native.native1:
    order: 0
  file.file1:
    order: 1
  ldap.ldap1:
    order: 2
  active_directory.ad1:
    order: 3
  saml.saml1:
    order: 4
  oidc.oidc1:
    order: 5

Best Practice: Place native and file realms first — they are fast local lookups. External realms (LDAP, SAML) involve network calls and should come later.


Quick Reference

Task Configuration / API
LDAP realm xpack.security.authc.realms.ldap.<name> in elasticsearch.yml
Active Directory realm xpack.security.authc.realms.active_directory.<name>
SAML realm xpack.security.authc.realms.saml.<name>
OIDC realm xpack.security.authc.realms.oidc.<name>
Store bind password elasticsearch-keystore add xpack.security.authc.realms.ldap.<name>.secure_bind_password
Kibana SAML config xpack.security.authc.providers.saml.<name> in kibana.yml
Role mapping POST /_security/role_mapping/<name>
File realm users elasticsearch-users useradd <user> -p <pass> -r <role>
Download IdP metadata curl -o idp-metadata.xml <metadata-url>
Realm order order: N (lower = checked first)

Next: Security Exercises — hands-on practice with everything you’ve learned.