Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions kagenti-operator/api/v1alpha1/agentruntime_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,13 @@ type IdentitySpec struct {
// SPIFFE specifies SPIFFE identity configuration overrides
// +optional
SPIFFE *SPIFFEIdentity `json:"spiffe,omitempty"`

// AllowedAudiences specifies additional JWT audiences that the AuthProxy
// sidecar should accept for inbound requests. This is a transitional
// mechanism to support application-to-agent flows until the auth model
// is finalized. See https://github.com/kagenti/kagenti-operator/issues/368
// +optional
AllowedAudiences []string `json:"allowedAudiences,omitempty"`
}

// SPIFFEIdentity configures SPIFFE workload identity for an AgentRuntime.
Expand Down
5 changes: 5 additions & 0 deletions kagenti-operator/api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,15 @@ spec:
identity:
description: Identity specifies optional per-workload identity overrides
properties:
allowedAudiences:
description: |-
AllowedAudiences specifies additional JWT audiences that the AuthProxy
sidecar should accept for inbound requests. This is a transitional
mechanism to support application-to-agent flows until the auth model
is finalized. See https://github.com/kagenti/kagenti-operator/issues/368
items:
type: string
type: array
spiffe:
description: SPIFFE specifies SPIFFE identity configuration overrides
properties:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package injector
import (
"context"
"fmt"
"slices"

agentv1alpha1 "github.com/kagenti/operator/api/v1alpha1"
"k8s.io/apimachinery/pkg/api/meta"
Expand All @@ -44,6 +45,9 @@ type AgentRuntimeOverrides struct {
AdminCredentialsSecretName *string
AdminCredentialsSecretNamespace *string

// Identity — from .spec.identity.allowedAudiences
AllowedAudiences []string

// Observability — from .spec.trace
TraceEndpoint *string
TraceProtocol *string // "grpc" or "http"
Expand Down Expand Up @@ -109,6 +113,11 @@ func extractOverrides(rt *agentv1alpha1.AgentRuntime) *AgentRuntimeOverrides {
overrides.SpiffeTrustDomain = &td
}

// .spec.identity.allowedAudiences — clone to decouple from CR memory
if rt.Spec.Identity != nil && len(rt.Spec.Identity.AllowedAudiences) > 0 {
Comment thread
akram marked this conversation as resolved.
overrides.AllowedAudiences = slices.Clone(rt.Spec.Identity.AllowedAudiences)
}

// .spec.trace.endpoint
if rt.Spec.Trace != nil && rt.Spec.Trace.Endpoint != "" {
ep := rt.Spec.Trace.Endpoint
Expand Down
53 changes: 51 additions & 2 deletions kagenti-operator/internal/webhook/injector/pod_mutator.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package injector
import (
"context"
"fmt"
"slices"

"github.com/kagenti/operator/internal/webhook/config"
appsv1 "k8s.io/api/apps/v1"
Expand Down Expand Up @@ -300,6 +301,14 @@ func (m *PodMutator) InjectAuthBridge(ctx context.Context, podSpec *corev1.PodSp
}
}

// ========================================
// Resolve AllowedAudiences (from AgentRuntime CR)
// ========================================
var allowedAudiences []string
if arOverrides != nil {
allowedAudiences = slices.Clone(arOverrides.AllowedAudiences)
}

if currentGates.PerWorkloadConfigResolution {
// Resolved path: build literal env vars from namespace config
// arOverrides was already read above as a gate check.
Expand Down Expand Up @@ -490,7 +499,7 @@ func (m *PodMutator) InjectAuthBridge(ctx context.Context, podSpec *corev1.PodSp
"reverse_proxy_backend": fmt.Sprintf("http://127.0.0.1:%d", newAgentPort),
"forward_proxy_addr": fmt.Sprintf(":%d", forwardProxyPort),
},
mtlsMode)
mtlsMode, allowedAudiences)
if err != nil {
return false, fmt.Errorf("proxy-sidecar per-agent ConfigMap: %w", err)
}
Expand Down Expand Up @@ -566,7 +575,7 @@ func (m *PodMutator) InjectAuthBridge(ctx context.Context, podSpec *corev1.PodSp
// we'd never reach this branch with a non-empty mtlsMode in practice;
// passing "" here is the explicit-defense complement.
perAgentCMName, err := m.ensurePerAgentConfigMap(ctx, namespace, crName,
ModeEnvoySidecar, nsConfig.AuthBridgeRuntimeYAML, nsConfig, nil, "")
ModeEnvoySidecar, nsConfig.AuthBridgeRuntimeYAML, nsConfig, nil, "", allowedAudiences)
if err != nil {
return false, fmt.Errorf("envoy-sidecar per-agent ConfigMap: %w", err)
}
Expand Down Expand Up @@ -743,6 +752,38 @@ func synthesizePipeline(nsConfig *NamespaceConfig) map[string]interface{} {
}
}

// injectAllowedAudiences walks into cfg["pipeline"]["inbound"]["plugins"] and sets
Comment thread
akram marked this conversation as resolved.
// allowed_audiences on the jwt-validation plugin's config block. If the pipeline
// structure does not contain a jwt-validation plugin, a warning is logged and the
// setting is silently dropped.
func injectAllowedAudiences(cfg map[string]interface{}, audiences []string) {
pipeline, _ := cfg["pipeline"].(map[string]interface{})
if pipeline == nil {
mutatorLog.Info("WARN: allowedAudiences set on AgentRuntime CR but no pipeline section in config; setting has no effect")
return
}
inbound, _ := pipeline["inbound"].(map[string]interface{})
if inbound == nil {
mutatorLog.Info("WARN: allowedAudiences set on AgentRuntime CR but no inbound pipeline; setting has no effect")
return
}
plugins, _ := inbound["plugins"].([]interface{})
for _, p := range plugins {
pm, _ := p.(map[string]interface{})
if pm == nil || pm["name"] != "jwt-validation" {
continue
}
pluginCfg, _ := pm["config"].(map[string]interface{})
if pluginCfg == nil {
pluginCfg = map[string]interface{}{}
pm["config"] = pluginCfg
}
pluginCfg["allowed_audiences"] = audiences
return
}
mutatorLog.Info("WARN: allowedAudiences set on AgentRuntime CR but jwt-validation plugin not in inbound pipeline; setting has no effect")
}

// ensurePerAgentConfigMap creates or updates a per-agent ConfigMap that merges the
// namespace-level authbridge-runtime-config with per-agent overrides (mode, listener
// addresses, mtls). The authbridge sidecar mounts this instead of the shared ConfigMap.
Expand All @@ -762,6 +803,7 @@ func (m *PodMutator) ensurePerAgentConfigMap(
nsConfig *NamespaceConfig,
listenerOverrides map[string]string,
mtlsMode string,
allowedAudiences []string,
) (string, error) {
cmName := perAgentConfigMapName(crName)

Expand Down Expand Up @@ -796,6 +838,13 @@ func (m *PodMutator) ensurePerAgentConfigMap(
cfg["pipeline"] = synthesizePipeline(nsConfig)
}

// Inject per-agent AllowedAudiences into the jwt-validation plugin config.
// This overrides any namespace-level audience setting with the per-agent value
// from the AgentRuntime CR's .spec.identity.allowedAudiences.
if len(allowedAudiences) > 0 {
injectAllowedAudiences(cfg, allowedAudiences)
}

// Override mode
cfg["mode"] = mode

Expand Down
Loading
Loading