Repository navigation
Egress Firewall
Outbound traffic is filtered by a separate gateway container (built from images/gateway). The dev container sits on a private Docker network (o3s-cage) that has no host NAT of its own, so the only working path to the internet is the gateway. A packet sent any other way leaves with a private source address and dies upstream.
The gateway runs dnsmasq, which resolves the allow-listed domains and adds their current IPs to an ipset as they are looked up, so the allowlist tracks IP changes on its own. Static IPs and CIDRs are added to the same sets directly. iptables then permits those addresses on their declared ports and default-denies the rest.
That admits an address, which every host sharing it answers on too. The proxy container, sharing the gateway's network namespace, closes that gap: it reads the name each TLS handshake asks for and kills the connection unless the allowlist declares it on 443. The allowlist is therefore exact by name, not only by address.
(Host) Add entries, one table per host:
- table name - a domain, an IPv4 host, or an IPv4 CIDR. A domain matches that name alone; the
*.prefix matches its subdomains, so list both forms to cover a name and everything under it. -
ports- required. One or more TCP ports the host may be reached on. -
secret- optional. Names the token the proxy swaps into requests to this host on egress, so the cage itself never holds it. -
inject- optional, defaults to["header"]. Where in the request the token is swapped in:header,query, orbody. A body is excluded by default because it carries the cage's own content, which may quote the marker.
nano .devcontainer/config.toml["10.8.0.0/24"]
ports = [443] # static subnet on HTTPS
["auth.example.com"]
ports = [443]
secret = "EXAMPLE_CLIENT_SECRET"
inject = ["header", "body"]
["github.com"]
ports = [22, 443] # HTTPS & SSH
["*.github.com"]
ports = [443]
["api.openai.com"]
ports = [443]
secret = "OPENAI_API_KEY"Apply, both because they share a network namespace:
docker compose [-f .devcontainer/compose.yaml] restart gateway proxy