Skip to content

Use Elasticsearch API keys and least-privilege roles

Severity: mediumApplies to: Elasticsearch 8.x / 9.xApplies to: Elasticsearch 7.x
The fix
Terminal window
# A role scoped to one index pattern and specific actions
curl -sk -u elastic -X PUT https://localhost:9200/_security/role/app_search -H 'Content-Type: application/json' -d '{
"indices": [{
"names": ["app-*"],
"privileges": ["read", "view_index_metadata"]
}]
}'
# An API key that grants only that role
curl -sk -u elastic -X POST https://localhost:9200/_security/api_key -H 'Content-Type: application/json' -d '{
"name": "app-search-key",
"role_descriptors": { "app_search": { "indices": [{ "names": ["app-*"], "privileges": ["read"] }] } }
}'

Every credential is a blast radius. An application authenticating as elastic can read every index, delete every index, change cluster settings and create users — so a SQL-injection equivalent, a leaked config file, or a compromised app container hands all of that to an attacker.

Scope it down and the same leak is bounded: a key limited to read on app-* reads some documents and can do nothing else. No deletes, no other indices, no cluster administration.

Elasticsearch’s RBAC makes this cheap, and there’s no reason an application should ever hold more than the specific privileges it uses.

A role descriptor narrows on three axes, and using all three is the point:

Axis Example Effect
Indices "names": ["app-*"] Cannot touch other indices, including system ones
Privileges ["read", "view_index_metadata"] No write, no delete_index, no manage
Documents / fields query and field_security Row- and column-level limits within an index

The index scope is the one people skip. "names": ["*"] in a role is elastic-lite — it can read everything even if the privileges are narrow. Document-level security (a query in the role) and field-level security (a field_security block) go further, letting one key see only certain documents or certain fields — useful when a reporting tool needs orders but not the PII columns in them.

API keys beat user passwords for applications

Section titled “API keys beat user passwords for applications”

For non-human callers, prefer an API key over a username and password:

  • Keys are independently revocable. Kill one application’s key without touching any other credential or restarting anything.
  • Keys can be scoped tighter than the user that created them — the role_descriptors in the key intersect with the creator’s privileges, so a key is never more powerful than intended even if created by a broad account.
  • Keys can expire. Set "expiration": "90d" and the credential rotates itself out.
  • A leaked key is one key. A leaked user password may be reused across services; a key is minted per consumer.

The one caveat: an API key is a bearer token. Anyone holding it is that key, so it still needs to be stored like a secret and sent only over TLS — which, on a secured cluster, it is.