OpenClaw 里工具被拦住了:沙箱、工具策略、elevated 三者的边界怎么分
在 OpenClaw 里碰到「这个工具怎么调不起来」的时候,最容易走的弯路是:看到提示里有 sandbox 字样,就直接去关沙箱;关了发现还是不行,再去翻 elevated;折腾一圈才发现拦住它的其实是一条 tools.deny。
官方文档专门为这件事写了一页 sandbox-vs-tool-policy-vs-elevated,开篇就把三者摆开:沙箱决定工具在哪儿跑,工具策略决定哪些工具可用,elevated 只是 exec 的一个逃生口。这三层管的是不同的事,配置键也在三个不同的位置。
这篇按文档把三者的分工、默认值、常见误判和官方自己声明的边界过一遍。需要说明的是,本文只依据 OpenClaw 官方文档的 /gateway/sandbox-vs-tool-policy-vs-elevated 与 /gateway/sandboxing 两页,我们没有安装运行过它,不含任何实测结论,也不对隔离效果做任何承诺。
三者分工:一张表先对上号
| 机制 | 管什么 | 主要配置键 |
|---|---|---|
| Sandbox 沙箱 | 工具在哪里执行(沙箱后端还是宿主机) | agents.defaults.sandbox.* / agents.entries.*.sandbox.* |
| Tool policy 工具策略 | 哪些工具存在、可被调用 | tools.*、tools.sandbox.tools.*、agents.entries.*.tools.* |
| Elevated 提权 | 仅对 exec,在被沙箱时跳出沙箱执行 | tools.elevated.* / agents.entries.*.tools.elevated.* |
对号入座的顺序建议按文档给的调试工具来,而不是靠猜:
openclaw sandbox explain
openclaw sandbox explain --session agent:main:main
openclaw sandbox explain --agent work
openclaw sandbox explain --json
文档说这条命令会打印:生效的沙箱模式 / 作用域 / 工作区访问方式;当前会话是否处于沙箱中(main 与非 main 的区别);生效的沙箱工具 allow/deny,以及这条规则是来自 agent 级、全局还是默认值;还有 elevated 的各道闸门和对应的「改哪个键」路径。最后那一项尤其省事——它直接告诉你要动哪个配置键,不用一层层反推。
沙箱这一层:模式、作用域、后端各自独立
文档写得很明确:沙箱默认是关的(mode 默认 off),而且网关进程本身永远留在宿主机上,启用后移进沙箱的只是工具执行。
| 设置 | 键 | 取值 | 默认 |
|---|---|---|---|
| 模式 Mode | agents.defaults.sandbox.mode | off / non-main / all | off |
| 作用域 Scope | agents.defaults.sandbox.scope | agent / session / shared | agent |
| 后端 Backend | agents.defaults.sandbox.backend | docker / podman / ssh / openshell | docker |
三个值里最容易出意外的是 non-main。文档把它称为群组/频道场景下常见的「惊喜」:主会话的键固定是 agent:<agentId>:main(当 session.scope 为 "global" 时是 global),且不可配置;群组和频道会话用的是它们自己的键,所以永远算非 main,永远会被沙箱。如果你在群里发现工具行为和私聊不一样,先用 sandbox explain 看一眼当前会话键是不是主会话键。
作用域决定创建多少个容器/环境:agent 每个 agent 一个,session 每个会话一个,shared 是所有被沙箱的会话共用一个——注意 shared 之下,per-agent 的 docker/ssh/browser 覆盖项会被忽略。
后端方面,Docker 与 Podman 共用 agents.defaults.sandbox.docker 这组配置;SSH 后端的配置在 agents.defaults.sandbox.ssh;OpenShell 的配置在 plugins.entries.openshell.config。文档同时列了几条能力差异:浏览器沙箱只有 Docker 引擎支持,SSH 后端不支持,OpenShell 按文档表述是「尚未支持」;额外的宿主目录挂载只有 Docker 的 docker.binds,SSH 与 OpenShell 都不支持以挂载方式实现,要靠预置文件或工作区同步。想按后端选型再细看,可以对照 OpenClaw Docker 部署要点。
Docker 后端的默认姿态在文档里是明确写出来的:network: "none"(无出网)、readOnlyRoot: true、capDrop: ["ALL"]、镜像 openclaw-sandbox:bookworm-slim,容器还带 init 进程和 no-new-privileges。这几条默认值连在一起有个直接后果,文档也提醒了:在一次对话回合里临时装包大概率会失败——没网、根文件系统只读、镜像用的还是非 root 用户。要装东西应该走自定义镜像,或者用 setupCommand(它只在容器创建后跑一次,不是每次执行都跑)并配齐相应权限。
工作区访问与 bind 挂载:两个独立的开关
workspaceAccess 控制沙箱能看到什么:
| 取值 | 行为 |
|---|---|
none(默认) | 工具看到的是 ~/.openclaw/sandboxes 下的隔离沙箱工作区 |
ro | 把 agent 工作区以只读方式挂到 /agent,write/edit/apply_patch 随之失效 |
rw | 把 agent 工作区以读写方式挂到 /workspace |
容易踩的一点是:workspaceAccess 和 bind 挂载的模式互相独立。文档明说改 workspaceAccess 不会把某条额外 bind 从 ro 变成 rw,反之亦然。而 bind 本身,文档用的词是「刺穿(pierces)沙箱文件系统」——你挂什么,沙箱里就以你给的模式看到什么;模式省略时默认是读写,所以源码和密钥类目录文档建议显式写 :ro。
围绕 bind,文档列了几条默认阻断规则,值得原样记下来:
- 默认阻断的来源路径包括系统路径(
/etc、/proc、/sys、/dev、/root、/boot)、Docker socket 目录(/run、/var/run及其docker.sock变体),以及常见的家目录凭据根(~/.aws、~/.cargo、~/.config、~/.docker、~/.gnupg、~/.netrc、~/.npm、~/.ssh)。 - 校验会做两次:先对规范化后的源路径检查,再沿最深的已存在父目录解析一次重新检查,因此即便末端路径还不存在,符号链接父目录的逃逸也会失败关闭。
- 目标路径若遮蔽保留挂载点(
/workspace、/agent)默认被拦,需要dangerouslyAllowReservedContainerTargets: true才能覆盖。 - 工作区允许根之外的来源默认被拦,需要
dangerouslyAllowExternalBindSources: true。文档特别强调这个开关只放开「来源在工作区之外」这一项,不会关掉系统路径、凭据、Docker socket、符号链接父目录、保留目标这几类检查。 - 挂
/var/run/docker.sock这件事,文档的原话是它等于把宿主机控制权交给沙箱,只在明确知道自己在干什么时才做。 scope: "shared"会忽略 per-agent 的 binds,只有全局 binds 生效。
改完挂载别忘了重建沙箱:
openclaw sandbox recreate --agent research
工具策略这一层:几条规则决定谁被拦
工具策略本身还分好几层:基础的 tools.profile(以及 per-agent 的同名键)、按供应商的 tools.byProvider[provider].profile、全局与 per-agent 的 tools.allow/tools.deny、按供应商的 allow/deny,以及只在被沙箱时才生效的 tools.sandbox.tools.allow/deny。
文档给的几条判断规则很关键:
deny永远胜出。allow只要非空,其余一律视为被拦。- 工具策略是硬停:
/exec无法绕过一个被 deny 掉的exec工具。 - 工具策略只按名字过滤工具可用性,不检查
exec内部的副作用。也就是说,如果exec被允许,那么把write、edit、apply_patch全 deny 掉,并不会让 shell 命令变成只读的。 /exec只为被授权的发送者调整会话默认值,它本身不授予工具访问权。
想做只读 agent,文档的建议是:除了禁掉会改文件的工具,还要一并 deny group:runtime,除非另有沙箱文件系统策略或宿主侧边界来兜住只读约束。这里的 group: 是策略里的批量简写,例如 group:runtime 展开为 exec、process、code_execution(bash 是 exec 的别名),group:fs 展开为 read、write、edit、apply_patch,还有 group:web、group:ui、group:memory、group:plugins 等若干组。
{
tools: {
sandbox: {
tools: {
allow: ["group:runtime", "group:fs", "group:sessions", "group:memory"],
},
},
},
}
插件与 MCP 工具在这里有一道额外的闸。文档写得很清楚:原生插件仍然与网关同进程、共享它的信任边界;被沙箱的会话要用插件工具或 MCP 工具,必须普通工具策略和 tools.sandbox.tools 两边都放行。如果配了 mcp.servers,但沙箱回合里只看得到内置工具,文档给的做法是把 bundle-mcp、group:plugins 或带服务器前缀的工具名/通配(如 outlook__send_mail、outlook__*)加进 tools.sandbox.tools.alsoAllow,然后重启或重载网关再重新抓一次工具列表。插件这一层的执行模型可以再看 OpenClaw 插件体系。
排查时别忘了看日志:文档说工具策略移除工具、或沙箱工具策略拦下调用时,网关日志里会有 agents/tool-policy 审计条目,用 openclaw logs 能看到规则标签、配置键和受影响的工具名。日志与诊断开关怎么开,见 OpenClaw 网关诊断与日志。
Elevated:只放开 exec,别指望它别的
这一层最常被误解。文档的表述是:elevated 不授予任何额外工具,它只影响 exec。
- 被沙箱时,
/elevated on(或调用exec时带elevated: true)会让这次执行跑在沙箱之外,走配置的逃生路径(默认是gateway,当 exec 目标配置为node时是node);文档注明「审批仍可能适用」。 /elevated full用于在本会话跳过 exec 审批。- 如果本来就是直接在宿主机跑,elevated 实际上是空操作(但仍然受闸门约束)。
- elevated 不是按 skill 划范围的,也不会覆盖工具的 allow/deny。
- 它不提供从
host=auto而来的任意跨主机覆盖,仍然遵循正常的 exec 目标规则,只有在已配置或会话目标本就是node时才保留node。
两道闸门分别是:tools.elevated.enabled(可选加 per-agent 的同名键)和发送者白名单 tools.elevated.allowFrom.<provider>(同样可 per-agent)。还有一句要记牢:沙箱如果本来就是关的,tools.elevated 什么也不改变,因为 exec 本来就在宿主机上跑。
两类高频「沙箱牢笼」的对症改法
文档给了两个典型场景和对应的改法键:
提示「Tool X blocked by sandbox tool policy」,任选一条:把 agents.defaults.sandbox.mode 设为 off(或 per-agent 的 agents.entries.*.sandbox.mode=off);或把该工具从 tools.sandbox.tools.deny 里移出去;或把它加进 tools.sandbox.tools.allow。同时查 openclaw logs 的 agents/tool-policy 条目,它会记录当时的沙箱模式,以及是 allow 规则还是 deny 规则拦下的。
「我以为这是主会话,怎么被沙箱了」:在 non-main 模式下,群组/频道的会话键不是 main。改法是改用 sandbox explain 显示的主会话键,或者把模式切成 off。
另外几条排查命令也一并记下:openclaw sandbox list 看容器状态、镜像是否匹配、存活时长与空闲时间以及关联的会话/agent;openclaw sandbox explain 除了前面列的字段,还会显示 Docker 挂载、宿主工作区与运行时工作目录,其中 workspaceRoot 是配置的沙箱根,effectiveHostWorkspaceRoot 才是当前工作区实际所在位置;openclaw sandbox recreate 支持 --all、--session、--agent、--browser、--force。三者在整套网关架构里的位置,可以对照 OpenClaw 架构总览 一起看。
什么时候不适用,以及文档自己说了不保证的部分
先把最重要的一句原样转述:官方文档在沙箱页开头的提示框里写的是,这不是一个完美的安全边界,但当模型做了蠢事时,它实质性地限制了文件系统和进程访问。文档用的是「限制 blast radius(爆炸半径)」这个说法,不是「保证安全」。所以下面这些前提要一并接受:
- 网关进程本身不在沙箱里,原生插件与网关同进程、共享同一信任边界,控制面 RPC 也不进沙箱。
- bind 挂载按设计就是穿透沙箱文件系统的,它的安全性完全取决于你挂了什么、挂成什么模式。
- 工具策略不看
exec里面发生了什么,只按工具名过滤。 - 默认关闭的 secret egress proxy 只在网关回环可达。文档明确写道:沙箱 exec 在该特性启用时会拿到代理和 CA 环境变量,但容器回环到不了网关宿主,而且默认的
network: "none"干脆完全阻断出网;沙箱/容器侧的代理可达性尚未实现,本版本不要指望开了沙箱网络就能让密钥替换生效。 openclaw doctor目前只检查mcp.servers里由 OpenClaw 管理的服务器的这类配置形状。文档说,从捆绑插件清单或 Claude.mcp.json加载的 MCP 服务器走的是同一道沙箱闸,但这项诊断还没有枚举那些来源——它们的工具在沙箱回合里消失时,得自己按同样的方式加白名单。- Podman 不支持浏览器沙箱;SSH 后端不支持浏览器沙箱和
sandbox.docker.*设置;OpenShell 后端按文档表述浏览器沙箱「尚未支持」,sandbox.docker.binds也不适用于它。 - SSH 与 OpenShell 的
remote模式都是「远端为准」:首次使用后从本地播种一次,之后本地在 OpenClaw 之外做的改动在远端不可见,直到你执行openclaw sandbox recreate;OpenClaw 也不会自动把远端改动同步回本地。 - 从旧版本升级后首次使用,非共享的运行时和沙箱工作区会按「带工作区限定」的新身份创建,已有的非共享运行时不会被沿用。文档说这是一次有意为之的一次性重置。
最后一句实操建议:判断「为什么被拦」这件事,别从改配置开始,先跑一次 sandbox explain 拿到生效值和 fix-it 键,再对照本文那张三层分工表决定动哪一层。跳过这一步去改 mode,很多时候改的根本不是拦住你的那一层。
延伸阅读
- 从头读起:OpenClaw 架构总览:网关、Agent、渠道、节点、插件五层各管什么,出问题该往哪查
- 本专题共 40 篇,完整分组目录见专题页
- OpenClaw 插件体系拆解:能力注册、加载四层与所有权边界,附最小插件从写到装的完整路径
- OpenClaw 该用哪种方式装:脚本、npm、Bun、Docker 的前置条件与升级路径对照
本文依据 OpenClaw 官方仓库(github.com/openclaw/openclaw)docs/ 下的官方文档整理,核对日 2026-08-17。
我们没有安装或运行过 OpenClaw,因此不涉及界面外观、操作手感与实测耗时的任何描述;
文中的默认值、命令与配置项均为文档口径,不构成对实际运行结果的保证。
该项目迭代很快,请以仓库最新内容为准。接入即时通讯平台前,请自行确认所在平台的规则与合规要求。