Overview
The OpenSandbox Egress Sidecar provides FQDN-based egress control for sandbox environments. It runs alongside the sandbox application container (sharing the same network namespace) and enforces declared network policies to control outbound traffic.Features
- FQDN-based Allowlist: Control outbound traffic by domain name (e.g.,
api.github.com) - Wildcard Support: Allow subdomains using wildcards (e.g.,
*.pypi.org) - Transparent Interception: Uses transparent DNS proxying; no application configuration required
- Dynamic DNS: Resolved IPs are added to nftables with TTL for network-layer enforcement
- Privilege Isolation: Requires
CAP_NET_ADMINonly for the sidecar; application runs unprivileged - Graceful Degradation: Warns and disables enforcement if
CAP_NET_ADMINis missing instead of crashing
Architecture
The egress control is implemented as a Sidecar that shares the network namespace with the sandbox application.Layer 1: DNS Proxy
- Runs on
127.0.0.1:15353 iptablesrules redirect all port 53 (DNS) traffic to this proxy- Filters queries based on the allowlist
- Returns
NXDOMAINfor denied domains
Layer 2: Network Filter (dns+nft mode)
WhenOPENSANDBOX_EGRESS_MODE=dns+nft:
- Uses
nftablesto enforce IP-level allow/deny rules - Resolved IPs for allowed domains are added to dynamic allow sets with TTL (dynamic DNS)
- At startup, the sidecar whitelists:
- 127.0.0.1 (redirect target for the DNS proxy)
- Nameserver IPs from
/etc/resolv.conf(so DNS resolution and proxy upstream work, including private DNS)
Nameserver count is capped and invalid IPs are filtered. See Configuration for details.
Requirements
- Runtime: Docker or Kubernetes
- Capabilities:
CAP_NET_ADMIN(for the sidecar container only) - Kernel: Linux kernel with
iptablesandnftablessupport
Configuration
Policy Bootstrap & Runtime
- Default: Deny-all
- Seed initial policy: Via
OPENSANDBOX_EGRESS_RULES(JSON, same shape as/policy) - Runtime updates: Via
/policyHTTP endpoint - Reset to deny-all: POST empty body to
/policy
HTTP Service
| Variable | Default | Description |
|---|---|---|
OPENSANDBOX_EGRESS_HTTP_ADDR | :18080 | Listen address |
OPENSANDBOX_EGRESS_TOKEN | (empty) | Auth token; if unset, endpoint is open |
OPENSANDBOX-EGRESS-AUTH: <token>
Egress Mode
| Mode | Description |
|---|---|
dns (default) | DNS proxy only, no nftables (IP/CIDR rules have no effect at L2) |
dns+nft | Enable nftables; if nft apply fails, fallback to dns. Required for IP/CIDR enforcement and DoH/DoT blocking |
OPENSANDBOX_EGRESS_MODE
DNS and Nameserver Whitelist (dns+nft mode)
Indns+nft mode, the sidecar automatically allows:
- 127.0.0.1 — so packets redirected by iptables to the proxy are accepted by nft
- Nameserver IPs from
/etc/resolv.conf— so client DNS and proxy upstream work (e.g., private DNS)
| Variable | Default | Description |
|---|---|---|
OPENSANDBOX_EGRESS_MAX_NS | 3 | Max nameservers to whitelist (0 = no cap, 1-10 = cap) |
DoH/DoT Blocking
- DoT (tcp/udp 853): Blocked by default
- DoH over 443: Optional via
OPENSANDBOX_EGRESS_BLOCK_DOH_443=true- If enabled without blocklist, all 443 traffic is dropped
- DoH blocklist:
OPENSANDBOX_EGRESS_DOH_BLOCKLIST="9.9.9.9,1.1.1.1/32,2001:db8::/32"
Runtime HTTP API
Default listen address::18080 (override with OPENSANDBOX_EGRESS_HTTP_ADDR)
Endpoints
| Endpoint | Method | Description |
|---|---|---|
/policy | GET | Returns the current policy |
/policy | POST | Replaces the policy (empty body resets to deny-all) |
Policy Examples
DNS Allowlist (default deny)
DNS Blocklist (default allow)
IP/CIDR Only
Mixed DNS + IP/CIDR
Build & Run
Build Docker Image
Run Locally (Docker)
To test the sidecar with a sandbox application:Server Integration
The egress sidecar is automatically injected when usingnetworkPolicy in sandbox creation requests.
Server Configuration
Example Request
Sidecar Behavior
- Main container shares the sidecar netns and explicitly drops
NET_ADMIN - Sidecar keeps
NET_ADMINto manage iptables - IPv6 is disabled in the shared namespace when egress sidecar is injected
- Sidecar image is pulled before start
- Delete/expire/failure paths attempt to clean up the sidecar
Development
Project Structure
- Language: Go 1.24+
- Key Packages:
pkg/dnsproxy: DNS server and policy matching logicpkg/iptables:iptablesrule managementpkg/nftables: nftables static/dynamic rules and DNS-resolved IP setspkg/policy: Policy parsing and definition
- Main (egress):
nameserver.go: Builds the list of IPs to whitelist for DNS in nft mode
Testing
E2E Benchmark: dns vs dns+nft
Compares dns (pass-through, no nft write) and dns+nft (syncAddResolvedIPs before each DNS reply):
Troubleshooting
iptables setup failed
iptables setup failed
Issue: Sidecar cannot set up iptables rulesSolution: Ensure the sidecar container has
--cap-add=NET_ADMINDNS resolution fails for all domains
DNS resolution fails for all domains
Possible Causes:
- Check if the upstream DNS (from
/etc/resolv.conf) is reachable - In
dns+nftmode, the sidecar whitelists nameserver IPs from resolv.conf at startup - Check logs for
[dns] whitelisting proxy listen + N nameserver(s) - Ensure
/etc/resolv.confis readable and contains valid, reachable nameservers
- The proxy prefers the first non-loopback nameserver from resolv.conf
- If only loopback exists (e.g., Docker 127.0.0.11), it is used (proxy upstream traffic bypasses the redirect)
- Fallback to 8.8.8.8 only when resolv.conf is empty or unreadable
Traffic not blocked
Traffic not blocked
Issue: Network policies are not being enforcedPossible Causes:
- If nftables apply fails, the sidecar falls back to dns mode
- Check logs for nftables errors
- Verify
nft list table inet opensandbox - Ensure
CAP_NET_ADMINis granted to the sidecar
Security Considerations
See SECURITY-RISKS.md for detailed security considerations including:- Nameserver whitelist trust model
- DNS resolver scope and limitations
- Best practices for production deployments
Support
- Issues: GitHub Issues
- Documentation: OpenSandbox Docs
- Community: Discussions