@@ -76,6 +76,10 @@ const (
7676 RossoctlTypeAgent = "agent"
7777 // RossoctlTypeTool is the label value that identifies tool workloads
7878 RossoctlTypeTool = "tool"
79+
80+ // tokenExchangePluginName is the name of the AuthBridge plugin that handles
81+ // OAuth2 token exchange for SPIFFE-based authentication.
82+ tokenExchangePluginName = "token-exchange"
7983)
8084
8185type PodMutator struct {
@@ -220,6 +224,27 @@ func (m *PodMutator) InjectAuthBridge(ctx context.Context, podSpec *corev1.PodSp
220224 nsConfig = & NamespaceConfig {}
221225 }
222226
227+ // Fetch the AgentRuntime CR for this workload (if it exists).
228+ // The CR may not exist if the workload was deployed before AgentRuntime CRDs were created.
229+ // In that case, we proceed with defaults (no spec.auth configuration).
230+ var agentRuntime * agentv1alpha1.AgentRuntime
231+ agentRuntimeList := & agentv1alpha1.AgentRuntimeList {}
232+ if err := reader .List (ctx , agentRuntimeList , client .InNamespace (namespace )); err != nil {
233+ mutatorLog .Info ("failed to list AgentRuntimes (proceeding with defaults)" ,
234+ "namespace" , namespace , "crName" , crName , "error" , err )
235+ } else {
236+ // Find AgentRuntime that targets this workload
237+ for i := range agentRuntimeList .Items {
238+ rt := & agentRuntimeList .Items [i ]
239+ if rt .Spec .TargetRef .Name == crName && rt .Spec .TargetRef .Kind == workloadKind {
240+ agentRuntime = rt
241+ mutatorLog .Info ("found AgentRuntime CR for workload" ,
242+ "namespace" , namespace , "crName" , crName , "agentRuntime" , rt .Name )
243+ break
244+ }
245+ }
246+ }
247+
223248 // ========================================
224249 // Resolve mTLS posture (namespace > "disabled")
225250 // ========================================
@@ -544,7 +569,7 @@ func (m *PodMutator) InjectAuthBridge(ctx context.Context, podSpec *corev1.PodSp
544569 "reverse_proxy_backend" : fmt .Sprintf ("http://127.0.0.1:%d" , newAgentPort ),
545570 "forward_proxy_addr" : fmt .Sprintf (":%d" , forwardProxyPort ),
546571 },
547- mtlsMode , tlsBridgeMode , spireEnabled )
572+ mtlsMode , tlsBridgeMode , spireEnabled , agentRuntime )
548573 if err != nil {
549574 return false , fmt .Errorf ("proxy-sidecar per-agent ConfigMap: %w" , err )
550575 }
@@ -680,7 +705,7 @@ func (m *PodMutator) InjectAuthBridge(ctx context.Context, podSpec *corev1.PodSp
680705 // inbound listener (gated on MTLSEnabled) and UpstreamTlsContext on
681706 // original_destination_tls (strict only).
682707 perAgentCMName , err := m .ensurePerAgentConfigMap (ctx , namespace , crName ,
683- ModeEnvoySidecar , nsConfig .AuthBridgeRuntimeYAML , nsConfig , nil , mtlsMode , "" , spireEnabled ) // bridge never runs under envoy-sidecar
708+ ModeEnvoySidecar , nsConfig .AuthBridgeRuntimeYAML , nsConfig , nil , mtlsMode , "" , spireEnabled , agentRuntime ) // bridge never runs under envoy-sidecar
684709 if err != nil {
685710 return false , fmt .Errorf ("envoy-sidecar per-agent ConfigMap: %w" , err )
686711 }
@@ -877,7 +902,7 @@ func synthesizePipeline(nsConfig *NamespaceConfig) map[string]interface{} {
877902 "outbound" : map [string ]interface {}{
878903 "plugins" : []interface {}{
879904 map [string ]interface {}{
880- "name" : "token-exchange" ,
905+ "name" : tokenExchangePluginName ,
881906 "config" : tokenCfg ,
882907 },
883908 },
@@ -907,6 +932,7 @@ func (m *PodMutator) ensurePerAgentConfigMap(
907932 mtlsMode string ,
908933 tlsBridgeMode string ,
909934 spireEnabled bool ,
935+ agentRuntime * agentv1alpha1.AgentRuntime ,
910936) (string , error ) {
911937 cmName := perAgentConfigMapName (crName )
912938
@@ -992,6 +1018,74 @@ func (m *PodMutator) ensurePerAgentConfigMap(
9921018 delete (cfg , "spiffe" )
9931019 }
9941020
1021+ // Generate token-exchange routes from AgentRuntime spec.auth.outbound.
1022+ // Routes tell AuthBridge which audiences to request when calling specific
1023+ // destinations. Routes are only effective when the namespace is configured
1024+ // with SPIFFE authentication (CLIENT_AUTH_TYPE=federated-jwt).
1025+ if agentRuntime != nil && agentRuntime .Spec .Auth != nil &&
1026+ len (agentRuntime .Spec .Auth .Outbound ) > 0 {
1027+
1028+ // Navigate to pipeline.outbound.plugins[token-exchange].config
1029+ pipeline , _ := cfg ["pipeline" ].(map [string ]interface {})
1030+ if pipeline == nil {
1031+ mutatorLog .Info ("WARN: no pipeline block found, cannot inject routes" ,
1032+ "namespace" , namespace , "crName" , crName )
1033+ } else {
1034+ outbound , _ := pipeline ["outbound" ].(map [string ]interface {})
1035+ if outbound == nil {
1036+ mutatorLog .Info ("WARN: no outbound block found, cannot inject routes" ,
1037+ "namespace" , namespace , "crName" , crName )
1038+ } else {
1039+ plugins , _ := outbound ["plugins" ].([]interface {})
1040+ if len (plugins ) == 0 {
1041+ mutatorLog .Info ("WARN: no outbound plugins found, cannot inject routes" ,
1042+ "namespace" , namespace , "crName" , crName )
1043+ } else {
1044+ // Find the token-exchange plugin
1045+ for i := range plugins {
1046+ plugin , _ := plugins [i ].(map [string ]interface {})
1047+ if plugin == nil {
1048+ continue
1049+ }
1050+ pluginName , _ := plugin ["name" ].(string )
1051+ if pluginName == tokenExchangePluginName {
1052+ pluginConfig , _ := plugin ["config" ].(map [string ]interface {})
1053+ if pluginConfig == nil {
1054+ pluginConfig = make (map [string ]interface {})
1055+ plugin ["config" ] = pluginConfig
1056+ }
1057+
1058+ // Generate routes from spec.auth.outbound
1059+ routes := make ([]interface {}, 0 , len (agentRuntime .Spec .Auth .Outbound ))
1060+ for _ , outboundRoute := range agentRuntime .Spec .Auth .Outbound {
1061+ route := map [string ]interface {}{
1062+ "audiences" : outboundRoute .Audiences ,
1063+ }
1064+
1065+ // Add destination match (host or hostRegex)
1066+ destination := make (map [string ]interface {})
1067+ if outboundRoute .Destination .Host != "" {
1068+ destination ["host" ] = outboundRoute .Destination .Host
1069+ }
1070+ if outboundRoute .Destination .HostRegex != "" {
1071+ destination ["hostRegex" ] = outboundRoute .Destination .HostRegex
1072+ }
1073+ route ["destination" ] = destination
1074+
1075+ routes = append (routes , route )
1076+ }
1077+
1078+ pluginConfig ["routes" ] = routes
1079+ mutatorLog .Info ("injected token-exchange routes from AgentRuntime spec.auth" ,
1080+ "namespace" , namespace , "crName" , crName , "routeCount" , len (routes ))
1081+ break
1082+ }
1083+ }
1084+ }
1085+ }
1086+ }
1087+ }
1088+
9951089 // Marshal back to YAML
9961090 data , err := yaml .Marshal (cfg )
9971091 if err != nil {
0 commit comments