Limiting Network Access in Your Harness
Default-deny beats blocklists for agent harness network isolation. This guide gives you a copy-paste egress allowlist and the proxy setup that enforces it.
import docker
client = docker.from_env()
def run_isolated(image, command):
return client.containers.run(
image=image,
command=command,
network_mode="none", # no network at all, by default
mem_limit="512m",
cap_drop=["ALL"],
detach=True,
)Most harness-hardening guides are wrong about network security. They tell you to add an allowlist of blocked domains, or to inspect outbound requests for suspicious patterns, and call it done. That's backwards, and it leaks. Real agent harness network isolation starts from the opposite default: the agent gets no network, and you grant back exactly the destinations it provably needs — nothing else. This guide gives you the copy-paste starting point, then explains the variables so you can adapt it.
The contrarian point worth sitting with: an agent that can reach the open internet can exfiltrate anything it can read, no matter how good your prompt hygiene is. You don't secure that by watching the traffic. You secure it by making the traffic impossible unless you explicitly allowed it.
Quick-Start (Copy This Right Now)
Here's a default-deny network setup for an agent running in a container. Drop it in and your agent has zero outbound access until you say otherwise.
[object Object], docker
client = docker.from_env()
,[object Object], ,[object Object],(,[object Object],):
,[object Object], client.containers.run(
image=image,
command=command,
network_mode=,[object Object],, ,[object Object],
mem_limit=,[object Object],,
cap_drop=[,[object Object],],
detach=,[object Object],,
)What this does: Runs the container with
network_mode="none"⚡ Pro tip: Before you add any network access, run your agent for a day with
network_mode="none"What Agent Harness Network Isolation Depends On
Three things determine your network policy, and getting them explicit is most of the work. Agent harness network isolation isn't one setting — it's the interaction of these three, and skipping any one of them leaves a gap the other two can't cover.
The destinations. What hosts does the agent legitimately need? Usually a very short list: your model API, maybe one or two internal services, occasionally a specific documentation site. If that list has more than a handful of entries, that's a signal your agent is doing too much, not that you need a bigger allowlist.
The direction. Almost all agent traffic should be outbound to known APIs. Inbound connections to a sandboxed execution environment are almost never legitimate and should be blocked entirely. If something is trying to connect in, treat it as an incident.
The enforcement point. You can enforce at the container (network modes), at the host (firewall rules), or at an egress proxy the container is forced to route through. The proxy approach is the most flexible for agent harness network isolation because it gives you per-host allowlisting and a log of every attempted connection, allowed or denied.
Step-by-Step: Building an Egress Allowlist
The strongest practical setup routes all agent traffic through a proxy that only permits allowlisted hosts. Here's how to build it.
Step one: Put the agent container on an internal-only Docker network with no direct internet route, alongside a proxy container that does have egress.
net = client.networks.create(,[object Object],, internal=,[object Object],, driver=,[object Object],)What this does: Creates a Docker network with
internal=TrueStep two: Run a tiny proxy (tinyproxy, Squid, or a few lines of Python) that permits only your allowlisted hosts and denies everything else with a logged rejection.
ALLOWED_HOSTS = {,[object Object],, ,[object Object],}
,[object Object], ,[object Object],(,[object Object],) -> ,[object Object],:
ok = host ,[object Object], ALLOWED_HOSTS
log.info(,[object Object],, extra={,[object Object],: host, ,[object Object],: ok})
,[object Object], okWhat this does: Checks each requested host against a small allowlist and logs the decision either way. Every connection the agent attempts is now visible, and anything not on the list is refused before a single byte leaves your network. The log doubles as an intrusion signal.
Step three: Point the agent container at the proxy by setting
HTTP_PROXYHTTPS_PROXYclient.containers.run(
image=,[object Object],,
network=net.name,
environment={
,[object Object],: ,[object Object],,
,[object Object],: ,[object Object],,
,[object Object],: ,[object Object],,
},
dns=[,[object Object],], ,[object Object],
detach=,[object Object],,
)What this does: Forces all HTTP and HTTPS traffic through the proxy and disables external DNS, so even if code tries to connect directly by IP, there's no route off the internal network to carry it. The agent can only reach what the proxy allows, and it can only discover hosts the proxy resolves.
Pro-Level Variations
Once the basics work, a few refinements harden it further.
For SSRF protection, have the proxy resolve each hostname and reject any that resolve to private IP ranges (
10.0.0.0/8169.254.169.254169.254.169.254For per-tenant isolation in a multi-tenant harness, run one internal network and proxy per tenant, so tenant A's agent physically cannot reach tenant B's services. Network-level separation is far harder to get wrong than application-level checks.
⚡ Pro tip: Allowlist by hostname and re-validate the resolved IP at connection time. Attackers use DNS rebinding — a hostname that resolves to a safe IP during your check and a malicious one microseconds later. Checking the IP at the moment of connection, not just at allowlist time, closes that window.
Troubleshooting Common Issues
The agent hangs instead of erroring. A denied connection often manifests as a timeout, not a clean failure, because the packet goes nowhere. Set aggressive connection timeouts in the agent's HTTP client so a blocked host fails fast and the model gets a usable error instead of stalling for 30 seconds.
DNS "works" when it shouldn't. If the container resolves external hostnames despite your config, it's using a DNS server you didn't intend — often the host's resolver leaking through. Pin
dnsnslookupLegitimate traffic gets blocked after a deploy. Your model provider or internal service changed hostnames or added a CDN domain. This is why the proxy log matters: a spike in denied connections to a plausible-looking host is usually a legitimate dependency that moved, not an attack — but you can't tell which without the log.
⚠️ Common mistake: Allowlisting a wildcard like
*.amazonaws.comTesting That the Isolation Can't Be Bypassed
A network policy you haven't attacked is a network policy you don't actually trust. Once the proxy and allowlist are in place, run an adversarial probe from inside the agent container that tries every bypass you can think of — a direct IP connection, a raw socket, a request to the cloud metadata endpoint, a lookup of a non-allowlisted host — and confirm every one fails.
BYPASS_ATTEMPTS = [
,[object Object],, ,[object Object],
,[object Object],, ,[object Object],
,[object Object],, ,[object Object],
,[object Object],, ,[object Object],
]What this does: Enumerates the classic egress bypasses — connecting by IP to skip DNS, probing the metadata service for credentials, hitting an unlisted host, opening a raw socket. Each should time out or be refused by the proxy. If any returns data, your agent harness network isolation has a leak, and better to find it from a test than from a leaked credential.
The metadata probe deserves special attention. On a cloud host,
169.254.169.254⚡ Pro tip: Run these bypass tests on a schedule, not just once. Cloud networking changes underneath you — a new route, a modified security group, a platform update — and a policy that was airtight at deploy can quietly develop a hole weeks later. A nightly probe that alerts on any success turns a silent regression into a page you can act on.
Your Turn
Take your agent and, today, wrap its execution in
network_mode="none"One caution as you roll this out: coordinate with whoever owns your model-provider integration before you flip the default to deny in production. Model APIs occasionally serve responses through CDN hostnames that differ from the documented endpoint, and a too-tight allowlist can break your agent's own calls to the model. Add the provider's hosts first, watch the proxy log for a day, and expand only for denials you can trace to a real dependency.
Keep the finished proxy config and allowlist versioned alongside your agent's prompts and tool definitions in a library like PromptABCD, because the set of hosts an agent is allowed to reach is as much a part of its behavior as its system prompt — and it deserves the same review, history, and reuse.
Continue Reading
Save the prompts from this post
PromptABCD is a free prompt manager. Paste, organize, and reuse your best AI prompts — no more hunting through chat history.
