OpenClaw 里工具被拦住了:沙箱、工具策略、elevated 三者的边界怎么分

2026-08-17

在 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),而且网关进程本身永远留在宿主机上,启用后移进沙箱的只是工具执行。

设置取值默认
模式 Modeagents.defaults.sandbox.modeoff / non-main / alloff
作用域 Scopeagents.defaults.sandbox.scopeagent / session / sharedagent
后端 Backendagents.defaults.sandbox.backenddocker / podman / ssh / openshelldocker

三个值里最容易出意外的是 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: truecapDrop: ["ALL"]、镜像 openclaw-sandbox:bookworm-slim,容器还带 init 进程和 no-new-privileges。这几条默认值连在一起有个直接后果,文档也提醒了:在一次对话回合里临时装包大概率会失败——没网、根文件系统只读、镜像用的还是非 root 用户。要装东西应该走自定义镜像,或者用 setupCommand(它只在容器创建后跑一次,不是每次执行都跑)并配齐相应权限。

工作区访问与 bind 挂载:两个独立的开关

workspaceAccess 控制沙箱能看到什么:

取值行为
none(默认)工具看到的是 ~/.openclaw/sandboxes 下的隔离沙箱工作区
ro把 agent 工作区以只读方式挂到 /agentwrite/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 被允许,那么把 writeeditapply_patch 全 deny 掉,并不会让 shell 命令变成只读的。
  • /exec 只为被授权的发送者调整会话默认值,它本身不授予工具访问权。

想做只读 agent,文档的建议是:除了禁掉会改文件的工具,还要一并 deny group:runtime,除非另有沙箱文件系统策略或宿主侧边界来兜住只读约束。这里的 group: 是策略里的批量简写,例如 group:runtime 展开为 execprocesscode_executionbashexec 的别名),group:fs 展开为 readwriteeditapply_patch,还有 group:webgroup:uigroup:memorygroup:plugins 等若干组。

{
  tools: {
    sandbox: {
      tools: {
        allow: ["group:runtime", "group:fs", "group:sessions", "group:memory"],
      },
    },
  },
}

插件与 MCP 工具在这里有一道额外的闸。文档写得很清楚:原生插件仍然与网关同进程、共享它的信任边界;被沙箱的会话要用插件工具或 MCP 工具,必须普通工具策略和 tools.sandbox.tools 两边都放行。如果配了 mcp.servers,但沙箱回合里只看得到内置工具,文档给的做法是把 bundle-mcpgroup:plugins 或带服务器前缀的工具名/通配(如 outlook__send_mailoutlook__*)加进 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 logsagents/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 官方仓库(github.com/openclaw/openclawdocs/ 下的官方文档整理,核对日 2026-08-17。 我们没有安装或运行过 OpenClaw,因此不涉及界面外观、操作手感与实测耗时的任何描述; 文中的默认值、命令与配置项均为文档口径,不构成对实际运行结果的保证。 该项目迭代很快,请以仓库最新内容为准。接入即时通讯平台前,请自行确认所在平台的规则与合规要求。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。