Securing AI agent MCP traffic on Kubernetes requires two enforcement layers: Kyverno admission policies control what gets deployed, and agentgateway External Authorization controls what those deployments can do at runtime. This post covers complete Kyverno 1.17 ValidatingPolicy YAML for agent pods, gateway ExtAuthz configuration, namespace isolation automation, and Shadow MCP detection.

Your cluster has an agent pod running. That agent discovered an MCP server offering a kubectl_exec tool. Nothing stopped it from calling that tool against any namespace it could reach. The agent did not need elevated RBAC because the MCP server already had it. By the time your incident response team looked at the logs, the agent had enumerated every secret in the production namespace.

This is not a hypothetical. KubeCon EU 2026 identified Shadow MCP deployments - developers connecting unmanaged MCP servers to production databases from their laptops - as a “real and present security risk.” The OWASP MCP Top 10 (v0.1 beta) catalogs the threat surface: Privilege Escalation via Scope Creep (MCP02), Tool Poisoning (MCP03), Insufficient Authentication and Authorization (MCP07), and Shadow MCP Servers (MCP09).

Kubernetes gives you the primitives to build a real enforcement architecture for agent workloads. Kyverno, now a CNCF Graduated project as of March 2026, gives you a policy-as-code layer that speaks native Kubernetes YAML with CEL expressions. This post shows how to combine Kyverno admission policies with agentgateway’s External Authorization to build two-layer security for AI agent MCP traffic: control what gets deployed, then control what those deployments can do.

Why AI Agents Need Kubernetes-Native Policy Enforcement

What MCP Traffic Looks Like in a Cluster

When AI agents run as pods, MCP traffic follows a specific pattern. Agent pods make tool calls via JSON-RPC over HTTP/SSE to MCP server pods, which may run in the same namespace or across namespace boundaries. MCP servers expose tools that agents discover via tools/list, then invoke via tools/call with structured parameters.

Without an enforcement layer, any agent that can reach an MCP server can call any tool that server exposes. That gap between “agent can call any tool it discovers” and “agent should only call authorized tools in its assigned scope” is where security incidents happen.

sequenceDiagram
    participant A as Agent Pod
    participant GW as agentgateway
    participant EA as ExtAuthz Service
    participant PE as Policy Engine
    participant M as MCP Server Pod

    A->>GW: tools/call (JSON-RPC)
    GW->>EA: authorization check (actor, tool, params)
    EA->>PE: evaluate Cedar/OPA rules
    PE-->>EA: allow/deny decision
    EA-->>GW: decision
    alt allowed
        GW->>M: forward tool call
        M-->>GW: tool result
        GW-->>A: tool result
    else denied
        GW-->>A: 403 Forbidden
    end

The MCP tool call flow through agentgateway’s authorization layer. The policy engine evaluates every call against Cedar or OPA rules before it reaches the MCP server.

An optional MCP gateway sits between agents and MCP servers, providing multiplexing, authorization, and observability. This gateway is the critical second enforcement layer - it operates at runtime on MCP wire traffic, which Kubernetes admission control cannot see.

The OWASP MCP Top 10 Risks That Apply

The OWASP MCP Top 10 (v0.1 beta) describes the threat landscape your policies need to address:

  • MCP02 (Privilege Escalation via Scope Creep): Agents accumulating tool permissions beyond their intended scope through excessive or overly permissive MCP server configurations
  • MCP03 (Tool Poisoning): Adversaries compromising MCP servers that agents depend on, causing agents to execute malicious actions
  • MCP07 (Insufficient Authentication and Authorization): Weak or absent identity verification in MCP ecosystems
  • MCP09 (Shadow MCP Servers): Unapproved MCP deployments that operate outside organizational security governance

The architecture in this post addresses all four. Kyverno handles MCP09 at admission and MCP03 via image verification. agentgateway handles MCP02 and MCP07 at runtime.

Kyverno in 2026: CNCF Graduated and CEL-Native

Kyverno graduated as a CNCF project on March 24, 2026, at KubeCon EU in Amsterdam. Jim Bugwadia, Kyverno co-creator and Nirmata CEO, noted at graduation that “policy-as-code provides the essential guardrails for autonomous governance” as AI adoption accelerates. The graduation announcement explicitly called out upcoming releases that extend policy enforcement to “support for artificial intelligence and Model Context Protocol (MCP) gateways” as a roadmap item.

The adoption scale validates the operational readiness: LinkedIn enforces Kyverno policies across 230+ clusters with 500,000+ nodes, handling over 20,000 admission requests per minute.

What Changed in Kyverno 1.17

Kyverno 1.17 (February 2026) promoted CEL-based policy types to v1 GA:

Policy TypePurpose
ValidatingPolicyBlock non-compliant resource creation/updates
MutatingPolicyMutate resources at admission (inject defaults, labels)
GeneratingPolicyCreate downstream resources when triggers fire
ImageValidatingPolicyVerify container image signatures and attestations
DeletingPolicyGarbage collect resources matching conditions

These use apiVersion: policies.kyverno.io/v1 - the stable, production-ready API. The legacy kyverno.io/v1 ClusterPolicy API continues to work in 1.17 but is deprecated and scheduled for removal in 1.20 (October 2026). All new agent policies should use the CEL syntax from day one.

How Do You Enforce Admission-Level Security on Agent Pods?

Kyverno’s primary enforcement point is the Kubernetes admission webhook. When agent pods or MCP server pods are created, Kyverno ValidatingPolicy intercepts the request and either allows or denies it before the pod runs.

Blocking Privileged Agent Pods

The first policy blocks any pod labeled as an agent from running with privileged containers. Privileged containers can access the host kernel, bypass namespace isolation, and read host-level secrets - none of which agents need.

apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
  name: deny-privileged-agent-pods
spec:
  validationActions:
    - Deny
  matchConstraints:
    resourceRules:
      - apiGroups: ['']
        apiVersions: ['v1']
        operations: [CREATE, UPDATE]
        resources: ['pods']
  matchConditions:
    - name: is-agent-pod
      expression: "'agent' in object.metadata.?labels.orValue({})"
  validations:
    - message: "Agent pods must not run as privileged"
      expression: >-
        !object.spec.containers.exists(c,
          c.?securityContext.?privileged.orValue(false) == true)

The matchConditions CEL expression targets only pods with an agent label, so this policy does not affect other workloads. The validations expression iterates containers and fails if any has privileged: true.

Enforcing Non-Root for MCP Servers

MCP server pods require a separate policy focused on the runAsNonRoot constraint. A root-running MCP server that gets compromised via Tool Poisoning (MCP03) has full filesystem access within the container, making post-exploitation significantly easier.

apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
  name: require-nonroot-mcp-servers
spec:
  validationActions:
    - Deny
  matchConstraints:
    resourceRules:
      - apiGroups: ['']
        apiVersions: ['v1']
        operations: [CREATE, UPDATE]
        resources: ['pods']
  matchConditions:
    - name: is-mcp-server
      expression: "'mcp-server' in object.metadata.?labels.orValue({})"
  validations:
    - message: "MCP server pods must run as non-root"
      expression: "object.spec.?securityContext.?runAsNonRoot.orValue(false) == true"

Install Kyverno via Helm before applying these policies:

helm repo add kyverno https://kyverno.github.io/kyverno/
helm repo update
helm install kyverno kyverno/kyverno -n kyverno --create-namespace

Apply both policies and test with a pod that violates them:

kubectl apply -f deny-privileged-agent-pods.yaml
kubectl apply -f require-nonroot-mcp-servers.yaml

# This pod creation should be denied:
kubectl run test-agent --image=nginx \
  --labels=agent=true \
  --overrides='{"spec":{"containers":[{"name":"test-agent","image":"nginx","securityContext":{"privileged":true}}]}}'
# Error from server: admission webhook denied the request

How Do You Authorize MCP Tool Calls at the Gateway Layer?

Kyverno secures what gets deployed. Once pods are running, you need a second enforcement layer for what they do: which MCP tools they can call, with which parameters, on behalf of which principal.

agentgateway, maintained by Solo.io and donated to the Linux Foundation, is built in Rust with deep MCP and A2A protocol awareness. It supports three authorization models: native Cedar policies, the MCP spec’s own authorization framework, and External Authorization (ExtAuthz) via the Envoy gRPC API.

agentgateway Architecture and ExtAuthz

agentgateway acts as a protocol-aware reverse proxy between agent pods and MCP servers. Critically, it also supports MCP server multiplexing: combining multiple MCP servers into a single composite backend. This makes the gateway the single enforcement point for all agent-to-tool communication, removing the possibility of agents bypassing authorization by connecting directly to MCP servers.

Configure ExtAuthz to send every MCP tool call to an external policy engine before forwarding it:

extAuthz:
  host: localhost:9000
  protocol:
    grpc:
      metadata:
        dev.agentgateway.jwt: '{"claims": jwt}'

The ExtAuthz service receives the full request context: headers, body (configurable, default 8192 bytes), request path, and custom metadata via CEL expressions. For MCP traffic, the body includes the tool name and parameters from the tools/call request - exactly what your policy engine needs to make an allow/deny decision.

agentgateway is API-compatible with the Envoy External Authorization gRPC service. This means you can wire it to OPA, an existing Envoy ExtAuth implementation, or a custom service without learning a new protocol.

Cedar Policies for Fine-Grained Tool Access

For native Cedar authorization, agentgateway evaluates Cedar policies that express fine-grained tool access rules. A policy granting a specific Kubernetes ServiceAccount access to read-only database tools but not write tools looks like:

permit (
  principal == k8s:ServiceAccount::"agent-ns/read-agent",
  action == mcp:Action::"tools/call",
  resource == mcp:Tool::"database/query"
);

forbid (
  principal == k8s:ServiceAccount::"agent-ns/read-agent",
  action == mcp:Action::"tools/call",
  resource == mcp:Tool::"database/exec"
);

The agent’s Kubernetes ServiceAccount identity flows through the JWT metadata in the ExtAuthz request, giving the policy engine a cryptographically verifiable principal to authorize against.

How Do You Isolate Agent Namespaces in Multi-Tenant Deployments?

In a multi-tenant cluster, agents in one tenant’s namespace must not be able to reach MCP tools in another tenant’s namespace. This requires two layers: network-level isolation and MCP session scoping.

Auto-Generating NetworkPolicy with Kyverno

Kyverno’s GeneratingPolicy creates downstream resources when triggers fire. Use it to automatically generate NetworkPolicy whenever a new agent tenant namespace is provisioned:

apiVersion: policies.kyverno.io/v1
kind: GeneratingPolicy
metadata:
  name: generate-agent-network-isolation
spec:
  matchConstraints:
    resourceRules:
      - apiGroups: ['']
        apiVersions: ['v1']
        operations: [CREATE]
        resources: ['namespaces']
  matchConditions:
    - name: is-agent-namespace
      expression: "'agent-tenant' in object.metadata.?labels.orValue({})"
  generateRules:
    - apiVersion: networking.k8s.io/v1
      kind: NetworkPolicy
      name: deny-cross-namespace-mcp
      namespace: "{{ request.object.metadata.name }}"
      data:
        spec:
          podSelector: {}
          policyTypes:
            - Ingress
            - Egress
          egress:
            - to:
                - namespaceSelector:
                    matchLabels:
                      kubernetes.io/metadata.name: agentgateway
              ports:
                - protocol: TCP
                  port: 8080

Note: This GeneratingPolicy YAML is a structural illustration based on the documented API. Verify it in a non-production cluster before deploying - the Kyverno docs do not yet include MCP-specific GeneratingPolicy examples.

Label namespaces to trigger auto-provisioning:

kubectl create namespace team-a
kubectl label namespace team-a agent-tenant=true
# Kyverno GeneratingPolicy fires -> NetworkPolicy created automatically
kubectl get networkpolicy -n team-a
# NAME                       POD-SELECTOR   AGE
# deny-cross-namespace-mcp   <none>         2s

Every new agent tenant namespace gets network isolation automatically, with egress allowed only to the agentgateway namespace on the MCP port.

graph TD
    NS_CREATE[Namespace Created\nwith label agent-tenant=true] --> KYVERNO[Kyverno GeneratingPolicy\nFires]
    KYVERNO --> NP[NetworkPolicy Created\nin new namespace]
    NP --> RULES{Rules}
    RULES --> DENY[Default Deny\nAll Ingress/Egress]
    RULES --> ALLOW[Allow Egress to\nagentgateway:8080 only]
    
    subgraph Team A Namespace
        AGENT_A[Agent Pod A]
    end
    
    subgraph Team B Namespace
        AGENT_B[Agent Pod B]
    end
    
    subgraph agentgateway Namespace
        GW[agentgateway]
    end
    
    AGENT_A -->|allowed| GW
    AGENT_B -->|allowed| GW
    AGENT_A -.->|blocked| AGENT_B
    
    style DENY fill:#ff6b6b,color:#fff
    style ALLOW fill:#51cf66,color:#fff

Kyverno’s GeneratingPolicy creates NetworkPolicy on namespace creation. Agent pods can only reach the agentgateway - direct cross-tenant access is blocked at the network layer.

Binding MCP Sessions to Namespace Scope

Network isolation handles the routing layer. At the protocol level, each agent’s MCP session is bound to specific namespaces in agentgateway’s routing configuration, so even if network isolation had a gap, the gateway would not forward tool calls to out-of-scope MCP servers. Cross-tenant access is prevented at the protocol level, not just at the reasoning level where the agent is making decisions.

How Do You Detect and Block Shadow MCP Servers on Kubernetes?

The Shadow MCP problem (OWASP MCP09) is concrete: a developer adds an MCP server pod to a production namespace that exposes a database_query tool pointed at the production database. It has no mcp-server label, no image signing, and bypasses your gateway entirely because it was not provisioned through your standard workflow.

Admission Policies That Block Unauthorized MCP Deployments

Kyverno can require that any pod exposing standard MCP server ports (3000, 8080) carries an approved label and a signed image. Here is the detection logic as a Kyverno ValidatingPolicy:

apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
  name: require-approved-mcp-servers
spec:
  validationActions:
    - Deny
  matchConstraints:
    resourceRules:
      - apiGroups: ['']
        apiVersions: ['v1']
        operations: [CREATE, UPDATE]
        resources: ['pods']
  matchConditions:
    - name: exposes-mcp-ports
      expression: >-
        object.spec.containers.exists(c,
          c.?ports.orValue([]).exists(p,
            p.containerPort == 3000 || p.containerPort == 8080))
  validations:
    - message: "Pods exposing MCP server ports must carry the approved mcp-server label"
      expression: "'mcp-server' in object.metadata.?labels.orValue({})"

Scoping note: Scope this policy to agent-managed namespaces rather than applying it cluster-wide. Ports 3000 and 8080 are common for general HTTP workloads (Spring Boot defaults, Node.js servers, many API services) - a cluster-wide policy would require all such services to carry the mcp-server label or be denied. Apply this via a namespace label matchCondition or restrict the matchConstraints to specific namespaces where MCP workloads run.

Image signing verification uses Kyverno’s ImageValidatingPolicy to reject MCP server containers that were not signed by your approved key. This blocks Tool Poisoning (MCP03) attacks where a compromised image is pushed under a legitimate name.

flowchart TD
    REQ[Pod CREATE Request] --> WEBHOOK[Kyverno Admission Webhook]
    WEBHOOK --> CHECK1{Exposes MCP\nserver ports?}
    CHECK1 -->|No| ALLOW[Allow Pod]
    CHECK1 -->|Yes| CHECK2{Has approved\nmcp-server label?}
    CHECK2 -->|No| DENY1[Deny - Shadow MCP\nBlocked]
    CHECK2 -->|Yes| CHECK3{Image signed\nby trusted key?}
    CHECK3 -->|Yes| ALLOW
    CHECK3 -->|No| DENY2[Deny - Unsigned\nMCP Server Image]
    DENY1 --> PR[PolicyReport\nViolation Entry]
    DENY2 --> PR
    PR --> KYVMCP[kyverno-mcp\nshow_violations]
    
    style DENY1 fill:#ff6b6b,color:#fff
    style DENY2 fill:#ff6b6b,color:#fff
    style ALLOW fill:#51cf66,color:#fff

Every pod exposing MCP ports is intercepted. Without an approved label and signed image, it is denied at admission. Violations are written to PolicyReport for monitoring.

Background Scans and PolicyReport Monitoring

Kyverno’s background scan mode evaluates existing resources against policies on a schedule, catching non-compliant deployments that predate your policies or were deployed during a window when Kyverno was unavailable.

# List all policy violations across namespaces
kubectl get policyreport -A

# Get details on a specific report
kubectl describe policyreport -n production

# Check cluster-wide violations
kubectl describe clusterpolicyreport

PolicyReport resources follow the Kubernetes Policy WG standard format, making them compatible with any tool that reads them - including the kyverno-mcp server.

The kyverno-mcp Feedback Loop

Nirmata’s kyverno-mcp server (GitHub: nirmata/kyverno-mcp) implements the Model Context Protocol to let AI assistants manage Kyverno policies and read violation reports. This creates a useful feedback loop: AI agents are governed by Kyverno policies, and those same agents (or AI assistants with cluster access) can query the policy status via the kyverno-mcp server.

The server operates in two modes: proactive assessment (scanning cluster resources against policies using kyverno apply without requiring Kyverno to be installed) and violation monitoring (reading existing PolicyReport/ClusterPolicyReport resources from a deployed Kyverno instance).

Exposed MCP tools:

  • list_contexts / switch_context - Multi-cluster context management
  • apply_policies - Scan against curated sets: pod-security, rbac-best-practices, kubernetes-best-practices, or all
  • show_violations - Read policy violations with severity levels and timestamps

Install and configure:

brew tap nirmata/tap
brew install kyverno-mcp

Add to your MCP client configuration:

{
  "mcpServers": {
    "kyverno": {
      "command": "/path/to/kyverno-mcp",
      "args": ["--kubeconfig=/path/to/kubeconfig"]
    }
  }
}

Run with HTTPS for production use:

./kyverno-mcp --http-addr :8443 \
  --tls-cert /path/to/cert.pem \
  --tls-key /path/to/key.pem

Now your AI assistant can ask “which agent namespaces have policy violations?” and get a structured answer from PolicyReport resources - without needing direct kubectl access.

Putting It Together: Reference Architecture

The full security architecture composes three enforcement layers:

graph TB
    subgraph "Admission Layer (Kyverno)"
        VAP[ValidatingPolicy\nDeny privileged pods\nRequire non-root\nBlock Shadow MCP]
        IVP[ImageValidatingPolicy\nVerify MCP server\nimage signatures]
        GP[GeneratingPolicy\nAuto-create NetworkPolicy\non namespace provisioning]
    end

    subgraph "Team A Namespace"
        AGENT_A[Agent Pod\nlabel: agent=true]
    end

    subgraph "Team B Namespace"  
        AGENT_B[Agent Pod\nlabel: agent=true]
    end

    subgraph "Gateway Layer (agentgateway)"
        GW[agentgateway\nMCP multiplexer]
        EA[ExtAuthz Service\nCedar / OPA policies]
    end

    subgraph "MCP Servers Namespace"
        MCP1[MCP Server\nDatabase tools]
        MCP2[MCP Server\nKubectl tools]
    end

    subgraph "Monitoring"
        PR[PolicyReport\nClusterPolicyReport]
        KYVMCP[kyverno-mcp server\nshow_violations]
    end

    POD_CREATE[Pod CREATE request] --> VAP
    POD_CREATE --> IVP
    VAP -->|allow| AGENT_A
    VAP -->|allow| AGENT_B

    AGENT_A -->|tools/call| GW
    AGENT_B -->|tools/call| GW
    GW --> EA
    EA -->|allow| MCP1
    EA -->|allow| MCP2
    EA -.->|deny| DENIED[Denied Tool Calls]

    VAP --> PR
    GW --> PR
    PR --> KYVMCP

    GP -->|generates| NP[NetworkPolicy\nper namespace]
    NP -.->|blocks| CROSS[Cross-namespace\ndirect access]

    style VAP fill:#4c9be8,color:#fff
    style GW fill:#4c9be8,color:#fff
    style PR fill:#74c0fc,color:#333
    style DENIED fill:#ff6b6b,color:#fff

Two enforcement planes: Kyverno at admission controls what gets deployed, agentgateway at runtime controls what those deployments can do. PolicyReports feed both the monitoring layer and kyverno-mcp for AI-assisted compliance queries.

The architecture separates concerns cleanly:

  • Kyverno (admission plane): Controls pod security posture, blocks Shadow MCP, auto-provisions namespace isolation, verifies image signatures. Fires once per resource lifecycle change.
  • agentgateway (runtime plane): Authorizes every MCP tool call against Cedar or OPA policies, using the agent’s ServiceAccount identity as the principal. Fires on every tools/call request.
  • PolicyReport (monitoring plane): Aggregates violations from both layers. kyverno-mcp makes this queryable via MCP from AI assistants.

Note that agentgateway (Solo.io, Linux Foundation) and kagent (CNCF Sandbox) are separate projects with different governance. kagent provides Kubernetes CRDs for defining agents and managing their lifecycle via a controller - it complements this architecture by giving you a standardized way to provision agent workloads that Kyverno policies then enforce.

Frequently Asked Questions

Can Kyverno directly intercept MCP protocol traffic between agents and tools?

No. Kyverno operates at the Kubernetes admission control layer - it evaluates API requests to create, update, or delete Kubernetes resources like pods. It does not inspect MCP wire protocol traffic at runtime. For runtime MCP traffic authorization, use an MCP-aware gateway like agentgateway with External Authorization. The two layers are complementary: Kyverno secures what gets deployed; the gateway secures what those deployments do at runtime.

What is a Shadow MCP server and how does Kyverno help prevent them?

A Shadow MCP server (OWASP MCP09) is an unapproved MCP server deployed outside your organization’s security governance - the most common pattern is developers connecting personal AI assistants to production databases. Kyverno prevents Shadow MCP at the cluster level by using admission policies that block pods exposing MCP server ports unless they carry approved labels and use signed container images. Background policy scans detect any existing non-compliant deployments and write them to PolicyReport resources, where they are surfaced by kyverno-mcp’s show_violations tool.

Should I use Kyverno or OPA for AI agent policy enforcement on Kubernetes?

Both work for different layers. Kyverno uses standard Kubernetes YAML with CEL expressions and no Rego requirement, making it faster to adopt for admission-level pod security policies. OPA’s Rego is more expressive for complex gateway authorization logic like evaluating MCP tool call parameters. agentgateway supports both via its ExtAuthz interface, plus Cedar for native policies. Most teams end up using Kyverno at admission and OPA or Cedar at the gateway layer - you do not have to choose one for everything.

How do I monitor Kyverno policy violations for my agent workloads?

Kyverno writes violations to PolicyReport (namespaced) and ClusterPolicyReport (cluster-wide) custom resources. Use kubectl get policyreport -A to list them. For AI-assisted monitoring, the kyverno-mcp server’s show_violations tool reads these reports through the MCP protocol, enabling AI assistants to query violation status across clusters without direct kubectl access. For GitOps workflows, Kyverno in Enforce mode triggers ArgoCD sync failures for non-compliant resources, making violations visible in the ArgoCD UI.

What Kyverno API should I use for agent policies in 2026?

Use policies.kyverno.io/v1 with the CEL-based types: ValidatingPolicy, MutatingPolicy, GeneratingPolicy, ImageValidatingPolicy. These were promoted to v1 GA in Kyverno 1.17 (February 2026). The legacy kyverno.io/v1 ClusterPolicy API continues to work in 1.17 but is deprecated and scheduled for removal in 1.20 (October 2026). Start new agent policies on the CEL syntax - migrating away from ClusterPolicy later is extra work you do not need.