Replies: 1 comment 1 reply
-
That is an interesting point. However, this seems to be specific to AWS/EKS. For Azure/AKS this is definitely not the case. For AKS, the tokens are issued by Kubernetes: https://learn.microsoft.com/en-us/azure/aks/use-oidc-issuer#get-the-discovery-document. I also successfully tested the current implementation with service account tokens from AKS.
In my opinion, this is more an issue of the client, which sets the client ID when using a bearer token for authentication. So, in this specific case, I think this should be addressed in the Terraform provider. But I see your point. At the beginning, I also had a hard time understanding why it was not working because of the strict client-id validation. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Summary
When integrating the new "Signed JWT - Federated" Client Authenticator for Kubernetes Service Accounts into UDS Core, we encountered a few corner cases that are worth discussing:
FederatedJWTClientValidatorandAbstractJWTClientValidatorencapsulate methods too tightly, which makes them non-extensibleKubernetesJwksEndpointLoaderlacks configuration (or extension points) to enable/disable attaching tokens to the requestsI put all the necessary details along with proposed solutions below. We managed to get all the bits working in the UDS Core platform (see uds-identity-config/pull/838) but we ended up forking many aspects of the original implementation.
@ahus1 @stianst @ryanemerson @sschu I'd love to get your thoughts on these. I'm more than happy to contribute these features back to upstream, but before I start creating tickets and PRs, I wanted to ensure that's the proper path forward.
Client ID validation
As described in keycloak/terraform-provider-keycloak#1542 and #48024, tools like the Keycloak Terraform Provider attach Client ID to each authentication request by default. The JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants RFC also highlights that with Client Assertions, sending the client_id is optional:
Kubernetes doesn't follow the above convention and sets the
subclaim to the Service Account (e.g.system:serviceaccount:my-namespace:my-serviceaccount). So if we wanted to follow the spec in this case, we'd need to use the Service Account names as Client ID. This is not feasible for many environments as Clients such assystem:serviceaccount:my-namespace:my-serviceaccountare no longer human-readable.I propose a different approach — relax Client ID validation for the Kubernetes case. This approach has already been proposed in:
client_idoptional for Signed JWT authentication terraform-provider-keycloak#1552client_idparameter validation for federated client assertions #48026Managed Kubernetes Clusters and centralized IdPs
Managed Kubernetes clusters, such as EKS or AKS, use a centralized IdP for both user authentication and issuing Service Account Tokens. When the
--oidc-*settings are used in the Kubernetes API Server, it no longer issues tokens but rather delegates this to a different IdP. At the same time, the JWKS URL remains the same and valid, so this integration doesn't affect token signature validation. See the commands below:The
jwks_urias well as Kube's JWKS endpoint return the same keys.Tokens issued by Kubernetes (and mounted to the Pods) in this scenario use a different issuer:
This doesn't play well with the implementation, which strongly assumes that Kubernetes will issue all the tokens. In this case, it's not Kubernetes — it's the OIDC service from EKS.
Also related to this, in a multi-cloud environment, services like UDS can be deployed on many clouds at the same time. So the implementation needs to be flexible enough to do dynamic issuer lookup before validating the JWT.
We're currently working on a patch for UDS Core to accommodate this scenario here: defenseunicorns/uds-identity-config#838
In essence, we implemented an alternative
ClientAssertionStrategy. At first, it tries the default strategy (that assumes Kubernetes issues tokens), but when it fails, it performs an alternative lookup (this can probably be optimized further, but here's what we have):The implementation also requires a custom IdP that is almost identical to the
KubernetesIdentityProviderbut uses an additional lookup for the issuer:The above piece of code has been implemented in a very defensive way. We believe it can be unified and squashed into one discovery call that should satisfy both cases (managed Kubernetes clusters and local ones).
The most complicated aspect of this is testing. Doing it properly, requires deploying Keycloak on EKS/AKS (and potentially others) which leads to this discussion: #47669
The
FederatedJWTClientValidatorandAbstractJWTClientValidatorencapsulate methods too tightly, which makes them non-extensibleWhen working on integrating Tofu with the "Signed JWT - Federated" Client Authenticator, we could not easily bypass the Client ID validation by providing our own
validateClientmethod. Unfortunately, all the methods inAbstractJWTClientValidatorare private, which requires copy-pasting them into our codebase and maintaining the changes.A much better approach would be to change all the method signatures to
protected, enabling custom implementations to override only certain aspects of the token validation.The
KubernetesJwksEndpointLoaderlacks configuration (or extension points) to enable/disable attaching tokens to the requestsThe
KubernetesJwksEndpointLoaderprevents sending the token on issuer mismatch:This approach makes a lot of sense to avoid sending tokens to fake services prepared by an attacker. It also works great when the issuer is known, but in case of dynamic configurations (like managed Kubernetes clusters - multi cloud environmens), that is not always the case. If we decide to implement a more dynamic approach, we will need a configuration switch that allows always including the token when probing the OIDC Discovery endpoint (which requires a token for managed cloud offerings).
Beta Was this translation helpful? Give feedback.
All reactions