Cannot POST /api/saved_objects/_import

When attempting to the Kibana API endpoint //api/saved_objects/_import with a POST call to upload an .ndjson file it is rejected with this response:

{“statusCode”:401,“error”:“Unauthorized”,“message”:"[undefined] Forbidden by ReadonlyREST ES plugin, with { due_to={ 0=“OPERATION_NOT_ALLOWED” } }"}

The user account is configured in the Read Only Rest Plugin as such:
readonlyrest:
- name: Admin
auth_key: admin:password
type: allow

Additional details:
Elasticsearch Version: 7.9.1
Read Only Rest Version: 1.28.0_es7.9.1
Kibana Version: 7.9.1

yes you need a valid cookie (you can get it by logging in with a post with “username” and “password” form parameters to “<kibana_host>/login”). And then you need to add it as a “Cookie” header in the API call.

So the endpoint you specified <kibana_host>/login throws a 404. I’ve done some searching and I don’t see any endpoints that enable you to get a cookie from the Kibana api. I do have X-Pack disabled, and all the other api calls I am making work with just the basic auth username and password, with the exception of hitting the _import endpoint. Do you have any other suggestions? I’ve also tried /api/security/v1/login but also get a 404.

What do you mean? What’s the login page URL you get when you attempt to login from the browser?

BTW you are using a 7.9.x Kibana, so you re on ROR new platform. Please do make sure you have patched Kibana!

So is there a way to avoid the UI in getting this cookie? The ultimate goal here is to configure a freshly spun up Kibana pod in Kubernetes and configure it with a script over API calls. Can I get this cookie via API?

Yes there is, and it involves sending credentials via POST just like the UI would. Open the network tab of your browser and “copy as cURL” the POST request the browser sends to the backend.

So frustratingly, I port-forward the service I have running for Kibana in Kubernetes. I launched Dev Tools network tab, then navigated to localhost:5601 and immediately I get a password prompt, I enter the credentials and authenticate, but the network tab does not seem to capture any particular request for this, certainly no POST requests. The list of requests are:
localhost. – a GET request to ht tp://localhost:5601/
enter – a GET request to ht tp://localhost:5601/spaces/enter
home – a GET request to ht tp://localhost:5601/app/home
then a lot of subsequent .js files retrieved.
followed by several other .js calls.

The request headers subsequently use Authorization: Basic |Tokenized Creds|

I think you forgot to tick the box:

[:white_check_mark:] Preserve logs

So when the browser navigated away from /login towards kibana main page you lost the network log.
Anyway, I tried myself, and obtained the following cURL command:

curl 'http://localhost:5601/login' \
  -H 'Connection: keep-alive' \
  -H 'Cache-Control: max-age=0' \
  -H 'sec-ch-ua: "Google Chrome";v="89", "Chromium";v="89", ";Not A Brand";v="99"' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'Origin: http://localhost:5601' \
  -H 'Upgrade-Insecure-Requests: 1' \
  -H 'DNT: 1' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36' \
  -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9' \
  -H 'Sec-Fetch-Site: same-origin' \
  -H 'Sec-Fetch-Mode: navigate' \
  -H 'Sec-Fetch-User: ?1' \
  -H 'Sec-Fetch-Dest: document' \
  -H 'Referer: http://localhost:5601/login' \
  --data-raw 'username=admin&password=passwd' \
  --compressed -vvv

And this is the result with the Cookie header in the response:

$ curl 'http://localhost:5601/login' \
>   -H 'Connection: keep-alive' \
>   -H 'Cache-Control: max-age=0' \
>   -H 'sec-ch-ua: "Google Chrome";v="89", "Chromium";v="89", ";Not A Brand";v="99"' \
>   -H 'sec-ch-ua-mobile: ?0' \
>   -H 'Origin: http://localhost:5601' \
>   -H 'Upgrade-Insecure-Requests: 1' \
>   -H 'DNT: 1' \
>   -H 'Content-Type: application/x-www-form-urlencoded' \
>   -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36' \
>   -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9' \
>   -H 'Sec-Fetch-Site: same-origin' \
>   -H 'Sec-Fetch-Mode: navigate' \
>   -H 'Sec-Fetch-User: ?1' \
>   -H 'Sec-Fetch-Dest: document' \
>   -H 'Referer: http://localhost:5601/login' \
>   -H 'Accept-Language: en,fi;q=0.9,en-US;q=0.8,it;q=0.7,es;q=0.6' \
>   --data-raw 'username=admin&password=passwd' \
>   --compressed -vvv
*   Trying 127.0.0.1:5601...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 5601 (#0)
> POST /login HTTP/1.1
> Host: localhost:5601
> Accept-Encoding: deflate, gzip, br
> Connection: keep-alive
> Cache-Control: max-age=0
> sec-ch-ua: "Google Chrome";v="89", "Chromium";v="89", ";Not A Brand";v="99"
> sec-ch-ua-mobile: ?0
> Origin: http://localhost:5601
> Upgrade-Insecure-Requests: 1
> DNT: 1
> Content-Type: application/x-www-form-urlencoded
> User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36
> Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
> Sec-Fetch-Site: same-origin
> Sec-Fetch-Mode: navigate
> Sec-Fetch-User: ?1
> Sec-Fetch-Dest: document
> Referer: http://localhost:5601/login
> Accept-Language: en,fi;q=0.9,en-US;q=0.8,it;q=0.7,es;q=0.6
> Cookie: rorCookie_saml_kc=s%3AhWwABX-v0Upxp08XJg_J60aPPhn_QbPI.JYvewvKK24mTiV5N7Jw0%2FkiiOVZPgxoXvl0op4CymeQ
> Content-Length: 30
> 
* upload completely sent off: 30 out of 30 bytes
* Mark bundle as not supporting multiuse
< HTTP/1.1 302 Found
< location: /
< kbn-name: d0687e9be223
< kbn-license-sig: 6590be5467c137168ef5b23fb85627e2aa342a999b32e1c71dd9a3ecb039108a
< kbn-xpack-sig: ef42adb69cd0ad9c36eb0323e8819b1d
< content-type: text/html; charset=utf-8
< cache-control: private, no-cache, no-store, must-revalidate
< set-cookie: rorCookie=Fe26.2**36679c19e7f57c58c0b8914df9423b790e831fd669a7fa1137eb94f4db2a938c*Ai8vqKqxy1pjUOt6T15apg*BOlIl0hY5gvfXdf3jsEtIZ8Czj-cb3mrR3snSwjdfVF50_w3lNfVdDIqng6H4otSNwbAcqjht055pQUTrirOgSfFutoQwlE1F0hdXUrBj6HQhRka4p091PjpTVxY0jX2poJzKVrFRkQ0nBJm98XLOYwaXdP3-C2H50DmgzwVol6c0AGrPchduYyGq4pYPxsE**b2a789778d5a0e5c29a1af6b2215246bbda725e0dac96f023d2b069b992b3666*x-CAed5GgsM5l-WEyKBWGbWxUtbiCj4HOuNHBA04BOc; Max-Age=259210; Expires=Sat, 10 Apr 2021 20:06:09 GMT; HttpOnly; Path=/
< set-cookie: nextUrl=; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Secure; HttpOnly
< content-length: 0
< Date: Wed, 07 Apr 2021 20:05:59 GMT
< Connection: keep-alive
< 
* Connection #0 to host localhost left intact ​

Now you have the “Cookie” header value for you next requests. I.e.

-H ‘Cookie: rorCookie_saml_kc=s%3AhWwABX-v0Upxp08XJg_J60aPPhn_QbPI.JYvewvKK24mTiV5N7Jw0%2FkiiOVZPgxoXvl0op4CymeQ’

I checked, I had the box checked but still see no POST call from my browser. In one of your earlier responses you mentioned patching Kibana, is that still required if you’re only using the Elasticssearch ROR plugin?

Oh so you are not using ROR for Kibana? Then you might me just missing an ACL block to allow that operation.

Try sending the call to the saved objects API, and at the same time tail the log of Elasticsearch. It will show you a log line with “FORBIDDEN” written on it, together with all the info about the request.

Something like this:
[2021-04-09T11:12:23,957][INFO ][t.b.r.a.l.AccessControlLoggingDecorator] [nash] FORBIDDEN by default req={ ID:902301628-1082240128#482, TYP:MainRequest, CGR:N/A, USR:[no info about user], BRS:true, KDX:null, ACT:cluster:monitor/main, OA:127.0.0.1/32, XFF:null, DA:127.0.0.1/32, IDX:<N/A>, MET:GET, PTH:/, CNT:<N/A>, HDR:Accept=/, Host=localhost:9200, User-Agent=curl/7.68.0, content-length=0, HIS:[::KIBANA-SRV::-> RULES:[auth_key->false]], [::PERSONAL_GRP::-> RULES:[groups->false]], [::ADMIN_GRP::-> RULES:[groups->false]], [::Infosec::-> RULES:[groups->false]], [proxy-> RULES:[proxy_auth->false]], [ReadonlyREST Enterprise instance #1-> RULES:[ror_kbn_auth->false]], }

Notice the action (ACT), path (PTH), and headers list (HDR). All this information is useful to create an ACL block to accept this kind of requests.

Plus, you should be able to add HTTP basic auth to your API request and Kibana should forward the Authorization header by default all the way to Elasticsearch. Please verify tthe HDR section of the log line above if in your case the Authorization header is present. This will be useful because you could require some credentials for a new “api_user” in the ACL block you will create.

Thanks! That helped, I did get it working.

1 Like