Unable to login to Kibana using API_KEY with restrictions

Use case:

I am implementing Attribute Based Access Control as mentioned on the official blog.

How can I create an API_Key that has access to the Kibana dashboard with restricted attribute-based access control?

Steps followed:

  • I created a role with elasticsearch and kibana priviledges as mentioned in the doc.

  • When I create an api_token with restricted privileges as shown below, I am not able to login to the dashboard using this token. Basically, on restricted api_token, I am losing the Kibana access.
    I am able to access the Elasticsearch data via REST API.
    Please note that the user has correct access privileges and I am passing the token in the correct header.

POST /_security/api_key


{

"name": "test-api-key",

"role_descriptors": {

"abac_kibana_role": {

"cluster": ["all"],

"index": [

{

"names": ["*"],

"privileges": ["read"],

"query": {

"terms_set": {

"security_attributes": {

"terms": ["D", "G", "E"],

"minimum_should_match_field": "required_matches"

}

}

}....
  • If I create the api_token without ABAC restrictions, I can access the Kibana dashboard using the token.
  • How can I use an api_token with ABAC and use the same token to access Kibana dashboard. In other words, how can I set Kibana privileges on the API_KEY .

I also understand from the documentation that API_TOKEN should not be used for accessing Kibana. Then what alternatives I have instead of API_TOKENs?

Hi @Bikram_Kundu

Welcome to our Discuss forum :wave:

The POST /_security/api_key endpoint belongs to Elasticsearch and not Kibana and is not meant to be used to create API keys for Kibana. There's no public API endpoint for creating API keys in Kibana though, and I recommend that you instead use the Kibana UI to do so. You can read about how to do that in the Kibana API Keys documentation.

Kibana API Keys are tied to the user who creates them and will always have the same or less permissions than that user. So make sure your user has all the permissions needed first. If you want to reduce the permission for the API Key, you need to toggle the "Restrict privileges" box when creating/editing the API Key. A text field will appear in which you now need to enter a role JSON snippet (this has the same format as when creating or updating roles via the API).

Please try this out and let me know how it goes :slight_smile:

Regards,
Thomas

Hi @wa7son,

Thanks for your response. But I have tried creating it from the UI as well. Let me explain it.

My question is very specific to attribute bases access control , please pay attension to this feature.

If I create a key without setting any restrictions (either from Kibana UI or ES Rest API), it works with Kibana dashboard. Problem is when I set any restriction. Same whether I try it through ES-Rest api or Kibana.

Steps to reproduce:

  • Create the API key as discussed on Kibana doc, but select Restrict priviledge option as well from the UI. Note, no other changes made.
  • Now try loging in to Kibana with this key, you won't be able to login. This is because you selected Restrict priviledge while creating the key.

Please make me understand this, why this does not work.

The JSON structure shown in the text field is as below, which is different from the link you shared.

{
  "superuser": {
    "cluster": [
      "all"
    ],
    "indices": [
      {
        "names": [
          "*"
        ],
        "privileges": [
          "all"
        ],
        "allow_restricted_indices": false
      },
      {
        "names": [
          "*"
        ],
        "privileges": [
          "monitor",
          "read",
          "view_index_metadata",
          "read_cross_cluster"
        ],
        "allow_restricted_indices": true
      }
    ],
    "run_as": [
      "*"
    ]
  }
}

I tried using the Kibana role format, for example the following structure is considered as invalid.

{
  "metadata" : {
    "version" : 1
  },
  "elasticsearch": {
    "cluster" : [ ],
    "indices" : [ ]
  },
  "kibana": [
    {
      "base": [],
      "feature": {
        "dashboard": ["read"]
      },
      "spaces": [
        "marketing"
      ]
    }
  ]
}

I can only set the ES related permissions from Kibana-UI. but it is loosing the Kibana related permissions. Thereform I am unable to access the UI.
Again, the question is how can I set the Attribute Based Access Control on API token and use the same token to access Kibana dashboard as well?

Ah ok, I see. Thanks for explaining.

I will try to see if I can figure out what's going on, but first I need to know which version of Kibana/Elasticsearch you are using?

I tried on both 8.2.0 and 8.3.0 versions.
Also made sure both ES and Kibana are on the same versions.

If you want to use the API key to "log into" Kibana, you need grant it relevant application privileges. For example:

{
   "YOUR_ROLE_NAME": {
     "cluster": [ ... ],
     "indices": [ ... ],
     "run_as": [ ... ],
     "applications": [
       {
          "application": "kibana-.kibana",
          "resources": ["*"],
          "privileges": ["*"]
       }
    ]
  }
}

As far as I know, log into Kibana with API keys are being deprecated. So you might want to consider that if you are setting up things for long term usage.

Hi @Yang_Wang, thank you for your reply.

  • I understand that it is deprecated. What is the alternative to API Tokens?
  • I have already created the role from Kibana, e.g. http://{KIBANA_URL}/api/security/role/abac_kibana_role. Problem is I am unable to set the security attributes as explained [Attribute Based Access Control ](Document-Level Attribute-Based Access Control in Elasticsearch | Elastic Blog). It does not accept that as a valid payload. Please read ABAC post, you will understand what I am trying to achieve.
  • I need a way to set security attributes while creating tokens.

Document level security (DLS) has nothing to do with kibana login access.

Can you share the full payload that "does not accept as a valid payload"? Not snippet, but the full thing. It is hard to diagnoise with partial information.

Please note DLS is a platinum (or trial) licensed feature. It should not stop you from creating API key with it. But you can encounter security exception (403) when you try to use the key.

Hello @Yang_Wang and @wa7son, thank you for your attention to this topic. We run several elastic instances hosted by elastic.co

What we are trying to achieve is to leverage Kibana dashboards (hosted by elastic.co) as embedded components in our solution. As such the (statically defined) Kibana user and roles are to be restricted for the current session to the subset of documents that this session is supposed to have access. We are following the ABAC authorization approach to do so as Bikram explained.

What we really would like to do is to create a (time-limited) user session token with the attribute constraints and place that token into the user's browser. For this we would need an API call to create this user token, which as @wa7son said, is not publicly available.

The alternative seemed to be to create an api key, which as the kibana documentation says at API Keys | Kibana Guide [master] | Elastic are "secondary credentials so that you can send requests on behalf of a user. Secondary credentials have the same or lower access rights"

Sounds exactly what we need, a derivative of our user credential that

  • has our ABAC attributes / constrained
  • is time limited / needs to be refreshed

If API keys are deprecated then I think this should be on the documentation page above. What is the replacement mechanism. Knowing this is fairly important to us as we are basing future development and use of elastic.co on this functionality.

Thank you for your help and guidance.

I am well aware of the fact that DLS is a premium feature as it gives a clear exception with details in return. I have enabled the trial license. Please answer all my questions (Q) asked below.

My original question is related to API token and DLS.

Q-1. How to access Kibana dashbaord with an API token that has DLS configured?
Q-2. If API Tokens are deprecated, what is the alternative that supports DLS? Obviously I don't want to use username/password.

Please understand the question clearly, I can set the attribute based access control on ES. Means if I create a role from ES, DLS works fine. But I can not set the DLS/ABAC in the payload when trying from Kibana URL.

Why do I need to create the role/API_token from Kibana? This is because, there is no way to specify Kibana related permissions in the ES roles/API_token payload.

Providing the detailed payloads below, please try it out.

  1. Create a role on ES with ABAC/DLS enabled.
POST _security/role/my_policy
{ 
    "indices": [{
        "names": ["my_index"],
        "privileges": ["read"],
        "query": {
            "template": {
                "source": "{\"bool\": {\"filter\": [{\"terms_set\": {\"security_attributes\": {\"terms\": {{#toJson}}_user.metadata.security_attributes{{/toJson}},\"minimum_should_match_script\":{\"source\":\"params.num_terms\"}}}}]}}"
            }
        }
    }]
}

This works fine.

  1. Create a role on Kibana with dashboard access rights. I am hitting the Kibana endpoint, not ES.
curl -X PUT --location "http://localhost:5601/api/security/role/abac_kibana_role_1" \
    -H "Content-Type: application/json" \
    -H "kbn-xsrf: reporting" \
    -d "{
          \"metadata\": {
            \"version\": 1
          },
          \"elasticsearch\": {
            \"cluster\": [
              \"all\"
            ],
            \"indices\": [
              {
                \"names\": [
                  \"*\"
                ],
                \"privileges\": [
                  \"read\"
                ]
              }
            ]
          },
          \"kibana\": [
            {
              \"base\": [],
              \"feature\": {
                \"discover\": [
                  \"all\"
                ],
                \"visualize\": [
                  \"all\"
                ],
                \"dashboard\": [
                  \"all\"
                ],
                \"dev_tools\": [
                  \"read\"
                ],
                \"advancedSettings\": [
                  \"read\"
                ],
                \"indexPatterns\": [
                  \"read\"
                ],
                \"graph\": [
                  \"all\"
                ],
                \"apm\": [
                  \"read\"
                ],
                \"maps\": [
                  \"read\"
                ],
                \"canvas\": [
                  \"read\"
                ],
                \"infrastructure\": [
                  \"all\"
                ],
                \"logs\": [
                  \"all\"
                ],
                \"uptime\": [
                  \"all\"
                ]
              },
              \"spaces\": [
                \"*\"
              ]
            }
          ]
        }" \
    --basic --user elastic:elastic123

This also works fine.

  1. Now, add the query attribute in the Kibana payload as used in the step-1 above. Note, the request fails cause it has a query template related to DLS/ABAC. I am sending the requests to Kibana endpoint.
curl -X PUT --location "http://localhost:5601/api/security/role/abac_kibana_role_2" \
    -H "Content-Type: application/json" \
    -H "kbn-xsrf: reporting" \
    -d "{
          \"metadata\": {
            \"version\": 1
          },
          \"elasticsearch\": {
            \"cluster\": [
              \"all\"
            ],
            \"indices\": [
              {
                \"names\": [
                  \"*\"
                ],
                \"privileges\": [
                  \"read\"
                ],
                \"query\": {
                    \"template\": {
                        \"source\": \"{\\\"bool\\\": {\\\"filter\\\": [{\\\"terms_set\\\": {\\\"security_attributes\\\": {\\\"terms\\\": {{#toJson}}_user.metadata.security_attributes{{/toJson}},\\\"minimum_should_match_script\\\":{\\\"source\\\":\\\"params.num_terms\\\"}}}}]}}\"
                    }
                }
              }
            ]
          },
          \"kibana\": [
            {
              \"base\": [],
              \"feature\": {
                \"discover\": [
                  \"all\"
                ],
                \"visualize\": [
                  \"all\"
                ],
                \"dashboard\": [
                  \"all\"
                ],
                \"dev_tools\": [
                  \"read\"
                ],
                \"advancedSettings\": [
                  \"read\"
                ],
                \"indexPatterns\": [
                  \"read\"
                ],
                \"graph\": [
                  \"all\"
                ],
                \"apm\": [
                  \"read\"
                ],
                \"maps\": [
                  \"read\"
                ],
                \"canvas\": [
                  \"read\"
                ],
                \"infrastructure\": [
                  \"all\"
                ],
                \"logs\": [
                  \"all\"
                ],
                \"uptime\": [
                  \"all\"
                ]
              },
              \"spaces\": [
                \"*\"
              ]
            }
          ]
        }" \
    --basic --user elastic:elastic123

This fails, as Kibana is unable to parse this template.
{"statusCode":400,"error":"Bad Request","message":"[request body.elasticsearch.indices.0.query]: expected value of type [string] but got [Object]"}

Q-3. How to set the ABAC/DLS configuration in the Payload here. Same worked in step-1?

  1. Let's create an user from Kibana console with the role abac_kibana_role_1 from step-2 as follows.
PUT _security/user/john
{
    "username": "john",
    "password":"john@123",
    "roles": ["abac_kibana_role_1"],
    "full_name": "John_Does",
    "email": "john@gmail.com"
}
  1. Login to the Kibana UI using the newly created username john and password john@123.

  2. Create an API_token exactly as below.

POST /_security/api_key
{
  "name": "test-api-key-1"
}

Copy and Save the token somewhere for later use. You will receive a response like this>

{
  "id" : "siwOsIIBIqatl7Ky_6Iv",
  "name" : "test-api-key-1",
  "api_key" : "D8PvyzO5TjiCI3FAfUi4cQ",
  "encoded" : "c2l3T3NJSUJJcWF0bDdLeV82SXY6RDhQdnl6TzVUamlDSTNGQWZVaTRjUQ=="
}
  1. Now, logout from the Kibana-UI. Use the above token to access the dashboard.
    You can see that I am using a chrome extension to inject the header. I am able to happily access the dashboard.

  2. I will now add the ABAC/DLS config in the API token and try to use the same token to access the dashbaord again. So logout and Login to the Kibana dashboard using Username and Password again. Create another API token as below from Kibana console:
    NOTE, now we are using the DLS/ABAC in the API token. Understand that this is a valid token.


POST /_security/api_key
{
  "name": "test-api-key-2",
  "role_descriptors": {
    "abac_kibana_role_1": {
      "cluster": ["all"],
      "index": [
        {
          "names": ["*"],
          "privileges": ["read"],
          "query": {
            "terms_set": {
                "security_attributes": {
                    "terms": ["EXECUTIVE", "ACCOUNTING"],
                    "minimum_should_match_field": "required_matches"
                }
            }
          }
        }
      ]
    }
  }
}

You will get a response similar to this.

{
  "id" : "tCwasIIBIqatl7KysqKl",
  "name" : "test-api-key-2",
  "api_key" : "vd8nb8PhRHiLFCideplPAw",
  "encoded" : "dEN3YXNJSUJJcWF0bDdLeXNxS2w6dmQ4bmI4UGhSSGlMRkNpZGVwbFBBdw=="
}
  1. Now try to access the dashboard again using this new token test-api-key-2 similar to step-7.

It is returning 403: forbidden.

Q-4: How did the token lost its Kibana access priviledge?
NOTE: Even if I remove the query section completely from payload, still it does not work with Kibana.

Let me know if you need anything further. I have provided detailed payload in every step.
Please anwer all my question highlighted as Q-*

Before diving into your detailed questions, I want to just make sure that an API key is the way to go or if there's a better approach we can take. I didn't catch exactly why you wanted to log into Kibana using an API key. Is it because you want to make a dashboard publicly available, either by sharing a link to it, or embedding it?

If that's the case, the answer to Q-2 is that you instead should enable public access on the dashboard.

Before enabling public access to a dashboard you must first create an anonymous access provider in the kibana.yml file. You can read about how to do that here: Authentication in Kibana | Kibana Guide [8.11] | Elastic

For this provider you can use the user you created with just the permissions you wanted.

Then for embedding with iFrames we have this documentation on how to make it public: Reporting and sharing | Kibana Guide [8.11] | Elastic

The key here is to select "Public URL" when generating the iFrame code:

Similarly there's a "Public URL" toggle on the share sheet for permalinks:

When an anonymous user accesses either the iFrame or the link they will be logged in with the anonymous provider specified in the kibana.yml and hence have the permissions of the user listed there.

I have a suspicion this is actually what you're looking for. Let me know if that's the case.

@mlorch I'd like to make it very clear that API keys themselves are not deprecated at all. Only using API keys as interactive users to log into Kibana web UI is deprecated. Based on your description, your usage seems to be programmatic and should not be concerned with the deprecation.

We run several elastic instances hosted by elastic.co

If you are a Cloud customer, I suggest you leverage the support contract as you can get help from dedicated support teams.

@Bikram_Kundu

As @wa7son suggested, you may need to first consider whether enabling anonymous access to the dashboard is a better approach. So please check that first before reading on.

  1. Now, add the query attribute in the Kibana payload as used in the step-1 above. Note, the request fails cause it has a query template related to DLS/ABAC. I am sending the requests to Kibana endpoint.
    This fails, as Kibana is unable to parse this template.

I believe this is a limitation of Kibana's API. It only accepts string for the query field. The workaround is to quote the whole value of the query field, e.g.:

...

\"query\": \"{ \"template\": { \"source\": \"{\\\"bool\\\": {\\\"filter\\\": [{\\\"terms_set\\\": {\\\"security_attributes\\\": {\\\"terms\\\": {{#toJson}}_user.metadata.security_attributes{{/toJson}},\\\"minimum_should_match_script\\\":{\\\"source\\\":\\\"params.num_terms\\\"}}}}]}}\" } }\"

...

This is not true. Kibana privileges are backed by Elasticsearch. So it is possible to directly create them in Elasticsearch. But it is generally not recommended because this is similar to directly changing database record without going through the designated API. But for API keys, currently there is no easy way to grant Kibana privileges to them without directly talking to Elasticsearch. So my suggestion is to check the abac_kibana_role_2 you created with the ES's GetRoles API and copy/paste its applications section into the API key creation payload.

For example, you may see something like the follows in the response when you run GetRoles for abac_kibana_role_2:

...
    "applications" : [
      {
        "application" : "kibana-.kibana",
        "privileges" : [
          "feature_discover.all",
          ...
        ],
        "resources" : [ "*" ]
      }
    ],
...

Copy the above and add it to the API key creation request payload, e.g.:

POST /_security/api_key
{
  "name": "test-api-key-2",
  "role_descriptors": {
    "abac_kibana_role_1": {
      "cluster": ...,
      "index": ...,
      "applications": [
        {
          "application" : "kibana-.kibana",
          "privileges" : ...,
          "resources" : [ "*" ]
        }
      ]
    }
  }
}

As for why

How did the token lost its Kibana access priviledge?

This is because you did not specify the above application privileges (kibana privileges) for the key. Therefore the key did not have permission to access kibana. In this case, an API key does not automatically inherit its owning user's permission.

Thank you so much, @Yang_Wang. This is exactly what I was looking for.

I assumed that the api_key will inherit the values from role if not specified in the API_Token payload. Understood now that we need to exclusively specify the application priviledges there.

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.