@@ -364,28 +364,22 @@ func (r *ClientRegistrationReconciler) reconcileOne(
364364 "clientId" , clientID )
365365 }
366366
367- // In federated-jwt mode, agents authenticate using their SPIFFE identity directly.
368- // No credential secret is needed — AuthBridge reads JWT-SVIDs from the workload
369- // API socket, not from a mounted secret. Skipping secret creation avoids leaving
370- // unused Keycloak client secrets in agent namespaces .
367+ // In federated-jwt mode pass an empty clientSecret so ensureClientCredentialsSecret
368+ // writes only client-id.txt (not client-secret.txt). The inbound jwt-validation
369+ // plugin in AuthBridge reads client-id.txt to determine the expected audience; without
370+ // it, all inbound requests to the agent are rejected with 503 indefinitely .
371371 secretName := keycloakClientCredentialsSecretName (ns , workloadName )
372- if authType != "federated-jwt" {
373- if err := r .ensureClientCredentialsSecret (ctx , owner , secretName , clientID , clientSecret ); err != nil {
374- logger .Error (err , "ensure client credentials secret" )
375- return ctrl.Result {RequeueAfter : 30 * time .Second }, nil
376- }
377- // Annotate the pod template with the secret name so the webhook mounts it.
378- if err := patchTemplate (ctx ); err != nil {
379- logger .Error (err , "patch workload pod template" )
380- return ctrl.Result {RequeueAfter : 30 * time .Second }, nil
381- }
382- } else {
383- // In federated-jwt mode, AuthBridge reads JWT-SVIDs directly from the SPIFFE
384- // workload API socket — no credential secret is needed or created. Skip both the
385- // secret and the pod template annotation so the webhook does not try to mount a
386- // non-existent secret.
387- logger .V (1 ).Info ("skipping credential secret and template patch (federated-jwt — AuthBridge uses SPIFFE identity directly)" ,
388- "workload" , workloadName , "namespace" , ns )
372+ credSecret := clientSecret
373+ if authType == "federated-jwt" {
374+ credSecret = ""
375+ }
376+ if err := r .ensureClientCredentialsSecret (ctx , owner , secretName , clientID , credSecret ); err != nil {
377+ logger .Error (err , "ensure client credentials secret" )
378+ return ctrl.Result {RequeueAfter : 30 * time .Second }, nil
379+ }
380+ if err := patchTemplate (ctx ); err != nil {
381+ logger .Error (err , "patch workload pod template" )
382+ return ctrl.Result {RequeueAfter : 30 * time .Second }, nil
389383 }
390384
391385 logger .Info ("operator client registration applied" ,
@@ -571,6 +565,10 @@ func (r *ClientRegistrationReconciler) ensureClientCredentialsSecret(ctx context
571565 if sec .StringData == nil {
572566 sec .StringData = map [string ]string {}
573567 }
568+ // In federated-jwt mode clientSecret is empty — agents authenticate via JWT-SVID.
569+ // We still write client-secret.txt (as an empty string) so the webhook's subPath
570+ // mount produces a file rather than a directory (Kubernetes creates a directory
571+ // when a subPath key is absent from the Secret).
574572 sec .StringData ["client-secret.txt" ] = clientSecret
575573 sec .StringData ["client-id.txt" ] = clientID
576574 return controllerutil .SetControllerReference (owner , sec , r .Scheme )
0 commit comments