From 84c945654f1bbfd7b37cc8a78ed9faecc0f680b8 Mon Sep 17 00:00:00 2001 From: QuentinBisson Date: Mon, 6 Jul 2026 10:39:53 +0200 Subject: [PATCH] refactor(agents): remove per-agent muster token machinery, forward-token only With M2M removed, the static SA-token path (muster-token-init Job, muster-refresh CronJob, token pod-spec helper, Secret-writer RBAC, and the headersFrom Authorization on the per-agent muster RemoteMCPServer) served no remaining mode. The RemoteMCPServer now carries no static credentials: the propagated muster on-behalf-of token is the only Authorization. The agents.definitions..obo flag and the agents.muster image/TTL/schedule values are removed with it. docs/authentication.md describes the forward-token model (one muster-issued token, validated and forwarded byte-identical). --- CHANGELOG.md | 1 + docs/authentication.md | 27 ++-- .../templates/kagent/agents/_agents.tpl | 16 +++ .../templates/kagent/agents/_muster-token.tpl | 129 ------------------ .../templates/kagent/agents/agents.yaml | 8 +- .../templates/kagent/agents/muster-sa.yaml | 49 ------- .../kagent/agents/muster-token-cronjob.yaml | 44 ------ .../kagent/agents/muster-token-job.yaml | 50 ------- .../kagent/agents/remotemcpservers.yaml | 22 +-- .../agentic-platform-connectivity/values.yaml | 74 +++------- helm/agentic-platform/values.yaml | 77 +++-------- 11 files changed, 74 insertions(+), 423 deletions(-) create mode 100644 helm/agentic-platform-connectivity/templates/kagent/agents/_agents.tpl delete mode 100644 helm/agentic-platform-connectivity/templates/kagent/agents/_muster-token.tpl delete mode 100644 helm/agentic-platform-connectivity/templates/kagent/agents/muster-sa.yaml delete mode 100644 helm/agentic-platform-connectivity/templates/kagent/agents/muster-token-cronjob.yaml delete mode 100644 helm/agentic-platform-connectivity/templates/kagent/agents/muster-token-job.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index 50fe7e9..34ce974 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed +- **The per-agent muster token machinery and the `agents.definitions..obo` flag.** The `muster-token-init` Job, the `muster-refresh` CronJob, the shared token pod-spec helper, the Secret-writer Role/RoleBinding, the static `headersFrom` `Authorization` on the per-agent muster `RemoteMCPServer`, and the `agents.muster.{kubectlImage,busyboxImage,tokenExpirationSeconds,tokenRefreshSchedule}` values are removed from `agentic-platform-connectivity` (and the umbrella defaults). With M2M gone, on-behalf-of is the only mode: the RemoteMCPServer carries no static credentials and kagent's propagated muster token (`KAGENT_PROPAGATE_TOKEN`) is the sole `Authorization` reaching muster, so the `obo` toggle has nothing left to switch. `agents.remoteMcpServers[].tokenSecret` now only accepts a pre-existing Secret; the chart no longer writes one. - **M2M agent impersonation RBAC.** The `agents..m2m` block (and the `impersonation.yaml` / `_agent-impersonation.tpl` templates it rendered) is removed from `agentic-platform-connectivity`. Agents no longer get a deputy `Role`/`ClusterRole` letting mcp-kubernetes impersonate the agent's own identity or bind its groups to ClusterRoles. The only supported flow is on-behalf-of: muster mints a token with the human as `sub` and the agent SA in the RFC 8693 `act` claim, and the mcp-kubernetes chart's `*-obo-impersonate` ClusterRole authorizes impersonating the human. Requires mcp-kubernetes with M2M removed and muster with the matching broker config. - **Retired the `agentic-platform-crds` bundle chart** — every component now owns its CRDs (app-owned CRDs). The standalone chart (`helm/agentic-platform-crds/`), its CircleCI build/test/push jobs, and the now-dead Renovate `helmv3` lockstep `packageRules` are deleted. There is no longer a `components.agentic-platform-crds` entry in the meta-package. diff --git a/docs/authentication.md b/docs/authentication.md index 1f18825..9531118 100644 --- a/docs/authentication.md +++ b/docs/authentication.md @@ -173,17 +173,22 @@ agentic-platform-mcps: clientSecretKey: client-secret ``` -### On-behalf-of (OBO): local mint + human impersonation - -A kagent agent reaching a downstream server forwards the human's muster token as -`Authorization` and its own ServiceAccount token as `X-Actor-Token`. muster -validates both and **local-mints** (`auth.mode: localMint`, `enableJWTMode: true`) a -token for the downstream audience carrying the human as `sub` and the agent SA in -the RFC 8693 `act` claim. The downstream server (e.g. mcp-kubernetes) cannot present -that token to the kube-apiserver as a bearer (wrong issuer/audience), so it -authenticates with its own SA token and sets `Impersonate-User` to the human plus -`Impersonate-Extra-actor` to the agent SA. Kubernetes RBAC and the audit log then -reflect the human, with the acting agent recorded. +### On-behalf-of (OBO): one muster-issued token, forwarded + +A kagent agent acts on behalf of the human who invoked it. At muster's +`/oauth/token` the agent performs one RFC 8693 exchange: the human's token as +`subject_token`, its own projected ServiceAccount token as `actor_token`. +muster validates both against its `trustedIssuers` and issues a single token it +signs itself (`enableJWTMode: true`): `iss` = muster, `aud` = muster's +`resourceIdentifier`, `sub` = the human, the agent SA in the RFC 8693 `act` +claim, `email`/`groups` copied from the subject. The agent presents that token +on `/mcp` and muster forwards it unchanged to downstream servers configured +with `auth.mode: forward` (`forwardToken: true`); there is no per-backend +re-issue. Each downstream server validates the token against muster's JWKS and +terminates it there: mcp-kubernetes cannot present it to the kube-apiserver as +a bearer (wrong issuer/audience), so it authenticates with its own SA token and +sets `Impersonate-User` to the human. Kubernetes RBAC and the audit log then +reflect the human. A trusted-issuer token with no `act` claim is rejected: only on-behalf-of is accepted, and any cryptographically validated actor is allowed (the impersonated diff --git a/helm/agentic-platform-connectivity/templates/kagent/agents/_agents.tpl b/helm/agentic-platform-connectivity/templates/kagent/agents/_agents.tpl new file mode 100644 index 0000000..603242e --- /dev/null +++ b/helm/agentic-platform-connectivity/templates/kagent/agents/_agents.tpl @@ -0,0 +1,16 @@ +{{/* +ServiceAccount an agent runs as: its identity when acting on behalf of a user +(the RFC 8693 actor presented to muster). Defaults to the agent name. +Input: an agents.definitions entry. +*/}} +{{- define "agentic-platform.agentServiceAccount" -}} +{{- .serviceAccount | default .name -}} +{{- end -}} + +{{/* +Name of the per-agent muster RemoteMCPServer (the agent's toolServer). +Input: an agents.definitions entry. +*/}} +{{- define "agentic-platform.musterServerName" -}} +{{- printf "muster-%s" .name -}} +{{- end -}} diff --git a/helm/agentic-platform-connectivity/templates/kagent/agents/_muster-token.tpl b/helm/agentic-platform-connectivity/templates/kagent/agents/_muster-token.tpl deleted file mode 100644 index 2956b88..0000000 --- a/helm/agentic-platform-connectivity/templates/kagent/agents/_muster-token.tpl +++ /dev/null @@ -1,129 +0,0 @@ -{{/* -ServiceAccount whose token an agent presents to muster (M2M subject). Defaults to -the agent name, which is the SA the kagent controller creates for the Agent CR. -Input: an agents.list entry. -*/}} -{{- define "agentic-platform.agentServiceAccount" -}} -{{- .serviceAccount | default .name -}} -{{- end -}} - -{{/* -Name of the per-agent muster RemoteMCPServer (the agent's toolServer). -Input: an agents.list entry. -*/}} -{{- define "agentic-platform.musterServerName" -}} -{{- printf "muster-%s" .name -}} -{{- end -}} - -{{/* -Name of the headersFrom Secret holding "Bearer " for one agent. -Input: dict "root" $ "agent" . -*/}} -{{- define "agentic-platform.musterTokenSecretName" -}} -{{- $root := .root -}} -{{- $agent := .agent -}} -{{- $agent.tokenSecret | default (printf "%s-muster-token-%s" (include "name" $root) $agent.name) -}} -{{- end -}} - -{{/* -Pod spec shared by one agent's muster-token bootstrap Job and refresh CronJob. -Input: dict "root" $ "agent" . - -The Job runs as the agent's own ServiceAccount. A projected serviceAccountToken -volume mints a short-lived token for that SA, scoped to the muster audience, so -muster authenticates the agent under its own identity. The init container reads the -token, prepends "Bearer ", and renders a Secret manifest; the distroless kubectl -container applies it. kagent's RemoteMCPServer reads that Secret via headersFrom and -its controller re-resolves the header when the Secret changes, so refreshing the -Secret rotates the token in place without a restart. -*/}} -{{- define "agentic-platform.musterTokenPodSpec" -}} -{{- $root := .root -}} -{{- $agent := .agent -}} -{{- $kagentNs := $root.Values.kagent.namespaceOverride | default $root.Release.Namespace -}} -{{- $tokenSecretName := include "agentic-platform.musterTokenSecretName" (dict "root" $root "agent" $agent) -}} -{{- $identitySA := include "agentic-platform.agentServiceAccount" $agent -}} -{{- $resourceId := $root.Values.muster.muster.oauth.server.resourceIdentifier -}} -{{- $hostname := first $root.Values.ingress.hostnames -}} -{{- if and (not $resourceId) (not $hostname) -}} - {{- fail "set muster.muster.oauth.server.resourceIdentifier or ingress.hostnames[0]: required to compute the muster token audience" -}} -{{- end -}} -{{- $musterAudience := $resourceId | default (printf "https://%s/mcp" $hostname) -}} -serviceAccountName: {{ $identitySA }} -restartPolicy: OnFailure -securityContext: - runAsNonRoot: true - runAsUser: 1000 - seccompProfile: - type: RuntimeDefault -volumes: - - name: muster-token - projected: - sources: - - serviceAccountToken: - audience: {{ $musterAudience | quote }} - expirationSeconds: {{ $root.Values.agents.muster.tokenExpirationSeconds | int64 }} - path: token - - name: work - emptyDir: {} - - name: tmp - emptyDir: {} -initContainers: - - name: render - image: {{ printf "%s/%s:%s" $root.Values.agents.muster.busyboxImage.registry $root.Values.agents.muster.busyboxImage.repository $root.Values.agents.muster.busyboxImage.tag | quote }} - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: ["ALL"] - readOnlyRootFilesystem: true - resources: - requests: - ephemeral-storage: 8Mi - limits: - ephemeral-storage: 16Mi - command: - - /bin/sh - - -ec - - | - header="$(printf 'Bearer %s' "$(cat /var/run/muster-token/token)" | base64 | tr -d '\n')" - cat > /work/secret.yaml <". - - The token must pass muster's trustedIssuers JWT validation, which requires the - cluster OIDC issuer in iss and an exp claim. Legacy - kubernetes.io/service-account-token Secrets carry iss "kubernetes/serviceaccount" - and no exp, so only TokenRequest-minted tokens pass. - - One Job per enabled agent bootstraps that agent's token Secret on install/upgrade - so the agent has a token immediately; the muster-token-refresh CronJob re-mints it - on a schedule (the token TTL is short, so the snapshot must be rotated, not minted - once). Both share the pod spec in _muster-token.tpl, which runs the Job as the - agent's own ServiceAccount. - - All Agent, ModelConfig, and RemoteMCPServer CRs must be in the same namespace - (kagent.namespaceOverride, defaulting to the release namespace) because the kagent - controller watches a single namespace scope for these resources. -*/}} -{{- range $name, $cfg := .Values.agents.definitions }} -{{- $agent := merge (dict "name" $name) $cfg }} -{{- if $agent.enabled }} ---- -apiVersion: batch/v1 -kind: Job -metadata: - name: {{ printf "%s-muster-token-init-%s" (include "name" $) $agent.name | trunc 63 | trimSuffix "-" }} - namespace: {{ $kagentNs }} - labels: - {{- include "labels.common" $ | nindent 4 }} - annotations: - "helm.sh/hook": post-install,post-upgrade - "helm.sh/hook-weight": "0" - "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded -spec: - ttlSecondsAfterFinished: 300 - template: - metadata: - labels: - app.kubernetes.io/name: kagent-muster-token-init - app.kubernetes.io/managed-by: {{ $.Release.Service }} - spec: - {{- include "agentic-platform.musterTokenPodSpec" (dict "root" $ "agent" $agent) | nindent 6 }} -{{- end }} -{{- end }} -{{- end }} diff --git a/helm/agentic-platform-connectivity/templates/kagent/agents/remotemcpservers.yaml b/helm/agentic-platform-connectivity/templates/kagent/agents/remotemcpservers.yaml index 3fa8e28..f224388 100644 --- a/helm/agentic-platform-connectivity/templates/kagent/agents/remotemcpservers.yaml +++ b/helm/agentic-platform-connectivity/templates/kagent/agents/remotemcpservers.yaml @@ -2,13 +2,13 @@ {{- $ns := .Values.kagent.namespaceOverride | default .Release.Namespace }} {{- $musterUrl := printf "http://%s.%s.svc.cluster.local:%v/mcp" (include "agentic-platform.musterFullname" .) .Release.Namespace (include "agentic-platform.musterServicePort" .) }} {{- if .Values.muster.enabled }} -{{/* One muster RemoteMCPServer per enabled agent: same muster Service. M2M agents -carry their own SA token via headersFrom so muster sees a per-agent identity; OBO -agents (obo.enabled) omit it and forward the propagated human muster token instead. */}} +{{/* One muster RemoteMCPServer per enabled agent, all pointing at the same muster +Service. No headersFrom: kagent's propagated muster token (KAGENT_PROPAGATE_TOKEN) +must be the only Authorization reaching muster; a static header would win (kagent +applies static headers last) and shadow the on-behalf-of identity. */}} {{- range $name, $cfg := .Values.agents.definitions }} {{- $agent := merge (dict "name" $name) $cfg }} {{- if $agent.enabled }} -{{- $tokenSecretName := include "agentic-platform.musterTokenSecretName" (dict "root" $ "agent" $agent) }} --- apiVersion: kagent.dev/v1alpha2 kind: RemoteMCPServer @@ -19,20 +19,6 @@ spec: description: {{ printf "muster MCP gateway (%s)" $agent.name | quote }} url: {{ $musterUrl | quote }} protocol: STREAMABLE_HTTP -{{- if not $agent.obo.enabled }} - {{/* M2M agent: inject the static SA Bearer token so muster sees the per-agent - identity. OBO agents omit this entirely so kagent's propagated human muster - token (KAGENT_PROPAGATE_TOKEN) is the only Authorization reaching muster — the - static header would otherwise win (kagent applies static headers last) and - shadow the user. No X-Actor-Token act-chain: the forwarded token is already a - muster access_token, so the human is the muster subject directly. */}} - headersFrom: - - name: Authorization - valueFrom: - type: Secret - name: {{ $tokenSecretName | quote }} - key: token -{{- end }} {{- end }} {{- end }} {{- end }} diff --git a/helm/agentic-platform-connectivity/values.yaml b/helm/agentic-platform-connectivity/values.yaml index 1cec0e9..c283ed7 100644 --- a/helm/agentic-platform-connectivity/values.yaml +++ b/helm/agentic-platform-connectivity/values.yaml @@ -693,14 +693,19 @@ kagent: # # muster RemoteMCPServer, SA, and egress CNP are gated on kagent.enabled + muster.enabled. agents: - # Shared muster token-mint infrastructure. Active when kagent.enabled + - # muster.enabled. Each agent in `list` below gets a RemoteMCPServer plus the - # Helm hook Job + refresh CronJob that mint a short-lived token for the agent's - # own SA and write "Bearer " into its headersFrom Secret; this block only - # holds the images and token lifetime shared by all of them. + # kagent agents, keyed by agent name. Each enabled entry renders: the Agent CR + # (prompt, model, tools), its ServiceAccount (the on-behalf-of actor identity), + # and a per-agent muster RemoteMCPServer named "muster-" carrying no + # static credentials: kagent propagates the caller's muster token + # (KAGENT_PROPAGATE_TOKEN) so muster sees the human as the subject and the + # agent SA as the RFC 8693 actor. Add an agent = add a key. + # Keyed by name (not a list) so per-cluster values deep-merge field-by-field; a + # cluster can flip enabled or tweak the prompt without restating the whole entry. + # Agents run the Python ADK. # # Prerequisite: muster.muster.oauth.server.trustedIssuers must include an - # entry for the cluster SA OIDC issuer (find with: + # entry for the cluster SA OIDC issuer so muster can validate the agent's + # actor token at /oauth/token (find with: # kubectl get --raw /.well-known/openid-configuration | jq .issuer). # Set in per-cluster values — see configmap-values.yaml.template. # Example entry: @@ -708,45 +713,13 @@ agents: # - issuer: "https:///" # allowedClaims: # sub: "system:serviceaccount:kagent:*" - muster: - # kubectl image used by the token hook Job to apply the Secret. Distroless - # (registry.k8s.io/kubectl), no shell — invoked with args only. - kubectlImage: - registry: gsoci.azurecr.io - repository: giantswarm/kubectl - tag: v1.36.2 - # busybox image used by the token hook Job's init container to read the - # projected token and render the Secret manifest. Needs a POSIX shell. - busyboxImage: - registry: gsoci.azurecr.io - repository: giantswarm/busybox - tag: "1.38.0" - # Expiration requested for the projected muster token, in seconds. The token - # is snapshotted into the headersFrom Secret and re-minted by the - # muster-token-refresh CronJob; keep this comfortably above tokenRefreshSchedule - # so a missed refresh still leaves a valid token. The API server caps it via - # --service-account-max-token-expiration. - tokenExpirationSeconds: 3600 - # Cron schedule for re-minting the muster token Secret. Must run well inside - # tokenExpirationSeconds (default: every 15 minutes, a quarter of the 1h TTL). - tokenRefreshSchedule: "*/15 * * * *" - - # kagent agents, keyed by agent name. Each enabled entry renders: the Agent CR - # (prompt, model, tools); a per-agent muster RemoteMCPServer named "muster-" - # whose headersFrom Secret carries the agent's OWN SA Bearer token (the - # on-behalf-of actor presented to muster as X-Actor-Token); the mint Job + - # refresh CronJob + Secret RBAC that run as that SA. Add an agent = add a key. - # Keyed by name (not a list) so per-cluster values deep-merge field-by-field — a - # cluster can flip enabled or tweak the prompt without restating the whole entry. - # The kagent controller auto-creates the SA named after the agent, which is the - # on-behalf-of actor. Agents run the Python ADK. # # Disabled by default; enable per cluster in giantswarm-config. definitions: sre-agent: enabled: false - # SA whose token is minted and presented to muster as the on-behalf-of actor. - # Defaults to the agent name (the SA the kagent controller creates for it). + # SA the agent runs as: the on-behalf-of actor presented to muster. + # Defaults to the agent name. serviceAccount: sre-agent # ModelConfig CR the agent uses. Defaults to the one kagent's sub-chart # creates from kagent.providers.anthropic. @@ -810,21 +783,8 @@ agents: - get_prompt # Request headers forwarded from the inbound A2A call onto muster tool calls. # With KAGENT_PROPAGATE_TOKEN the inbound Authorization is forwarded regardless; - # ["authorization"] additionally allows explicit per-header forwarding. Whether - # the call runs as the user or as the agent SA is governed by `obo.enabled` below. + # ["authorization"] additionally allows explicit per-header forwarding. allowedHeaders: ["authorization"] - # OBO (on-behalf-of) mode. When enabled, this agent's muster RemoteMCPServer - # OMITS the static SA-token `headersFrom` injection, so kagent's propagated - # human muster access_token (iss=muster, aud=.../mcp) reaches muster as the real - # user instead of being shadowed by the agent SA. The forwarded token is already - # a muster token, so no RFC 8693 act-chain (X-Actor-Token) is used — the human is - # the muster subject directly. Trade-off: a tokenless autonomous (M2M) run has no - # Authorization and cannot reach muster, so keep obo disabled for agents that must - # run autonomously against muster. (kagent-dev/kagent#2087 would let the static SA - # token stay as an M2M fallback while a propagated user token still wins; until it - # lands the two are mutually exclusive and this chart drops the static token.) - obo: - enabled: true # Optional container resources for the agent Deployment. resources: {} # Optional extra env for the agent Deployment. @@ -842,9 +802,9 @@ agents: # List of additional RemoteMCPServer CRs to create in the kagent namespace. # Use for MCP servers other than the built-in muster. Each entry creates one - # CR; agents reference it by name. tokenSecret defaults to the chart-managed - # SA-token Secret (written by the muster token hook Job, key "token", value - # "Bearer "); set it only for servers needing a different credential. + # CR; agents reference it by name. Set tokenSecret to a pre-existing Secret + # holding key "token" with value "Bearer " for servers that need a + # static credential; omit it for servers reached with the propagated token. remoteMcpServers: [] # - name: external-mcp # url: "https://external-mcp.example.com/mcp" diff --git a/helm/agentic-platform/values.yaml b/helm/agentic-platform/values.yaml index e260f61..c20571d 100644 --- a/helm/agentic-platform/values.yaml +++ b/helm/agentic-platform/values.yaml @@ -870,14 +870,19 @@ kagent: # # muster RemoteMCPServer, SA, and egress CNP are gated on kagent.enabled + muster.enabled. agents: - # Shared muster token-mint infrastructure. Active when kagent.enabled + - # muster.enabled. Each agent in `list` below gets a RemoteMCPServer plus the - # Helm hook Job + refresh CronJob that mint a short-lived token for the agent's - # own SA and write "Bearer " into its headersFrom Secret; this block only - # holds the images and token lifetime shared by all of them. + # kagent agents, keyed by agent name. Each enabled entry renders: the Agent CR + # (prompt, model, tools), its ServiceAccount (the on-behalf-of actor identity), + # and a per-agent muster RemoteMCPServer named "muster-" carrying no + # static credentials: kagent propagates the caller's muster token + # (KAGENT_PROPAGATE_TOKEN) so muster sees the human as the subject and the + # agent SA as the RFC 8693 actor. Add an agent = add a key. + # Keyed by name (not a list) so per-cluster values deep-merge field-by-field; a + # cluster can flip enabled or tweak the prompt without restating the whole entry. + # Agents run the Python ADK. # # Prerequisite: muster.muster.oauth.server.trustedIssuers must include an - # entry for the cluster SA OIDC issuer (find with: + # entry for the cluster SA OIDC issuer so muster can validate the agent's + # actor token at /oauth/token (find with: # kubectl get --raw /.well-known/openid-configuration | jq .issuer). # Set in per-cluster values — see configmap-values.yaml.template. # Example entry: @@ -885,48 +890,13 @@ agents: # - issuer: "https:///" # allowedClaims: # sub: "system:serviceaccount:kagent:*" - muster: - # kubectl image used by the token hook Job to apply the Secret. Distroless - # (registry.k8s.io/kubectl), no shell — invoked with args only. - kubectlImage: - registry: gsoci.azurecr.io - repository: giantswarm/kubectl - tag: v1.36.2 - # busybox image used by the token hook Job's init container to read the - # projected token and render the Secret manifest. Needs a POSIX shell. - busyboxImage: - registry: gsoci.azurecr.io - repository: giantswarm/busybox - tag: "1.38.0" - # Expiration requested for the projected muster token, in seconds. The token - # is snapshotted into the headersFrom Secret and re-minted by the - # muster-token-refresh CronJob; keep this comfortably above tokenRefreshSchedule - # so a missed refresh still leaves a valid token. The API server caps it via - # --service-account-max-token-expiration. - tokenExpirationSeconds: 3600 - # Cron schedule for re-minting the muster token Secret. Must run well inside - # tokenExpirationSeconds (default: every 15 minutes, a quarter of the 1h TTL). - tokenRefreshSchedule: "*/15 * * * *" - - # kagent agents, keyed by agent name. Each enabled entry renders: the Agent CR - # (prompt, model, tools); a per-agent muster RemoteMCPServer named "muster-" - # whose headersFrom Secret carries the agent's OWN SA Bearer token (the - # on-behalf-of actor presented to muster as X-Actor-Token); the mint Job + - # refresh CronJob + Secret RBAC that run as that SA. Add an agent = add a key. - # An agent that forwards the human token (allowedHeaders includes "authorization", - # OBO mode) drops the headersFrom injection AND the mint Job/CronJob/RBAC — it - # presents the propagated user muster token, not an SA token. - # Keyed by name (not a list) so per-cluster values deep-merge field-by-field — a - # cluster can flip enabled or tweak the prompt without restating the whole entry. - # The kagent controller auto-creates the SA named after the agent, which is the - # on-behalf-of actor. Agents run the Python ADK. # # Disabled by default; enable per cluster in giantswarm-config. definitions: sre-agent: enabled: false - # SA whose token is minted and presented to muster as the on-behalf-of actor. - # Defaults to the agent name (the SA the kagent controller creates for it). + # SA the agent runs as: the on-behalf-of actor presented to muster. + # Defaults to the agent name. serviceAccount: sre-agent # ModelConfig CR the agent uses. Defaults to the one kagent's sub-chart # creates from kagent.providers.anthropic. @@ -989,21 +959,8 @@ agents: - get_prompt # Request headers forwarded from the inbound A2A call onto muster tool calls. # With KAGENT_PROPAGATE_TOKEN the inbound Authorization is forwarded regardless; - # ["authorization"] additionally allows explicit per-header forwarding. Whether - # the call runs as the user or as the agent SA is governed by `obo.enabled` below. + # ["authorization"] additionally allows explicit per-header forwarding. allowedHeaders: ["authorization"] - # OBO (on-behalf-of) mode. When enabled, this agent's muster RemoteMCPServer - # OMITS the static SA-token `headersFrom` injection, so kagent's propagated - # human muster access_token (iss=muster, aud=.../mcp) reaches muster as the real - # user instead of being shadowed by the agent SA. The forwarded token is already - # a muster token, so no RFC 8693 act-chain (X-Actor-Token) is used — the human is - # the muster subject directly. Trade-off: a tokenless autonomous (M2M) run has no - # Authorization and cannot reach muster, so keep obo disabled for agents that must - # run autonomously against muster. (kagent-dev/kagent#2087 would let the static SA - # token stay as an M2M fallback while a propagated user token still wins; until it - # lands the two are mutually exclusive and this chart drops the static token.) - obo: - enabled: true # Optional container resources for the agent Deployment. resources: {} # Optional extra env for the agent Deployment. @@ -1011,9 +968,9 @@ agents: # List of additional RemoteMCPServer CRs to create in the kagent namespace. # Use for MCP servers other than the built-in muster. Each entry creates one - # CR; agents reference it by name. tokenSecret defaults to the chart-managed - # SA-token Secret (written by the muster token hook Job, key "token", value - # "Bearer "); set it only for servers needing a different credential. + # CR; agents reference it by name. Set tokenSecret to a pre-existing Secret + # holding key "token" with value "Bearer " for servers that need a + # static credential; omit it for servers reached with the propagated token. remoteMcpServers: [] # - name: external-mcp # url: "https://external-mcp.example.com/mcp"