Skip to content
Open
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.<agent>.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.<name>.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.

Expand Down
27 changes: 16 additions & 11 deletions docs/authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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 -}}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@
{{- if $agent.enabled }}
{{- $identitySA := include "agentic-platform.agentServiceAccount" $agent }}
---
# Chart-owned so it exists before the muster-token bootstrap Job (a post-install
# hook running as this SA) and before the kagent controller needs it. The Agent's
# serviceAccountName below pins it, so kagent uses this SA instead of creating its
# own — without it the SA would only appear once the agent Deployment is up, which
# needs the muster RemoteMCPServer accepted, which needs the token this SA mints.
# Chart-owned: the agent's identity is the RFC 8693 actor muster sees on token
# exchange, so it must be stable and exist independently of the kagent
# controller's Agent reconciliation. The Agent's serviceAccountName below pins it.
apiVersion: v1
kind: ServiceAccount
metadata:
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 }}
Expand Down
Loading