How to Put an Egress Proxy in Front of AI Agents
An AI agent egress proxy is a server that all outbound traffic from an AI agent must pass through. It allows only approved destinations, blocks internal and cloud metadata addresses, and logs every request. Agents need one because the model chooses URLs and tool calls, and prompt injection can steer those requests toward data theft or internal systems.
This guide is the how-to behind the "enforce egress at the network layer" item in our MCP SSRF analysis: AI agent egress control and SSRF prevention for AI agents, with Stripe's Smokescreen as the worked example. Vendor settings carry the doc version or date we checked on 24 October 2026; re-check them for your version.
Why instructions to the model are not a boundary
In OpenAI's report (updated 15 October 2026), a model told "Internet access is enabled only for HTTP GET requests." still obtained a government website's configuration files, which held "a secret used to sign authorization tokens", and requested the site's account directory "through a public proxy". Anthropic reported on 9 October 2026 that models got around a fetch tool's URL length limit "by using free URL shortening services" (our report), and its UK AISI incident assessment (23 October 2026) says a model used "Tor and a proxy service to get around GitHub's sign-up checks".
CERT-In advisory CIAD-2026-0049 (15 October 2026) says: "Enforce the restriction at the network layer, not through instructions to the model." OWASP LLM03:2026 Excessive Agency asks for "an independent pre-execution policy decision point between the tool and the downstream system". In our assessment, an egress proxy is that decision point for network traffic, one that turns model behavior into auditable configuration.
The reference architecture
The agent and its tools, including MCP servers, have no direct route out. A forward proxy is the only reachable destination; it applies per-client hostname allowlists, checks the IP each name resolves to, and logs every decision. The MCP security best practices (spec version 2026-07-28) say operators of server-side MCP clients "SHOULD consider using an egress proxy that enforces network policies" and name Smokescreen. That guidance covers OAuth metadata discovery; extending it to every tool call is our assessment.
Step 1: Inventory every egress path
List every process that can open a socket: the agent loop, each tool, local MCP servers, remote MCP connectors and their OAuth discovery, fetch and browser tools, code execution and package installs (see our MCP explainer). The Claude Code sandboxing page (dated 23 October 2026) says "The sandbox covers shell commands only. Claude's file tools, MCP servers, and hooks run outside it." The Codex Cloud (Legacy) internet access page says its "command-network controls do not, by themselves, disable hosted web search, apps, or MCP." Any path you miss is, in our assessment, a bypass.
Step 2: Take away the direct route out
Kubernetes NetworkPolicy
The Kubernetes v1.37 docs (last modified 14 April 2026) say "By default, a pod is non-isolated for egress; all outbound connections are allowed." Start from the documented default-deny policy:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-egress
spec:
podSelector: {}
policyTypes:
- Egress
Then allow egress only to the proxy. The page warns that a policy "without a controller that implements it will have no effect", that default deny "also blocks DNS traffic", and that NetworkPolicy cannot handle TLS or log. In our assessment, because a CONNECT proxy resolves names itself, the agent pod needs no DNS, which closes the channel in ASI02 scenario 7 of the OWASP Top 10 for Agentic Applications: "exfiltrating data through DNS queries."
VMs and container hosts
Anthropic's reference init-firewall.sh (last changed 30 June 2026) sets iptables -P OUTPUT DROP and allows IPs resolved from listed domains. In our assessment, with a proxy the only allow rule you need is the proxy's address.
Step 3: Make Smokescreen the only way out
The Smokescreen README (master, checked 24 October 2026; latest release v0.1.0, 21 September 2026) calls it "a HTTP CONNECT proxy" that uses "a pre-configured hostname ACL" and "resolves each domain name that is requested, and ensures that it is a publicly routable IP address and not an internal IP address." Pin your version and check its --help.
In our assessment, some clients ignore proxy environment variables, hence Step 2. ACL actions are Open, Report and Enforce, and Enforce "Only allows traffic to remote hosts provided in the list". Set the default rule to enforce with an empty list, and use --disable-acl-policy-action to forbid open. In our assessment, a short period in report mode, which "warns if client accesses a remote host which is not in the list", shows what each agent needs before you enforce.
In our test of v0.1.0 on 24 October 2026, Smokescreen denied every request from a client without a certificate with HTTP 407 "Client role cannot be determined", so it failed closed.
Step 4: Give each agent and each tool its own allowlist
Smokescreen applies "a configurable per-client ACL" and by default identifies clients by the "common name" in their TLS certificate (README). In our assessment, issue one certificate per agent role and one per MCP server, so a compromised scraper cannot borrow the GitHub server's allowlist. OWASP ASI02 asks for "per-tool least-privilege profiles (scopes, maximum rate, and egress allowlists)"; Smokescreen's rate limits default to 0 (unlimited). See least privilege.
Wildcards match only within one registrable domain, which the README says stops access-analyzer.*.amazonaws.com matching an S3 bucket "that anyone can claim". Never set unsafe_allow_wildcards_across_public_suffixes. In our assessment, URL shorteners, public proxies, Tor and tunnels are open relays, and allowing all of github.com lets an agent push data to any public repository.
Route remote MCP connectors and OAuth discovery through the proxy too: CVE-2026-8355 in IBM ContextForge MCP Gateway (published 19 October 2026 UTC) "bypasses existing SSRF protections that are applied to the gateway.url field but not to OAuth configuration parameters."
Step 5: Check IPs after the DNS lookup
The OWASP SSRF Prevention Cheat Sheet (last changed 4 October 2026) says "Domain allowlisting alone does not prevent DNS rebinding" and asks that the client "connects only to validated addresses." The MCP spec adds "Consider pinning DNS resolution results between check and use".
In our test of v0.1.0, allowlisted hostnames that resolved to 169.254.169.254, 10.0.0.1, 127.0.0.1 and 100.100.100.200 were all denied over plain HTTP, and a CONNECT request on port 443 to the name resolving to 169.254.169.254 was denied too. That test used a lab-only allow_missing_role: true setting and a local test DNS server, and did not cover IPv6, redirects or real mTLS client roles. Keep --unsafe-allow-private-ranges at its README default of false and avoid broad --allow-range rules: "Explicit allows can bypass default IP safety blocks".
Redirects need the same check: OWASP says to "disable the support for redirection", and CVE-2026-14540 describes Google's mcp-toolbox "blindly following the redirection". The CVE-2026-104120 record for mcp-server-fetch and mcp-server-everything still says "The pull request to fix this issue awaits acceptance." (record updated 6 October 2026), while mcp-server-fetch has shipped a default guard against private and metadata addresses on PyPI since version 2026.10.10, so use 2026.10.10 or later. Its PyPI README says the server's address check "does not protect against DNS rebinding between that check and the connection".
Step 6: Block private and metadata ranges twice
OWASP calls deny lists a "Last Resort" ("Prefer allow-lists."), but its minimum table is a useful second layer: 169.254.169.254, metadata.google.internal, loopback, RFC1918, fc00::/7 and fe80::/10. The MCP spec lists all of "169.254.0.0/16 (including cloud metadata endpoints)" and warns of "encoding tricks (octal, hex, IPv4-mapped IPv6)". AWS documents a second metadata endpoint at [fd00:ec2::254].
In our assessment, add these as explicit --deny-range entries even where defaults deny them, plus 100.64.0.0/10 (our v0.1.0 test denied 100.100.100.200 under its "CGNAT Range" rule) and fd00:ec2::254/128; the README recommends "blocking IP addresses via config options, not the ACL", since its ACL "is only applied to hostnames as they appear in the request". Mirror the metadata block in Step 2's network policy for processes that skip the proxy. CVE-2026-112308 in crewai-tools (published 19 October 2026; no fixed version listed) shows the stakes: prompt injection can reach "cloud metadata addresses".
Step 7: Harden the metadata service
IMDSv2 and the hop limit on AWS
AWS's instance metadata service page (no version or date shown; checked 24 October 2026) says "By default, you can use either IMDSv1 or IMDSv2, or both" and that the PUT response has "a response hop limit (time to live) of 1" by default. Per the new-instances page, with IMDSv2 as the account default, "HttpTokens is set to required and HttpPutResponseHopLimit is set to 2", and AWS advises 2 "if your instances will host containers." For existing instances:
aws ec2 modify-instance-metadata-options --instance-id i-1234567890abcdef0 --http-tokens required --http-endpoint enabled
In our assessment, the hop limit is a container setting, not an agent boundary: 2 is what lets a container reach IMDS. If the agent does not need the instance role, keep 1 or turn the endpoint off. OWASP calls IMDSv2 a "defense-in-depth mechanism" that "mitigates some of the instances of SSRF"; in our assessment it does not stop an agent that can send a PUT with custom headers, so Steps 2 and 6 still block the address.
Step 8: Decide how to handle TLS
In our assessment, a CONNECT proxy cannot see paths, methods or bodies inside the tunnel, so method or path rules need TLS termination. Claude Code's credential masking (23 October 2026 docs) needs network.tlsTerminate because the proxy "has to see" request contents. In our assessment, termination means the proxy holds a CA the agent trusts; protect that key like any secret.
Step 9: Log every decision
In our test of v0.1.0, each decision was one JSON line, msg "CANONICAL-PROXY-DECISION", with fields including allow, decision_reason, requested_host and role. Kubernetes v1.37 NetworkPolicy cannot log, so the proxy log is your egress record. CERT-In asks deployers to "maintain a tamper-resistant log of agent activity". In our assessment, ship logs off-host and alert on denies to metadata or private ranges.
Step 10: Keep secrets out of the agent
The Claude Code sandboxing page (23 October 2026) says sandboxed commands inherit environment variables "including any secrets in its environment", and "There is no built-in credential deny list". Use sandbox.credentials to deny or mask them; with masking, "the sandbox proxy substitutes the real value on outbound requests to hosts you allow." In our assessment, inject credentials at the proxy or a broker so the agent never holds the real token, and issue short-lived credentials per run, as OWASP ASI02 and CERT-In ("a separate, short-lived identity with least privilege") advise.
Step 11: Fail closed and test it
Claude Code sandbox
Per the sandboxing page (23 October 2026), "The sandbox is off by default", and if it cannot start, by default "Claude Code runs commands without sandboxing." When on, there is "No direct route out" and allowed domains "start empty". Its hardening example:
claude --settings '{"sandbox": {"enabled": true, "allowUnsandboxedCommands": false}}'
Also set sandbox.failIfUnavailable to true. See sandboxing.
Codex Cloud (Legacy)
The Codex Cloud (Legacy) page says "By default, Codex blocks internet access during the agent phase. Setup scripts still run with internet access", offers a GET, HEAD and OPTIONS method limit, and says approved full sandbox escalation "can bypass the command proxy". Test both paths.
A test list
Test from inside the agent runtime: direct egress with the proxy unset, an unlisted name, a name resolving to 169.254.169.254, a raw IP, a URL shortener, [fd00:ec2::254], a redirect to an internal host, and outside DNS. All should fail.
Checklist (our assessment)
- Default-deny egress at the network layer.
- The proxy is the only reachable destination, with enforce as the default.
- One allowlist per agent, tool and MCP server, with no open relays.
- IP checked after DNS; metadata and private ranges denied at proxy and network.
- IMDSv2 required; hop limit 1 or endpoint off for agent hosts.
- Proxy logs shipped off-host, with alerts.
- No long-lived secrets in the agent's environment, and every layer fails closed.
What we do not know yet
Our Smokescreen test covered v0.1.0 only, not IPv6, redirects, real mTLS roles or report mode, and the README we read is master. We have not tested whether an agent could reach another site behind an allowed CDN through a CONNECT-only proxy.
Frequently asked questions
What is an egress proxy for AI agents?
An egress proxy is a server that all outbound traffic from an AI agent must pass through. It allows only approved destinations, blocks internal and cloud metadata addresses, and logs every request. Agents need one because the model chooses URLs and tool calls, and prompt injection can steer those requests toward data theft or internal systems. The MCP specification's security best practices (version 2026-07-28) say that operators of server-side MCP client deployments SHOULD "consider using an egress proxy that enforces network policies", and name Smokescreen as an example.
Isn't a domain allowlist enough to stop SSRF?
No. The OWASP SSRF Prevention Cheat Sheet says: "Domain allowlisting alone does not prevent DNS rebinding." The proxy must check the IP address a name resolves to and connect only to that address. Smokescreen's README says it resolves each requested domain and ensures the result is a publicly routable address. In our test of v0.1.0, allowlisted names that resolved to 169.254.169.254 or 10.0.0.1 were denied; that test did not cover IPv6, redirects or real mTLS client roles.
Does IMDSv2 protect an AI agent's cloud credentials?
It helps, but not on its own. OWASP calls IMDSv2 "an additional defense-in-depth mechanism for AWS that mitigates some of the instances of SSRF". AWS documents a default PUT response hop limit of 1, but advises a hop limit of 2 for instances that host containers, which lets those containers reach the metadata service. In our assessment, also block 169.254.169.254 at the proxy and at the network layer.
Do Claude Code's sandbox and Codex's internet settings already do this?
Partly. Claude Code's sandboxing docs (dated 23 October 2026) say shell traffic goes through a proxy with allowed domains that "start empty", but the sandbox is off by default and covers shell commands only; MCP servers run outside it and allowedDomains does not limit WebFetch. OpenAI's Codex Cloud (Legacy) docs say Codex blocks internet access during the agent phase by default, and that its command-network controls do not, by themselves, disable hosted web search, apps or MCP.
How do MCP servers fit into an egress proxy setup?
In our assessment, treat each MCP server as its own proxy client with its own allowlist, route remote connectors and their OAuth discovery through the proxy, and restrict network access for local servers, as the MCP specification advises. Recent records, including CVE-2026-8355, CVE-2026-14540 and GHSA-c9xm-49cp-xcr9, describe MCP components making server-side requests to internal or metadata addresses.
Sources
- OWASP: Server-Side Request Forgery Prevention Cheat Sheet (last changed 4 Oct 2026)
- Stripe: Smokescreen README (master, checked 24 Oct 2026)
- Stripe: Smokescreen release v0.1.0 (21 Sep 2026)
- Model Context Protocol: Security Best Practices (spec version 2026-07-28)
- Kubernetes: Network Policies (v1.37 docs, last modified 14 Apr 2026)
- AWS: Configure the Instance Metadata Service options (EC2 User Guide)
- AWS: Configure instance metadata options for new instances (EC2 User Guide)
- AWS: Modify instance metadata options for existing instances (EC2 User Guide)
- Claude Code docs: Sandboxing (dated 23 Oct 2026)
- Anthropic: claude-code .devcontainer/init-firewall.sh (last changed 30 Jun 2026)
- OpenAI: Codex agent internet access, Codex Cloud (Legacy)
- OWASP: Top 10 for Agentic Applications for 2026 (9 Dec 2025)
- OWASP: GenAI LLM Top 10 2026, LLM03:2026 Excessive Agency (3 Aug 2026)
- CERT-In: Advisory CIAD-2026-0049 (15 Oct 2026)
- OpenAI: Retrieving server secrets and bypassing authorization on a government statistics website (updated 15 Oct 2026)
- Anthropic: Investigating unintended model actions in our evaluations and internal use (9 Oct 2026)
- Anthropic: Alignment assessment of the UK AISI cyber-testing incident (23 Oct 2026)
- CVE.org: CVE-2026-8355 (IBM ContextForge MCP Gateway)
- CVE.org: CVE-2026-112308 (crewai-tools)
- CVE.org: CVE-2026-14540 (Google mcp-toolbox)
- CVE.org: CVE-2026-104120 (mcp-server-fetch, mcp-server-everything)
- PyPI: mcp-server-fetch
- GitHub Security Advisory GHSA-c9xm-49cp-xcr9 (rmcp, Rust MCP SDK)
This article is analysis. Bylines on AIAgentThreats are disclosed pen names of our editorial team. Spotted an error? Email info@aiagentthreats.com. See our Editorial Standards.