trailofbits / trailofbits/coop

Restrict guest network egress (allowlist + offline lockdown)

Open
#2 4 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
243
Forks
13
Avg merge
1d 20h
Merged PRs (30d)
30

Description

Restrict guest VM egress so a compromised or prompt-injected agent can't reach arbitrary endpoints. Two options, with deliberately different strength:

1. No route out (structural — the strong option)

For the offline / local-model case, don't filter egress — don't give the guest a routed path at all. The guest gets a non-routed interface only:

  • model in-guest: the agent reaches it over loopback; the VM has no external NIC (or the NIC has no default route). Simplest and airtight, but the VM needs the resources to run the model.
  • model on the host: a host-local link with no default route / no NAT, so the agent can reach the local endpoint but has no path to the internet. Lets the model use host GPU/Metal (matters on Apple Silicon).

This is not a boundary inside the VM — it is the absence of a route. There is nothing for guest root to flush (the NOPASSWD:ALL sudo below is irrelevant) and no filter to misconfigure. It closes the "communicate externally" leg of the lethal trifecta for that session, and unlike a filter it also defeats DNS tunneling, because there is no resolver path out. Pairs with coop model <instance> local (#343).

2. Allowlist (filter — the weaker option)

Only when the agent genuinely needs some egress (cloud model API, GitHub, Docker Hub, package registries). This is a filter, so it carries the usual filter gaps; use it when "no route out" isn't acceptable.

[network]
egress = "allowlist"            # "open" | "allowlist" | "none"
allowed_hosts = ["api.anthropic.com", "github.com", "registry.npmjs.org"]

A sensible default allowlist should ship out of the box. egress = "none" selects option 1 (no route); egress = "allowlist" selects this one.

Enforcement must be host-side, never in-guest

The original plan was in-guest iptables, on the assumption that the agent runs as non-root ubuntu and cannot flush the rules. That assumption does not hold: the guest user has passwordless sudo (NOPASSWD:ALL in /etc/sudoers.d/<user>, set in scripts/guest/guest-config.sh:77 and src/lima.rs:1410), so a compromised agent in bypass mode can sudo iptables -F and remove any in-guest restriction. Any in-VM control is exactly the in-band protection coop exists to avoid. So if filtering is used at all, it lives where guest root can't reach it:

  • Firecracker — the host owns the TAP device and routing/NAT (jailer + TAP networking). Apply policy on the host (iptables/nftables on the TAP/FORWARD path, or a restrictive NAT/route policy); guest root cannot touch it, and it closes the boot-window gap.
  • Lima / macOS — networking goes through Virtualization.framework / vmnet (socket_vmnet). Host-side filtering needs macOS pf or vmnet/bridge-level control; harder, and likely the limiting platform. Needs investigation. (Note: "no route out" is simpler to realize here than a host-side filter — drop the default route / don't NAT.)

Known limitations (option 2 only)

  • CDN IP rotation: resolve-at-start may go stale for long-lived VMs.
  • DNS tunneling: an allowed resolver can still exfiltrate data. Only option 1 (no route) avoids this.

Related: #343 (ease local-model use). The disposable VM remains the boundary coop leans on regardless of egress policy.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the guest privilege setup in scripts/guest/guest-config.sh:77 and src/lima.rs:1410, then trace the Firecracker TAP/routing path and the Lima/macOS networking path. Compare the proposed egress modes and determine where host-side enforcement or no-route networking can be applied. Done means the selected policy is implemented for the supported paths without relying on in-guest controls, with the limitations documented.

Written by the indexing model from the issue text.

Assessment

Tech stack
macos, rust
Domain
infrastructure, networking, security
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.