Failed LDAP authentication does not capture user id

hi,

When using LDAP authentication, with audit logging enabled, if user attempts logging in with incorrect id/pwd, though the FORBIDDEN status is captured in audit index, along with all other attributes (content, action, path, type, etc), the audit record does not capture the actual userid itself which was attempting to perform the action. So if anyone tries to generate report using this audit index, we wont have the actual user to investigate against.

If we enable info mode, for successful request, user id is captured. But for failed request, user id is still not captured.

As part of the audit information, the actual user id should be captured for LDAP authentication and made available on the ROR audit index.

I somehow vaguely recall reporting this in version 1.16.x also (may be as passing comment in another thread). But I see this behavior in 1.18.7 as well. Hence opened a new thread.

Please let me know if you need any further details.

Thanks
askids

This appears to be the same as ReadonlyRest login fail hook function

Which we provided a solution for, but in form of custom serializer jar. Which I re-link here: ror-cust-serializer-basic-auth-failure-log-user.jar. And can be installed as per documentation.

@coutoPL we should have an issue for this, right?

Thanks @sscarduzio. But we are using tech.beshu.ror.requestcontext.QueryAuditLogSerializer for the audit serializer. This captures actual content of the executed query. So will the new custom serializer also have this feature?

No it will only be different from the default one for the populated user field at the moment.

yes, we have. Default serializer will be improved soon.

Unfortunately, for us, both (user id and query) are key info to be captured during audit logging for failed attempts. So looks like we will need to wait for the fix to be made available in the QueryAuditLogSerializer.

@sscarduzio maybe you can build your serializer once again, with a small change - extending QueryAuditLogSerializer?

I wrote this:

package tech.beshu.ror.audit.instances

import java.util.Base64

import org.json.JSONObject
import tech.beshu.ror.audit.AuditResponseContext
import tech.beshu.ror.audit.AuditResponseContext.Forbidden

import scala.util.Try

class ScalaCustomAuditLogSerializer extends QueryAuditLogSerializer {

  override def onResponse(responseContext: AuditResponseContext): Option[JSONObject] = {

    super.onResponse(responseContext).map(event => {
      responseContext match {
        case Forbidden(_) => {
          if (responseContext.requestContext.uriPath.startsWith("/_readonlyrest/metadata/current_user")) {
            val header = responseContext.requestContext.headers("authorization")
            val user = Try{
              val encoded = header.strip().split(" ",2)(1).trim
              new String(Base64.getDecoder.decode(encoded)).split(":")(0)
            }.getOrElse("raw_authorization_header " + header)
            event.put("user", user)
          }
        }
        case _ => ()
      }
      event
    })
  }
}

Download the assembled jar from here: [ror-cust-serializer-basic-auth-failure-log-user-with-query.jar](https://readonlyrest-data.s3.eu-west-1.amazonaws.com/tmp/ror-cust-serializer-basic-auth-failure-log-user-with-query.jar

Looks like the link has expired :frowning: Can you please provide a new download link?

Also, will this be available with any of the new versions that will be available for download anytime soon? I am asking about it because though i can test this jar in lower lanes, i wont be able to copy individual jar to higher lanes and will need to plan to upgrade it with ROR upgrade. So please let me know.

Thanks!

Now the URL is unprotected. Try again.

For google and whomever will benefit from this, the code behind the custom serializer jar is this:

class ScalaCustomAuditLogSerializer extends QueryAuditLogSerializer {

  override def onResponse(responseContext: AuditResponseContext): Option[JSONObject] = {

    super.onResponse(responseContext).map(event => {
      responseContext match {
        case Forbidden(_) => {
          if (responseContext.requestContext.uriPath.startsWith("/_readonlyrest/metadata/current_user")) {
            val header = responseContext.requestContext.headers("authorization")
            val user = Try{
              val encoded = header.strip().split(" ",2)(1).trim
              new String(Base64.getDecoder.decode(encoded)).split(":")(0)
            }.getOrElse("raw_authorization_header " + header)
            event.put("user", user)
          }
        }
        case _ => ()
      }
      event
    })
  }
}

Also please note that from 1.19.0 onwards, we are including the username of the failed basic authentication in the ES text logs and in the audit log indices.
Should we fail parsing the attempted username as basic auth, we will print the whole Authorization header as “raw_header: xxxx” in the audit logs, and NOT in the ES text logs (for security reasons).

@sscarduzio I still dont see the user id logged in ROR audit index. Also, in ES log file, I see this below error logged numerous times (almost 2000+). Interestingly, when using AD group, it works fine for some users in same AD group. But others continuously get password prompts and then forbidden error message from ROR.

[2020-01-20T04:22:20,324][ERROR][t.b.r.a.b.d.l.i.UnboundidLdapAuthenticationService] [MYNODENAME] LDAP authenticate operation failed - cause [80090308: LdapErr: DSID-0C09042F, comment: AcceptSecurityContext error, data 52e, v2580 ]

Also, noticed that there was a NULL character written after v2580 (at the end, but before ]) because of which we are not able to copy from log directly.

please, check out 1.19.0. A serializer like the one above is now a default one.

This log just say, that your user entered invalid credentials (52e - Invalid credentials). There is nothing wrong with it.

We were previously using tech.beshu.ror.requestcontext.QueryAuditLogSerializer. Should we switch back to that if we upgrade to 1.19.0?

This one is deprecated. Please, use tech.beshu.ror.audit.instances. QueryAuditLogSerializer

Thanks. Will try and let you know.

I tried 1.19.0, but had to rollback as we ran into a major issue. We started getting Index not found error, when running queries from Kibana for all queries. We have LDAP rules setup for users.Typically, we would get a prompt for user to enter the user id/pwd. But after upgrade to 1.19.0, we are directly getting “index_not_found_exception”. In the index name, we see that index name is different than what we are querying. If we ran GET on myindex/_search, then the index name would show myindex_ROR_cRx35NQiOa.

We are using 7.2.0 on Windows 2012 R2, running default JDK.

Thanks!

@askids yes, this was introduced in last update. See doc: https://github.com/beshu-tech/readonlyrest-docs/blob/master/elasticsearch.md#indices

This is weird, because our change should not affect old configs. Previously you will get 403 Forbidden, now you get 404 index not found.

please send us your configuration, requests which is sent to ES + ROR and ES logs.

I will try to send it tomorrow. In previous instances, we would have first got a forbidden message behind the scenes (we can see it in log), but in Kibana it would have prompted for username/password immediately. Once we provide a valid username/password, it would then executive the query and return results. In new version, since there is no prompt at all, there is no way for user to enter credentials.

yes, but this is not valid any more. User should have a feeling that he is alone on the cluster, so when he asks for an index which is not allowed for him, he should see the same response when he calls an index which really doesn’t exist.