Inner codex sandbox stays danger-full-access by choice (bwrap path is a separate decision); this hardens everything around it: - DockerExecutor: --security-opt plumbing (repeatable, dedups no-new-privileges), --pids-limit (CLI default 512, fork-bomb guard), per-call extra_env (-e NAME=VALUE, non-secrets only, e.g. proxy URLs; the model key still goes by -e NAME only). - Worker: --egress-proxy-port (0 = off). When on, Docker task containers get HTTP(S)_PROXY pointing at the host filtering proxy via their network gateway IP (tenant network gateway or default bridge gateway). Gateway unresolvable => the attempt fails instead of running unproxied (fail closed, same rule as tenant network creation failure). - AppArmor profile deploy/apparmor/docker-codex: docker-default base + deny network raw/packet, deny ptrace, deny mount/ umount/pivot_root, deny dangerous capabilities, deny writes to /proc/sys, /sys, /proc/keys. Deliberately NO userns rule (that would enable the bwrap path, not this one). File rules mirror docker-default (broad + sensitive denies); a strict file allowlist would need profiling codex's file access set and is not claimed here. - Egress proxy deploy/egress/: Tinyproxy with FilterDefaultDeny + anchored domain allowlist (relay, github, pypi, npm). Anchoring matters: an unanchored 'github.com' regex would also match 'evilgithub.com'. - Seccomp honesty (README): Docker default seccomp already enforces on all task containers; no new stricter allowlist is claimed in this change (building one from memory risks silently breaking the toolchain; it needs syscall profiling first). Tests: 3 outer-hardening tests (security-opt dedup, pids limit, proxy env without leaking the key value, gateway parse/fail, proxy fail-closed). Full suites pass.
53 lines
2.0 KiB
Plaintext
53 lines
2.0 KiB
Plaintext
# Tinyproxy egress allowlist for codex task containers.
|
|
#
|
|
# Only domains in the Filter file are reachable. Everything else
|
|
# gets 403 from the proxy (fail closed: a task that needs a new
|
|
# domain fails loudly instead of silently bypassing the list).
|
|
#
|
|
# Domains (one per line in tinyproxy-filter):
|
|
# - xcmapi.org: model relay (the only model endpoint tasks need)
|
|
# - github.com, *.githubusercontent.com: git clone/fetch
|
|
# - pypi.org, files.pythonhosted.org: pip install
|
|
# - registry.npmjs.org: npm install
|
|
# Add a domain here + reload tinyproxy when a legitimate task need
|
|
# appears; never add a wildcard like * (that voids the list).
|
|
#
|
|
# Install on host:
|
|
# apt-get install -y tinyproxy
|
|
# cp tinyproxy.conf /etc/tinyproxy/tinyproxy.conf
|
|
# cp tinyproxy-filter /etc/tinyproxy/tinyproxy-filter
|
|
# systemctl enable tinyproxy && systemctl restart tinyproxy
|
|
# Worker: --egress-proxy-port 8888 (containers reach the proxy via
|
|
# their network gateway IP, e.g. http://172.18.0.1:8888).
|
|
# Firewall (host, restricts WHO can use the proxy to containers):
|
|
# iptables -A INPUT -p tcp --dport 8888 -s 172.16.0.0/12 -j ACCEPT
|
|
# iptables -A INPUT -p tcp --dport 8888 -s 127.0.0.1 -j ACCEPT
|
|
# iptables -A INPUT -p tcp --dport 8888 -j DROP
|
|
# (Docker bridge subnets live in 172.16.0.0/12; the host LAN is
|
|
# NOT in that range, so LAN hosts cannot use the proxy.)
|
|
|
|
User tinyproxy
|
|
Group tinyproxy
|
|
Port 8888
|
|
Listen 0.0.0.0
|
|
Timeout 600
|
|
LogLevel Info
|
|
PidFile "/run/tinyproxy/tinyproxy.pid"
|
|
MaxClients 100
|
|
MinSpareServers 5
|
|
MaxSpareServers 20
|
|
StartServers 10
|
|
DefaultErrorFile "/usr/share/tinyproxy/default.html"
|
|
StatFile "/usr/share/tinyproxy/stats.html"
|
|
# CONNECT is required for HTTPS (relay + git are HTTPS).
|
|
ConnectPort 443
|
|
ConnectPort 80
|
|
# Allowlist: deny everything not in the filter file.
|
|
FilterDefaultDeny Yes
|
|
Filter "/etc/tinyproxy/tinyproxy-filter"
|
|
# Upstream: direct (no parent proxy). The host's own egress is
|
|
# unrestricted; only container traffic goes through this proxy.
|
|
Upstream none
|
|
# Do not forward proxy headers that leak container info.
|
|
DisableViaHeader Yes
|