Enforce all container egress traffic through a managed HTTPS proxy with TLS interception (SSL bump). This provides a single enforcement point for traffic policy, with extension points for credential injection (#93) and PII masking (#94).
Problem
Currently, jailoc uses iptables rules in entrypoint.sh to restrict outbound traffic by destination IP/host. This has limitations:
- No content inspection — can't inspect HTTPS payloads
- DNS-based bypass — iptables rules resolve hostnames at setup time; DNS changes can bypass them
- No HTTP-level policy — can't enforce per-domain HTTP method restrictions (e.g. allow GET but deny POST)
- No traffic logging — no visibility into what the agent sends/receives
Goal
All container egress flows through a preconfigured Squid forward proxy with:
- SSL bump (TLS interception) via per-workspace ephemeral CA
- Domain + HTTP method allowlisting at the proxy level (replaces iptables host restrictions)
- Request/response logging (optional, for audit)
- Upstream chaining to a corporate/shared proxy via Squid
cache_peer
Architecture: sidecar is always present
The sidecar proxy is always the agent's proxy. The agent always sees a simple HTTP_PROXY=http://proxy:8080. Upstream chaining is transparent to the agent.
Mode A: Direct egress (default)
┌─────────────────────────────────────────────────┐
│ Docker network (workspace) │
│ │
│ ┌──────────┐ ┌──────────┐ ┌───────────┐ │
│ │ opencode │───▶│ squid │───▶│ internet │ │
│ │ │ │ (sidecar)│ │ │ │
│ └──────────┘ └──────────┘ └───────────┘ │
│ │ │
│ │ ┌─────────┐ │
│ └─────────▶│ dind │ │
│ └─────────┘ │
└─────────────────────────────────────────────────┘
Squid does SSL bump, enforces domain + method allowlists, optionally logs — then forwards directly to the internet.
Mode B: Upstream chaining
The sidecar Squid chains to an external upstream proxy via cache_peer. The agent still only talks to the sidecar:
┌──────────────────────────────────────────────────────────────────┐
│ Docker network (workspace) │
│ │
│ ┌──────────┐ ┌──────────┐ ┌───────────┐ ┌───────────┐ │
│ │ opencode │───▶│ squid │───▶│ upstream │───▶│ internet │ │
│ │ │ │ (sidecar)│ │ proxy │ │ │ │
│ └──────────┘ └──────────┘ └───────────┘ └───────────┘ │
│ │ │
│ │ ┌─────────┐ │
│ └─────────▶│ dind │ │
│ └─────────┘ │
└──────────────────────────────────────────────────────────────────┘
Upstream chaining is a standard Squid feature (cache_peer). Auth to the upstream proxy is the upstream proxy's concern — not solved at the sidecar level.
Agent-facing env vars
HTTP_PROXY=http://proxy:8080
HTTPS_PROXY=http://proxy:8080
http_proxy=http://proxy:8080
https_proxy=http://proxy:8080
NO_PROXY=localhost,127.0.0.1,dind
All four proxy vars set (uppercase + lowercase). NO_PROXY includes dind so Docker-in-Docker traffic stays local.
Squid configuration concepts
SSL bump
Squid intercepts TLS connections using a jailoc-generated ephemeral CA:
http_port 8080 ssl-bump \
cert=/etc/squid/ssl/ca.pem \
key=/etc/squid/ssl/ca.key \
generate-host-certificates=on \
dynamic_cert_mem_cache_size=4MB
sslcrtd_program /usr/lib/squid/security_file_certgen -s /var/lib/squid/ssl_db -M 4MB
ssl_bump peek step1
ssl_bump bump all
Domain + HTTP method allowlisting
Proven pattern: ACL rules that combine destination domain with HTTP method restrictions.
# Domain allowlists
acl allowed_domains dstdomain .api.openai.com
acl allowed_domains dstdomain .api.anthropic.com
acl allowed_domains dstdomain .googleapis.com
# HTTP method restrictions
acl safe_methods method GET HEAD OPTIONS
acl write_methods method POST PUT PATCH DELETE
# Allow API domains (all methods)
http_access allow allowed_domains
# Default: deny everything else
http_access deny all
jailoc generates these ACLs from config.toml allowed_hosts at workspace start. More granular per-domain method restrictions can be added later.
Upstream chaining via cache_peer
# Chain all traffic to upstream proxy
cache_peer upstream-proxy.corp.example.com parent 3128 0 \
no-query \
default \
name=upstream
# Force all requests through the upstream peer
never_direct allow all
This is standard Squid cache_peer configuration. The sidecar simply forwards everything to the upstream — no auth logic in the sidecar itself.
iptables enforcement
The opencode container's iptables rules must block direct outbound — only allow traffic to the sidecar proxy + dind. This prevents the agent from bypassing the proxy entirely.
Possible config
[defaults.proxy]
enabled = true
log_traffic = false # audit logging
# --- Upstream chaining (Mode B) ---
# upstream = "upstream-proxy.corp.example.com:3128"
# upstream_ca_cert = "/path/to/upstream-ca.pem" # if upstream does its own SSL bump
[defaults.proxy.limits]
bandwidth = "10MB/s"
requests_per_minute = 300
Implementation notes
- Squid is the recommended proxy — battle-tested forward proxy with native SSL bump,
cache_peer upstream chaining, and ACL system
- Ephemeral CA: jailoc generates per-workspace CA cert/key, injects into both Squid config and
opencode trust store
- ACL generation: jailoc compiles
allowed_hosts + allowed_networks from config.toml into Squid ACL includes at workspace start
- Container:
squid-openssl package (Debian/Ubuntu) provides SSL bump support
Relationship to other issues
OpenCode compatibility
OpenCode respects HTTP_PROXY/HTTPS_PROXY env vars (source). All outbound fetch() calls route through the proxy automatically.
Enforce all container egress traffic through a managed HTTPS proxy with TLS interception (SSL bump). This provides a single enforcement point for traffic policy, with extension points for credential injection (#93) and PII masking (#94).
Problem
Currently, jailoc uses iptables rules in
entrypoint.shto restrict outbound traffic by destination IP/host. This has limitations:Goal
All container egress flows through a preconfigured Squid forward proxy with:
cache_peerArchitecture: sidecar is always present
The sidecar proxy is always the agent's proxy. The agent always sees a simple
HTTP_PROXY=http://proxy:8080. Upstream chaining is transparent to the agent.Mode A: Direct egress (default)
Squid does SSL bump, enforces domain + method allowlists, optionally logs — then forwards directly to the internet.
Mode B: Upstream chaining
The sidecar Squid chains to an external upstream proxy via
cache_peer. The agent still only talks to the sidecar:Upstream chaining is a standard Squid feature (
cache_peer). Auth to the upstream proxy is the upstream proxy's concern — not solved at the sidecar level.Agent-facing env vars
All four proxy vars set (uppercase + lowercase).
NO_PROXYincludesdindso Docker-in-Docker traffic stays local.Squid configuration concepts
SSL bump
Squid intercepts TLS connections using a jailoc-generated ephemeral CA:
Domain + HTTP method allowlisting
Proven pattern: ACL rules that combine destination domain with HTTP method restrictions.
jailoc generates these ACLs from
config.tomlallowed_hostsat workspace start. More granular per-domain method restrictions can be added later.Upstream chaining via
cache_peerThis is standard Squid
cache_peerconfiguration. The sidecar simply forwards everything to the upstream — no auth logic in the sidecar itself.iptables enforcement
The
opencodecontainer's iptables rules must block direct outbound — only allow traffic to the sidecar proxy + dind. This prevents the agent from bypassing the proxy entirely.Possible config
Implementation notes
cache_peerupstream chaining, and ACL systemopencodetrust storeallowed_hosts+allowed_networksfromconfig.tomlinto Squid ACL includes at workspace startsquid-opensslpackage (Debian/Ubuntu) provides SSL bump supportRelationship to other issues
OpenCode compatibility
OpenCode respects
HTTP_PROXY/HTTPS_PROXYenv vars (source). All outboundfetch()calls route through the proxy automatically.