Table of Contents
1. Introduction
In the first part I show you basic functionality of API Keys. Now I want to elaborate a little more especially about role descriptors.
2. Start Elasticsearch
Start Elasticsearch cluster with trial license needed for field and document level security then setup password to continue next steps.
docker run --rm \
--name elk \
-d \
-p 9200:9200 \
-e xpack.license.self_generated.type=trial \
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"
2.1 Load test data
To be able to follow later commands load sample records
curl -k -XPOST -u elastic:123456 "https://localhost:9200/documents/_doc" -H 'Content-Type: application/json' -d'
{
"country": "China",
"data": "*********"
}'
curl -k -XPOST -u elastic:123456 "https://localhost:9200/documents/_doc" -H 'Content-Type: application/json' -d'
{
"country": "USA",
"data": "*********"
}'
curl -k -XPOST -u elastic:123456 "https://localhost:9200/documents/_doc" -H 'Content-Type: application/json' -d'
{
"country": "Russia",
"data": "*********"
}'
3. Request body structure – Create Key
Body elements of JSON message sent with request contain
- name – obligatory field that is minimum to create API Key
- expiration – time definition in seconds, minutes, hours etc
- metadata – custom metadata to attach to API Key
- role_descriptors – permissions definition, allow restrict user more
You can specify limitation as role descriptor to allow user to find only particular document. Imagine you are asked by user to have a view over China documents in database that contain documents specific for other countries too. Now you can give API Key with limitation that will allow person to see only specific documents. This feature require license for Elasticsearch.
curl -k -u elastic:123456 -XPUT "https://localhost:9200/_security/api_key" -H 'content-type: application/json' -d'
{
"name": "china-docs",
"expiration": "1h",
"role_descriptors": {
"china": {
"indices": [
{
"names": ["documents"],
"privileges": ["read"],
"query": "{\"match\":{\"country\": {\"query\": \"China\"}}}"
}
]
}
},
"metadata": {
"intention": "Only to view China docs"
}
}
'
# response
# {
# "id": "3wOakosB5nUlv0OITHaT",
# "name": "china-docs",
# "expiration": 1698975267604,
# "api_key": "DnqLG4FOR0qKD86MX1liWQ",
# "encoded": "M3dPYWtvc0I1blVsdjBPSVRIYVQ6RG5xTEc0Rk9SMHFLRDg2TVgxbGlXUQ=="
# }
Now someone has 1 hour to search for documents matching query where country is equal to China
curl -k -H "Authorization: ApiKey M3dPYWtvc0I1blVsdjBPSVRIYVQ6RG5xTEc0Rk9SMHFLRDg2TVgxbGlXUQ==" "https://localhost:9200/_search?pretty"
# response
# {
# "took" : 1,
# "timed_out" : false,
# "_shards" : {
# "total" : 1,
# "successful" : 1,
# "skipped" : 0,
# "failed" : 0
# },
# "hits" : {
# "total" : {
# "value" : 1,
# "relation" : "eq"
# },
# "max_score" : 1.0,
# "hits" : [
# {
# "_index" : "documents",
# "_id" : "2AOQkosB5nUlv0OIoHYf",
# "_score" : 1.0,
# "_source" : {
# "country" : "China",
# "data" : "*********"
# }
# }
# ]
# }
# }
Another example can be if you want user to only check health status of cluster. Therefore define below descriptor
curl -k -u elastic:123456 -XPUT "https://localhost:9200/_security/api_key" -H 'content-type: application/json' -d'
{
"name": "healthcheck",
"role_descriptors": {
"for-monitoring": {
"cluster": [
"monitor"
]
}
},
"metadata": {
"intention": "Only to view cluster health"
}
}
'
# response
# {"id":"5QOkkosB5nUlv0OIfHai","name":"healthcheck","api_key":"v-Gy-jmgSoGY82vlNBBNiQ","encoded":"NVFPa2tvc0I1blVsdjBPSWZIYWk6di1HeS1qbWdTb0dZODJ2bE5CQk5pUQ=="}
curl -k -H "Authorization: ApiKey NVFPa2tvc0I1blVsdjBPSWZIYWk6di1HeS1qbWdTb0dZODJ2bE5CQk5pUQ==" "https://localhost:9200/_cluster/health?pretty"
# response
# {
# "cluster_name" : "docker-cluster",
# "status" : "yellow",
# "timed_out" : false,
# "number_of_nodes" : 1,
# "number_of_data_nodes" : 1,
# "active_primary_shards" : 2,
# "active_shards" : 2,
# "relocating_shards" : 0,
# "initializing_shards" : 0,
# "unassigned_shards" : 1,
# "delayed_unassigned_shards" : 0,
# "number_of_pending_tasks" : 0,
# "number_of_in_flight_fetch" : 0,
# "task_max_waiting_in_queue_millis" : 0,
# "active_shards_percent_as_number" : 66.66666666666666
# }
4. Update API Key
You can update created before API Key before it getting expired.
curl -k -u elastic:123456 -XPUT "https://localhost:9200/_security/api_key" -H 'content-type: application/json' -d'
{
"name": "short",
"expiration": "20s"
}'
In response you are getting id(not api_key), that you will use for update queries.
{
"id": "DciLkYsBO9UU75Sr77um",
"name": "short",
"expiration": 1698953969095,
"api_key": "3oxaXqfUQF6wmhvx8Ro0wA",
"encoded": "RGNpTGtZc0JPOVVVNzVTcjc3dW06M294YVhxZlVRRjZ3bWh2eDhSbzB3QQ=="
}
curl to update API Key metadata
curl -k -u elastic:123456 -XPUT "https://localhost:9200/_security/api_key/DciLkYsBO9UU75Sr77um" -H 'content-type: application/json' -d'
{
"metadata": {
"toughcoding": {
"presenter": "tomd"
}
}
}'
response:
{"updated":true}
If you run query after API Key is expired you will see an error:
{
"error": {
"root_cause": [
{
"type": "illegal_argument_exception",
"reason": "cannot update expired API key [DciLkYsBO9UU75Sr77um]"
}
],
"type": "illegal_argument_exception",
"reason": "cannot update expired API key [DciLkYsBO9UU75Sr77um]"
},
"status": 400
}
5. Get information about key
It is possible to get information about expired key
curl -k -u elastic:123456 -XGET "https://localhost:9200/_security/api_key?id=DciLkYsBO9UU75Sr77um"
{
"api_keys": [
{
"id": "DciLkYsBO9UU75Sr77um",
"name": "short",
"type": "rest",
"creation": 1698953949095,
"expiration": 1698953969095,
"invalidated": false,
"username": "elastic",
"realm": "reserved",
"metadata": {
"toughcoding": {
"presenter": "tomd"
}
},
"role_descriptors": {}
}
]
}
Also about all keys if no parameter provided
curl -k -u elastic:123456 -XGET "https://localhost:9200/_security/api_key"
6. DELETE key
Deleting key will invalidate it
curl -k -u elastic:123456 -XDELETE "https://localhost:9200/_security/api_key" -H 'content-type: application/json' -d'
{
"ids" : ["DciLkYsBO9UU75Sr77um"]
}'
curl -k -u elastic:123456 -XGET "https://localhost:9200/_security/api_key?id=DciLkYsBO9UU75Sr77um&pretty"
{
"api_keys" : [
{
"id" : "DciLkYsBO9UU75Sr77um",
"name" : "short",
"type" : "rest",
"creation" : 1698953949095,
"expiration" : 1698953969095,
"invalidated" : true,
"username" : "elastic",
"realm" : "reserved",
"metadata" : {
"toughcoding" : {
"presenter" : "tomd"
}
},
"role_descriptors" : { }
}
]
}
If you want to invalidate all keys owned by currently authenticated user you can run
curl -k -u elastic:123456 -XDELETE "https://localhost:9200/_security/api_key" -H 'content-type: application/json' -d'
{
"owner" : "true"
}'
# response
# {
# "invalidated_api_keys": [
# "Bch_kYsBO9UU75Sr0btY",
# "C8iLkYsBO9UU75SrOLtU",
# "Ach9kYsBO9UU75Sr3rtU",
# "A8h-kYsBO9UU75Sr9Lu3",
# "_ch4kYsBO9UU75Sr0brt",
# "_8h8kYsBO9UU75Sr4boW",
# "B8iBkYsBO9UU75SrHrsp",
# "CciBkYsBO9UU75SroLvS"
# ],
# "previously_invalidated_api_keys": [],
# "error_count": 0
# }
Now checking active keys will return empty list
curl -k -u elastic:123456 -XGET "https://localhost:9200/_security/api_key?active_only=true&pretty"
# response:
# {
# "api_keys" : [ ]
# }
7. Summary
After these exercises you have practiced how to create, modify and delete API Key. You also practice query based security limitation applied with role descriptor. If you have any questions please leave in comments section.