Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support disabling the Accept header when requesting Json Web Key Sets. #201

Merged
merged 9 commits into from
Aug 9, 2023

Conversation

hmadison
Copy link
Contributor

For certain servers which provide Json Web Key Sets, such as the Kubernetes API Server, if the Accept header sent by the client is present it is expected to be application/jwk-set+json. Based off of the discussion in the Strimzi slack channel, I've added an option which allows for users to skip sending the Accept header when making the JWKS request.

I've also updated the test documentation to cover how to use MiniKube and Helm to simplify the testing process and included an example of how to use this feature to authenticate into a Kafka cluster with a Service Account's token. To validate this locally, you can apply kafka-oauth-single-authz-service-accounts.yaml to a Kubernetes cluster with the operator installed and using a version of strimzi-kafka-oauth installed then:

  • Generate a JWT for the default service account in the default namespace with kubectl create token default.
  • Enter the Kafka pod via kubectl exec -it pod/test-cluster-kafka-0 -- /bin/bash.
  • Connect to the cluster via one of the included scripts usingenv OAUTH_ACCESS_TOKEN="<token>" bin/kafka-console-producer.sh --broker-list test-cluster-kafka-bootstrap.default:9092 --topic my-topic --producer-property 'security.protocol=SASL_PLAINTEXT' --producer-property 'sasl.mechanism=OAUTHBEARER' --producer-property 'sasl.jaas.config=org.apache.kafka.common.security.oauthbearer.OAuthBearerLoginModule required;' --producer-property 'sasl.login.callback.handler.class=io.strimzi.kafka.oauth.client.JaasClientOauthLoginCallbackHandler'

hmadison added 3 commits July 27, 2023 08:44
When attemping to use Kubernetes' api-server as the source of
JWK keys for JWT validation, it will only process requests which
either do not have the "Accept" header set or requests which
have the header set to "application/jwk-set+json".

Signed-off-by: Hunter Madison <[email protected]>
@hmadison hmadison force-pushed the hm/jwks-skip-header branch from e217d8b to ce72e6b Compare July 27, 2023 12:45
Comment on lines 38 to 43
* `kafka-oauth-single-authz-service-accounts.yaml`

A single node Kafka cluster using [service account tokens](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#service-account-tokens) for authorization and the `simple` authorizer.
It requires that the `kube-root-ca.crt` be copied from its ConfigMap to a Secret:

kubectl get configmap/kube-root-ca.crt -o=json | jq -r '.data."ca.crt"' | kubectl create secret generic kube-root-ca --from-file=ca.crt=/dev/stdin
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this really something what works with every single Kubernetes cluster based on Service accounts only? Or does it really on OpenID provider?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Kubernetes api-server supports OpenID Connect to validate the credentials which (since version 1.22) get projected into a pod based off of it's service account. You don't need anything outside of a Kubernetes cluster to make use of this authentication.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, interesting. I will have to try that.

Comment on lines 26 to 29
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: "system:anonymous"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should have the service account of the Kafka brokers I guess and not anonymous?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since strimizi-kafka-oauth isn't setting its Bearer token to the one projected into the container, you need to allow system:anonymous to access the endpoints needed to retrieve the OpenID Connect settings and the Json Web Key Sets.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If that is not enabled for everyone by default, it means this is likely a security risk? Should it really be used here? Shouldn't you instead use the broker's service account?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the same applies to system:serviceaccount:default:default -> maybe you should use some better example SA then the default one.

I'm also not sure I follow the relation ship of this example to the PR here. How do they relate? You do not seem to use any new flag in the example to disable the Accept header. So while the Kubernetes example is interesting, I do not follow the relationship here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm setting the value of oauth.include.accept.header to false via spec.kafka.jvmOptions.javaSystemProperties on line 63.

From my perspective, the ODIC and JWKS endpdoints in a "normal" application are exposed over the public internet. If someone is in a position to make a web request from inside of the kubernetes cluster to these endpoints, they are also going to be able to access the token for the service account and make those requests with authentication. If we want to use the brokers service account, then we should back this example out of this pull request and I'll open a new one which includes a dedicated authenticator for Kubernetes service accounts.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll need to add the includeAcceptHeader config option to oauth listener authentication and keycloak authorization when we integrate the next OAuth release into the Strimzi Operator (another PR). We can then improve the example Kafka CR.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm setting the value of oauth.include.accept.header to false via spec.kafka.jvmOptions.javaSystemProperties on line 63.

I missed that part. As mentioned by Marko, this will need to be added tot he options after the next OAuth release. Until then it is a bit misleading since it will not work with OAuth 0.13.

From my perspective, the ODIC and JWKS endpdoints in a "normal" application are exposed over the public internet. If someone is in a position to make a web request from inside of the kubernetes cluster to these endpoints, they are also going to be able to access the token for the service account and make those requests with authentication. If we want to use the brokers service account, then we should back this example out of this pull request and I'll open a new one which includes a dedicated authenticator for Kubernetes service accounts.

It seems that from Kubernetes perspective it is not something what they thing should be public. Also, thing like this will discourage users from using it as it does not look secure. If this works on any Kubernetes cluster, I think this would be a great usecase. But I think we need to work out the details to make sure it uses the service account and does not connect over as anonymous. So my vote would be to move this to a separate PR and do this through the broker service account.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fine by me. I'll back this change out and start on a dedicated Kubernetes service account configuration which supports using the pod's service account token to authenticate with the api server.

@scholzj scholzj requested a review from mstruk July 27, 2023 21:10
Copy link
Contributor

@mstruk mstruk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR. As I mentioned in another comment I think this new option constant should really live in Config.java, and functionally be used not only in JWTSignatureValidator.java but also in OAuthIntrospectionValidator.java, and in KeycloakRBACAuthorizer.java and JaasClientOauthLoginCallbackHandler.

In KeycloakRBACAuthorizer.java the analogous option should also be introduced strimzi.authorization.include.accept.header that falls back to oauth.include.accept.header the same way some other options in there do.

@hmadison hmadison force-pushed the hm/jwks-skip-header branch from 5c1b7fe to 2c41701 Compare July 28, 2023 16:35
@hmadison hmadison requested a review from mstruk July 28, 2023 16:56
@hmadison
Copy link
Contributor Author

@mstruk I've made the requested changes. Do you have any idea why the s390x portion of the build is currently breaking?

@hmadison hmadison force-pushed the hm/jwks-skip-header branch 6 times, most recently from d9e321f to adfbf8e Compare July 28, 2023 19:47
@hmadison hmadison force-pushed the hm/jwks-skip-header branch from adfbf8e to c55abba Compare July 28, 2023 19:55
@mstruk
Copy link
Contributor

mstruk commented Jul 29, 2023

@mstruk I've made the requested changes. Do you have any idea why the s390x portion of the build is currently breaking?

The issue is definitely unrelated to this PR. travis-ci.com has had some issues with JDK 11 on s390x platform last weeks. They apparently got things working to some degree but it seems JDK 11 is still partially broken. Also the .travis/build.sh script does some special build in s390x with Keycloak and it's that particular custom thing that now breaks. I'll see if I can fix it in a separate PR.

Copy link
Contributor

@mstruk mstruk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some minor issues, otherwise it looks good to me.

Signed-off-by: Hunter Madison <[email protected]>
@hmadison hmadison requested a review from mstruk July 31, 2023 13:55
metadata:
name: oidc-reader-binding
annotations:
kubernetes.io/description: |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still not ok. It's this line (19) that should be indented in order to be a child of annotations. Currently it is a child of metadata which is incorrect.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based off of @scholzj's feedback, I've backed this example out and will open up a subsequent PR with a better "out of the box" experience around service account usage.

Comment on lines 26 to 29
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: "system:anonymous"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll need to add the includeAcceptHeader config option to oauth listener authentication and keycloak authorization when we integrate the next OAuth release into the Strimzi Operator (another PR). We can then improve the example Kafka CR.

@mstruk mstruk added this to the 0.14.0 milestone Jul 31, 2023
Copy link
Member

@scholzj scholzj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One nit. LGTM otherwise.

@hmadison hmadison force-pushed the hm/jwks-skip-header branch from 33e8980 to b7f488d Compare August 1, 2023 16:53
@mstruk
Copy link
Contributor

mstruk commented Aug 8, 2023

I added some tests to the testsuite and fixed some additional issues I found while writing tests: hmadison#1

Added configuration tests to the testsuite + fixed some issues
@mstruk mstruk merged commit 75044c8 into strimzi:main Aug 9, 2023
@mstruk
Copy link
Contributor

mstruk commented Aug 9, 2023

@hmadison Thank you very much for the contribution!

@hmadison hmadison deleted the hm/jwks-skip-header branch August 9, 2023 09:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants