test(k8s): pin Kubernetes workload identity against a real cluster - #778
Open
lakhansamani wants to merge 1 commit into
Open
test(k8s): pin Kubernetes workload identity against a real cluster#778lakhansamani wants to merge 1 commit into
lakhansamani wants to merge 1 commit into
Conversation
The Kubernetes workload-identity path had no test against a real cluster. Every existing test substitutes an httptest server, which replaces the single property that decides whether the feature works: the ADDRESS the cluster publishes. Measured on kind: issuer https://kubernetes.default.svc.cluster.local (ClusterIP) jwks_uri https://172.27.0.2:6443/openid/v1/jwks (RFC 1918) apiserver https://127.0.0.1:60438 (loopback) validators.SafeHTTPClient refuses every one of those unconditionally, and clientauth has no AllowPrivate escape hatch. So an in-cluster deployment cannot fetch the cluster's JWKS OR reach TokenReview — while performTokenReview reads Authorizer's own in-cluster ServiceAccount token to authenticate to an apiserver it can never dial. The two assumptions contradict each other. performTokenReview documented half of this for TokenReview. The JWKS half was undocumented, and applies with or without TokenReview: static_jwks_url and oidc_discovery are refused for the same reason. The operator-visible symptom is a bare 400 invalid_client / "Client authentication failed" with nothing pointing at the refused fetch. These tests assert current behaviour, not desired behaviour. They are a pin: if the SSRF policy changes, they say exactly which Kubernetes behaviour changed with it, and the end-to-end case names itself as the assertion to invert once the address problem is solved. - scripts/k8s-e2e.sh: kind (default) or k3d via K8S_RUNTIME, always tears down, K8S_KEEP=1 to debug. - make test-k8s - .github/workflows/k8s.yml: scheduled + on PRs touching the code that decides cluster reachability. Not per-push — it provisions a cluster to guard a limitation that changes rarely. - Behind the `k8s` build tag, so `make test` is unaffected (verified: 43 packages, 0 FAIL, file not compiled in). - README no longer lists Kubernetes TokenReview without the caveat.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Asked to make sure nothing breaks in Kubernetes and to add k3d/kind tests. The
tests found something bigger than the change that prompted them.
The finding
In-cluster Kubernetes workload identity cannot work. Not TokenReview
specifically — the whole path.
Measured against a real kind cluster:
SafeHTTPClienthttps://kubernetes.default.svc.cluster.localjwks_urihttps://172.27.0.2:6443/openid/v1/jwkshttps://127.0.0.1:61411validators.SafeHTTPClientrejects private, loopback and link-local addressesunconditionally, and
clientauthhas noSafeHTTPClientAllowPrivateescapehatch (the two that exist are gated on
--env=e2eand belong to oauth_sso andwebhook delivery).
Meanwhile
performTokenReviewreads Authorizer's own in-clusterServiceAccount token (
/var/run/secrets/kubernetes.io/serviceaccount/token) toauthenticate to an apiserver it can never dial. The two assumptions contradict
each other.
performTokenReviewalready documented half of this. The JWKS half wasundocumented and is the wider problem: it applies with or without TokenReview,
because
static_jwks_urlandoidc_discoveryboth point at the cluster's ownprivate address. That is why this is not a kind artefact —
kubernetes.default.svcis always a ClusterIP, and
jwks_urialways points at the apiserver.The operator-visible symptom, captured by the test:
Nothing points at the refused fetch. Worth fixing whenever the address problem is.
Why these tests, and why they assert failure
They assert current behaviour, deliberately. They are a pin, not an
aspiration: if someone changes the SSRF policy, the suite says precisely which
Kubernetes behaviour they changed.
TestK8sWorkloadAuthenticationEndToEndnamesitself as the assertion to invert once this is fixed.
Three cases, and the middle one earns its place:
TestK8sProjectedTokenIsWellFormedproves the token side is correct — real
iss,sub, audience-boundaud— sothe end-to-end failure is attributable to the fetch address and nothing else.
Without it a reader could reasonably blame the token.
Mechanics
scripts/k8s-e2e.sh— kind by default,K8S_RUNTIME=k3dfor k3d,K8S_KEEP=1to leave the cluster up. Always tears down, including on failure.
make test-k8s.github/workflows/k8s.yml— scheduled weekly plus PRs touching the code thatdecides cluster reachability. Not per-push: it provisions a cluster to guard a
limitation that changes rarely.
k8sbuild tag. Verifiedmake testis unaffected — 43 packages,0 FAIL, and the file is not compiled into normal runs.
Verification
Cold run from no cluster: create → test → teardown, exit 0, no cluster left behind.
Not fixed here
Making in-cluster workload identity actually work needs a security decision, not
a patch: a scoped SSRF exemption for an operator-declared apiserver/JWKS host
with CA pinning, or an explicit in-cluster transport.
SafeHTTPClientAllowPrivateasks for careful review before a third caller, and this would be one. README now
states the constraint instead of listing the feature unqualified.