Skip to content

feat: HTTPS proxy sidecar with SSL bump and upstream chaining #95

Description

@thejoeejoee

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:

  1. No content inspection — can't inspect HTTPS payloads
  2. DNS-based bypass — iptables rules resolve hostnames at setup time; DNS changes can bypass them
  3. No HTTP-level policy — can't enforce per-domain HTTP method restrictions (e.g. allow GET but deny POST)
  4. 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    dockerContainer, compose, DinD, image resolutionfeatureNew functionalityneeds-designRequires design discussion before implementationnetworkFirewall rules, host/network allowlistingsecuritySecurity hardening or vulnerability

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions