feat(executor): worker OS 级沙箱 + 资源限额(sandbox-exec / ulimit)[tsk_oTFzOMeAU20D]
进程隔离之上再加一层 OS 级硬隔离,默认关闭,便于先在一个项目灰度:
- 新增 src/executor/sandbox.ts:
- macOS sandbox-exec 写围栏 profile(allow default 基线 + deny file-write* 收口
+ 逐条放行 worktree/主仓.git/node_modules/runs/transcripts/tmp/工具缓存)。
不 deny default 全围栏——合法 git/npm/go/node 需读海量系统路径,写才是破坏向量。
- ulimit 资源上限(CPU 时间/虚拟内存/打开文件数/单文件大小),经 sh -c 下发,
exec 链保证 pid 不变(daemon 记录的 workerPid 仍是真 worker)。
- 全部由 env 解析(MAESTRO_SANDBOX*),网络默认放行(SDK 连 Anthropic)。
- orchestrator.defaultSpawnWorker 接入:开启时把命令包成
sh -c 'ulimit…; exec sandbox-exec -f profile <原命令>';关闭时行为零变化。
startOrchestrator 启动时打印生效策略,每个 run 的 profile 落 runs/<runId>/sandbox.sb。
- README 增补环境变量与「worker 沙箱」说明。
- 新增 test/sandbox.test.ts(18 例:config 解析 / profile 生成 / ulimit / 包装 / 预建目录)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -102,3 +102,21 @@ exec_review accept 时自动 `--no-ff` 合并:若 defaultBranch 在主检出
|
||||
| `MAESTRO_MODEL_REVIEW_EASY` | reviewer Easy 模型覆盖 | `claude-sonnet-4-6` |
|
||||
| `MAESTRO_MODEL_REVIEW_MEDIUM` | reviewer Medium 模型覆盖 | `claude-opus-4-8` |
|
||||
| `MAESTRO_MODEL_REVIEW_HARD` | reviewer Hard 模型覆盖 | `claude-opus-4-8` |
|
||||
| `MAESTRO_SANDBOX` | worker OS 级沙箱总开关(`on`/`off`) | 关闭 |
|
||||
| `MAESTRO_SANDBOX_PROFILE` | 是否套 `sandbox-exec`(仅 macOS;`off` 则仅留 ulimit) | 开启 |
|
||||
| `MAESTRO_SANDBOX_NET` | 是否放行网络(SDK 需连 Anthropic;`off` 隔离) | 放行 |
|
||||
| `MAESTRO_SANDBOX_CPU` | CPU 时间上限(秒),`0`=不限 | `0` |
|
||||
| `MAESTRO_SANDBOX_MEM` | 虚拟内存上限(MB),`0`=不限(macOS 对 `-v` 支持不稳定) | `0` |
|
||||
| `MAESTRO_SANDBOX_NOFILE` | 打开文件描述符上限 | `4096` |
|
||||
| `MAESTRO_SANDBOX_FSIZE` | 单文件大小上限(MB),`0`=不限 | `0` |
|
||||
| `MAESTRO_SANDBOX_WRITE_EXTRA` | 额外放行写的绝对路径(冒号分隔) | 空 |
|
||||
|
||||
## worker 沙箱(OS 级硬隔离,可选)
|
||||
|
||||
进程隔离(独立 worker 进程 + env 收敛,密钥/DB/API 与 daemon 隔开)之上,再加一层 OS 级硬隔离,**默认关闭**,开启后对每个 worker 生效:
|
||||
|
||||
- **文件写围栏**(macOS `sandbox-exec`):基线 `allow default`(放行读 / exec / 网络 / mach——keychain 鉴权所需),仅对**写**收口,逐条放行 worker 必需的写目标:任务 worktree、主仓 `.git`/`node_modules`、`runs/<runId>`、`transcripts`、`$TMPDIR` 与常用工具缓存(`~/.npm`、`~/Library/Caches`、`GOCACHE` 等)。越界写(改系统、污染其它仓库、动 maestro 自身 DB)被内核拒绝。为什么不 `deny default` 全围栏:合法的 git/npm/go/node 需读海量系统/缓存路径,全围栏极易误伤——真正的破坏向量是「写」。
|
||||
- **资源上限**(`ulimit`):CPU 时间 / 虚拟内存 / 打开文件数 / 单文件大小,防失控 agent 拖垮机器;超限由内核杀进程,daemon 的 reaper 据此回收并重试/转 `needs_attention`。
|
||||
- 生效的策略会在 daemon 启动日志打一行;每个 run 的实际 profile 落在 `runs/<runId>/sandbox.sb`(清晰记录)。
|
||||
|
||||
**先在一个项目灰度**:开 `MAESTRO_SANDBOX=on` 后跑一轮含 `npm/go/git` 的真实任务,确认能跑通、越界写被拒、超限被杀且日志清晰,再逐步铺开。个别项目若有特殊写路径,用 `MAESTRO_SANDBOX_WRITE_EXTRA` 放行。
|
||||
|
||||
Reference in New Issue
Block a user