TL;DR
Every Model Context Protocol (MCP) server in your cluster is an active egress path. Without network-layer enforcement, a rogue or compromised server can reach anything the host can reach.
The Aviatrix-Obot Validated Containment Architecture (VCA) enforces a per-server, default-deny egress policy at the spoke gateway using Kubernetes-native Custom Resource Definitions (CRDs). No sidecars. No code changes.
Obot is the deployment plane: it spawns MCP servers with predictable labels. Aviatrix is the enforcement plane: it targets those labels and drops disallowed egress inline.
Every blocked or permitted connection ties back to a specific MCP server, a specific destination FQDN, and a specific timestamp. That is the forensic proof regulated industries require.
Introduction
An MCP server is a privileged process running inside your cluster with access to credentials, data APIs, and external network paths. Most enterprises deploying AI agents have dozens of them. Few have an accurate inventory. Almost none have per-server egress controls. This is not a gap so much as a founding principle.
The threat is already a reality. In March 2026, supply-chain attacks compromised npm packages used by Axios, LiteLLM, Trivy, and others. A poisoned dependency inside an MCP server inherits the server’s egress permissions for free. The server is permitted to call your Electronic Health Record (EHR). The malicious package inside it calls an attacker-controlled endpoint instead. Your Kubernetes NetworkPolicy does not catch it: NetworkPolicy operates at the IP and port layer, and the attacker’s endpoint listens on port 443 just like the legitimate one.
One control stops this: network-layer, domain-aware egress enforcement scoped per MCP server. That is what this architecture delivers.
Introducing the Validated Containment Architecture for Obot MCP Servers
This VCA pairs two capabilities:
Obot (deployment plane): Obot deploys MCP servers on Kubernetes with consistent, predictable labels. Every pod gets an app=<server-id> label that uniquely identifies it. That label is the contract between the deployment plane and the security plane.
Aviatrix Distributed Cloud Firewall (enforcement plane): Aviatrix DCF enforces egress policy at the spoke gateway using Kubernetes-native FirewallPolicy CRDs. Policy evaluation runs at the eBPF dataplane inline, before packets leave the cluster. No sidecars, no Istio, no per-pod resource overhead.
When you deploy an MCP server in Obot and set egressDomains on the server manifest, the built-in network policy controller automatically generates a FirewallPolicy CRD and pushes it to the Aviatrix spoke gateway. No operator steps. The policy is live before the first request leaves the pod. This shipped in Obot v0.21.0.
Lab Notes
Environment
The reference lab runs on Azure Kubernetes Services (AKS) with Aviatrix 8.2. The architecture applies equally to Amazon Elastic Kubernetes Service (EKS) and Google Kubernetes Engine (GKE), with cloud-specific infra domain lists documented in the reference architecture.
Two namespaces, two responsibilities:
Namespace | Contents | Purpose |
obot-system | Obot orchestrator, Aviatrix network policy controller | Deployment and policy plane |
obot-mcp | MCP server pods, traffic generator | Workload enforcement target |
DCF enforcement targets obot-mcp. Obot’s own egress (Anthropic API, GitHub, Helm chart repo) is permitted separately via a V1 rule scoped to obot-system pod IPs.

Three-Tier Policy Evaluation
Traffic from any MCP server pod traverses three tiers in order.
Tier 1: V1 named permit rules evaluate first. These cover infrastructure egress (ACR, blob storage, AKS API server) and Obot orchestrator egress. They are CIDR-scoped and exist independent of any MCP workload.
Tier 2: FirewallPolicy CRDs are per-pod permit/deny rules applied by the Aviatrix network policy controller. Each MCP server carries its own CRD in obot-mcp, specifying exactly which domains it may reach on port 443.
Tier 3: POST_RULES default action catches everything else. Set to DENY + LOG. Any connection not explicitly permitted in Tier 1 or Tier 2 is blocked and logged.
Why the order matters: a V1 deny at any priority evaluates before the CRD block, which would negate all CRD permits. The POST_RULES default action must be a default action rule, not a V1 entry.

The Label Contract
After Obot deploys an MCP server, the pod gets a deterministic label:

The app label is the Obot server ID. Aviatrix targets this label in the FirewallPolicy CRD. No knowledge of Obot internals required on the Aviatrix side.
Deployment plane and security plane share a single stable identifier: the pod label, and nothing else. That is the whole contract.
FirewallPolicy CRD: One Per MCP Server
A minimal FirewallPolicy for a benign MCP server that needs to reach api.openai.com:

The deny-all rule at the end is not redundant with POST_RULES. It produces per-server log lines with the CRD name in the Rule column, which is exactly what incident response needs: which server tried to reach which destination, and which policy blocked it. A rogue MCP server with no legitimate external egress gets an even simpler CRD: no permit rules, deny-all only.
What Discovery Looks Like Before Enforcement
Before any FirewallPolicy CRDs are applied, set the DCF default action to PERMIT with logging. After a few minutes of traffic, CoPilot Policy Logs shows:
benign-mcp pod IP calling api.openai.com on port 443: Permitted (Default Action Rule)
rogue-mcp pod IP calling httpbin.org on port 443: Permitted (Default Action Rule)
rogue-mcp pod IP calling webhook.site on port 443: Permitted (Default Action Rule)
Both servers are visible, but neither is contained. This is, remarkably, considered a mature posture. [Screenshot: CoPilot Policy Logs, pre-enforcement — mixed benign and rogue traffic both Permitted under Default Action Rule]
What Enforcement Looks Like After CRDs Apply
Apply the CRDs first, wait for reconciliation (the controller sets status.ruleset when the policy reaches the gateway), then flip the default action to DENY:

kubectl apply -f k8s/benign-mcp-fw.yaml -f k8s/rogue-mcp-fw.yaml # Wait for reconciliation until kubectl get firewallpolicy rogue-mcp-fw -n obot-mcp \ -o jsonpath='{.status.ruleset}' 2>/dev/null | grep -q '.'; do sleep 5; done
CoPilot Policy Logs now shows:
benign-mcp calling api.openai.com: Rule = allow-openai, Result = Permitted
rogue-mcp calling httpbin.org: Rule = deny-all, Result = Denied
rogue-mcp calling webhook.site: Rule = deny-all, Result = Denied
One FirewallPolicy per MCP server. Default-deny breaks the exfiltration chain. The benign server keeps its single approved dependency. The rogue server has nowhere to go.

[Screenshot: CoPilot Policy Logs, post-enforcement — benign Permitted under allow-openai, rogue Denied under deny-all]
The Compliance Audit Record
Every row in Policy Logs carries:
Column | Value | Why it matters |
Source IP | Pre-NAT pod IP | Ties back to specific MCP server, not translated gateway IP |
SNI | Destination FQDN | Exact domain, not just a port or IP |
Rule | CRD rule name | Which policy fired: allow-openai, deny-all, or Default Action Rule |
Result | Permitted / Denied | Binary enforcement outcome |
Timestamp | Event time | Lines up with incident timeline |
To translate a pod IP to a server name during an incident:

Follow the chain: pod IP to pod name to Obot server ID to the user who deployed it. For HIPAA, PCI-DSS, and SOC 2, this is architectural proof of enforcement, not a policy document.
How Obot Generates Enforcement Policy
The egressDomains field on each MCP server manifest drives the FirewallPolicy. Set it via the Obot API or UI and the built-in network policy controller reconciles the corresponding CRD in obot-mcp:
![VCA for Obot 5 - How Obot Generates Enforcement Policy
{
"name": "claims-lookup",
"npxConfig": {
"package": "@org/claims-lookup-mcp",
"egressDomains": ["api.openai.com"]
}
}](https:////images.ctfassets.net/mvxxnmpzvwpe/4C2pMRRsP2AlxvVs2kSAoz/28225a05f43b3003b4c865ab6cb5e3d4/VCA_for_Obot_5_-_How_Obot_Generates_Enforcement_Policy.jpg)
The controller produces a FirewallPolicy CRD with the exact structure shown above. Delete the MCP server in Obot and the controller garbage-collects the policy at the gateway. No orphaned permits accumulate.
Deploy a server in Obot. Get a network-layer containment policy at the gateway.
Conclusion
MCP servers are a new egress surface, and the default posture for almost every enterprise running them is wide open. The Aviatrix-Obot Validated Containment Architecture changes that posture at the architecture level: one policy per server, enforced inline at the spoke gateway, with a complete audit record tied to workload identity.
Obot deploys. Aviatrix contains. Every connection is logged, named, and attributable.
Schedule a Demo
See this architecture in your environment. We will identify the MCP servers running today, walk through the FirewallPolicy CRD model, and show you the egress paths you do not currently see.
Learn about other Aviatrix Validated Containment Architectures.
Explore Zero Trust for AI Workloads and Aviatrix AgentGuard, which enforce security polices for AI workloads at the network layer.
Frequently Asked Questions
AWS handles the AgentCore service infrastructure under the shared-responsibility model. The agent itself — the code running inside the Runtime container — initiates its own outbound traffic. By default, that egress routes to the open internet through AWS-managed infrastructure with no in-line inspection point a customer's security team can reason about. Compromised agent traffic is indistinguishable on the wire from legitimate calls. The VCA is the enforcement point.
Not for the baseline network controls. The Runtime is created in VPC mode with a flag at creation time. AgentCore drops the per-session microVM's ENI in subnets you control; egress follows the subnet's route table from there. The agent code does not change. Selective TLS decryption on supply-chain hosts is the one optional dependency that requires the Aviatrix MITM CA in the container's trust store — a five-line addition to the Dockerfile, with a fetch script provided in the repo.
25 to 30 minutes on a fresh AWS account with one terraform apply. That includes the Aviatrix transit, the AgentCore spoke, the client spoke, the two interface VPC endpoints, the Distributed Cloud Firewall SmartGroups, WebGroups, and policies, the IAM guardrail, the ECR repo with the sample agent image, and a sample AgentCore Runtime you can invoke immediately for smoke tests. Destroy is one command and leaves zero orphans.
IAM condition keys on bedrock-agentcore:subnets and bedrock-agentcore:securityGroups. The VCA's IAM managed policy denies CreateAgentRuntime, CreateAgentRuntimeEndpoint, and UpdateAgentRuntime unless the request's subnets and security groups come from the approved set. A PUBLIC mode Runtime or a foreign-subnet attachment fails at the AWS API with an explicit deny — the call never reaches AgentCore. The drift scenario in the blueprint UI demonstrates this end-to-end.
This release covers AgentCore Runtime. Browser, Code Interpreter, and Gateway are deferred to a follow-on. The architecture accommodates them — the MITM CA is already provisioned to Secrets Manager so Browser and Code Interpreter sessions can pull it on session start via the platform's first-class certificates parameter — but they are not in the current deployable scope.
The architecture is one repository, one Terraform module, one set of validated controls. Everything required to deploy on a fresh AWS account is in the box. You’ll receive an insertion pattern, SmartGroup model, Baseline Distributed Cloud Firewall policy pack, Bill of Materials, and GitHub repository.
















