Search
Close this search box.

Token Based Authentication Using API Keys to access Elasticsearch

Table of Contents

1. Introduction

There are 3 Token-based authentication services: service-accounts, token-service and api-key-service. This time I want to show you how to use the last one API Key.

2. Start Elasticsearch

Start Elasticsearch cluster with basic license and setup password to continue next steps.

				
					docker run --rm \
--name elk \
-d \
-p 9200:9200 \
docker.elastic.co/elasticsearch/elasticsearch:8.10.4

# set exaple password 123456 :)

docker exec -it elk bash -c "(mkfifo pipe1); ( (elasticsearch-reset-password -u elastic -i < pipe1) & ( echo $'y\n123456\n123456' > pipe1) );sleep 5;rm pipe1"

				
			

3. Create API Key

The simplest form of API Key is call with name specified. This will create time unlimited API Key for you.

				
					# time unlimited API Key
curl -k -u elastic:123456 -XPUT "https://localhost:9200/_security/api_key" -H 'content-type: application/json' -d'
{
  "name": "first-api-key"
}'

# example response 
# {
#     "id": "ldQkjYsBntCu-NA93Poe",
#     "name": "first-api-key",
#     "api_key": "eo4AWfu-SMqmbvdoTcfqtw",
#     "encoded": "bGRRa2pZc0JudEN1LU5BOTNQb2U6ZW80QVdmdS1TTXFtYnZkb1RjZnF0dw=="
# }
				
			

Notice that ‘encoded’ field is actually the value for you. ‘api_key’ will not work with requests. To check cluster health with that key as authentication method run command

				
					# request authenticated with API Key
curl -k -H "Authorization: ApiKey bGRRa2pZc0JudEN1LU5BOTNQb2U6ZW80QVdmdS1TTXFtYnZkb1RjZnF0dw==" "https://localhost:9200/_cluster/health?pretty"
				
			

4. More Options

One of the improvement can be time limit, so key will expire and nobody will be able to use it after specific time. You can also add metadata to your request.

				
					curl -k -u elastic:123456 -XPUT "https://localhost:9200/_security/api_key" -H 'content-type: application/json' -d'
{
  "name": "short",
  "expiration": "33s",
  "metadata": {
    "toughcoding": {
       "presenter": "tomd"
    }
  }
}'
				
			

If you try to use it after 33 seconds it will show an error that API key is expired.

				
					{
    "type": "security_exception",
    "reason": "unable to authenticate with provided credentials and anonymous access is not allowed for this request",
    "additional_unsuccessful_credentials": "API key: api key is expired",
    "header": {
        "WWW-Authenticate": [
            "Basic realm=\"security\" charset=\"UTF-8\"",
            "Bearer realm=\"security\"",
            "ApiKey"
        ]
    }
}
				
			

5. API Key Bomb?

If you thought about creating API Key with privileges that allow to create API Key, then imagine user continuously creating new and new keys. Each key could be used for accessing Elasticsearch. This lead to uncontrolled access. Fortunately it is not possible to create API Key with API Key created with API Key.

I will show you this with an example. First you will create API Key and then you will use it to create another API Key. Final API Key you will use to obtain information about cluster and see results.

				
					curl -k -u elastic:123456 -XPUT "https://localhost:9200/_security/api_key" -H 'content-type: application/json' -d'
{
  "name": "api-key-creator",
  "role_descriptors": {
    "apikey-role-descriptor": {
      "cluster":  [ "manage_own_api_key" ]
    }
  }
}'

# example response
# {
#     "id": "mdQ3jYsBntCu-NA97vqT",
#     "name": "api-key-creator",
#     "api_key": "n4plLD9WSG6UmkdpypmChQ",
#     "encoded": "bWRRM2pZc0JudEN1LU5BOTd2cVQ6bjRwbExEOVdTRzZVbWtkcHlwbUNoUQ=="
# }

				
			

First round done. Now if you run same request but using created API key as auth method, you will see an error.

				
					curl -k -XPUT "https://localhost:9200/_security/api_key" -H "Authorization: ApiKey bWRRM2pZc0JudEN1LU5BOTd2cVQ6bjRwbExEOVdTRzZVbWtkcHlwbUNoUQ==" -H 'content-type: application/json' -d'
{
  "name": "api-key-creator-2",
  "role_descriptors": {
    "apikey-role-descriptor": {
      "cluster":  [ "manage_own_api_key" ]
    }
  }
}'

# error response
# {
#     "error": {
#         "root_cause": [
#             {
#                 "type": "illegal_argument_exception",
#                 "reason": "creating derived api keys requires an explicit role  descriptor that is empty (has no privileges)"
#             }
#         ],
#         "type": "illegal_argument_exception",
#         "reason": "creating derived api keys requires an explicit role  descriptor that is empty (has no privileges)"
#     },
#     "status": 400
# }
				
			

Creating API Key is still possible but with empty role descriptor.

				
					# Creating API Key with explicit empty role descriptors
curl -k -XPUT "https://localhost:9200/_security/api_key" -H "Authorization: ApiKey bWRRM2pZc0JudEN1LU5BOTd2cVQ6bjRwbExEOVdTRzZVbWtkcHlwbUNoUQ==" -H 'content-type: application/json' -d'
{
    "name": "auth-only-api-key",
    "expiration": "250s",
    "role_descriptors": {
        "empty-role-descriptor": {}
    }
}'

# example response
# {
#     "id": "ntQ-jYsBntCu-NA9wfq0",
#     "name": "auth-only-api-key",
#     "expiration": 1698882032196,
#     "api_key": "ZEUeTvTOQSSPtDmDW31yMA",
#     "encoded": "bnRRLWpZc0JudEN1LU5BOXdmcTA6WkVVZVR2VE9RU1NQdERtRFczMXlNQQ=="
# }
				
			

API Key is created but if you want to access anything other that authentication API, you will fail.

				
					curl -k -H "Authorization: ApiKey bnRRLWpZc0JudEN1LU5BOXdmcTA6WkVVZVR2VE9RU1NQdERtRFczMXlNQQ==" "https://localhost:9200/_cluster/health?pretty"

# example response
# {
#     "error": {
#         "root_cause": [
#             {
#                 "type": "security_exception",
#                 "reason": "action [cluster:monitor/health] is unauthorized for API key id [ntQ-jYsBntCu-NA9wfq0] of user [elastic], this action is granted by the cluster privileges [monitor,manage,all]"
#             }
#         ],
#         "type": "security_exception",
#         "reason": "action [cluster:monitor/health] is unauthorized for API key id [ntQ-jYsBntCu-NA9wfq0] of user [elastic], this action is granted by the cluster privileges [monitor,manage,all]"
#     },
#     "status": 403
# }
				
			

You can only call Authenticate API with that key.

				
					# Check if API Key is working, using Authenticate API
curl -k -H "Authorization: ApiKey YzlRMWlJc0JudEN1LU5BOU12cjI6eE1BZUNDaGVUSktPajBJMXhBaFVQdw==" "https://localhost:9200/_security/_authenticate"

# example response:
# {
#     "username": "elastic",
#     "roles": [],
#     "full_name": null,
#     "email": null,
#     "metadata": {
#         "_reserved": true
#     },
#     "enabled": true,
#     "authentication_realm": {
#         "name": "_es_api_key",
#         "type": "_es_api_key"
#     },
#     "lookup_realm": {
#         "name": "_es_api_key",
#         "type": "_es_api_key"
#     },
#     "authentication_type": "api_key",
#     "api_key": {
#         "name": "auth-only-api-key",
#         "id": "ntQ-jYsBntCu-NA9wfq0"
#     }
# }
				
			

6. Final thoughts

In this article you have learned how to use API Key for Elasticsearch access. API Key can be used easily with Logstash and therefore you can adjust expiration period with planned Logstash execution, so you will not forget to revoke access. There are other options available with this API that allows fine security tuning. I will cover this in future article.

Have a nice coding!

2 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

Follow me on LinkedIn
Share the Post:

Enjoy Free Useful Amazing Content

Related Posts