DEV Community

Cover image for Your Coding Agent Has Your AWS Keys and an Open Internet Connection
Sebastian Buzdugan
Sebastian Buzdugan

Posted on Originally published at Medium

Your Coding Agent Has Your AWS Keys and an Open Internet Connection

The agent needs the network to install its dependencies. Then it runs code a model wrote, in the same box, with the same unrestricted egress, next to your credentials file. Both of those things cannot be true at once.

The two phases of an agent run want opposite things

Watch what a coding agent actually does when you hand it a task.

It clones the repo. It runs pip install or npm ci. It pulls a base image, fetches a dataset, hits your model provider's API. Every one of those steps needs outbound network access, and there is no way around it.

Then the phase changes. The model produces a patch, a script, a test file, and the agent executes it. That code was not written by you, it was not reviewed by anyone, and it now runs inside a box that still has the credentials, the source tree, and a wide open route to the internet.

The security literature has a name for the resulting failure. A prompt injection buried in a dependency's README, an issue comment, or a scraped web page tells the agent to read ~/.aws/credentials and POST it somewhere. The agent obliges, because from its point of view that is just another tool call.

So you want the network on during setup and off during execution, in the same run, on the same machine. That is the whole problem.

Setup earns the network. Execution loses it.

Why one decision at creation is hard to get right

A common model is to configure network access when the sandbox is created. One decision, taken before anything runs. Changing it mid-run is becoming a first-class capability: Harbor standardizes it as dynamic_network_policy, and Tensorlake is one of the providers that implements it. What separates implementations is how fast and how cleanly the swap lands, so that is what I set out to measure.

Pick "deny" and setup never gets off the ground. I created a sandbox with egress disabled from birth and tried the most ordinary setup step there is:

$ pip install requests
ERROR: No matching distribution found for requests
Enter fullscreen mode Exit fullscreen mode

No package index, no install, no agent. You cannot lock the box before the work that needs the network happens.

So everyone picks "allow", and the sandbox stays open through the dangerous half of the run. The credentials are reachable, the exfil route is reachable, and the only thing standing between them is the model's good judgment.

The third option is to tear the sandbox down after setup and rebuild a locked one. You keep the security and you throw away the environment: the installed packages, the cloned repo, the warm caches, the partially completed work. For a fleet of agents doing thousands of tasks, that is not a rounding error, it is the entire cost model.

Change the wall while the box keeps running

Tensorlake's sandboxes let you replace the network policy on a live sandbox. The process keeps running, the filesystem stays exactly where it was, and the firewall swaps underneath it.

The API is one call:

from tensorlake.sandbox import Sandbox, NetworkConfig
sb = Sandbox.create(allow_internet_access=True)
sb.run("bash", ["-lc", "pip install requests && git clone $REPO /tmp/work"])
sb.update(network=NetworkConfig(allow_internet_access=False, allow_out=[]))
sb.run("bash", ["-lc", "python /tmp/work/model_written_patch.py"])
Enter fullscreen mode Exit fullscreen mode

Line three is the interesting one. Everything before it had the internet. Everything after it does not, and nothing was rebuilt in between.

I measured that call ten times in a row on a running sandbox, alternating between open and sealed:

127 ms median atomic policy flip

A median of 127 milliseconds, a range of 126 to 143, no restart. The docs describe the swap as atomic, applied to the running firewall in one step so there is no window where egress is half enforced, and established connections are not torn down. That is the part worth sitting with: the security posture of a running machine changed completely, and the process on top of it kept going as if nothing had happened.

What the lockdown actually stops

Talk is cheap here, so I ran the whole workflow end to end with a fake credentials file sitting in the sandbox.

Setup phase, network on. pip install requests finished in 1.3 seconds and resolved version 2.34.2. git clone pulled a repo into /tmp/work. An arbitrary host answered 200.

Then the flip, then the same commands again:

[before lock] arbitrary host   -> 200
[before lock] POST secret      -> request sent
[after  lock] arbitrary host   -> 000 blocked
[after  lock] POST secret      -> 000 blocked
[after  lock] import requests  -> 2.34.2
[after  lock] /tmp/work        -> present
Enter fullscreen mode Exit fullscreen mode

The last two lines matter as much as the blocked ones. The dependency the agent installed is still importable. The repo it cloned is still on disk. The lockdown took away the network and nothing else, which is exactly the trade you want: the environment survives, the escape route does not.

One detail worth knowing while you are in there. Sandbox commands run as an unprivileged tl-user rather than root, which is a sensible default. That user can also sudo without a password, so root is there when a task genuinely needs it. Treat the default as a helpful floor rather than a boundary, and let the network policy do the heavy lifting.

What this unlocks

Once the policy is a runtime call instead of a creation-time flag, a single agent run can have a different network posture at every stage.

  • Setup: broad access while it installs dependencies and clones the repo
  • Reasoning: model API only, so it can think but cannot leak
  • Execution: full deny the moment it runs generated code

Three security postures inside one sandbox, each one a sub-second call, with the environment you built carried straight through all of them.

That is a different design space from "pick a policy and hope." The network stops being a property of the box and becomes a property of the phase.

How the three settings combine

Rather than reason from the field names, I applied every combination to a live sandbox and measured what each one actually does. Tensorlake has since expanded the docs on exactly this, with a configuration summary and a section on how allow_internet_access and allow_out combine, so treat what follows as a confirmation of that behavior rather than a substitute for it.

NetworkConfig has three knobs: allow_internet_access, allow_out, and deny_out. I applied six different policies to one running sandbox and probed three hosts after each.

One running sandbox, six policies, applied live

Two results are worth internalizing.

The first is that allow_out behaves like a proper allowlist and it is the setting you want for the interesting case. With allow_out=["api.anthropic.com"] the sandbox could still reach the model API and nothing else. That is the policy for an agent that has to keep talking to its model while losing every other route: it can think, it cannot leak.

The second is subtler, and it is about DNS rather than the allowlist. Adding allow_internet_access=False to that same allowlist stops the hostname from working, but the rule itself is still in force. Name resolution is egress too, so the lookup dies before the request is ever made.

Traffic to an allowed address still goes through. With internet access off and 1.1.1.1 on the allowlist, a request by IP still returned 301 while the same request by hostname failed. Pin the address, or resolve before you lock down, and the allowlist keeps doing its job.

The mental model that survives contact with the data: use deny_out to carve out specific hosts, use allow_out alone to pin traffic to a short list, and use allow_internet_access=False with an empty allow_out when you want the box fully sealed. If you combine the last one with an allowlist, remember that you are also turning off name resolution, so pin addresses rather than hostnames.

Where this leaves the threat model

Cutting egress mid-run closes the exfiltration path. It does not make the agent trustworthy, and it is worth being precise about what is left.

The agent can still corrupt everything inside its own sandbox, which is why per-task isolation matters as much as the network policy. Anything already cached locally during the open phase is still readable after the lock, so a secret the agent fetched at minute one is in memory at minute five. Policy changes settle in about a second, so a probe fired immediately after the call can still slip through; wait for the settle before you start untrusted work. And an allowlisted model API is a real channel, so a determined injection can encode data into a prompt.

None of that argues for leaving the box open. It argues for treating the flip as one control in a stack: fresh sandbox per task, unprivileged user, minimum viable allowlist, egress cut before the untrusted phase.

Final Thoughts

The interesting question for the next agent platform you build is not "should the sandbox have network access." It is "when."

Almost every agent runtime today answers that question once, at creation, and then lives with a policy that only ever fits half the run. Setup needs the world. Execution needs a wall. Deciding upfront means trading away either the setup or the safety.

A 127 millisecond policy swap on a live sandbox turns that permanent tradeoff into a scheduling detail. Install what you need, take the network away, run the untrusted thing. The environment you built survives, and the route out does not.

If you are running model-written code next to real credentials right now, that is the cheapest control available to you, and it is one API call away.

Sponsored by Tensorlake. All experiments, numbers, and terminal output in this piece are my own, run live on their sandboxes.

Resources & References

Stay in Touch

Short takes and discussions on X
https://x.com/sebuzdugan

Practical AI / ML videos on YouTube
https://www.youtube.com/@sebuzdugan/

Partnerships & collabs
sebuzdugan@gmail.com


Originally published on Medium.

Top comments (0)