LDAP Elasticsearch authentication no permissions for [cluster:monitor/health]-SOLVED

Hello,

i’m new to ELK and Search Guard.

I followed this guide and configured SSL: https://gryzli.info/2018/12/01/elasticsearchelk-stack-security-howto/

I’m trying to enable LDAP authentication using cn attribute, Elasticsearch server and Kibana are on the same machine (ekl.test.com)

I’m searching Default User OU with Domain Admin credentials (as a test), i created user user1 and put him in Administrators group (same group as Domain admin) and using cn as user name

cn=user1
samaccountname=user1

curl -Ss -k https://user1:Pasw0rd01@vm1.test.com:9200/_cluster/health

unauthorized

Error:

Authentication finally failed for user user1

Note i DIDN’T enable SSL between DC and elasticsearch/Kibana server

 ldap:
        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 # 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:
              - dc.test.com:389
            bind_dn: cn=Administrator,cn=Users,dc=test,dc=com
            password: Pass
            userbase: 'ou=Users,dc=test,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: cn

           authz:
  roles_from_myldap:
    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 # 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:
          - dc.test.com:389
        bind_dn: cn=Administrator,cn=Users,dc=test,dc=com
        password: Pass
        rolebase: 'ou=Users,dc=test,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: cn
        # Roles as an attribute of the user entry
        userrolename: disabled

kibana.yml (this is user from internal database) and it works fine.

elasticsearch.hosts: ["https://ekl.test.com:9200"]

elasticsearch.username: "admin"
elasticsearch.password: "Pass"

I can telnet to Domain controller on port 389

From documentation i can’t find how to point Kibana to use LDAP backend.

When trying to user LDAP username for authentication i’m getting error below, i;m getting

[WARN ][c.f.s.a.BackendRegistry ] [ekl.test.com] Authentication finally failed for user1 from 172.31.34.158:35678

same error when tried

curl -Ss -k https://user1:Pass@ekl.test.com:9200/_cluster/health

Can you please give me hint what needs to be reconfigured.

Thanks

Kibana does not connect to LDAP/AD, since all security features are implemented on Elasticsearch layer. Kibana uses the configured authentication/authorization modules on Elasticsearch/Search Guard to validate the credentials of the user.

So you need to first configure the connection settings:

And then authentication and authorization:


OK, in this picture is my test environment. In test group is user1 which needs to authenticate to Easticsearch
in service_account OU is service account which searches AD for users

In UA OU is user1 (member of test security group)

sg_config.yml

authc:
      kerberos_auth_domain:
        http_enabled: false
        transport_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:
        http_enabled: true
        transport_enabled: true
        order: 4
        http_authenticator:
          type: basic
          challenge: true
        authentication_backend:
          type: intern
      proxy_auth_domain:
        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:
        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:
        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:
        http_enabled: false
        transport_enabled: false
        order: 2
        http_auithenticator:
          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:
              - dc.test.com:389
            bind_dn: 'CN=service,OU=service_accounts,DC=test,DC=com'
            password: 'Pass'
            userbase: 'CN=test,OU=groups,DC=test,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: cn
    authz:
      roles_from_myldap:
        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 # 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:
              - dc.test.com:389
            bind_dn: 'CN=service,OU=service_accounts,DC=test,DC=com'
            password: 'Pass'
            rolebase: 'OU=UA,DC=test,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={1})'
            # Specify the name of the attribute which value should be substituted with {2} above
            userroleattribute: cn
            # 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: 'CN=test,OU=groups,DC=test,DC=com'
            # Filter to search for users (currently in the whole subtree beneath userbase)
            # {0} is substituted with the username
            usersearch: "(cn={0})"
            username_attribute: "cn"
            # 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:
        enabled: false
        authorization_backend:
          type: ldap # NOT FREE FOR COMMERCIAL USE
          #config goes here ...

sg_roles.yml

sg_ad_admin:
  readonly: true
  cluster:
    - UNLIMITED
  indices:
    '*':
      '*':
        - UNLIMITED
  tenants:
    admin_tenant: RW

sg_roles_mapping.yml

sg_ad_admin:
  readonly: true
  backendroles:
    - "CN=test,OU=groups,DC=test,DC=com"

user1 is cn

curl -Ss -k https://user1:Pass1@ekl.test.com:9200/_cluster/health

logs:

[2019-04-02T07:51:59,275][DEBUG][c.f.s.a.BackendRegistry  ] [ekl.test.com] user1 not cached, return from internal backend directly
[2019-04-02T07:51:59,275][DEBUG][c.f.s.a.BackendRegistry  ] [ekl.test.com] Can not authenticate user1 due to com.google.common.util.concurrent.UncheckedExecutionException: ElasticsearchSecurityException[user1 not found]
com.google.common.util.concurrent.UncheckedExecutionException: ElasticsearchSecurityException[user1 not found]
        at com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2050) ~[guava-25.1-jre.jar:?]
        at com.google.common.cache.LocalCache.get(LocalCache.java:3951) ~[guava-25.1-jre.jar:?]
        at com.google.common.cache.LocalCache$LocalManualCache.get(LocalCache.java:4870) ~[guava-25.1-jre.jar:?]
        at com.floragunn.searchguard.auth.BackendRegistry.authcz(BackendRegistry.java:694) [search-guard-6-6.6.2-24.2.jar:6.6.2-24.2]
        at com.floragunn.searchguard.auth.BackendRegistry.authenticate(BackendRegistry.java:523) [search-guard-6-6.6.2-24.2.jar:6.6.2-24.2]
        at com.floragunn.searchguard.filter.SearchGuardRestFilter.checkAndAuthenticateRequest(SearchGuardRestFilter.java:134) [search-guard-6-6.6.2-24.2.jar:6.6.2-24.2]
        at com.floragunn.searchguard.filter.SearchGuardRestFilter.access$000(SearchGuardRestFilter.java:50) [search-guard-6-6.6.2-24.2.jar:6.6.2-24.2]
        at com.floragunn.searchguard.filter.SearchGuardRestFilter$1.handleRequest(SearchGuardRestFilter.java:80) [search-guard-6-6.6.2-24.2.jar:6.6.2-24.2]
        at org.elasticsearch.rest.RestController.dispatchRequest(RestController.java:240) [elasticsearch-6.6.2.jar:6.6.2]
        at org.elasticsearch.rest.RestController.tryAllHandlers(RestController.java:336) [elasticsearch-6.6.2.jar:6.6.2]
        at org.elasticsearch.rest.RestController.dispatchRequest(RestController.java:174) [elasticsearch-6.6.2.jar:6.6.2]
        at com.floragunn.searchguard.ssl.http.netty.ValidatingDispatcher.dispatchRequest(ValidatingDispatcher.java:63) [search-guard-ssl-6.6.2-25.6.jar:6.6.2-25.6]
        at org.elasticsearch.http.netty4.Netty4HttpServerTransport.dispatchRequest(Netty4HttpServerTransport.java:551) [transport-netty4-client-6.6.2.jar:6.6.2]
        at org.elasticsearch.http.netty4.Netty4HttpRequestHandler.channelRead0(Netty4HttpRequestHandler.java:137) [transport-netty4-client-6.6.2.jar:6.6.2]
        at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:105) [netty-transport-4.1.32.Final.jar:4.1.32.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362) [netty-transport-4.1.32.Final.jar:4.1.32.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348) [netty-transport-4.1.32.Final.jar:4.1.32.Final]

Made some progress:

Now can authenticate but role is not mapped:

ldap:
        http_enabled: true
        transport_enabled: true
        order: 2
        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:
              - dc.test.com:389
            bind_dn: "CN=service,OU=service_accounts,DC=test,DC=com"
            password: "Pass"
            userbase: "OU=UA,DC=test,DC=com"
            # Filter to search for users (currently in the whole subtree beneath userbase)
            # {0} is substituted with the username
            usersearch: "(cn={0})"
            # Use this attribute from the user as username (if not set then DN is used)
            username_attribute: "cn"
    authz:
      roles_from_myldap:
        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 # 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:
              - "dc.test.com:389"
            bind_dn: "CN=service,OU=service_accounts,DC=test,DC=com"
            password: "Pass"
            #rolebase: "OU=UA,DC=test,DC=com"
            rolebase: "CN=test,OU=groups,DC=test,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: 'CN=test,OU=groups,DC=test,DC=com'
            # Filter to search for users (currently in the whole subtree beneath userbase)
            # {0} is substituted with the username
            usersearch: "(cn={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:
        enabled: false
        authorization_backend:
curl -Ss -k https://user1:Pass@ekl.test.com:9200/_cluster/health
{"error":{"root_cause":[{"type":"security_exception","reason":"no permissions for [cluster:monitor/health] and User [name=user1, roles=[], requestedTenant=null]"}],"type":"security_exception","reason":"no permissions for [cluster:monitor/health] and User [name=user1, roles=[], requestedTenant=null]"},"status":403}

The LDAP authz section is still disabled in your config:

You need to enable both authc and authz.

I then suggest that you use the authinfo endpoint to check if your LDAP user has the correct backend roles (i.e. LDAP groups), and if these backend roles are mapped correctly to Search Guard roles:

https://docs.search-guard.com/latest/troubleshooting-search-guard-user-roles

If your LDAP user has the correct role but you get permission errors, there is also a troubleshooting guide on how to debug permission problems:

Thanks, now works fine, thanks for the tip !!