Network and Egress Controls for Cloud Coding Agent VMs
Network controls are the actual security boundary for untrusted agent workloads.

A cloud coding agent earns trust from the environment it runs in, not from its own good intentions, and network controls are the mechanism that makes that true. These agents generate and execute actions dynamically, unlike earlier developer tools built around fixed code paths. A file the agent opens, a web page it fetches, a README it reads in a cloned repository can all shape what the agent does next, in ways the operator never specified in advance. Claude Code's secure deployment documentation gives a concrete picture of what that means in practice: a malicious file can instruct an agent to send customer data to an external server, and the documentation notes that network controls can block that request. An agent that runs shell commands, installs packages, and calls APIs with no egress controls in place is an unmonitored process that happens to have internet access. The argument this piece makes is that the environment the agent runs inside, specifically what it can reach on the network, is the actual trust surface. Everything that follows builds on that claim.
Why agent workloads are harder to control than standard application traffic
Standard application traffic is predictable at build time. A web server's outbound connections are known before deployment: a database here, a payment processor there, maybe a logging endpoint. Agent workloads don't work that way. They generate outbound traffic across a surface that includes package registries, external APIs, web search, version control hosts, and whatever endpoints a given task happens to require, and that surface changes with every task the agent runs, not with every deploy of the system around it.
A firewall rule written before a task starts cannot fully anticipate what the agent decides to fetch mid-task. An agent might decide it needs a dependency it wasn't asked to install, or needs to call an external service to complete a step nobody wrote into its instructions, and this compounds with a second problem: some platforms allow broader network access during environment setup, when dependencies get installed, and restrict access once the agent moves into active execution. Knowing which phase a given connection belongs to, and configuring each phase differently, is a control most teams don't think to apply until something has already gone wrong.
The sharpest version of the problem is adversarial. Prompt injection lets an attacker instruct the agent, through content it processes rather than through its original task, to route traffic somewhere the operator never intended. Claude Code's deployment documentation frames the agent's flexibility (acting on context instead of fixed code paths) as the very thing that makes it useful, but it ties that same flexibility directly to prompt injection risk. The traffic an agent generates is not just hard to predict. Content the agent was never supposed to trust in the first place can actively steer it.
The isolation layer that network controls depend on: VM, container, or sandbox runtime
An egress rule is only as trustworthy as the boundary it's enforced on. A shared-kernel container and a microVM enforcing the same allowlist do not provide an equal guarantee. A kernel vulnerability in a container can let an attacker escape to the host network directly, bypassing every egress rule sitting above that kernel. So whether the control holds depends on the isolation layer underneath it.
Claude Code's secure deployment documentation lays out a comparison across four isolation technologies. A sandbox runtime, built on OS primitives like bubblewrap on Linux and sandbox-exec on macOS, gives good isolation with very low overhead and low operational complexity, but it shares the host kernel, so a kernel vulnerability remains a theoretical escape path. Containers, Docker in particular, have isolation strength that depends heavily on setup: low overhead, medium complexity, and suitable for multi-tenant use only when properly hardened with measures like --userns-remap, seccomp profiles, and --network none paired with a proxy. gVisor intercepts syscalls in user space before they reach the host kernel, giving excellent isolation at the cost of medium to high performance overhead, and it fits compute-heavy agent workloads where a full VM's overhead isn't justified. VMs built on Firecracker or QEMU give each workload a hardware-enforced, dedicated kernel. Firecracker provides hardware-enforced isolation through KVM, with each workload running its own dedicated kernel; QEMU paired with KVM uses hardware-assisted virtualization to give each guest a dedicated guest operating system. That isolation comes at higher overhead and medium-to-high complexity, but it's the right baseline when you handle untrusted or proprietary code in multi-tenant or production environments.
For agent workloads running untrusted or user-influenced code in a multi-tenant setting, a dedicated kernel per workload is the correct baseline, because it's the only tier where an escape doesn't land directly on the host network. The isolation choice sets the ceiling on everything built above it. A wildcard egress rule running on a properly isolated microVM is a materially stronger control than the identical rule running on a shared-kernel container, because the container gives an attacker a shorter path to the host.
The proxy pattern for egress controls at the network layer
In the standard architecture for controlling agent egress, a proxy sits outside the agent's isolation boundary, and all outbound traffic routes through it. The agent itself never gets a direct external network interface. This single design choice is what makes allowlisting, credential protection, and logging enforceable.
Claude Code's deployment documentation describes two implementations of this pattern, one for containers and one for VMs. In the container case, the --network none flag strips all network interfaces from the container. The agent talks only to a mounted Unix socket, which connects to a host-side proxy. That proxy enforces domain allowlists, injects credentials into outbound requests, and logs all traffic passing through it. In the VM case, the agent VM has no external network interface. Traffic routes through a vsock, a virtual socket connecting the VM to its host, to a host-side proxy that performs the same functions: enforcing allowlists and injecting credentials before anything reaches the internet.
The same logic extends to cloud infrastructure. Run agent containers in a private subnet with no internet gateway, configure firewall rules to block all egress except traffic to the proxy, and let the proxy handle allowlisting and credential injection before forwarding requests outward.
This pattern yields three security properties that compound with each other. The proxy enforces egress allowlists at a single, auditable choke point. So credentials never sit inside the agent's environment at all, because the agent makes API calls through the proxy but never holds the key itself. And it produces a complete log of every outbound request, attributable to a specific agent run. Placing sensitive API keys outside the agent's security boundary means a prompt-injected agent cannot exfiltrate a key even when it's been manipulated into trying, because the key was never inside the boundary to begin with. An attacker can compromise the agent's behavior completely and still come away with nothing to steal.
Egress allowlist design
An allowlist is only as strong as its narrowest-looking entry, and the entries that look narrow are often the ones doing the most damage. A wildcard pattern such as *.s3.us-east-1.amazonaws.com reads like a scoped permission for a team's own storage. It actually permits egress to every S3 bucket in that entire region, not just the team's own. So a prompt-injected agent can construct a presigned URL pointing at a bucket the operator doesn't control and never intended to reach, and exfiltrate data that way.
The underlying design principle is least privilege, applied specifically at the network layer: restrict the agent to the exact endpoints its task requires, and nothing broader. Package registries like registry.npmjs.org, pypi.org, and crates.io belong on the allowlist, and ideally only during the setup phase, before the agent moves into active execution. Version control access should be scoped to the specific organization or repository prefix the task touches, not the entire host domain. Internal APIs and services should be reached through private endpoint addresses. External APIs a task genuinely needs should be allowlisted by specific host, never by a wildcard on the provider's broader domain.
The most common finding in an audit of agent network controls is an incomplete allowlist, not a missing one. Network egress gets locked down correctly, but configuration-file write protection doesn't get applied alongside it. The result: a prompt-injected agent can't exfiltrate data over the network, but it can still modify hooks, MCP server configurations, or IDE extension directories, and use that foothold to persist its behavior across future runs even after the original injection is gone. Egress controls and filesystem write restrictions protect against different failure modes, and a hardened environment needs both. Treating one as a substitute for the other leaves a gap that's easy to miss in a review focused only on network traffic.
Secret handling inside agent VMs
Network egress controls govern what leaves the VM. A separate question is what the agent can see while it's running inside the VM, since a prompt-injected agent that already holds a secret can try to exfiltrate it through any outbound channel the operator left open. Limiting what secrets are visible inside the environment in the first place cuts down what a successful injection attack can actually get away with.
Cloud agent secret handling documentation describes three functional tiers, and each one fits a different category of credential. An Environment Variable is visible to the agent, encrypted at rest and in transit, and appropriate for non-sensitive configuration such as feature flags or public URLs the agent legitimately needs to read. A Runtime Secret loads as an environment variable but gets redacted from tool-call results, the chat transcript, commits, and commit messages, replaced with [REDACTED] wherever those outputs would otherwise show it. It fits sensitive credentials that shouldn't be committed or logged. A Build Secret is available only to the Docker build process and never reaches the running agent's environment at all, making it the right fit for private package registry tokens and other build-time credentials.
The precision that matters most here concerns the Runtime Secret tier, and it's easy to misread. Redaction is narrower than the name suggests: the actual value stays present in the process environment the whole time. Redaction replaces the copy that would otherwise show up in tool-call results, the transcript, and commit messages with [REDACTED], while the value itself stays in the process environment. Anyone who can open a terminal inside that environment can still read the real secret. Treat a Runtime Secret as a control on what gets written down in logs and outputs, not as a control on what the agent can reach. An operator relying on this tier as a hard confidentiality boundary has likely misconfigured the environment, because the boundary it provides is narrower than the name implies.
For Build Secrets, you should mount the credential through a Docker secret mount inside a RUN step, not copy it directly into the image layer. That keeps the credential from persisting in any image layer an attacker could inspect after the build completes.
For the most sensitive credentials an agent might touch, the proxy-based credential injection pattern described earlier remains the stronger option by a wide margin. The agent is never handed the key at all, which removes the in-VM exfiltration surface. A Runtime Secret still leaves a value sitting in the process environment for anyone with terminal access, while the proxy pattern leaves nothing in the agent's environment to steal.
Network access modes: team-level, environment-level, and user-level controls
Cloud agent network access documentation describes three modes, each settable at the user, environment, or team level. Allow all gives the agent full outbound internet access during execution. Default-plus-allowlist permits internet access except for an explicit block list, supplemented by additional allowed destinations. Allowlist-only restricts the agent to destinations explicitly named, blocking everything else outbound.
These three modes raise a governance question: which level wins when settings conflict. If team-level policy doesn't override user-level settings, an individual user can select "allow all" and sidestep the team's network controls without needing any special privilege to do so. A carefully designed allowlist at the organization level does nothing if any engineer can opt out of it from their own account.
For engineering teams operating at any real scale, the right default posture is allowlist-only, set at the team level, with environment-level overrides permitted only where a specific environment has a documented and reviewed exception. That's the principle of least privilege applied to organizational scope instead of to an individual request: default to the narrowest reasonable permission, and require a specific justification before widening it.
A related organizational control sits one layer above network access: scoping which agents can reach a codebase. GitHub's documentation describes organization admins setting a default runner for cloud agents and locking that setting so individual repositories can't override the organization default. So that control bounds which agents can reach a given codebase before any network rule even applies to the traffic those agents generate. Network controls determine what an agent can reach once it's running. Runner-locking determines which agents get to run against the codebase in the first place, and the two controls work at different points in the same chain of trust.
