Skip to main content
InfraGap.com Logo
Home
Getting Started
Core Concept What is a CDE? How It Works Benefits CDE Assessment Getting Started Guide Inner Loop vs Outer Loop Environment Drift Local vs Cloud CDEs for Startups
AI & Automation
AI Coding Assistants Agentic AI AI-Native IDEs Agentic Engineering AI Agent Orchestration AI Governance AI-Assisted Architecture Shift-Left AI LLMOps Autonomous Development AI/ML Workloads CDEs for Data Science GPU Computing
Agent Infrastructure
Agent Experience (AX) Agent Egress Control Computer Use Agents Agent Evals Agent Runbooks Agent Client Protocol AGENTS.md MCP Servers Git Worktrees Kubernetes Agent Sandbox Agent Fleets Agent Identity Prompt Injection Defense Agent Observability Context Engineering AI Code Review Bottleneck Headless Agents in CI Spec-Driven Development Agent Readiness Code Provenance
Implementation
Architecture Patterns DevContainers Advanced DevContainers Language Quickstarts IDE Integration CI/CD Integration Platform Engineering Developer Portals Container Registry Multi-CDE Strategies Remote Dev Protocols Nix Environments Hermetic Builds OpenTofu for CDEs Kubernetes Development
Operations
Performance Optimization High Availability & DR Disaster Recovery Monitoring Capacity Planning Multi-Cluster Development Troubleshooting Runbooks Ephemeral Environments Sandbox Environments Workspace Snapshots Database Branching
Security
Security Deep Dive Zero Trust Architecture Secrets Management Vulnerability Management Network Security IAM Guide Supply Chain Security Air-Gapped Environments AI Agent Security MicroVM Isolation Compliance Guide EU AI Act Cyber Resilience Act Data Residency Governance
Planning
Pilot Program Design Stakeholder Communication Risk Management Migration Guide Cost Analysis FinOps GreenOps Vendor Evaluation Training Resources Developer Onboarding Team Structure Platform Maturity Model Open Source CDEs AI Productivity Paradox Build vs Buy DevEx Metrics Productivity Engineering Industry Guides CDEs for Healthcare CDEs for Financial Services CDEs for Government Edge Development WebAssembly in CDEs
Resources
Tools Comparison State of CDEs 2026 Isolation Decision Tool Template Library
Learning Paths
All Paths Platform Engineer Security and Compliance Engineering Manager
Vendor Reviews
GitHub Codespaces Coder Ona Google Workstations Microsoft Dev Box Okteto Eclipse Che DevPod Daytona E2B
Head to Head
Coder vs Codespaces Coder vs Ona Ona vs Codespaces Coder vs Okteto Self-Hosted vs Managed E2B vs Daytona CDE Market Guide CDE vs Alternatives Case Studies Lessons Learned Glossary FAQ Sources & Citations

Agent Egress Control and Network Policy for AI Agents

Allowlisting where an agent may connect, proxying package installs through infrastructure you own, and why egress is the most effective control surface for autonomous code

Why Egress Is the Control Surface

Covered elsewhere on this site as a threat; here as wiring

Prompt injection is the case where text an agent reads is treated as an instruction it follows. Simon Willison's lethal trifecta names what makes that dangerous - private data, untrusted content, and the ability to communicate externally - and the threat model lives on prompt injection defense. Of the three, external communication is the only one you can revoke and still have a useful agent.

The question is how, not whether. Everything below assumes the agent runs in a container you control (sandbox environments), that its identity is scoped rather than shared, and that one policy covers a coding agent, a CI job and a browser-driving agent (computer use agents) alike.

Allowlist by Destination, Method and Path - Not by Port

Port rules stopped carrying information when everything moved to 443

A rule permitting outbound TCP 443 permits the package registry, the code host, the model provider, a pastebin and an attacker's collector alike, because they are all the same port. The unit that carries meaning is the destination host, then the HTTP method and path.

Method and path separate reading from writing at one hostname: permission to reach api.github.com covers reading an issue, force-pushing a branch and rotating a webhook alike. "GET and HEAD to api.github.com, POST only to one path prefix" is an actual boundary. Coder's Agent Firewall expresses rules as space-separated key=value pairs using the keys domain, method and path; the documentation notes that "Agent Firewall was previously known as 'Agent Boundaries'."

Rule forms documented for the Coder Agent Firewall

# space-separated key=value pairs; keys are domain, method, path
domain=github.com
domain=*.github.com
method=GET,HEAD domain=api.github.com
method=POST domain=api.example.com path=/users,/posts
path=/api/v1/*,/api/v2/*

# the open-source engine underneath takes the same rules on the CLI
boundary --allow "key=value [key=value ...]" -- command

The engine is coder/boundary, a Go project created in September 2025, whose README states a "Default deny-all security model" where "all traffic is denied unless explicitly allowed," implemented through "HTTP/HTTPS interception with transparent proxy and TLS certificate injection." Releases ran through v0.10.0 (July 7, 2026), which added session IDs, audit logging and header injection. Sourcing matters: those are README claims, while the product documentation states neither and covers audit instead, recording "Whether the request was allowed (allow) or blocked (deny)."

TLS Interception: What You Gain and What You Pay

Domain granularity is cheap; path granularity costs the encryption boundary

Two honest positions, and the choice decides most of the design. The cheap one filters on the connection request only. Squid's peek-and-splice parses "the TLS Client Hello and extracts SNI (if any)," then splices the connection to "become a TCP tunnel without decoding the connection" (Squid project wiki). You get the hostname, you allow or deny on it, and no plaintext exists in your infrastructure.

The expensive one is that method and path rules are not expressible without decryption. They live inside the encrypted request, so a proxy enforcing them must terminate TLS and be trusted by the workspace - which is why coder/boundary injects a certificate. No third option filters on paths and preserves end-to-end encryption.

Interception is defensible when the workspace is ephemeral, the CA is minted per run, and only agent traffic crosses the proxy. It is not defensible when the path carries third-party or end-user data, the traffic is regulated, the CA would outlive the run, or a human developer shares the proxy. There, stay on SNI-only filtering and accept domain granularity.

A MITM Proxy Is the Highest Value Target You Own

It sees every token, source file and payload in cleartext, across every run, in one process. Treat it as tier-zero infrastructure, or do not run it.

Package Installs Through a Mirror You Own

The largest legitimate hole in any agent allowlist

An agent that runs an install command needs a registry, and the naive fix is to allowlist the public one and its CDN. That rule reopens most of what the allowlist was for: installation is arbitrary code execution against a namespace anyone can publish into. Google's Artifact Registry documents the alternative: "A remote repository acts as a proxy for the upstream source so that you have more control over your dependencies," caching each package version on first request and serving the cached copy thereafter (Google Cloud documentation). The agent then needs one package destination, and it is one you can freeze.

Three details decide whether this helps. An unknown package must fail loudly: a mirror that falls through to upstream on a miss has no teeth, and the miss should surface as a build error with a name attached. Resolution must come from the lockfile only, with no float. And install scripts resolve their own domains at build time, which never appear in the dependency manifest and will be most of your unexplained denials in week one. See supply chain security.

Credential Injection at the Proxy

The strongest version keeps the token out of the model's context

If the agent holds the credential, any injection that reads an environment variable holds it too. Cloudflare's Sandbox SDK documents the alternative: "Your Worker issues a short-lived JWT token to the sandbox," and "The Worker validates the JWT and injects the real credential before forwarding the request," so that "Real credentials never enter the sandbox" (Cloudflare documentation). Note the scope: that is credential injection, not allowlisting, and the product docs say only that it is "Available on Workers Paid plan."

Pipelock, an open-source agent firewall covered by Help Net Security (May 4, 2026), states the same shape as a two-zone split. It "sits outside the agent at the egress boundary," so "the agent process holds the secrets and operates without direct network access, the proxy holds network access and no secrets, and all traffic crosses a scanning boundary between the two zones." Its repository dates from February 2026: a reference architecture, not a settled dependency. Which identity the proxy injects for is agent identity, on the zero trust assumption that the workspace is hostile.

Illustrative egress-proxy policy (not a vendor schema)

default: deny            # anything not matched below is refused and logged

routes:
  - name: read-github-api
    match:
      domain: api.github.com
      method: [GET, HEAD]
    inject:
      header: Authorization
      secret_ref: vault://agents/repo-read   # resolved at the proxy, never in the workspace
    log: full

  - name: publish-release
    match:
      domain: api.github.com
      method: [POST]
      path: /repos/example-org/*/releases
    approval: human        # held until an operator approves
    inject:
      header: Authorization
      secret_ref: vault://agents/release-write
    log: full

  - name: internal-package-mirror
    match:
      domain: artifacts.internal.example.com
      method: [GET, HEAD]
    inject: none           # mirror needs no credential
    log: summary

Default Deny, and How to Discover the Real Destination List

Nobody writes this list from memory, and guessing gets the rollout reverted

Default deny is the right end state and the wrong first move. Turn it on cold and every build breaks within the hour, the change is reverted by lunch, and the second attempt is politically harder. Run the proxy in observe mode first, logging everything, over a full release cycle - a single day misses the monthly dependency refresh.

Rank the result two ways: by total request count, and by the number of distinct runs that touched each destination. A host with a million requests from one run is a build cache; a host with three requests across two hundred runs is load-bearing and easy to miss. Split into permanent destinations, which get real rules; one-off destinations, which get dropped; and anything you cannot attribute to a person or pipeline, which goes to review before enforcement day. The long tail will be dozens of hosts at one or two runs each. The temptation is a wildcard that swallows them; leave them denied.

Three Things to Ship With Enforcement

A break-glass path that adds a destination in minutes with an owner and an expiry, or engineers route around the proxy and you lose the visibility too. An alert on every denial, rare enough in steady state that each is worth a look. And a scheduled review, because allowlists rot: drop rules with no traffic, alert on any bare wildcard.

Kubernetes NetworkPolicy and Its Real Limits

Necessary as a floor, insufficient as the whole control

Deny all egress from agent pods, then add back what is needed. The caveat that costs an afternoon: default-deny-egress also blocks DNS, so it needs an explicit allow to cluster DNS on UDP and TCP port 53.

Default deny egress, then allow DNS and the proxy

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
spec:
  podSelector: {}
  policyTypes:
  - Egress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns-and-egress-proxy
spec:
  podSelector:
    matchLabels:
      role: agent-workspace
  policyTypes:
  - Egress
  egress:
  - to:
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: kube-system
    ports:
    - protocol: UDP
      port: 53
    - protocol: TCP
      port: 53
  - to:
    - ipBlock:
        cidr: 10.0.0.0/24
    ports:
    - protocol: TCP
      port: 5978

Peers are only podSelector, namespaceSelector and ipBlock, which "selects particular IP CIDR ranges to allow as ingress sources or egress destinations." There is no hostname peer, and the project's list of what network policies cannot do includes "Targeting of services by name" and "The ability to log network security events (for example connections that are blocked or accepted)" (Kubernetes documentation). "Allow api.github.com" is not a sentence the API can express. One nuance is often stated wrongly: policy is already deny-by-default once it selects a pod; what is missing is explicit deny rules, so you cannot carve an exception out of a broad allow.

CIDR-based egress against a CDN-fronted service is therefore unworkable: the address set behind a code host or a registry is large, shared with unrelated tenants, and changes without notice, so any list you write is wrong within weeks. Use NetworkPolicy for the coarse shape and an L7 egress proxy or service mesh on top for host, method and path. See Kubernetes agent sandbox and network security.

Control Surfaces Compared

Control surfaceWhat it can expressWhat it costs
Kubernetes NetworkPolicyPod label, namespace label, IP CIDR, portNo hostnames, methods, paths, logging or deny rules
SNI-only egress proxyDestination hostname, without decryptingBlind to method, path and payload
MITM L7 proxyDomain, method, path, header injection, full auditCA trust in the workspace; every token visible in one place
Internal package mirrorWhich package versions resolve, from vetted copiesYou now operate a registry; install scripts still escape it
Credential-injection proxyWhich identity may call which routeConcentrates every credential; writes need approval

Log Every Outbound Request

The allowlist is the control; the log is the detection surface

Record destination, method, path, response size and outcome, keyed to a run identifier. In a cluster only the proxy can produce this, because the Kubernetes documentation lists logging of blocked and accepted connections among the things NetworkPolicy cannot do.

Three anomalies are cheap to compute: a destination never seen before for that agent, which catches a new exfiltration path on first use; response bytes far above the distribution for that route across peer runs; and a burst of requests to one host. Feed them into agent observability, and write the response down in advance - who kills the run, who rotates which credential, who quarantines the branch - as covered in agent runbooks.

What Egress Control Does Not Buy You

It bounds the damage from an attack; it does not prevent one

Egress control does not stop an injection succeeding. The agent still reads the poisoned comment and still acts on it; what changes is where the attempt terminates. The hardest remaining gap is exfiltration through an allowed destination. If the agent may push a branch or comment on an issue - and it must, or it cannot do the job - those are channels to anyone who can read them, and no rule tells a legitimate commit from one whose diff carries a secret.

Below that sit low-bandwidth channels that are close to unclosable: a branch name, a commit message, request ordering, timing, DNS lookups themselves. The proxy is also a single point of failure and a concentration of every credential it injects. Allowlists rot. Method and path rules exist only if you decrypt. And an agent inside a perfect allowlist can still write a subtle bug into a file it was legitimately allowed to edit, which no egress policy will ever see.

Measure the Allowlist by What It Denies

A rule set that has never blocked anything is either perfectly tuned or quietly permissive, and the two look identical from outside.