Policy: Caller Authorization & Capability Enforcement for Agents #2618
Replies: 1 comment 1 reply
|
This is very well-though of, and highly important for enterprise adoption, the Policy CRD implementation proposal looks very solid, and completely valid. Thinking it a bit further, and using Google as a strong reference, the proposal is also very aligned with the approach adopted by the Gemini Enterprise Platform. They landed on the same two elements (a SPIFFE-based per-agent identity, plus an enforcement gate on who may call what), and they've also made the choice of moving away from shared service accounts toward per-agent least-privilege principals. Where Google has taken a step further is in the "execution-time identity" of the agent, not just the caller, and this could be extended in our proposal. In the current proposal the agent always runs with whatever identity and capabilities its bound Policy allows, regardless of whether it was invoked by an automated pipeline or directly by a human user. Gemini's model adds a new dimension on top of it: when an agent is triggered by an user (human), it can switch from acting under its own authority to acting "on behalf of that user", thus borrowing their credentials. I would suggest this very same as a further addition to the proposal |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Proposing a
PolicyCRD that governs who may invoke an agent and what that agent is allowed to use at query-dispatch time. Part of the Agent Identity Management work (#1539); this draft covers the policy layer only, not the SPIFFE identity-minting layer it builds on.Summary
Today any caller that can create a
Queryagainst an agent runs it with whatever models, tools, and downstream agents that agent declares. There is no way to say "only service accountXmay call this agent" or "this agent may use completions but not tools."This change adds a namespaced
PolicyCRD and an enforcement gate in the Query controller. An agent opts in viaspec.policyRef; when set, every query targeting that agent is checked against the bound policy before it is dispatched to the executor. Denials fail the query with a specific reason and emit aPolicyEnforcementevent.Policy is opt-in and fail-safe-by-allow on empty: agents without a
policyRefare unaffected, and empty allow-lists mean "no restriction."What a Policy looks like
Bind it on an agent:
Enforcement flow
The gate runs in
QueryReconciler.executeQueryAsync, after the dispatch address is resolved but beforesendQueryA2A:policyRef→ allow (opt-in).IdentityReady=Truecondition +status.identity.spiffeIDmatching the expectedspiffe://<trust-domain>/ns/<ns>/agent/<name>). This is the coupling to the SPIFFE layer.query.spec.serviceAccountagainstallowedServiceAccounts, or its derived caller SPIFFE ID (spiffe://<trust-domain>/ns/<ns>/sa/<sa>) againstallowedSPIFFEIDs.Allow-lists treat empty as "no restriction" and support a
*wildcard. Downstream-agent checks resolve an agent-typeTool(tool.spec.agent.name) to the real target agent name before matchingallowedAgents.Denial reasons
AgentNotFoundAgentIdentityNotReadyPolicyNotFoundpolicyRefpoints at a missing PolicyCallerNotAuthorizedCompletionsNotAllowedallowCompletions: falseToolsNotAllowedallowTools: falseand agent declares a non-built-in toolModelNotAllowedmodelRefnot inallowedModelsToolNotAllowedallowedToolsAgentNotAllowedallowedAgentsOn any denial the query goes to
phase=errorwith responsepolicy denied: <Reason>: <Message>.Components added / changed
Controller (Go)
ark/api/v1alpha1/policy_types.go—PolicyCRD (PolicySpec=CallerAuthorization+Capabilities),PolicyStatus.ark/api/v1alpha1/agent_types.go—Agent.spec.policyRef(PolicyRef) andAgent.status.identity(AgentIdentityStatus).ark/internal/controller/policy_enforcement.go— theenforceAgentPolicygate and helpers.ark/internal/controller/policy_controller.go— minimal reconciler that stampsReadyon a Policy.ark/internal/controller/query_controller.go— wires the gate into dispatch; addsTrustDomain.ark/cmd/main.go— registersPolicyReconciler; adds--spire-trust-domainflag (defaultark.local).config/crd/bases/...policies.yaml, chart CRD,policies/policies/status/policies/finalizersgrants.policy_enforcement_test.go—TestEnforceAgentPolicycovers ~16 positive/negative cases (one per reason above).API (Python / FastAPI)
services/ark-api/.../api/v1/policies.py—GET /v1/policies,GET /v1/policies/{name},POST /v1/policies.services/ark-api/.../models/policies.py— request/response models.agents.py/models/agents.py—policyRefplumbed through agent create/update/read.Dashboard (Next.js)
app/(dashboard)/policies/new/page.tsx— create-policy form.lib/services/policies.ts— policy service client.All reactions