Outer hardening: AppArmor profile, pids limit, egress allowlist proxy

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.
This commit is contained in:
host committed 2026-10-26 07:42:14 +08:00
1 parent 4f7c7eb13e
commit 1e4623c61c
8 files changed
+399 -18

No files matched your search

+16
View File
@@ -491,6 +491,22 @@ interrupted 且终态任务 cancel 返回 400、心跳后 sweep 不扫刚心跳
密钥值),绝不复制 thread_history。注意:内层 codex 沙箱仍
是 danger-full-access(主机内核禁 user namespace,bwrap 不可
用),隔离边界是容器 + 挂载 + 网络三层,不是内层沙箱。
- 外层加固(内层保持 danger-full-access 时的补偿层):
`--docker-security-opt`(可重复,如 `apparmor=docker-codex`,
见 `deploy/apparmor/docker-codex`:在 Docker 默认 profile
基础上禁 raw 网络、ptrace、mount 与危险 capability;注意
它不含 `userns`,即不启用 bwrap 内层沙箱路线);
`--docker-pids-limit`(默认 512,防 fork 炸弹);
`--egress-proxy-port`(0 = 不启用;生产配 8888):启用后
Docker 任务容器的 HTTP(S) 出口全部走宿主上的 Tinyproxy
(见 `deploy/egress/`:FilterDefaultDeny 白名单,只放行
relay、github、pypi、npm 域名,其余 403;网关查不到时
任务直接 failed,绝不无代理运行)。seccomp 现状诚实说明:
Docker 默认 seccomp profile 已在所有任务容器上生效(这是
不弱的基线),本轮没有新写一个更严的 seccomp allowlist——
从记忆里拼一个 allowlist 极易漏掉 codex 工具链要的系统调用
而静默破坏任务,正确的更严 profile 需要先对 codex 做系统
调用画像,那是下一项工作,不是本轮假装做到的。
- 网络白名单的诚实边界:docker run 默认 bridge 网络,本执行
器不做 egress 白名单——真正的“只放行模型 API”需要宿主防火
墙或代理规则,那是部署项,不是本轮代码假装做到的,不要宣称