Don't Teach Your Agents Karate

I keep watching the same pattern repeat across teams building agents on Kubernetes.
A team builds an agent. It calls external APIs, talks to other agents, maybe exposes MCP tools. At some point someone asks, “Who can call this thing? How do we rate limit it? How do we make sure it doesn’t leak the OpenAI API key?” And then the team spends two weeks building auth middleware they’ll never maintain properly.
Multiply that by every agent team in the org and you get a mess. Inconsistent token validation. Hardcoded API keys in environment variables. Rate limiting that exists in some agents and not others. No central visibility into who can call what.
This is the part of agent development that nobody wants to do, and everybody does badly. So I built agent-access-control to make it a platform problem instead of an application problem.
The numbers are worse than you think
Gravitee surveyed 900+ practitioners for their State of AI Agent Security report. Some findings that stuck with me:
- 45.6% of teams still use shared API keys for agent-to-agent auth.
- 27.2% have resorted to custom, hardcoded logic for authorization.
- Only 14.4% report that all agents went live with full security approval.
That last one is telling. 80.9% of teams have moved past planning into active testing or production. But barely a seventh of them have security’s blessing. The agents are out there. The controls aren’t.
OWASP released their Agentic AI Top 10 in December 2025. Three of the top four risks center on identity, tool access, and delegated trust. Their new concept of “least agency” is the principle of least privilege applied to autonomous systems. An agent should have the minimum autonomy, tool access, and credential scope needed for its task. Nothing more.
In practice, most agents hold 10x more privileges than required. SaaS defaults to “read all files” when only a folder is needed. Faster for developers, but it means a compromised agent gets everything.
What I kept running into
I work on Kubernetes-based AI infrastructure. Across different projects, I kept hitting the same three problems:
Inbound access control. Agent A calls Agent B. But who checks that Agent A is allowed? Each agent team builds its own JWT validation, its own allowlists. Or worse, they don’t. They assume the network is safe because it’s inside the cluster. (It’s not.)
Rate limiting. An orchestrator agent fires off 200 requests per second to a downstream agent during a batch job. The downstream agent falls over. Nobody set limits because “we’ll add that later.” Later never comes.
Outbound credential management. The agent needs to call the GitHub API with an OAuth token. It needs to call OpenWeatherMap with an API key. Those credentials get baked into ConfigMaps, mounted as environment variables, and forgotten. The agent holds the credential directly. If the agent is compromised, so is the credential.
Every team solves these problems on their own. And every team solves them just enough to ship, then moves on.
What if the platform handled it?
The idea is pretty direct: take those three concerns out of the agent entirely and push them into the platform.
I built a Kubernetes controller called Agent Access Control. It watches two CRDs and generates all the security infrastructure automatically.
The first CRD is AgentCard. It represents an agent’s identity: what protocols it speaks (A2A, MCP, REST), what skills it offers, what port it listens on. In production, a discovery controller creates these automatically by probing running agents for their /.well-known/agent.json or MCP tools/list endpoint. The agent developer never writes a CRD.
The second CRD is AgentPolicy. This is where the platform team defines access rules. It selects agents by label and declares:
- Who can call this agent (JWT-authenticated identity)
- What this agent can call (other agents, by name)
- What external APIs it can reach, and how credentials are handled (Vault, token exchange, passthrough, or deny)
- How many requests per minute it accepts
From those two resources, the controller generates five types of Kubernetes objects:
| Generated Resource | What It Does | Who Enforces |
|---|---|---|
| HTTPRoute | Routes traffic through the gateway | Gateway API |
| AuthPolicy | JWT validation + identity-based access control | Authorino (Kuadrant) |
| RateLimitPolicy | Per-agent rate limits with centralized counters | Limitador (Kuadrant) |
| ConfigMap | Sidecar proxy outbound credential rules | Auth sidecar |
| MCPServerRegistration | Registers MCP tools for federation | MCP Gateway |
The agent contains zero auth code. The developer deploys a container, labels it, and the platform does the rest.
How the pieces fit
Say a weather agent joins the cluster. A discovery controller finds it, reads its capabilities, and creates:
apiVersion: kagenti.com/v1alpha1
kind: AgentCard
metadata:
name: weather-agent
labels:
tier: standard
spec:
description: "Weather forecasts and alerts"
protocols: [a2a, rest]
skills:
- name: get-forecast
- name: weather-alerts
servicePort: 8080
The platform team has a policy that covers all tier: standard agents:
apiVersion: kagenti.com/v1alpha1
kind: AgentPolicy
metadata:
name: standard-tier
spec:
agentSelector:
matchLabels:
tier: standard
ingress:
allowedAgents: [orchestrator, planner]
external:
defaultMode: deny
rules:
- host: api.openweathermap.org
mode: vault
vaultPath: secret/data/openweathermap
header: x-api-key
- host: weather.gov
mode: passthrough
rateLimit:
requestsPerMinute: 60
The controller reconciles and generates four resources. An HTTPRoute that exposes the agent through the gateway on /agents/weather-agent. An AuthPolicy that tells Authorino to validate JWTs and only allow requests where sub is orchestrator or planner. A RateLimitPolicy that tells Limitador to enforce 60 requests per minute with centralized counters. And a ConfigMap for the sidecar proxy that says “fetch the API key from Vault for openweathermap, pass through to weather.gov, deny everything else.”
When I check the HTTPRoute status on the cluster:
kuadrant.io/AuthPolicyAffected: True
kuadrant.io/RateLimitPolicyAffected: True
Authorino and Limitador are actively enforcing. Not configured. Enforcing. On a live cluster, right now.
The outbound side matters more than people think
Everyone focuses on inbound auth. “Who can call my agent?” That’s important but it’s the easier half.
The harder question is: “What can my agent call, and with what credentials?”
CyberArk wrote about this in their 2026 agent security market analysis. Credential velocity is outpacing governance. Every new agent, workflow, or integration can mint credentials in minutes. Most organizations still track that in spreadsheets.
The sidecar approach handles this without the agent touching credentials at all. The sidecar config for our weather agent looks like:
gateway:
host: agent-gateway.agent-demo.svc.cluster.local
mode: passthrough
allowedAgents: [summarizer, classifier]
external:
defaultMode: deny
rules:
- host: api.openweathermap.org
mode: vault
vaultPath: secret/data/openweathermap
header: x-api-key
- host: weather.gov
mode: passthrough
The agent makes a regular HTTP call to api.openweathermap.org. The sidecar intercepts it, fetches the API key from Vault, injects it into the x-api-key header, and forwards. The agent never sees the credential. If the agent is compromised, the attacker gets an HTTP client that can call two whitelisted hosts. They don’t get the API key. They can’t call anything else because the default mode is deny.
Compare that to an environment variable containing the raw API key that the agent reads directly. A compromised agent with that pattern gets the credential itself, forever, until someone rotates it manually.
What about MCP?
If an agent’s protocols include mcp, the controller also creates an MCPServerRegistration. This registers the agent’s tools with the MCP Gateway, which aggregates tools from multiple agents into a single /mcp endpoint with prefix-based routing and per-agent tool filtering.
The MCP Gateway CRD isn’t installed on every cluster, so the controller checks for it and skips if it’s missing. It logs a message and moves on. No crash, just “MCP not available, skipping.”
Lifecycle management
Everything the controller generates has an owner reference pointing back to the source CRD. Delete an AgentCard and the HTTPRoute, AuthPolicy, RateLimitPolicy, and ConfigMap all cascade-delete automatically. Recreate the AgentCard and the controller regenerates everything within seconds.
This matters when agents are ephemeral. A scale-to-zero agent that wakes up on demand gets its full security posture reconstructed as part of the reconciliation loop. No manual cleanup, no orphaned resources.
The developer experience
Here’s what the agent developer actually does:
- Build an agent that speaks A2A or MCP.
- Push the container image.
- Deploy it with the label
kagenti.com/agent: true.
That’s it. No CRDs to write. No gateway to configure. No auth middleware to build. No credentials to manage. The discovery controller finds the agent. The platform team’s policy matches it. The controller generates everything.
The platform team writes one policy per tier. standard-tier covers all standard agents. premium-tier covers premium agents with higher rate limits and access to more external APIs. Add a new agent and it inherits the policy for its tier automatically.
Where this sits in the broader picture
This project is the policy and control plane layer. It generates configuration. The actual runtime enforcement happens elsewhere:
Authorino (part of Kuadrant) validates JWTs and enforces identity-based access at the gateway. Limitador (also Kuadrant) enforces rate limits with centralized counters. The auth sidecar enforces outbound credential rules per request. MCP Gateway handles tool federation. Gateway API provides the routing substrate.
The controller ties these together with a single declarative API. Without it, someone manually creates and maintains HTTPRoutes, AuthPolicies, RateLimitPolicies, and sidecar configs for every agent. With it, they write an AgentPolicy and walk away.
Try it
The controller runs on any Kubernetes cluster with Gateway API. Kuadrant is optional (for auth and rate limiting). The MCP Gateway is optional (for tool federation). Without them, the controller still generates HTTPRoutes and sidecar ConfigMaps.
git clone https://github.com/agentoperations/agent-access-control.git
cd agent-access-control
make install # CRDs
make deploy # Controller + RBAC
kubectl apply -f config/samples/
There’s a narrated demo showing the full lifecycle on a live OpenShift cluster, and a second demo that walks through the three layers of enforcement.
The code is Apache 2.0 on GitHub. The APIs are pre-alpha so expect breakage. But the pattern itself, treating agent access control as a platform concern rather than an application concern, is what I think is worth talking about.
Agents should fetch weather forecasts and review pull requests. Not validate JWTs.