Repository navigation
Resolve Kubernetes identity provider issuer from discovery - #49600
chance-coleman wants to merge 1 commit into
Conversation
| } | ||
|
|
||
| private String discoveryUrl(String issuer) { | ||
| return issuer.replaceAll("/+$", "") + "/.well-known/openid-configuration"; |
There was a problem hiding this comment.
i believe this was fixed in the duplicate comment
Unreported flaky test detectedIf the flaky tests below are affected by the changes, please review and update the changes accordingly. Otherwise, a maintainer should report the flaky tests prior to merging the PR. org.keycloak.testsuite.forms.LoginTest#loginWithForcePasswordChangePolicyKeycloak CI - Store IT (mariadb) org.keycloak.testsuite.forms.LoginTest#loginAfterExpiredTimeoutKeycloak CI - Store IT (mariadb) org.keycloak.testsuite.forms.LoginTest#loginWithoutForcePasswordChangePolicyKeycloak CI - Store IT (mariadb) org.keycloak.testsuite.forms.LoginTest#loginSuccessKeycloak CI - Store IT (mariadb) |
bc0cbc4 to
38d666b
Compare
There was a problem hiding this comment.
Fantastic work Chance! I added two comments (which might be quite impactful). Once we sort them out, I think we can start asking the maintainers for review.
38d666b to
ce915b7
Compare
ce915b7 to
b9057a3
Compare
|
@chance-coleman - I've updated the description that it closes issue #49039 - let me know if this is the case. When discussing this this @slaskawi I was hoping for a simpler PR. Given the size of the PR and the current items on the backlog, I can't say when someone will have the time to review it. |
|
Hi @chance-coleman, I'm sharing this from Niro, an open-source PR pentesting tool we're building to help OSS maintainers catch security regressions before merge. I ran it against this PR and it found a reproducible SSRF issue in Kubernetes issuer discovery. Issue Note on reproduction: The commands below are exported from Niro's Docker test harness. To reproduce locally, replace Pre-conditions: Obtain a token for a delegated realm admin with permission to manage identity providers in the target realm. REALM_TOKEN=$(curl -s -X POST \
"http://host.docker.internal:8080/realms/niro-realm/protocol/openid-connect/token" \
-d "grant_type=password&client_id=admin-cli&username=realm-admin&password=password" \
| jq -r '.access_token')Step 1 - Create a Kubernetes IDP with an internal issuer discovery URL: curl -s -w "\nHTTP_STATUS:%{http_code}" -X POST \
"http://host.docker.internal:8080/admin/realms/niro-realm/identity-provider/instances" \
-H "Authorization: Bearer $REALM_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"alias": "k8s-ssrf-test",
"providerId": "kubernetes",
"config": {
"automaticIssuerDiscovery": "true",
"issuerDiscoveryUrl": "http://localhost:8080/realms/master",
"issuer": "http://placeholder"
}
}'Observed: HTTP 201 Created. Step 2 - Read back the persisted issuer: curl -s \
"http://host.docker.internal:8080/admin/realms/niro-realm/identity-provider/instances/k8s-ssrf-test" \
-H "Authorization: Bearer $REALM_TOKEN" \
| jq '.config | {issuer, issuerDiscoveryUrl}'Observed: {
"issuer": "http://localhost:8080/realms/master",
"issuerDiscoveryUrl": "http://localhost:8080/realms/master"
}The persisted Step 3 - Reproduce with a second internal address: Delete the IDP and recreate it with Why this matters The PR adds a new server-side fetch in the admin write path. A delegated realm admin who can configure Kubernetes identity providers can now cause the Keycloak server to request URLs reachable from the Keycloak process, including loopback/private-network destinations. This is more specific than normal OIDC provider configuration because Suggested remediation Add request-destination validation before As defense in depth, consider whether automatic issuer discovery should only allow the expected Kubernetes discovery endpoint(s), rather than accepting arbitrary |
Closes keycloak#49039 Signed-off-by: UnicornChance <chance@defenseunicorns.com>
b9057a3 to
662405f
Compare
|
@shawkins @vmuzikar Could I kindly ask you to review this PR? To get you on the same page - this Pull Request introduces an option to automatically discover the issuer in managed Kubernetes clusters (like AKS/EKS). This might be helpful for OpenShift as well and relates to your recent fixes: #49488. With this change and String oicdConfig = k8sclient.raw("/.well-known/openid-configuration");
String issuer = (String) Serialization.unmarshal(oicdConfig, Map.class).get("issuer");
idp.getConfig().put("issuer", issuer);@chance-coleman @shawkins If you agree with the approach and implementation here, I was thinking if we could modify the |
|
Closing this PR so that we can follow up with 2 PRs instead, first one will be to address the bug found in this PR and the second will be to enhance the idp issuer which will follow the bug fix. |
Summary
Resolve and persist the issuer from Kubernetes OIDC discovery when saving a Kubernetes identity provider.
Managed Kubernetes clusters can expose service account token discovery through the in-cluster Kubernetes API URL while issuing tokens with an external issuer URL. For example, EKS exposes discovery through
https://kubernetes.default.svc, but service account tokens use an issuer likehttps://oidc.eks.<region>.amazonaws.com/id/<cluster-id>.This change resolves the discovery document during identity provider validation and stores the discovered
issuerin the existing issuer config field before issuer uniqueness validation runs.Motivation
Closes: #49039
Related Discussion: #48130
Kubernetes service account token client authentication looks up the Kubernetes identity provider by the JWT issuer. If admins configure the in-cluster Kubernetes discovery URL, but tokens contain the managed cluster's external issuer, runtime lookup cannot find the identity provider.
Persisting the discovered issuer at save time keeps runtime client authentication unchanged while supporting managed Kubernetes clusters.
Implementation
/.well-known/openid-configurationfrom the configured Kubernetes issuer during create/update validation.issuerwhen present.Testing
Commands run:
Manual EKS validation:
AI assistance disclosure: I used an AI coding agent to help explore the codebase, draft a plan, and help debug verification steps. I reviewed and understand the changes.