Looks like there's one feature missing from this that I care about: I'd like more finely grained control over what outbound internet connections code running on the box can make.
As far as I can tell it's all or nothing right now:
this.ctx.container.start({
enableInternet: false,
});
I want to run untrusted code (from users or LLMs) in these containers, and I'd like to avoid someone malicious using my container to launch attacks against other sites from them.As such, I'd like to be able to allow-list just specific network points. Maybe I'm OK with the container talking to an API I provide but not to the world at wide. Or perhaps I'm OK with it fetching data from npm and PyPI but I don't want it to be able to access anything else (a common pattern these days, e.g. Claude's Code Interpreter does this.)
Cloudflare has Outbound Workers for exactly this use-case: https://developers.cloudflare.com/cloudflare-for-platforms/w...
If these aren't enabled for containers / sandboxes yet, I bet they will be soon
You may be interested in the Dynamic Worker Loader API, which lets you set up isolate-based sandboxes (instead of containers) and gives you extremely fine-grained, object-capability-based control over permissions.
It was announced as part of the code mode blog post:
https://blog.cloudflare.com/code-mode/
API docs: https://developers.cloudflare.com/workers/runtime-apis/bindi...
I’m extending Packj sandbox for agentic code execution [1]. You can specify allowlist for network/fs.
1. https://github.com/ossillate-inc/packj/blob/main/packj/sandb...
This simple feature bumps up the complexity of such a firewall by several orders of magnitude, which is why no similar runtime (like Deno) offers it.
Networking as a whole can easily be controlled by the OS or any intermediate layer. For controlling access to specific sites you need to either filter it at the DNS level, which can be trivially bypassed, or bake something into the application binary itself. But if you are enabling untrusted code and giving that code access to a TCP channel then it is effectively impossible to restrict what it can or cannot access.
The most convincing implementation I've seen of this so far is to lock down access to just a single IP address, then run an HTTP proxy server at that IP address which can control what sites can be proxied to.
Then inject HTTP_PROXY and HTTPS_PROXY environment variables so tools running in the sandbox know what to use.
- [deleted]
Codex remote environments seem to do this, we had to add support (via two lines of code) for these proxy environment variables to our CLI to support talking to GitHub from these environments.
> This simple feature bumps up the complexity of such a firewall by several orders of magnitude, which is why no similar runtime (like Deno) offers it.
My uneducated question, why not BPF? It's the actual original use case. Declare a filter rule (using any DSL you like), enforce it within the sandbox, move processing to the "real" firewall/kernel where applicable, etc.
At least on macOS, there is a third way where you can control the network connection on the PID/binary level by setting up a network system extension and then setting up a content filter so you can allow/deny requests. It is pretty trivial to set this up, but the real challenge is usually in how you want to express your rules.
Little Snitch does this pretty well: https://www.obdev.at/products/littlesnitch/index.html
That’s true, but Cloudflare is uniquely positioned to avoid this complexity by leveraging the functionality of all their existing products. For them, sandboxing the network is probably the easiest problem to solve for this product…
If a single TCP channel is all that is allowed, on a single port to a single orchestrator IP, and the only service attached to that channel on the other end is the orchestrator which reports results to the host worker, why would you need anything to do with DNS? Isn't this a simple thing to do with a firewall rule, once you know the orchestrator's network-local IP?
(Certainly this would prevent things like package manager installations, etc... but if you're in a use case where you really want to sandbox things, you wouldn't want people to have e.g. NPM access as I'm sure there are ways to use that for exfiltration/C&C!)
deno does support per-host network permissions https://docs.deno.com/runtime/fundamentals/security/#network...
You cannot bypass DNS within Cloudflare’s environment.
What does that mean? That's essentially like saying "you cannot bypass HTTP" within Cloudflare's environment. It doesn't make any sense.
Do you mean they force you to use their DNS? What about DOH(s)? What about just skipping domain lookup entirely and using a raw IP address?
You can restrict outbound network to HTTP using the outbound worker mentioned elsewhere in the thread and filter the domain name of the outbound request against a whitelist of domains you control. The DNS resolution of the domain happens within the CF network stack that you have no control over and that can’t be overwritten in anyway meaning if you restrict outbound to Google.com, there’s no way for that request to end up anywhere else. The whitelist filter you put in place would disallow raw IP addresses and DoH isn’t relevant because again your whitelist of servers you control can just not expose DoH.
- [deleted]
When you say that the filter would disallow connecting directly to IP addresses, how would that work? When I open a tcp connection, there's no reference to any domain name. Do you think CF would proactively resolve all the domain names in my whitelist (repeatedly, in case the IPs change) and check the IP I'm connecting to against the list of IPs those domains would resolve to? That sounds like a very brittle solution.
It sounds like you haven’t done the requisite research and are asking me to do it for you. That’s not very nice. The TLDR is that the outbound request doesn’t go directly to the internet. It first goes through your interposer worker where you can sent direct TCP requests and only allow HTTP requests through after filtering for domain.
Can I send a UDP packet to a server on port 53 and receive a packet back?
You choose. But you can also choose to block that.