UltiCode Docker 沙箱:资源隔离与基础设施错误分类
工程笔记
发布 / DavidHLPL
证据状态:本文依据 UltiCode 当前固定提交的 Docker 执行器、错误分类器和沙箱测试整理。它说明实现了明确的隔离与分类边界,不等于完成了完整的容器逃逸审计或生产安全证明。
运行用户代码的难点不只是启动一个进程,而是让代码无法轻易影响宿主资源,同时让平台知道“是用户代码失败,还是 Docker/宿主机失败”。如果两类故障都返回同一个 Runtime Error,系统既无法正确提示用户,也无法决定是否应该重试基础设施。
D-form 执行路径
SandboxExecutorImpl 的 D-form 路径会:
- 解析语言 profile 和有效资源限制;
- 创建临时 job workspace;
- 写入只读
input.json和工作区内容; - 通过生命周期 runner 启动 Docker;
- 解析结果 envelope;
- 在
finally中尽力清理 job 目录。
安全和资源参数是显式的
当前 Docker 命令包含:
--network none
--cap-drop ALL
--read-only
--user 1000:1000
--security-opt no-new-privileges
--security-opt seccomp=<resolved profile>
--memory <effective limit>
--cpus <effective limit>
--pids-limit <effective limit>
--ulimit nofile=128:128
--tmpfs /tmp:rw,exec,size=64m
<workspace>:/workspace:ro
--rm
这组参数形成了网络、Linux capability、文件系统、用户身份、进程数、文件描述符和 CPU/内存的多层边界。seccomp profile 的解析失败不会静默删除过滤器,而是保留错误,使 Docker 返回明确的配置失败。
这种行为是 fail-closed:配置不完整时宁愿拒绝执行,也不把“暂时能跑”当成隔离仍然有效。
分类比一个 FAILED 更重要
SandboxOutcomeClassifier 将结果区分为:
| 现象 | 分类 |
|---|---|
| 进程启动本身失败 | launch failure |
| exit code 137 | out of memory |
| exit code 125 | OCI/daemon error |
Cannot fork、pids、RLIMIT_NPROC |
fork limit |
| 编译器拒绝代码 | compile error |
| 其余非零退出 | runtime error |
因此 Docker daemon 没有创建容器、seccomp 路径错误或宿主 pids 资源不足时,不会被伪装成用户程序的普通运行错误。错误分类还应与监控、重试和用户提示保持一致,否则分类器只是漂亮的枚举。
安全边界仍需诚实描述
这些参数是重要的防护层,但源码阅读不能推出“彻底阻断逃逸”。真实安全结论还需要在目标 Docker、内核、镜像、Rootless/remote daemon 和权限配置上做专门测试与审计。本文只确认实现中存在 fail-closed 参数和基础设施错误分类。
对 LLM/Agent 系统的迁移
模型生成代码、插件和工具脚本应当被视作不可信执行输入。除容器隔离外,还需要显式的文件、网络、进程、时间和输出大小上限;同时把 provider/权限拒绝和模型生成错误分开。这样 Agent 才能知道是修改输入、请求授权,还是重试基础设施。
最小验证路径
阅读 SandboxExecutorImpl.java、SandboxOutcomeClassifier.java,再看 SandboxExecutorImplForkDetectionTest 和 SandboxExecutorImplSeccompResolutionTest。
来源与关联文档
研究记录
- 原始文档: src/content/note/zh-cn/ulticode-docker-sandbox.md
- 来源版本: f49953fb0d4bd52a39bf81167b0c4b7dd7d78a64
- 证据状态: 暂定