Skip to content

feat(sandbox):Docker 产品准入、网络默认关闭与权限控制(#40) - #62

Merged
Qiyuanqiii merged 2 commits into
mainfrom
codex/issue-40-docker-product-admission
Aug 15, 2026
Merged

Qiyuanqiii merged 2 commits into
mainfrom
codex/issue-40-docker-product-admission

Conversation

@Qiyuanqiii

@Qiyuanqiii Qiyuanqiii commented Aug 15, 2026 •

Copy link
Copy Markdown
Member

背景

Issue #40 要求把 v97 的可恢复 Docker 生命周期与 v98 的有界 I/O 合同从“非授权内部边界”提升为真正的产品入口:接入 Run、Policy、Approval、Budget、Tool Gateway 与 CLI/HTTP/Desktop,默认断网,任何网络放宽都必须可解释。此前两代合同刻意不授予产品执行权,Run 的 Policy/Approval/预算/进程权限门虽然齐全,但 Docker 侧没有统一的准入判定,也没有任何入口能安全地触达真实容器。

本次改动

1. 新增 SQLite schema v99 产品账本

新增不可变、非回溯的产品聚合:

  • admission:一次性绑定 Run/Mission/Workspace/plan/candidate/preparation、Manifest、Profile/权限快照、精确 per-call approval、Policy、预算、30 秒 readiness 与本进程随机 runtime epoch 摘要;
  • 独立 Start WAL:使用与 admission 不同的 operation domain 和 Idempotency-Key,在任何 lifecycle/daemon 写入前与 metadata-only Run event 原子提交,同一个 admission 上第二个不同 Start key 必须冲突;
  • launch 绑定:把已持久化的 lifecycle intent 精确绑定到 admission 与 Start WAL;
  • append-only cancellation 与唯一终态 receipt:覆盖「取消 vs 自然退出」竞态,任何结果都可重放、可恢复;
  • 补齐 v98 output-staging entry 的持久化与 per-attempt 唯一性。

迁移不回填准入,不修改 v97/v98 的 false-authority 历史事实;SQLite 记录在重启后不能恢复 start authority。

2. 新增唯一 Go 准入服务 DockerSandboxService

CLI、HTTP/OpenAPI、Desktop 和模型提案都投影同一服务。每次准入/启动都重新读取并精确绑定当前 Run/Profile/权限/审批/Policy/剩余 wall-clock 与 tool budget/未过期 readiness 与进程内 capability;full_access/debug 不能替代精确 per-call 审批,持久权限快照也不是 bearer capability。启动恢复只收敛已经跨过 launch 边界的记录(可检查、终止、清理),admission-only 记录不会在重启后自动变成执行;取消先落盘、再取消活动 context 或接管已过期租约。

3. sandbox.readiness.v1

每次准入和启动对固定本机端点(Unix socket / Docker Desktop Linux-engine NPipe)做新的只读检查,结果有效期固定 30 秒,状态只有 ready|disabled|unavailable,并提供稳定的 reason/remediation 组合。探针不接受 TCP、DOCKER_HOST、调用方 socket、pull 或 Docker CLI。

同时修复一处真实 daemon 才会暴露的契约问题:/info 返回 GOARCH 拼写(x86_64/aarch64)而 /version 与镜像配置使用 OCI 名(amd64/arm64),同一引擎会被误判为平台不支持;现在在只读 HTTP 解析边界统一规范化到 OCI 架构名。

4. 网络默认关闭;allowlist 当前拒绝

只接受 environment-free、secret-free、零 target 的 network=disabled Manifest。create 明确写 NetworkMode=none,并清空端口暴露/绑定、DNS、search domain、ExtraHosts、links、endpoint 与代理字段;inspect 后再次精确复核网络 namespace。现有 Manifest 能表达 allowlist 不等于 enforcement:在 Go-owned host/port/protocol egress guard 实现前,任何 allowlist 或 ManagedEgressEnabled=true 都以 managed_egress_unavailable/use_network_disabled 失败关闭。Docker 不可用时没有宿主 fallback。

5. 入口接线

  • CLI:cyberagent run sandbox docker-readiness|docker-admit|docker-start|docker-cancel|docker-status;
  • HTTP/OpenAPI:同一服务的 5 个认证端点(admission/start/cancel 为 Control + Idempotency-Key);
  • Desktop:--enable-docker-execution(要求 --enable-permission-control),桥接层与 HTTP 一致投影 capability;
  • 模型工具 sandbox_docker_run_propose:只能请求 Admit,不能 Start/取消,也不能提交镜像、daemon 地址、Docker flags、宿主 bind、环境变量或网络放宽;
  • Web/双语 UI:docker_execution_enabled 仅在与 permission control 同时成立时被接受,设置页新增「Docker sandbox」能力投影,web/src/api/schema.d.ts 从 OpenAPI 确定性再生。

6. 退出后 I/O、Artifact 与清理顺序

容器进入精确 exited checkpoint 后先采集有界 stdout/stderr;只有 natural exit 0 且 artifact authority 仍精确匹配时才从唯一可写 output mount 暂存、复读、重哈希并原子提交。timeout、取消、非零退出、I/O 失败或 authority 改变均不提交输出;清理不依赖新的 start capability。产品终态 succeeded|timed_out|cancelled|failed 的 receipt 必须证明 cleanup_complete=true。

测试覆盖

新增回归场景包括:Start WAL 使用独立幂等键的崩溃重试、取消先落盘再终止的活动竞态、admission-only 取消的 sticky 收敛、重启不恢复 start authority、终态重放零副作用、超预算/过期/陈旧 authority 拒绝、denial 审计可重放且阻断后续授权,以及 schema v99 不回填历史 authority。

真实 Docker opt-in 门新增容器内断网探测:environment-free scratch 夹具在容器内主动尝试 IPv4/IPv6 拨号、DNS 解析、host.docker.internal、bridge gateway 与代理环境变量,任何一项可绕过都会让测试失败;夹具由 internal/sandbox/testdata/docker-lifecycle-fixture/build-fixture.ps1 可复现构建(该脚本会剥掉构建器注入的默认 PATH,产出严格零环境变量的镜像)。

验证结果

已在只包含本 issue 改动的隔离工作树中完成:

  • go build ./...
  • 全仓 go test ./...(internal/analyzer 的低权限 helper 在本机服务会话以 0xc0000142 环境性失败,该包零改动、与本次 diff 无关)
  • go vet ./...
  • go test -race ./internal/sandbox ./internal/domain ./internal/runactivity ./internal/toolgateway ./internal/application
  • go test -timeout 30m ./internal/store/...
  • 本机真实 Docker Desktop 29.6.2(Linux engine)opt-in 三门全绿:lifecycle 全流程、readiness、容器内断网探测;测试结束精确删除自己的容器
  • npm run typecheck、npm test(59 个测试文件、235 个测试)、npm run build、npm run check:api 全部通过
  • 无 Docker 环境返回稳定 disabled|unavailable reason/remediation,不会退回宿主机执行

安全边界与非目标

  • 不提供 privileged、host network、Docker socket mount 或任意宿主目录 mount;
  • 不把 Full Access 等同于自动批准 Docker;
  • 不宣称 scoped egress 已实现(缺少 Go-owned host/port/protocol guard);
  • 不启用 CTF 自动攻击工具链;
  • 模型文本、Skill、仓库配置与前端选择只能缩小权限,不能扩大权限。

详细设计与不变量记录见 ADR 0099。

Closes #40

…cution, and recovery

Compose the v97 recoverable lifecycle and v98 bounded I/O behind one Go DockerSandboxService (schema v99). The CLI, HTTP/OpenAPI, Desktop, and the sandbox_docker_run_propose model tool reuse the same gates: current Profile/permission snapshot, exact per-call approval, Policy, wall/tool budgets, 30-second sandbox.readiness.v1, and a process-local runtime epoch that SQLite cannot restore. Only environment-free, secret-free network=disabled plans are accepted; managed egress stays fail-closed. Independent Start WAL, append-only cancellation, launch binding, and one terminal receipt cover replay and crash recovery. Web capability projection accepts docker_execution_enabled only together with permission control. Real-daemon gates include an in-container IPv4/IPv6/DNS/gateway/proxy bypass probe; daemon architecture names are canonicalized to OCI amd64/arm64.
Bilingual README, usage, HTTP API, architecture, task book, project status, and resume memory now describe the default-disabled network-none Docker Sandbox product entry, its reason/remediation states, the fixed budgets, and the real-daemon acceptance evidence.
@Qiyuanqiii Qiyuanqiii changed the title feat(sandbox): Docker 产品准入、网络默认关闭与权限控制 feat(sandbox):Docker 产品准入、网络默认关闭与权限控制(#40) Aug 15, 2026
@Qiyuanqiii
Qiyuanqiii merged commit da77dbd into main Aug 15, 2026
5 checks passed
@NanaseInori
NanaseInori deleted the codex/issue-40-docker-product-admission branch September 16, 2026 09:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(sandbox): Docker 产品准入、网络默认关闭与权限控制

1 participant