2-3 权限与沙箱设计
权限层解决什么问题
工具给了 agent 能力,权限层决定这些能力在什么范围内可以行使。
两者的区别:工具是"agent 能做什么",权限是"agent 被允许做什么"。一个没有权限约束的 agent,能力越强,一次判断失误的破坏半径就越大。
核心思路来自安全工程的老原则:最小权限(Principle of Least Privilege)——只给完成当前任务必须的权限,其余全收。
Allow-list vs Deny-list
两种策略,适用场景不同:
Allow-list(白名单):明确列出允许的操作,列表之外全部拒绝。
{
"allow": [
"Read",
"Write:src/**",
"Bash:npm test",
"Bash:npm run lint"
]
}
- 优点:边界清晰,新增工具默认拒绝,不会因为"忘了禁某个操作"出问题
- 缺点:维护成本高,任务稍微扩展就要改配置
- 适合:生产环境、无人值守 loop、高风险操作场景
Deny-list(黑名单):明确列出禁止的操作,其余全部放行。
{
"deny": [
"Bash:rm -rf*",
"Bash:git push*",
"Write:/etc/**",
"Write:~/.ssh/**"
]
}
- 优点:灵活,agent 探索空间大,开发调试阶段摩擦小
- 缺点:容易漏,你永远不知道下一个危险操作是什么
- 适合:开发阶段、本地沙箱、人在旁边盯着的场景
实践原则:开发阶段用 deny-list 跑通流程,上生产前切成 allow-list。两者不是对立的,可以叠加——先过 allow-list,再过 deny-list,都通过才放行。
多 Agent 场景下的权限边界
单 agent 的权限设计相对简单。一旦引入 orchestrator + sub-agent 结构,权限边界就需要重新设计。
核心问题:sub-agent 应该继承 orchestrator 的权限,还是独立配置?
答案是独立配置,且应该更窄。
原因:orchestrator 的职责是拆任务、分配、汇总——它不需要写文件、跑命令。sub-agent 只负责执行具体工作单元,只需要完成该工作单元所需的最小权限。
orchestrator
├── 权限:Read、调用 sub-agent
├── sub-agent A(代码生成)
│ └── 权限:Read:src/**, Write:src/**
└── sub-agent B(测试执行)
└── 权限:Read:src/**, Bash:npm test
orchestrator 没有写权限,写文件这件事只发生在 sub-agent 层。每一层只拿它那一层需要的权限。
另一个关键点:sub-agent 不应该能提升自己的权限。如果模型可以通过工具调用修改权限配置文件,整个权限体系就形同虚设。权限配置本身应该在 agent 的写权限范围之外。
沙箱设计
权限是逻辑边界,沙箱是物理边界——从操作系统层面隔离 agent 能触达的资源。
为什么需要沙箱:即使权限配置正确,agent 运行的进程仍然继承了当前用户的所有系统权限。一个被注入恶意指令的 agent(prompt injection),可能绕过逻辑权限直接操作文件系统。
常见沙箱手段:
Docker 容器是最实用的方案——把 agent 限制在容器里,挂载只读或受限目录,网络访问按需开放:
docker run --rm \
-v $(pwd)/workspace:/workspace \ # 只挂工作目录
-v $(pwd)/CLAUDE.md:/CLAUDE.md:ro \ # 配置只读
--network none \ # 默认断网,按需开
--memory 2g \
my-agent-image
文件系统层面,至少要做到:
- agent 的工作目录和系统目录物理隔离
- git 仓库的
.git/config、SSH 密钥目录、环境变量文件在挂载范围之外
可观测性:怎么知道 agent 在干什么
权限和沙箱是事前约束,可观测性是事后还原。两者都不能少。
最低要求的三条日志:
每次工具调用记录入参和出参:
{"ts": "2025-05-10T14:23:01Z", "tool": "Write", "input": {"path": "src/utils.py"}, "status": "allowed"}
{"ts": "2025-05-10T14:23:05Z", "tool": "Bash", "input": {"command": "rm -rf tmp"}, "status": "blocked", "hook": "pre_tool_use"}
三个关键问题,日志必须能回答:
- agent 做了哪些操作,顺序是什么
- 哪些操作被拦截了,原因是什么
- 哪一步之后结果开始偏离预期
没有这三条,loop 出了问题只能重跑,无法 debug。
实践建议:可观测性不要等出了问题再加。在 harness 搭起来的时候,日志结构就应该定好——入参、出参、时间戳、是否被拦截、拦截原因,每一条工具调用都要有。
本节核心一句话
权限是逻辑边界,沙箱是物理边界,可观测性是事后复盘的底——三者缺一,agent 跑起来就是在赌运气。