Skip to content

[Bug]: Phase 5 — Container Sandbox Supervisor: Kernel-Level Network Isolation, Seccomp, DNS Proxy, Policy Engine #35

@initializ-mk

Description

@initializ-mk

Component

forge-core (registry, tools, security, channels, LLM), forge-cli (commands, TUI wizard, runtime), forge-skills (skill parser, compiler, analyzer, trust)

Description

Forge's current egress enforcement relies on two app-level mechanisms that are bypassable:

  1. EgressEnforcer — wraps Go's http.RoundTripper (only catches Go HTTP calls)
  2. EgressProxy — localhost HTTP forward proxy with HTTP_PROXY env injection (voluntary — programs can ignore it)

Any subprocess that ignores HTTP_PROXY or makes raw TCP/UDP connections reaches the internet directly. The K8s NetworkPolicy Forge generates only allows TCP 80/443 to any destination — domain enforcement is advisory only.

This phase introduces a container sandbox supervisor (forge-supervisor) — a lightweight static binary that runs as PID 1 inside Forge-built containers, providing kernel-level network isolation via iptables, DNS filtering, seccomp syscall filtering, and a policy engine.

Depends on: Phase 1 (C-1 through C-3 must land first — the supervisor's DomainMatcher inherits the same SSRF bypass issues if unfixed)
Reference: PLAN-04-CONTAINER-SANDBOX-SUPERVISOR.md

Bypass Vectors Addressed

Vector Current Mitigation After Supervisor
Go HTTP client EgressEnforcer RoundTripper EgressEnforcer + transparent proxy
Subprocess HTTP HTTP_PROXY env var (voluntary) iptables REDIRECT to transparent proxy
Raw TCP socket None iptables REDIRECT + SNI/Host extraction
DNS exfiltration None DNS proxy with domain filtering
Dangerous syscalls None Seccomp profile filtering

What's NOT Covered by Phases 1-4

All items below are new work not addressed by the existing security phases:

  1. forge-supervisor Go module — New PID 1 binary for containers (~2,100 lines)
  2. Transparent TCP proxy — iptables uid-owner REDIRECT + SO_ORIGINAL_DST + TLS SNI extraction
  3. DNS proxy — Intercept UDP 53, filter by allowed domains, block exfiltration
  4. Seccomp profile generation — Block dangerous syscalls (mount, ptrace, unshare, bpf, etc.)
  5. Policy engine — Phase 1: DomainMatcher reuse; Phase 2: OPA/Rego for custom policies
  6. Privilege drop — setuid/setgid to UID 1000, prctl(PR_SET_NO_NEW_PRIVS), drop all capabilities
  7. Build pipeline stages — RegoStage (egress_allowlist → policy.rego), SeccompStage (skill reqs → seccomp.json)
  8. Dockerfile template — Supervisor ENTRYPOINT, multi-stage with supervisor image
  9. K8s deployment template — SecurityContext (readOnlyRootFilesystem, capability restrictions, proper probes, emptyDir volumes)
  10. forge.yaml supervisor config — New supervisor: section with opt-in enablement
  11. Health/denial/metrics endpoints/healthz, /denials, /metrics on port 15000
  12. NDJSON audit logging — Structured denial events on stdout

Also Covers Deferred Items from FORGE-SECURITY-UPDATE.md

# Issue Why Covered Here
M-10 TOCTOU/symlink swap in file operations Supervisor's read-only rootfs + seccomp (blocking mount/unshare) reduces TOCTOU attack surface
M-12 OTLP endpoint URL sanitization Supervisor's egress enforcement covers OTLP endpoints going to unintended destinations

Expected behavior

After Phase 5, Forge containers have 6 security layers (up from 1):

  1. K8s NetworkPolicy (existing)
  2. K8s SecurityContext (readOnlyRootFilesystem, capability drop)
  3. Seccomp syscall filter
  4. Transparent TCP proxy (iptables REDIRECT + domain allowlist + SNI extraction)
  5. DNS proxy (domain-filtered DNS resolution)
  6. App-level EgressEnforcer + EgressProxy (existing)

All egress from the agent process (UID 1000) is redirected through the supervisor's transparent proxy. Connections to domains not in the allowlist are RST'd and logged. DNS queries for non-allowed domains are refused. Dangerous syscalls are blocked by seccomp.

Actual behavior

Only 1 real layer (EgressEnforcer, in-process Go only). Subprocesses, raw TCP, and DNS are unprotected.

Tasks

Supervisor Phase 1: MVP (Weeks 5-6)

Transparent TCP proxy with domain allowlist enforcement inside container.

  • Create forge-supervisor/ Go module (go.mod, main.go)
  • Implement iptables setup (OUTPUT REDIRECT for UID 1000) — supervisor/iptables.go (~80 lines)
  • Implement transparent TCP proxy with SO_ORIGINAL_DSTsupervisor/proxy.go (~200 lines)
  • Implement TLS SNI extraction (peek ClientHello, no termination) — supervisor/sni.go (~100 lines)
  • Implement HTTP Host header extraction (plain HTTP) — supervisor/http.go (~40 lines)
  • Port or import DomainMatcher from forge-core — supervisor/matcher.go
  • Load egress_allowlist.json, build matcher — supervisor/policy.go (~60 lines)
  • Implement privilege drop (setuid/setgid + no_new_privs) — supervisor/privdrop.go (~50 lines)
  • Implement process exec + signal forwarding (PID 1 duties) — supervisor/exec.go (~80 lines)
  • Health endpoint (/healthz, /denials) — supervisor/health.go (~60 lines)
  • NDJSON audit logging (stdout) — supervisor/audit.go (~40 lines)
  • Dockerfile for supervisor (static binary, scratch base) — supervisor/Dockerfile
  • Integration test: build container, verify allowed/denied — supervisor/integration_test.go

Supervisor Phase 2: Build Pipeline Integration (Week 7)

forge build produces supervisor-enabled containers when configured.

  • Add supervisor section to ForgeConfig types — forge-core/types/config.go
  • Add supervisor config to JSON schema + validation — forge-core/validate/schema.go
  • Create RegoStage (egress_allowlist → policy.rego) — forge-cli/build/rego_stage.go
  • Create SeccompStage (skill reqs → seccomp.json) — forge-cli/build/seccomp_stage.go
  • Update DockerfileStage template for supervisor — forge-cli/templates/Dockerfile.tmpl
  • Update K8sStage templates (SecurityContext, probes, volumes, resources) — forge-cli/templates/deployment.yaml.tmpl
  • Register new stages in pipeline — forge-cli/build/pipeline.go
  • Update forge init TUI to offer supervisor option
  • Tests for new stages

Supervisor Phase 3: DNS Proxy + Seccomp (Week 8)

Close DNS exfiltration vector. Apply syscall filtering.

  • DNS proxy (intercept UDP 53, filter by allowed domains) — supervisor/dns.go (~150 lines)
  • Apply seccomp profile to child process — supervisor/seccomp.go (~80 lines)
  • Read seccomp.json from /etc/forge/seccomp.json
  • DNS integration tests
  • Seccomp integration tests
  • End-to-end test: full supervisor with DNS + seccomp + proxy

Supervisor Phase 4: OPA Policy Engine (Weeks 9-10)

Support custom Rego policies beyond simple domain matching.

  • Integrate OPA Go library — supervisor/opa.go
  • Policy loading: auto-detect Rego vs JSON allowlist — supervisor/policy.go
  • Extend policy input with port, protocol, binary path
  • /proc/PID/exe readlink for binary identity — supervisor/procfs.go
  • Document Rego policy authoring
  • Tests for OPA integration

Deferred Items Now Covered

  • M-10: TOCTOU/symlink swap — mitigated by read-only rootfs + seccomp blocking mount/unshare
  • M-12: OTLP endpoint sanitization — mitigated by supervisor egress enforcement on all outbound connections

Architecture

Container start
    │
    ▼
forge-supervisor (PID 1, UID 0)
    ├─ 1. Load policy (/etc/forge/egress_allowlist.json)
    ├─ 2. Setup iptables (REDIRECT UID 1000 TCP → :15001, UDP 53 → :15002)
    ├─ 3. Start transparent TCP proxy (:15001) — SNI/Host extraction + policy eval
    ├─ 4. Start DNS proxy (:15002) — domain-filtered resolution
    ├─ 5. Start health endpoint (:15000)
    ├─ 6. Apply seccomp profile to child
    ├─ 7. Drop privileges (setuid 1000, drop all caps, PR_SET_NO_NEW_PRIVS)
    └─ 8. Fork+exec agent (UID 1000)

Estimated Effort

Sub-phase New Code Modified Code Timeline
Supervisor MVP ~950 lines ~0 Weeks 5-6
Build pipeline ~220 lines ~365 lines Week 7
DNS + Seccomp ~560 lines ~0 Week 8
OPA engine ~370 lines ~70 lines Weeks 9-10
Total ~2,100 lines ~435 lines 6 weeks

Risks

Risk Mitigation
iptables unavailable in minimal images Use nftables fallback; document base image requirements
CAP_NET_ADMIN denied by cluster policy Graceful fallback to HTTP_PROXY mode with warning
Supervisor crash kills agent (PID 1) Fork model: supervisor stays PID 1, agent is child
SNI not present (raw TCP without TLS) Fall back to reverse DNS → resolved IPs of allowed domains → deny (fail closed)
Performance overhead Simple relay, no TLS termination. <1ms per connection.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingenhancementNew feature or requestsecuritySecurity vulnerability fixes

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions