Opensearch not able to connect with openid keycloak

Hello,
I am unable to connect with OpenSearch with keycloak.
Below is the config.yml

_meta:
  type: "config"
  config_version: 2


config:
  dynamic:
    # Set filtered_alias_mode to 'disallow' to forbid more than 2 filtered aliases per index
    # Set filtered_alias_mode to 'warn' to allow more than 2 filtered aliases per index but warns about it (default)
    # Set filtered_alias_mode to 'nowarn' to allow more than 2 filtered aliases per index silently
    #filtered_alias_mode: warn
    #do_not_fail_on_forbidden: false
    #kibana:
    # Kibana multitenancy
    #multitenancy_enabled: true
    #server_username: kibanaserver
    #index: '.kibana'
    http:
      anonymous_auth_enabled: false
      xff:
        enabled: false
        internalProxies: '192\.168\.0\.10|192\.168\.0\.11' # regex pattern
        #internalProxies: '.*' # trust all internal proxies, regex pattern
        #remoteIpHeader:  'x-forwarded-for'
        ###### see https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html for regex help
        ###### more information about XFF https://en.wikipedia.org/wiki/X-Forwarded-For
        ###### and here https://tools.ietf.org/html/rfc7239
        ###### and https://tomcat.apache.org/tomcat-8.0-doc/config/valve.html#Remote_IP_Valve
    authc:
      kerberos_auth_domain:
        http_enabled: false
        transport_enabled: false
        order: 6
        http_authenticator:
          type: kerberos
          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:
        description: "Authenticate via HTTP Basic against internal users database"
        http_enabled: true
        transport_enabled: true
        order: 4
        http_authenticator:
          type: basic
          challenge: false
        authentication_backend:
          type: intern
      openid_auth_domain:
        description: "Authenticate via Keycloak OpenID"
        http_enabled: true
        transport_enabled: true
        order: 1
        http_authenticator:
          type: openid
          challenge: false
          config:
            subject_key: preferred_username
            roles_key: roles
            openid_connect_url: http://<keycloak host>/auth/realms/<realm>/.well-known/openid-configuration
            verify_hostnames: false
            enable_ssl: false
      proxy_auth_domain:
        description: "Authenticate via proxy"
        http_enabled: false
        transport_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
      jwt_auth_domain:
        description: "Authenticate via Json Web Token"
        http_enabled: false
        transport_enabled: false
        order: 0
        http_authenticator:
          type: jwt
          challenge: false
          config:
            signing_key: "base64 encoded HMAC key or public RSA/ECDSA pem key"
            jwt_header: "Authorization"
            jwt_url_parameter: null
            roles_key: null
            subject_key: null
        authentication_backend:
          type: noop
      clientcert_auth_domain:
        description: "Authenticate via SSL client certificates"
        http_enabled: false
        transport_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:
        description: "Authenticate via LDAP or Active Directory"
        http_enabled: false
        transport_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
          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
    authz:
      roles_from_myldap:
        description: "Authorize via LDAP or Active Directory"
        http_enabled: false
        transport_enabled: false
        authorization_backend:
          # LDAP authorization backend (gather roles from a LDAP or Active Directory, you have to configure the above LDAP authentication backend settings too)
          type: ldap
          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
            rolebase: 'ou=groups,dc=example,dc=com'
            # Filter to search for roles (currently in the whole subtree beneath rolebase)
            # {0} is substituted with the DN of the user
            # {1} is substituted with the username
            # {2} is substituted with an attribute value from user's directory entry, of the authenticated user. Use userroleattribute to specify the name of the attribute
            rolesearch: '(member={0})'
            # Specify the name of the attribute which value should be substituted with {2} above
            userroleattribute: null
            # Roles as an attribute of the user entry
            userrolename: disabled
            #userrolename: memberOf
            # The attribute in a role entry containing the name of that role, Default is "name".
            # Can also be "dn" to use the full DN as rolename.
            rolename: cn
            # Resolve nested roles transitive (roles which are members of other roles and so on ...)
            resolve_nested_roles: true
            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: '(uid={0})'
            # Skip users matching a user name, a wildcard or a regex pattern
            #skip_users:
            #  - 'cn=Michael Jackson,ou*people,o=TEST'
            #  - '/\S*/'
      roles_from_another_ldap:
        description: "Authorize via another Active Directory"
        http_enabled: false
        transport_enabled: false
        authorization_backend:
          type: ldap
          #config goes here ...

I am successfully able to get keycloak login page but after that getting ERR_TOO_MANY_REDIRECTS

I put the log level to debug then to I am not getting anything informatic in the logs
But it is working fine with basic auth.
Please guide me here to resolve this issue

@Vivek123 Can you provide opensearch-dashboards.yml configuration?

Thank you for reply
Here is the opensearch_dashboard.yml

opensearch.hosts: https://opensearch-cluster-master:9200
opensearch.password: kibanaserver
opensearch.requestHeadersWhitelist:
- securitytenant
- Authorization
opensearch.ssl.verificationMode: none
opensearch.username: kibanaserver
opensearch_security.auth.type: openid
opensearch_security.cookie.secure: true
opensearch_security.multitenancy.enable_filter: false
opensearch_security.multitenancy.enabled: true
opensearch_security.multitenancy.tenants.enable_global: true
opensearch_security.multitenancy.tenants.enable_private: true
opensearch_security.multitenancy.tenants.preferred:
- Private
- Global
opensearch_security.openid.base_redirect_url: http://<kibana_ip>:32001
opensearch_security.openid.client_id: obf
opensearch_security.openid.client_secret: 044ffaff-75e4-4d04-974f-09b82a0711fc
opensearch_security.openid.connect_url: http://<keycloak_ip>:8080:/auth/realms/master/.well-known/openid-configuration
opensearch_security.openid.verify_hostnames: false
opensearch_security.readonly_mode.roles:
- kibana_read_only
server.host: 0.0.0.0

@Vivek123 what port is kibana/dashboards is running on? The below entry doesn’t seems to make much sense if this is your complete config file as the default would be 5601:
http://<kibana_ip>:32001

I am running it on kubernetes , container port of kibana is 5601. But I am using it with node port and external IP since I have given my node port

@Vivek123 Can you share your keycloak config, in particular the Valid Redirect URIs


Here 10.196.18.85 is my node IP

@Vivek123 I’m not sure why the node ip is there. Can you try http://<kibana_ip>:32001 instead?

Or better yet, perhaps try ‘*’ as a starting point and work from there?

I have also tried with ‘*’ option in redirect url then to getting same issue. It is getting redirected again and again and not showing any error log in opensearch elasticsearch


I am also able to check, the session is getting created in keycloak

So I think issue is opensearch kibana side only

@Vivek123 in my experience the infinite redirect is a result of misconfiguration from either side.
Can you ensure that openid_connect_url in config.yml matches the opensearch_security.openid.connect_url in opensearch_dashboards.yml (and includes the port)

Please also remove the verify_hostnames: false in both configs.

Can you also change basic_internal_auth_domain to order 0, with challenge kept as false and set this flag to true on the openid_auth_domain.

Having said this, I have yet to run this using nodeport, have you tried using ingress without changing ports, just to even check if the config is correct?

Here is my opensearch.yml

cluster.name: opensearch-cluster

# Bind to all interfaces because we don't know what IP address Docker will assign to us.
network.host: 0.0.0.0

# # minimum_master_nodes need to be explicitly set when bound on a public IP
# # set to 1 to allow single node clusters
# discovery.zen.minimum_master_nodes: 1

# Setting network.host to a non-loopback address enables the annoying bootstrap checks. "Single-node" mode disables them again.
# discovery.type: single-node

# Start OpenSearch Security Demo Configuration
# WARNING: revise all the lines below before you go into production
plugins:
  security:
    ssl:
      transport:
        pemcert_filepath: esnode.pem
        pemkey_filepath: esnode-key.pem
        pemtrustedcas_filepath: root-ca.pem
        enforce_hostname_verification: false
      http:
        enabled: true
        pemcert_filepath: esnode.pem
        pemkey_filepath: esnode-key.pem
        pemtrustedcas_filepath: root-ca.pem
    allow_unsafe_democertificates: true
    allow_default_init_securityindex: true
    authcz:
      admin_dn:
        - CN=kirk,OU=client,O=client,L=test,C=de
    audit.type: internal_opensearch
    enable_snapshot_restore_privilege: true
    check_snapshot_restore_write_privileges: true
    restapi:
      roles_enabled: ["all_access", "security_rest_api_access"]
    system_indices:
      enabled: true
      indices:
        [
          ".opendistro-alerting-config",
          ".opendistro-alerting-alert*",
          ".opendistro-anomaly-results*",
          ".opendistro-anomaly-detector*",
          ".opendistro-anomaly-checkpoints",
          ".opendistro-anomaly-detection-state",
          ".opendistro-reports-*",
          ".opendistro-notifications-*",
          ".opendistro-notebooks",
          ".opendistro-asynchronous-search-response*",
        ]
######## End OpenSearch Security Demo Configuration ########

config.yml

_meta:
  type: "config"
  config_version: 2
config:
  dynamic:
    authc:
      basic_internal_auth_domain:
        http_enabled: true
        transport_enabled: true
        order: 0
        http_authenticator:
          type: basic
          challenge: false
        authentication_backend:
          type: intern

      openid_auth_domain:
        http_enabled: true
        transport_enabled: true
        order: 1
        http_authenticator:
          type: openid
          challenge: true
          config:
            openid_connect_idp:
              enable_ssl: true
              pemtrustedcas_filepath: /usr/share/opensearch/config/certificates/ca/ca.pem
            subject_key: preferred_username
            roles_key: roles
            openid_connect_url: https://10.196.18.106:8443/auth/realms/master/.well-known/openid-configuration
        authentication_backend:
          type: noop

opensearch_dashboard.yml

opensearch.hosts: https://opensearch-cluster-master:9200
opensearch.password: kibanaserver
opensearch.requestHeadersWhitelist:
- securitytenant
- Authorization
opensearch.ssl.verificationMode: none
opensearch.username: kibanaserver
opensearch_security.auth.type: openid
opensearch_security.cookie.secure: true
opensearch_security.multitenancy.enable_filter: false
opensearch_security.multitenancy.enabled: true
opensearch_security.multitenancy.tenants.enable_global: true
opensearch_security.multitenancy.tenants.enable_private: true
opensearch_security.multitenancy.tenants.preferred:
- Private
- Global
opensearch_security.openid.base_redirect_url: http://10.196.18.106:32001
opensearch_security.openid.client_id: obf
opensearch_security.openid.client_secret: 044ffaff-75e4-4d04-974f-09b82a0711fc
opensearch_security.openid.connect_url: https://10.196.18.106:8443/auth/realms/master/.well-known/openid-configuration
opensearch_security.readonly_mode.roles:
- kibana_read_only

keycloak config (redirect url)

Here are my all configuration files and I have followed what you have suggested me then to I am getting the same error.

Please help here

Could you please open Developer tools in browser, reproduce the error and see the error, that might give a pointer or two.

Here is the network option of developer tool

After login it is going to hit below api

http://10.196.18.106:32001/auth/openid/login?state=ym7HLky7hk0Y_2JRrxiOsF&session_state=3fd75767-016a-49a0-8b22-758ecba28532&code=513d71d2-ec52-42aa-8d2a-c522f891a6d8.3fd75767-016a-49a0-8b22-758ecba28532.0d8b0c3a-1099-4ae0-8f49-d62d2e737587
then above api redirects to below api

http://10.196.18.106:32001/auth/openid/login
then it redirects to below api

https://10.196.18.106:8443/auth/realms/master/protocol/openid-connect/auth?client_id=obf&response_type=code&redirect_uri=http%3A%2F%2F10.196.18.106%3A32001%2Fauth%2Fopenid%2Flogin&state=HSTDn4z9iSaTFpqHynEJVy&scope=openid%20profile%20email%20address%20phone

and after that below api get hit

http://10.196.18.106:32001/auth/openid/login?state=HSTDn4z9iSaTFpqHynEJVy&session_state=3fd75767-016a-49a0-8b22-758ecba28532&code=9a90002f-3478-47b3-b138-f82ad9f81242.3fd75767-016a-49a0-8b22-758ecba28532.0d8b0c3a-1099-4ae0-8f49-d62d2e737587

then this flow go in loop and hit infinite times

@Vivek123 I see that you have changed to https for keycloak, I’ve seen cases where mixing http and https causes issues. Can you set up https on OpenSearch Dashboards and update both of the config files (config.yml and opensearch_dashboards.yml).

Also, to ensure the certificate is not causing any issues, would you be able to extract the certificate from the browser when you connect to keycloak and save it under: pemtrustedcas_filepath (see similar case here)

Hello Anthony,
After a lot of debugging I put some logger in opensearch-security code and got some below logs

It is showing No ‘Basic Authorization’ header. That means it is taking the auth type is basic auth instead of OAuth(openid). So suggest to me what I am doing wrong here.
Here is the config.yml

Replied to your last message: I have also tried to put opensearch-dashboard on https but problem is still same

@Vivek123 can you update the config as per above?
basic_auth order 0, challenge false,
openID order 1, challenge true.

Once this is updated and loaded into to security index, can you paste the JWT (Bearer token) that you are seeing in the logs. (in text format)

Also looking at your keycloak setup, although I’ve a different version to you, I have below set to “On”:
Service Account Enabled
Authorization Enabled

(Not sure if it makes any difference)

Here is the config as you suggested

I also made changes in keycloak

But the result is the same :frowning:

Here I have tried to login in the OpenSearch elasticsearch with this bearer token but it is showing unauthorized
Here is the jwt token :

eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJ3enFXb3dnNVFEclhHMVNfb252aVlyd2QxNmFoTjAyQVNpc3hzek9IXy0wIn0.eyJleHAiOjE2NDY5MzE4NjIsImlhdCI6MTY0Njg5NTkyOCwiYXV0aF90aW1lIjoxNjQ2ODk1ODYyLCJqdGkiOiIyNmViZGViZS05YzBhLTQ0NjAtYjI4Ni1kMjhjZTE4NTI5NDIiLCJpc3MiOiJodHRwczovLzEwLjE5Ni4xOC4xMTU6ODQ0My9hdXRoL3JlYWxtcy9tYXN0ZXIiLCJhdWQiOiJvYmYiLCJzdWIiOiI2YzJhMjk4Ny1kNGVhLTQ2ZjEtOTM0OS00NmExMzAwZDFiYzQiLCJ0eXAiOiJJRCIsImF6cCI6Im9iZiIsInNlc3Npb25fc3RhdGUiOiI0NDBiYzlmZC1jNWVmLTRmNDQtOTI1Yi0wZDU0Nzg1MDNiZDgiLCJhdF9oYXNoIjoiQW02ZGQ1TDJrYklFeXk2Wkk2NjFGQSIsImFjciI6IjAiLCJzaWQiOiI0NDBiYzlmZC1jNWVmLTRmNDQtOTI1Yi0wZDU0Nzg1MDNiZDgiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImFkZHJlc3MiOnt9LCJyb2xlcyI6WyJjcmVhdGUtcmVhbG0iLCJkZWZhdWx0LXJvbGVzLW1hc3RlciIsIm9mZmxpbmVfYWNjZXNzIiwiYWRtaW4iLCJ1bWFfYXV0aG9yaXphdGlvbiJdLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJhZG1pbiJ9.IRJRw-naNOB-f2o3qoc2tN6_jsbo3SUy1-EZCgJmA8fEWFE5aKi-sCFMP7POw1x38n2Q8RSnP9sD8SDxbN50B3jUzT_IioQoFrZ6-lvmI99f9PkHb5yJPN2MdsiXQK3qL2SUjTP9HXj42qW_wu18UXdrcNqNWZUtjRS8Kg2-O-l3yXxgcuyI4qxjLLffCPcwHXkvQdmdZr-c5OXdL3cjS0d8gl5azztTYprP8wvcTrBqRXULofP4Fp6m2Nsx7KQycyJkQcJvRxuFEAWecOdle6ybvrkXbUSNh5dcj-WvB7NR7xHohNkGswfnej0WJxj1soght_9ZIH8IJAHF5UmekA

I have also put the keycloak logs to debug mode and got the below message that
Could not find cookie KEYCLOAK_SESSION,

Please suggest me if you find anything informative with the above information

@Vivek123 regarding the cookie, I noticed you have below setting in opensearch_dashboards.yml:
opensearch_security.cookie.secure: true

This would indicate that the cookie would only be sent via secure channel (TLS).
You can try to either set this to ‘false’ or enable TLS on the dashboards using below:

server.ssl.enabled: true
server.ssl.key: /usr/share/opensearch-dashboards/config/opensearch.example.org.key
server.ssl.certificate: /usr/share/opensearch-dashboards/config/opensearch.example.org.cert

Having said this, I have yet to try keycloak authentication in kubernetes, there might be a need for additional configuration on ingress

@Vivek123 Can you confirm that you have keycloak and opensearch running on a same cluster? And you use NodePorts to expose both? Once I hear back I will try to reproduce from my end.

Hi @Vivek123
I am having the same problem, have you found out what the problem is?