Skip to content

Attribution (K8s half): scoped impersonate RBAC for impersonatedUser in the audit log #40

Description

@stxkxs

Part of the agent action attribution roadmap — the K8s half. Companion to
#39 (the AWS-half session-broker role) and #38 (direct runtime).

Blocked on Phase 0 — settling the standing hub cluster. Don't start until the
hub is validated + stable. Tracking now so the gap is visible.

What

Ship the impersonate RBAC the runtime needs so an agent's K8s action records a
human/session impersonatedUser in the audit log (instead of a bare
system:serviceaccount:…).

The runtime (the attributable path — #38, AgentSandbox/direct) calls the API with
kubectl --as=<id> (Impersonate-User header). The same scoped session id used for
the AWS-side SourceIdentity (#39) is used here, so one operator string binds both
surfaces
(CloudTrail sourceIdentity + K8s audit impersonatedUser).

Current state (the gap)

  • The operator creates no tenant RBAC and never sets an Impersonate header —
    actions trace only to the SA system:serviceaccount:tenants-<platform>:tenant-runtime
    (operators/internal/controller/agentfleet_reconcile.go:44-47;
    platform_iam.go:99,163). This is the authentic UNATTRIBUTED finding today.
  • The impersonate ClusterRole is documented in fab but not shipped on the
    substrate (fab/docs/attribution.md:57-60,92-108); FAB_OPERATOR sessions get
    kubectl Forbidden until it's applied.

Shape of the change

  1. A ClusterRole with verbs: [impersonate] on users (and groups if needed),
    scoped by resourceNames to the session id — never impersonate *.
  2. A binding to the session ServiceAccount, keyed off a new spec field (rides with
    the per-session spec.operator work).
  3. Ship it in charts/operator (or as an eks-gitops addon so spokes
    self-reconcile it). Off-by-default — only rendered when the tenant opts into the
    attributable path.

Scope / guardrails

  • Blast radius = the impersonated identity's own RBAC. The synthetic
    agent:<tenant>:<session> identity is bound to a least-privilege Role, so
    impersonating it grants nothing beyond that.
  • resourceNames-scoped, never wildcard impersonation.
  • One-tenant pilot first.

Note on the kagent path

kagent's MCP tool-server makes the real K8s call under its own SA
(agentfleet_reconcile.go:216-225), so it's unattributable at the agent layer
without the tool-server itself impersonating. The attributable workload therefore
runs on the direct/AgentSandbox runtime (#38); tool-server impersonation is a
later, harder option. Keep kagent as the governed/unattributed exhibit for now.

Independence

crossbearing consumes the audit log only; nothing crossbearing-specific lands here.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions