Fix(controller,webhook): write client-id.txt in federated-jwt mode for inbound JWT validation#483
Conversation
…r inbound JWT validation In federated-jwt mode the inbound jwt-validation plugin in AuthBridge reads /shared/client-id.txt to determine the expected audience for incoming requests. PR #476 skipped the entire ensureClientCredentialsSecret call in this mode, which meant client-id.txt was never written and every inbound request to the agent was rejected with HTTP 503 indefinitely. Controller: - Pass empty clientSecret (not clientID) to ensureClientCredentialsSecret in federated-jwt mode so the Secret is still created with client-id.txt only. - Remove the authType guard so patchTemplate still runs in federated-jwt mode, keeping the pod template annotation that causes the webhook to mount the Secret. - In ensureClientCredentialsSecret, omit client-secret.txt when clientSecret is empty rather than always writing it. Webhook: - Remove the federated-jwt guard that skipped annotation pre-population. The Secret still exists in federated-jwt mode (with client-id.txt only), so the volume mount annotation is needed for the first pod to come up correctly. Fixes #482 Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Alan Cha <Alan.cha1@ibm.com>
A subPath mount for an absent Secret key creates a directory at the mount path rather than a file. Write client-secret.txt as an empty string so the mount produces a regular file. AuthBridge ignores the file in SPIFFE mode (it never reads client-secret.txt when identity.type=spiffe), but a directory at that path is confusing and fragile. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Alan Cha <Alan.cha1@ibm.com>
pdettori
left a comment
There was a problem hiding this comment.
Correct, minimal fix that restores the inbound-JWT audience config broken by #476.
Verified the logic against the full head-branch files: the controller now always calls ensureClientCredentialsSecret + patchTemplate (passing an empty clientSecret in federated-jwt mode), and the webhook now always pre-populates the annotation. The two sides are consistent — the Secret is created for every auth type and the webhook annotates for every eligible workload. The subPath-creates-a-directory-for-absent-key rationale for writing an empty client-secret.txt is a real Kubernetes quirk and a sound defensive choice.
Supply-chain gate clear (no .claude//.vscode/ changes). All 16 CI checks green, both commits signed off.
One non-blocking nit inline.
Assisted-By: Claude Code
| // Skip in federated-jwt mode: AuthBridge reads JWT-SVIDs from the SPIFFE workload API | ||
| // socket directly and no credential Secret is created or needed. Injecting the volume | ||
| // mount for a non-existent Secret would leave pods stuck in Init. | ||
| // In federated-jwt mode the Secret still exists (written by the operator with only |
There was a problem hiding this comment.
nit: this comment says the Secret is "written by the operator with only client-id.txt, no client-secret.txt", but the controller change in this same PR writes client-secret.txt as an empty string rather than omitting it (that's the whole point of the subPath fix). Suggest rewording to "written with client-id.txt and an empty client-secret.txt" so it matches the actual behavior and the controller-side comment.
Problem
In
federated-jwtmode, agent pods reject all inbound authenticated requests with HTTP 503:The
authbridge-proxysidecar polls indefinitely for/shared/client-id.txt— the file the inboundjwt-validationplugin reads to determine the expected audience when validating incoming JWTs.Root cause
PR #476 correctly intended to skip writing
client-secret.txtin federated-jwt mode. However it skipped the entireensureClientCredentialsSecretcall, which also skippedclient-id.txt. It also skippedpatchTemplate, so the Secret annotation was never set and the webhook never mounted the Secret into the pod at all. The broken chain:ensureClientCredentialsSecretskipped → no Secret, noclient-id.txtpatchTemplateskipped → no annotation on pod template → webhook doesn't mount SecretFix
Controller — in federated-jwt mode, pass an empty
clientSecrettoensureClientCredentialsSecretinstead of skipping the call. The Secret is always written with both keys:client-id.txt(the agent's SPIFFE ID) andclient-secret.txt(empty string in federated-jwt mode). Writing an emptyclient-secret.txtrather than omitting the key avoids a Kubernetes quirk where a subPath mount for an absent Secret key creates a directory at the mount path instead of a file. Remove theauthTypeguard sopatchTemplatealso runs in federated-jwt mode.Webhook — remove the federated-jwt guard on annotation pre-population. The Secret now exists in this mode, so the volume mount annotation is needed for the first pod to start without a pod recreate.
In federated-jwt mode AuthBridge uses
identity.type=spiffefor outbound token exchange and never readsclient-secret.txt, so the empty value is harmless.Testing
go build ./...cleango vet ./internal/controller/... ./internal/webhook/...cleanFixes #482
Assisted-By: Claude Code