ELK Stack — API Keys

3 minute read

API Keys

API keys provide a lightweight, token-based authentication mechanism for programmatic access to Elasticsearch. Unlike user credentials, API keys can be scoped to specific privileges, given an expiration time, and revoked independently — making them ideal for CI/CD pipelines, microservices, and automated ingestion agents.


Creating an API Key

curl -k -u elastic:password -X POST "https://localhost:9200/_security/api_key" \
  -H "Content-Type: application/json" -d'
{
  "name": "ingestion-pipeline-key"
}'

Response:

{
  "id": "VuaCfGcBCdbkQm-e5aOx",
  "name": "ingestion-pipeline-key",
  "api_key": "ui2lp2axTNmsyakw9tvNnw",
  "encoded": "VnVhQ2ZHY0JDZGJrUW0tZTVhT3g6dWkybHAyYXhUTm1zeWFrdzl0dk5udw=="
}

The encoded value is a Base64 encoding of id:api_key — use it directly in the Authorization header.


Using an API Key

With the Authorization header (preferred):

curl -k -H "Authorization: ApiKey VnVhQ2ZHY0JDZGJrUW0tZTVhT3g6dWkybHAyYXhUTm1zeWFrdzl0dk5udw==" \
  https://localhost:9200/_cluster/health

With the id and api_key encoded manually:

# Encode id:api_key in Base64
echo -n "VuaCfGcBCdbkQm-e5aOx:ui2lp2axTNmsyakw9tvNnw" | base64

API Key with Role Descriptors

You can scope an API key to specific privileges that are a subset of the creating user’s permissions:

curl -k -u elastic:password -X POST "https://localhost:9200/_security/api_key" \
  -H "Content-Type: application/json" -d'
{
  "name": "log-reader-key",
  "role_descriptors": {
    "log_reader": {
      "cluster": ["monitor"],
      "indices": [
        {
          "names": ["logs-*", "filebeat-*"],
          "privileges": ["read", "view_index_metadata"]
        }
      ]
    }
  }
}'

This key can only read from logs-* and filebeat-* indices — even though it was created by the elastic superuser.

API Key with Multiple Roles

curl -k -u elastic:password -X POST "https://localhost:9200/_security/api_key" \
  -H "Content-Type: application/json" -d'
{
  "name": "dashboard-service-key",
  "role_descriptors": {
    "read_logs": {
      "indices": [
        {
          "names": ["logs-*"],
          "privileges": ["read"]
        }
      ]
    },
    "read_metrics": {
      "indices": [
        {
          "names": ["metrics-*"],
          "privileges": ["read"]
        }
      ]
    }
  }
}'

API Key Expiration

Set an expiration to automatically invalidate keys:

curl -k -u elastic:password -X POST "https://localhost:9200/_security/api_key" \
  -H "Content-Type: application/json" -d'
{
  "name": "temp-migration-key",
  "expiration": "7d",
  "role_descriptors": {
    "migrator": {
      "indices": [
        {
          "names": ["old-index-*", "new-index-*"],
          "privileges": ["read", "write", "create_index"]
        }
      ]
    }
  }
}'

Supported time units: d (days), h (hours), m (minutes), s (seconds).


Retrieving API Key Information

Get a specific key by ID:

curl -k -u elastic:password "https://localhost:9200/_security/api_key?id=VuaCfGcBCdbkQm-e5aOx"

Get all keys for a user:

curl -k -u elastic:password "https://localhost:9200/_security/api_key?username=elastic"

Get all keys in the cluster:

curl -k -u elastic:password "https://localhost:9200/_security/api_key"

Response:

{
  "api_keys": [
    {
      "id": "VuaCfGcBCdbkQm-e5aOx",
      "name": "ingestion-pipeline-key",
      "creation": 1743500000000,
      "expiration": null,
      "invalidated": false,
      "username": "elastic",
      "realm": "reserved",
      "metadata": {}
    }
  ]
}

Invalidating API Keys

Invalidate by ID:

curl -k -u elastic:password -X DELETE "https://localhost:9200/_security/api_key" \
  -H "Content-Type: application/json" -d'
{
  "ids": ["VuaCfGcBCdbkQm-e5aOx"]
}'

Invalidate by name:

curl -k -u elastic:password -X DELETE "https://localhost:9200/_security/api_key" \
  -H "Content-Type: application/json" -d'
{
  "name": "ingestion-pipeline-key"
}'

Invalidate all keys for a user:

curl -k -u elastic:password -X DELETE "https://localhost:9200/_security/api_key" \
  -H "Content-Type: application/json" -d'
{
  "username": "elastic"
}'

Cross-Cluster API Keys

For cross-cluster search and replication, create API keys that grant access from a remote cluster:

curl -k -u elastic:password -X POST "https://localhost:9200/_security/cross_cluster/api_key" \
  -H "Content-Type: application/json" -d'
{
  "name": "remote-cluster-key",
  "access": {
    "search": [
      {
        "names": ["logs-*", "metrics-*"]
      }
    ],
    "replication": [
      {
        "names": ["critical-data-*"]
      }
    ]
  }
}'

Using API Keys in Beats

Replace username/password with an API key in Filebeat:

# /etc/filebeat/filebeat.yml

output.elasticsearch:
  hosts: ["https://es-node-1:9200"]
  api_key: "VuaCfGcBCdbkQm-e5aOx:ui2lp2axTNmsyakw9tvNnw"
  ssl:
    enabled: true
    certificate_authorities: ["/etc/filebeat/certs/ca.crt"]

The api_key format is id:api_key (not Base64 encoded — Beats handles the encoding).


Using API Keys in Logstash

# /etc/logstash/conf.d/output.conf

output {
  elasticsearch {
    hosts => ["https://es-node-1:9200"]
    api_key => "VuaCfGcBCdbkQm-e5aOx:ui2lp2axTNmsyakw9tvNnw"
    ssl_enabled => true
    ssl_certificate_authorities => ["/etc/logstash/certs/ca.crt"]
  }
}

Service Tokens for Kibana and Fleet

Service tokens are special tokens designed for Kibana and Fleet Server. They are stored in the Elasticsearch keystore and do not expire.

Create a service token for Kibana:

/usr/share/elasticsearch/bin/elasticsearch-service-tokens create \
  elastic/kibana kibana-token-1

Output:

SERVICE_TOKEN elastic/kibana/kibana-token-1 = AAEAAWVsYXN0aWMva2liYW5hL2tpYmFuYS10b2tlbi0xOk...

Configure Kibana to use the service token:

# /etc/kibana/kibana.yml

elasticsearch.serviceAccountToken: "AAEAAWVsYXN0aWMva2liYW5hL2tpYmFuYS10b2tlbi0xOk..."

Create a service token for Fleet:

/usr/share/elasticsearch/bin/elasticsearch-service-tokens create \
  elastic/fleet-server fleet-token-1

API Key Management in Kibana

Kibana provides a UI for managing API keys:

  1. Navigate to Stack Management → Security → API Keys
  2. View all active keys with owner, creation date, and expiration
  3. Create new keys with the Create API key button
  4. Invalidate keys by selecting them and clicking Invalidate

Tip: Kibana only shows keys the current user is authorized to manage. Users with the manage_api_key privilege can see all keys; others see only their own.


Quick Reference

Task API / Command
Create API key POST /_security/api_key
Use API key Authorization: ApiKey <encoded> header
Scope API key "role_descriptors": { ... } in create request
Set expiration "expiration": "7d" in create request
Get key info GET /_security/api_key?id=<id>
Invalidate key DELETE /_security/api_key with id/name/username
Cross-cluster key POST /_security/cross_cluster/api_key
Beats API key api_key: "id:api_key" in output.elasticsearch
Logstash API key api_key => "id:api_key" in elasticsearch output
Kibana service token elasticsearch-service-tokens create elastic/kibana <name>

Next: LDAP and SAML — integrate external identity providers.