Skip to content

Use JsonPath functions to compute username claim #292

@mlallaouret

Description

@mlallaouret

Description

Hello,

I am deploying a strimzi cluster that use strimzi-kafka-oauth for the user authentication and configuring kubernetes as authorization server to use service account token to extract information about the user.

Here is the configuration I used for my listener:

- name: oauth
  port: 9094
  tls: false
  type: internal
  authentication:
    type: custom
    sasl: true
    listenerConfig:
      connections.max.reauth.ms: 3600000
      sasl.enabled.mechanisms: OAUTHBEARER
      oauthbearer.sasl.server.callback.handler.class: io.strimzi.kafka.oauth.server.JaasServerOauthValidatorCallbackHandler
      principal.builder.class: io.strimzi.kafka.oauth.server.OAuthKafkaPrincipalBuilder
      oauthbearer.sasl.jaas.config: >
        org.apache.kafka.common.security.oauthbearer.OAuthBearerLoginModule required
        unsecuredLoginStringClaim_sub="useless"
        oauth.valid.issuer.uri="<clusterIssuerUri>"
        oauth.jwks.endpoint.uri="https://kubernetes.default.svc.cluster.local/openid/v1/jwks"
        oauth.server.bearer.token.location="/var/run/secrets/kubernetes.io/serviceaccount/token"
        oauth.username.claim="<usernameClaim>"
        oauth.check.access.token.type="false"
        oauth.include.accept.header="false"
        oauth.ssl.truststore.location="/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
        oauth.ssl.truststore.type="PEM";

And here is an example of kubernetes token:

{
  "aud": [
    "<audience>"
  ],
  "exp": 1798103464,
  "iat": 1766567464,
  "iss": "<iss>",
  "jti": "<jti>",
  "kubernetes.io": {
    "namespace": "default",
    "node": {
      "name": "<nodeName>",
      "uid": "<uid>"
    },
    "pod": {
      "name": "<podName>",
      "uid": "<uid>"
    },
    "serviceaccount": {
      "name": "my-service-account",
      "uid": "<uid>"
    },
    "warnafter": 1766571071
  },
  "nbf": 1766567464,
  "sub": "system:serviceaccount:default:my-service-account"
}

What is working well for the oauth.username.claim property is to set it to ['kubernetes.io']['serviceaccount']['name'], it allows to retrieve the name of the service account and to bind it to an existing KafkaUser.
But with this solution we miss the binding of the namespace and the service account name.
I saw that you are using the JsonPath library to extract information from JWT token. In their documentation they mention function can be used in path expression: https://github.com/json-path/JsonPath/tree/json-path-2.10.0?tab=readme-ov-file#functions

I tried this solution in a java project to ensure they work with JsonPath library:

String result = JsonPath.read(token, "concat($.['kubernetes.io']['namespace'], \"-\", $.['kubernetes.io']['serviceaccount']['name'])");

But when using this expression in the property oauth.username.claim it does not work.

It gives me this kind of error

io.strimzi.kafka.oauth.validator.ValidationException: Failed to extract principal - check usernameClaim, fallbackUsernameClaim configuration
	at io.strimzi.kafka.oauth.validator.JWTSignatureValidator.extractPrincipal(JWTSignatureValidator.java:509) ~[kafka-oauth-common-0.17.1.jar:?]
	at io.strimzi.kafka.oauth.validator.JWTSignatureValidator.validate(JWTSignatureValidator.java:493) ~[kafka-oauth-common-0.17.1.jar:?]
	at io.strimzi.kafka.oauth.server.JaasServerOauthValidatorCallbackHandler.validateToken(JaasServerOauthValidatorCallbackHandler.java:739) [kafka-oauth-server-0.17.1.jar:?]
	at io.strimzi.kafka.oauth.server.JaasServerOauthValidatorCallbackHandler.handleCallback(JaasServerOauthValidatorCallbackHandler.java:681) [kafka-oauth-server-0.17.1.jar:?]
	at io.strimzi.kafka.oauth.server.JaasServerOauthValidatorCallbackHandler.delegatedHandle(JaasServerOauthValidatorCallbackHandler.java:664) [kafka-oauth-server-0.17.1.jar:?]
	at io.strimzi.kafka.oauth.server.JaasServerOauthValidatorCallbackHandler.handle(JaasServerOauthValidatorCallbackHandler.java:643) [kafka-oauth-server-0.17.1.jar:?]
	at org.apache.kafka.common.security.oauthbearer.internals.OAuthBearerSaslServer.process(OAuthBearerSaslServer.java:156) [kafka-clients-4.1.1.jar:?]
	at org.apache.kafka.common.security.oauthbearer.internals.OAuthBearerSaslServer.evaluateResponse(OAuthBearerSaslServer.java:101) [kafka-clients-4.1.1.jar:?]
	at org.apache.kafka.common.security.authenticator.SaslServerAuthenticator.handleSaslToken(SaslServerAuthenticator.java:461) [kafka-clients-4.1.1.jar:?]
	at org.apache.kafka.common.security.authenticator.SaslServerAuthenticator.authenticate(SaslServerAuthenticator.java:286) [kafka-clients-4.1.1.jar:?]
	at org.apache.kafka.common.network.KafkaChannel.prepare(KafkaChannel.java:181) [kafka-clients-4.1.1.jar:?]
	at org.apache.kafka.common.network.Selector.pollSelectionKeys(Selector.java:548) [kafka-clients-4.1.1.jar:?]
	at org.apache.kafka.common.network.Selector.poll(Selector.java:486) [kafka-clients-4.1.1.jar:?]
	at kafka.network.Processor.poll(SocketServer.scala:1010) [kafka_2.13-4.1.1.jar:?]
	at kafka.network.Processor.run(SocketServer.scala:914) [kafka_2.13-4.1.1.jar:?]
	at java.base/java.lang.Thread.run(Thread.java:840) [?:?]

Did someone already tried it or is there a documentation about how to make it work with strimzi-kafka-oauth ?

Environment

  • Strimzi version: 0.49.1
  • Kafka version: 4.1.1
  • Auth provider: kubernetes

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions