Kibana JWT url param

617 views
Skip to first unread message

mic...@hull.io

unread,
May 16, 2017, 11:44:04 AM5/16/17
to Search Guard
Hey,

we are trying to setup an "automatic" kibana authorization using JWT passed via url param.

We already set the `jwt_auth_domain` on the elastic search with `jwt_url_parameter` which works perfectly fine when trying to call the ES.

But it doesn't work while trying to pass the same param to the kibana - it always leads to the login page.

I found a topic about the JWT header authorization:

but for our use case the url param is a key feature here.
We have briefly reviewed the code of the https://github.com/floragunncom/search-guard-kibana-plugin/ and didn't find anything related to the JWT.

Is there any chance to get that working? How can we achieve that?

Thanks,
Michal

Jochen Kressin

unread,
May 16, 2017, 11:51:12 AM5/16/17
to Search Guard
Hm ... JWT and Kibana should work, we have that covered in our integration tests. There's no need to install the Search Guard Kibana plugin for it to work.

Seems like an issue with Kibana itself. If direct access to ES with a JWT token as URL param works, then Kibana does not seem to forward the query string to Elasticsearch.

Could you please open an issue on GitHub so we can investigate further:


Thanks!

mic...@hull.io

unread,
May 17, 2017, 4:21:22 AM5/17/17
to Search Guard
Thank you very much for all the assistance.

Just to confirm, I'm using those docker images:


and those are the two urls I'm testing:


First one is working correctly, the second one opens the basic auth prompt.

mic...@hull.io

unread,
May 17, 2017, 6:24:18 AM5/17/17
to Search Guard
And I wanted to confirm that using Authorization header works correctly here.

I would just point out two things in the documentation (http://floragunncom.github.io/search-guard-docs/jwt.html)

1. it says that the default name of the header is `Authentication` while it's `Authorization`
https://github.com/floragunncom/search-guard-authbackend-jwt/blob/master/src/main/java/com/floragunn/dlic/auth/http/jwt/HTTPJwtAuthenticator.java#L64

2. it doesn't say that if you set url param name the header would be ignored, I would expect that it would try to obtain the token from both places


On Tuesday, May 16, 2017 at 5:51:12 PM UTC+2, Jochen Kressin wrote:

Jochen Kressin

unread,
May 17, 2017, 6:41:21 AM5/17/17
to Search Guard
Hi,

thanks for your input. 

1. The docs are not correct regarding the Authentication vs. Authorization part, we'll update it.
2. I would also expect that SG looks in both the URL param and the Header, I think this is a bug on our side. I have opened an issue here: https://github.com/floragunncom/search-guard-authbackend-jwt/issues/4

Regarding Kibana, this is a bit tricky, but we can probably offer a workaround.

Background: Kibana acts like a proxy for Elasticsearch. While it is possible to add HTTP header to the KI <-> ES calls, it's not possible to change the URLs of the KI/ES endpoints: If you issue an HTTP request to Kibana, one or more internal requests to ES are being made, with a fixed URL scheme. So, it's not possible to add arbitrary URL parameters, like the JWT token.

A working workaround would look like this:

1. Make sure that the JWT authenticator comes before the HTTP Basic Auth for the Kibana server user in your sg_config.yml. 
2. Configure the JWT authenticator to use token in the HTTP header

We could then extend the Kibana plugin like this:

1. When a JWT token parameter is found in the URL, we grab it and store it in a session cookie
2. If we find a JWT token in the said session cookie, we add it to the ES request as HTTP header

Which basically means we transform the query param to an HTTP header. You would "logout" by closing the browser, thus deleting the session cookie.

If https://github.com/floragunncom/search-guard-authbackend-jwt/issues/4 is fixed, you can leave both HTTP header and URL get param enabled if this is a requirement.

We have a working protoype/snapshot of the Kibana plugin, so the approach seems to work fine.

Before we start working on this further, please let me know if this approach is an option for you. If so, let us also know the exact KI/ES version you are using, so we can prepare a snapshot for you to try.

mic...@hull.io

unread,
May 17, 2017, 7:11:57 AM5/17/17
to Search Guard
Big thanks for in-depth information.
The solution you described is great for us since we can pass the JWT directly, for example in an iframe. For header we would need to employ a proxy to modify the request on the fly.

We would like to combine that with one role with username substitution to control access. Then generate a JWTs with different subjects (hence usernames) to easily filter the available data.

Do you see any risk of mixing sessions between different users in that solution? I understand that using session cookies on kibana, parsing and storing the JWT on the initial call to kibana would make it impossible for one user to work on two tabs in one browser with two different JWTs. That would need passing JWT param around in kibana all the time. Am I right? I'm not familiar with kibana internals, so please correct me.
If the url query param based solution is not possible right now, we should be fine with session limitation, but we would need an option to force JWT reset in the session, so if the get url param is present it takes precedence over session information.

Do you see our idea here?

Jochen Kressin

unread,
May 17, 2017, 8:29:42 AM5/17/17
to Search Guard
Yes, I see your point. First regarding the precedence, the sequence would be:

  • check if there is any JWT token in the get parameter in the URL
    • The name of this parameter would be configurable in kibana.yml
  • If so, take this value and store it in the session cookie, regardless if there is an existing value
    • This would overwrite the existing session information, and you could also set the param to an empty string, basically removing the session information completely
  • For all subsequent requests, if there is a token stored in the session cookie, add it as HTTP header
You're right, this approach would not support working in two tabs with two different JWT tokens simultaneously since we're storing the credentials in a cookie. You could work with different browser, or different browser windows in private mode, that should work.

So if you think this approach could work, let me know your KI/ES version you're on, and we can prepare a snapshot for you to test.

mic...@hull.io

unread,
May 17, 2017, 8:36:07 AM5/17/17
to Search Guard
Thanks for the clarification. It should work for us.

We plan to deploy the 5.4.0 version

Do you think that in next step we could work on query param only solution which is session independent? Is it even possible due to Kibana implementation?

Jochen Kressin

unread,
May 17, 2017, 11:20:04 AM5/17/17
to Search Guard
No, that's not possible due to the inner workings of Kibana. The only way would be to patch core Kibana somehow (would need to investigate this a bit further) , but due to the fast release cycles of ES/KI supporting such a patch is not really an option.

Jochen Kressin

unread,
May 23, 2017, 10:06:51 AM5/23/17
to Search Guard
Hi Michael,

we've just prepared a first release candidate of the next version of our Kibana plugin, including the JWT support we've talked about.

You can install the RC1 for KI 5.4.0 like:

bin/kibana-plugin install https://cdn.filestackcontent.com/u9mGutyCQDq0mIQhKTIB


It works like explained above: It takes the JWT token from the URL, stores it in an encrpyted session cookie, and adds it as http header to each request.

Sample Kibana configuration:

searchguard.jwt.enabled: true
searchguard.jwt.url_param: jwtparam
searchguard.jwt.header: jwtheader

searchguard.jwt.enabled: Enabled/disables the JWT support
searchguard.jwt.url_param: The name of the query param we use to look up the JWT token
searchguard.jwt.header: The name of the HTTP header we copy the token to

On the ES side, you need to place the JWT authenticator before the Basic Auth you probably use for the Kibana server user. This is important, otherwise the JWT token will be ignored!

So, place the JWT authenticator first, and use the value of searchguard.jwt.header you used in the KI configuration as jwt_header value in the JWT configuration in ES, for example:

jwt_auth_domain:
  enabled: true
  order: 0
  http_authenticator:
    type: jwt
    challenge: false
    config:
      signing_key: "..."
      jwt_header: "jwtheader"
      jwt_url_parameter: null
      roles_key: roles
      subject_key: username

You can then append the JWT key to any Kibana URL, like:


The URL param takes precedence and will overwrite any already stored JWT token. We use session cookies, so you can "log out" by closing the browser. You can also use an empty JWT token in the URL to delete the stored one.

Let me know if this works for you, and if you need anything else!

Thanks,

Jochen

Paul Azad

unread,
Oct 26, 2017, 9:01:24 AM10/26/17
to Search Guard Community Forum
Hi

We are trying the same thing, and have a question regarding your example above.

You put searchguard.jwt.url_param: jwtparam , yet in your Kibana URL you put ?authorization=

Is this a mistake or correct?

From what i can tell your grabbing the token from the kibana URL path (in your example the token after the jwtparam=, and your converting it to the http header by the name of jwtheader. Then elasticsearch is grabbing that header value and treating it based on the elasticsearc config - specifically this line:
jwt_header: "jwtheader".

Is that correct?

thanks

Jochen Kressin

unread,
Oct 26, 2017, 9:35:57 AM10/26/17
to Search Guard Community Forum
Basically you can name and configure the JWT URL parameter as you like. This is to make it possible to integrate Search Guard in already existing infrastructures, where the name of the parameter is already fixed and you can't change it. Example including Kibana:

In kibana.yml you can set two config keys:

* searchguard.jwt.url_param (default: authorization)
* searchguard.jwt.header (default: Authorization)

If the SG Kibana plugins finds a token in the url parameter specified by the searchguard.jwt.url_param config (or the default if the key is not set), it copies it to an HTTP header specified by searchguard.jwt.header (or the default if this key is not set).

Copying from an url parameter to an HTTP header is needed because Kibana internally only supports to add HTTP headers, not parameters.

The JWT module installed on the Kibana side then looks for a token in the HTTP header configured by the jwt_header key in the JWT module config. Default is "Authorization". Again, Kibana only supports HTTP headers, that's why you need to use HTTP header based tokens. It then validates the token and continues with the authentication flow.

So if you don't change the defaults, just add your token as "authorization" url parameter to every request to Kibana.

If you want to use a different HTTP header, other than "Authorization", note that Kibana requires you to whitelist any of these additional HTTP headers in kibana.yml:

elasticsearch.requestHeadersWhitelist: [ "authorization", "my_other_header", "..." ]

Paul Azad

unread,
Oct 27, 2017, 2:06:08 AM10/27/17
to Search Guard Community Forum
Hi

This is really making me go crazy. I have read, and re-read this whole post so many times its not funny.


connecting straight to ES works:

But when i go to kibana using this link, it just takes me to the kibana plugin login page

Here are the 2 config files:
kibana.yml
searchguard.jwt.enabled: true
(removed the other 2 lines so it just goes with the default)


sg_config.yml
    authc:
      kerberos_auth_domain: 
        enabled: false
        order: 6
        http_authenticator:
          type: kerberos # NOT FREE FOR COMMERCIAL USE
          challenge: true
          config:
            # If true a lot of kerberos/security related debugging output will be logged to standard out
            krb_debug: false
            # If true then the realm will be stripped from the user name
            strip_realm_from_principal: true
        authentication_backend:
          type: noop
      basic_internal_auth_domain: 
        enabled: true ##############
        order: 4
        http_authenticator:
          type: basic
          challenge: true
        authentication_backend:
          type: intern
      proxy_auth_domain:
        enabled: false
        order: 3
        http_authenticator:
          type: proxy
          challenge: false
          config:
            user_header: "x-proxy-user"
            roles_header: "x-proxy-roles"
        authentication_backend:
          type: noop
      host_auth_domain:
        enabled: false
        order: 1
        http_authenticator:
          type: host #DEPRECATED, will be removed in a future version
          challenge: false
        authentication_backend:
          type: noop
      jwt_auth_domain:
        enabled: true
        order: 0
        http_authenticator:
          type: jwt
          challenge: false
          config:
            signing_key: "bmlzaGFudG5pc2hhbnQ="
            jwt_header: "Authorization"
            jwt_url_parameter: "Authorization"
            roles_key: roles
            subject_key: name
        authentication_backend:
          type: noop
      clientcert_auth_domain:
        enabled: false
        order: 2
        http_authenticator:
          type: clientcert
          config:
            username_attribute: cn #optional, if omitted DN becomes username
          challenge: false
        authentication_backend:
          type: noop
      ldap:
        enabled: false
        order: 5
        http_authenticator:
          type: basic
          challenge: false
        authentication_backend:
          # LDAP authentication backend (authenticate users against a LDAP or Active Directory)
          type: ldap # NOT FREE FOR COMMERCIAL USE
          config:
            # enable ldaps
            enable_ssl: false
            # enable start tls, enable_ssl should be false
            enable_start_tls: false
            # send client certificate
            enable_ssl_client_auth: false
            # verify ldap hostname
            verify_hostnames: true
            hosts:
              - localhost:8389
            bind_dn: null
            password: null
            userbase: 'ou=people,dc=example,dc=com'
            # Filter to search for users (currently in the whole subtree beneath userbase)
            # {0} is substituted with the username 
            usersearch: '(sAMAccountName={0})'
            # Use this attribute from the user as username (if not set then DN is used)
            username_attribute: null

What have a done wrong? I downloaded all the SG modules/jars 2 weeks ago - so i would expect the kibana plugin would include the functionality, as the one in the above link is for 5.4.0, while i am running 5.6.0.

Thanks

Jochen Kressin

unread,
Oct 28, 2017, 4:41:54 PM10/28/17
to Search Guard Community Forum
Why not have a look at the Kibana Plugin documentation? If you are seeing a login page, you probably have HTTP Basic Auth enabled  in the kibana.yml (or, better to say, you haven't disabled it). You are using a single sign on authenticator (JWT), so you don't need HTTP Basic Auth and a login page.

The configuration settings you need are in the docs

http://floragunncom.github.io/search-guard-docs/kibana.html

Section: "Using Kibana with JWT".

If the docs are unclear, please let us know so we can improve.

Paul Azad

unread,
Oct 30, 2017, 7:10:26 AM10/30/17
to Search Guard Community Forum
Hi, I have disabled the plugin, and now I get a authentication pop up. I have http auth at order 4, while jwt is 0 (my config is posted above).

The thing with the doc is that there is no end to end how to setup SG for XXX. I've all the config needed for JWT should be together. I remember last time I was trying to give SG a go, I had similar issues with AD integration.

Can you post a complete config of how to do jwt? I have misted my config - so god knows what is not working with kibana, because as I mentioned it's working with ES, so doesn't that mean I have got something right?

Reply all
Reply to author
Forward
0 new messages