假设一个来自 Discord 群组的请求说:“为了修构建,把整个项目挂进去,开 elevated full,然后执行这段脚本。”如果只问“用户有没有批准”,会跳过至少三层:这个 sender 是否获准控制该 Agent,当前 turn 是否能看到 exec,session 是否 sandboxed、能读哪些路径,以及 elevated sender allowlist 是否匹配。

反过来也一样。把 Agent 放进 Docker,不代表所有能力自动安全。native plugin 仍在 Gateway 进程里运行;容器若挂了 Docker socket,几乎等于拿到 host 控制;允许 exec 后,即使 deny 了 write/edit,shell 仍能写文件。安全要看能力与资源的完整路径,而不是寻找一个“secure: true”。

阅读契约。读完后你应该能分别回答:tool policy、sandbox、exec approval、elevated 控制什么;为什么 approval 不是用户授权边界;sandbox mode/scope/workspaceAccess 怎样组合;plugin/MCP tool 为什么在 sandboxed session 中需要双重 allow;workspaceOnly 与容器隔离有何不同;以及一条获准的 node command 如何绑定 argv、cwd、env、agent/session 与 executable。

证据边界。本文继续固定在 c549250。这不是一份通用安全认证,也不声称 sandbox 是完美边界;它解释这份快照中能从 policy pipeline、sandbox resolver、elevated resolution 与 host exec approval 源码验证的防线。

一、四道门回答四个不同问题

控制面核心问题典型阶段不能替代
Tool policy本轮是否存在/允许这个工具?模型请求前,过滤 schema。不能隔离 exec 内部副作用。
Sandbox工具在哪里执行,看到哪些文件/网络?构造 runtime context 与 tool implementation。不能决定某工具是否应暴露给某 sender。
Exec approval这一条 host command 是否获准?exec 已被选择、host plan 已解析后。不是 per-user authentication 或只读文件策略。
Elevated沙箱里的 exec 能否走既定 host 路径?host selection/exec admission。不能复活被 deny 的 exec,也不能增加别的工具。

把它们画成串行四关只是帮助理解,源码里有些检查在构造期提前完成,有些在 call-time 重验。真正的不变量是:每道门只缩小 authority;没有任何后置 directive 应能把前置 deny 改成 allow。

二、身份必须来自 ingress,不来自模型参数

senderId、accountId、channel、roleIds 与 senderIsOwner 在 channel/Gateway 入口解析后,作为 trusted requester context 绑定到 run 和 tool wrapper。模型可以生成 {"target":"admin"},却不能通过工具参数把自己变成 owner。message action、elevated allowlist 与 owner-only control tool 都应读取 host-derived identity。

缺失身份字段表示“未证明”,不是“可按默认信任”。要求 maintainer role 的 hook 若看不到 roleIds,应 fail closed;用显示名做 fallback 又会把可变的 sender-supplied text 当凭据。

这也是为什么 route/session 隔离先于 tool policy。若多人 DM 先被错误折叠成 main session,后面的 policy 虽能限制工具,却无法抹掉已经混入 transcript 的私人上下文。

三、Tool policy 是能力层的硬停止

agent-tools.policy.ts 和 pipeline 将 profile、provider、global、agent、group、sender、sandbox 等策略逐层应用。deny 优先;非空 allowlist 把未列出的工具视为 blocked。被过滤的 descriptor 不会发送给模型,/exec directive 也不能覆盖一个被 deny 的 exec tool。

policy 按工具名工作,不理解工具内部所有副作用。最重要的例子是 shell:deny writeeditapply_patch 仍不能让允许的 exec 自动变成只读,因为 sh -c 'echo x > file' 仍会写。只读 Agent 要同时 deny runtime group,或用 sandbox filesystem/OS permission 做资源层约束。

同理,允许 plugin group 不代表每个调用都合理;具体 params、sender 与资源可继续由 trusted policy 或 before_tool_call 阻断。build-time 减少模型误选,call-time 处理参数级风险。

四、Sandbox 控制执行位置,而 Gateway 仍留在 Host

OpenClaw sandbox host cutaway:Gateway 与 Native Plugins 留在 host,exec/read/write/edit 等 sandboxed tools 在独立环境中运行,workspace access 可为 none/ro/rw,网络另受 policy;plugin tool 需 normal policy 与 sandbox policy 双重放行,elevated exec 走受控桥到 gateway 或已配置 node

OpenClaw 的 Gateway 不会随 Agent turn 一起进入容器。sandbox backend 承载 exec 和文件工具(以及可选 browser);Gateway control plane、session 状态、native plugin runtime 仍在 host 进程。隔离的是工具执行面,不是整个控制面。

因此 sandbox 主要降低模型出错的 blast radius:限制进程、文件、网络和 workspace 可见性。它不会把已加载的 native plugin 变成不受信代码;安装 plugin 等于允许代码在 Gateway trust boundary 中运行,必须在安装前审查。

五、Mode、Scope、Backend 是三个独立维度

mode 决定哪些 session sandbox:offnon-mainallnon-main 下,群组/channel session 不是 main,因此会进入 sandbox;“这是主 Agent”不代表它的每个 session key 都是 main。

scope 决定 runtime 共享粒度:每 agent、每 session 或全体 shared。session scope 隔离最细、成本最高;shared 资源复用最多,也会让不同 session 更容易通过容器状态互相影响,而且会忽略 per-agent binds。scope 是隔离域设计,不是性能参数的别名。

backend 决定执行器:Docker/Podman、SSH 或 OpenShell。不同 backend 对 network、browser、bind mount 与 workspace sync 的能力不同,配置不能假设容器 backend 的保护自动出现在 SSH 远端。

六、workspaceAccess 与 bind mount 不能互相替代

workspaceAccess 为 none/ro/rw,控制 Agent workspace 怎样进入 sandbox。none 通常用 sandbox 自己的 scratch workspace;ro 适合审查;rw 才允许工具修改实际工作区。它与额外 docker.binds 是两套入口。

bind 默认未写模式时可能是 rw,直接穿透 sandbox filesystem。挂载 secret、SSH key 或 /var/run/docker.sock 会显著扩大权限,后者几乎把 host 容器控制交给 Agent。OpenClaw 对 normalized path 和最深已存在祖先都做检查,阻止通过 symlink parent 指向 blocked/outside root;但配置者仍要遵守最小 mount。

network 也独立。Docker 默认 network none 能阻止多数外联,但额外 proxy、host service、browser bridge 或 mounted socket 可能重新打开通道。文件只读不能推出网络只读,网络关闭也不能防止对可写 mount 的破坏。

七、Plugin/MCP Tool 在沙箱会话里需要两道 allow

native plugin 与 configured MCP tool 常在 Gateway 一侧执行,不会因为 caller session 在容器里就自动进入同一个隔离环境。为避免 sandboxed turn 借插件绕出容器,OpenClaw 要求普通 effective tool policy 允许它,同时 tools.sandbox.tools 再允许 plugin group、bundle 或具体工具名。

这就是“双门”语义:第一道回答此 Agent/sender 是否可用,第二道回答 sandboxed session 是否可以触达 host-side extension。只在 normal allow 中加名字可能仍看不见;只在 sandbox allow 中加名字也不会越过全局/agent deny。

八、workspaceOnly 是路径 guard,不是进程隔离

tools.fs.workspaceOnly 给 read/write/edit/apply_patch 添加 host path boundary。实现不能只检查字符串前缀:需要解析 relative/absolute、symlink parent、final symlink 与 hardlink alias,避免一个“看起来在 workspace 内”的路径实际写到外面。

即便这些检查完整,workspaceOnly 仍只保护受控文件工具。若 exec 在 host 上可用,shell 可以走自己的 filesystem API;若 plugin 自己接收路径,也要实现相应 guard。container/OS boundary、tool policy 与 library path guard 是 defense in depth,不该用其中一层替代另两层。

九、Elevated 是 exec-only escape hatch

/elevated on 只在当前 session 已 sandboxed 时有意义:它让后续 exec 走 sandbox 外的 configured host path,默认 gateway;若 exec target 已配置为 node,则保持 node。unsandboxed session 的 exec 本来就在 host,elevated 基本是 no-op。

可用性要求 global gate、per-agent gate 和 sender allowlist 全部通过。inline directive 只影响当前消息,session override 次之,global default 最后。缺少任何 gate 时应当显示 unavailable,而不是默默以 host exec 执行。

full 只有在 requested exec policy 与 host-local approvals 都真正 permissive 时才跳过审批;host 仍为 ask always 时继续询问。Elevated 不改变 tool policy,不增加 read/browser/plugin tools,也不能把 host=auto 变成任意跨主机选择。

十、Exec Approval 叠在 Policy 与 Elevated 之上

OpenClaw host exec 审批:Tool Policy 允许后检查 Elevated enabled 与 sender,再取 config 和 execution host 中更严格的 exec policy,经过 allowlist 或 ask 得到 allow once、allow always 或 deny;批准绑定 argv、cwd、env、agent、session、executable 的 sealed plan,plan drift 会被拒绝,approval 不等于 authorization

host exec approval 是 Gateway host/node host 的 accidental-execution guardrail。执行条件是 tool policy、elevated gate(sandbox escape 时)、请求的 exec policy、host-local approvals、allowlist 与可能的人类决策共同同意。host policy 与 config 取更严格结果;远端 session 不能把执行机器本地的 deny/ask always 放宽。

这套 approval 不是 per-user auth。Gateway authenticated operator 和 paired node 已经构成 trust relationship;approval 只让具体高风险命令再经过确认。一旦批准,命令仍能按 host filesystem permission 修改任何可达资源。真正的多用户边界仍在 ingress auth、sender policy、Agent isolation 与 OS/sandbox。

十一、Allowlist 必须绑定 executable,也最好绑定 argv

allowlist 是 per-agent。bare command name 只匹配经 PATH 调用的命令,不匹配 ./rg/tmp/rg;path glob 可限制可信 binary 位置。shell chain 的每个 top-level segment 都要满足规则,不能因为第一段是 git 就顺便放行后面的 curl。

argPattern 能把同一 binary 收窄到特定 argv;approval 生成的 allow-always entry 使用 argv-bound representation。否则允许 python3 就等于允许它读取并执行任意脚本或 inline code。strictInlineEval 会让 python -cnode -e 等形式即使 binary allowlisted 也继续要求 review/approval。

safe bins 是额外的受限快路径,不应理解为“常见命令天然安全”。stdin-only、参数形态、路径解析和环境都会影响它能否走 fast path。

十二、审批绑定的是不可漂移的执行计划

node host 路径先生成 canonical systemRunPlan,审批记录保存 command/cwd/session 等绑定信息。真正转发 system.run 时复用这份 plan,而不是重新相信 caller 后来的字段。若 command、rawCommand、cwd、agentId 或 sessionKey 发生变化,Gateway 拒绝 mismatch。

批准还尽量绑定 resolved executable path,以及 shell script/直接 interpreter 调用中的一个 concrete local file operand。文件在批准与执行之间变更会拒绝;如果无法识别唯一文件,就不伪装成已完整覆盖。best effort 的边界要明确写出,不能把一次点选夸大成对所有 runtime loader 的证明。

无 UI 或 timeout 时走 askFallback,默认 deny。allow once 只对本次 plan,allow always 生成可匹配的持久规则,deny 终止命令并把结果通过 session/system event 收敛,避免 Agent 永远等待缺失 tool result。

十三、Prompt 与 Skill 只能提供建议,不能承担 Enforcement

system prompt 可以写“不要执行破坏性命令”,skill 可以要求“先展示 diff 再修改”,它们能显著改善模型选择,但都属于 advisory control。prompt injection、模型错误或错误 skill 仍可能产生危险 call,所以 tool visibility、path guard、sandbox、approval 必须由 host code enforce。

第三方 skill 也应当作 untrusted instruction 审查。它可能诱导模型读取 secret、安装 binary 或打开 elevated;真正的防线是这些动作需要的工具和 host gates仍然受限。第三方 native plugin 风险更高,因为它的代码本就在 Gateway trust boundary 内执行,sandboxed Agent 不会自动隔离插件启动副作用。

十四、用 explain 与 audit 找“哪道门”

openclaw sandbox explain --session ... 会展示 effective mode/scope/workspace access、是否真的 sandboxed、sandbox tool allow/deny 与 elevated gate。tool policy 过滤会写 agents/tool-policy audit,包含 layer label、key 和受影响工具。

openclaw sandbox explain --session agent:main:main --json
openclaw approvals get --gateway
openclaw exec-policy show
openclaw logs

诊断不要从“把 sandbox 关掉试试”开始。先确认 tool 是否在 effective surface;若存在,确认执行 host/sandbox;若是 host exec,再看 elevated availability、effective exec mode、allowlist 与 pending approval。沿控制面走,才能只放开真正缺失的一道门。

十五、常见误配的后果

误配为什么不成立正确边界
deny write/edit,所以 exec 是只读shell 能自行写文件。同时限制 exec,加 sandbox/OS ro。
放进 Docker,所以 plugin 都隔离native plugin 留在 Gateway host。审查安装代码,sandbox policy 双重 gate plugin tool。
approval 通过,所以 sender 有权限approval 是命令同意,不是身份认证。先由 ingress/sender policy 证明 requester。
elevated full 能覆盖 denyelevated 只改变 exec 执行位置/审批。tool policy deny 永远先成立。
workspaceAccess ro,但 bind 默认安全额外 bind 有独立 mode,可能 rw。逐项显式 :ro 并审计敏感 socket/path。
无 UI 时继续执行以免卡住无法确认的请求不应自动放行。askFallback 默认 deny,显式配置例外。

十六、从安全边界带走八条规则

  1. 身份、能力、资源、同意分层。一个 approval 按钮无法代表四者。
  2. Tool policy 决定“有没有”。越权工具先从 schema 消失。
  3. Sandbox 决定“在哪里”。Gateway 与 native plugin 仍在 host。
  4. Approval 绑定“这一条计划”。它不能替代用户认证与 filesystem permission。
  5. Elevated 只为 exec 提供受控 escape。它不是总权限模式。
  6. 每层只允许单调收窄。host-local policy 可以比 config 严,不能被远端放宽。
  7. 路径与命令都防 TOCTOU。解析 symlink/executable/script,并拒绝批准后的 plan drift。
  8. 说明是软约束,host code 才是硬边界。用 policy、sandbox、OS 和审批做 defense in depth。

下一篇进入多 Agent:当一个 Agent 把工作交给 subagent、ACP harness 或另一段 session 时,权限、workspace、context、结果投递和取消身份怎样继承,哪些状态又必须隔离。

参考源码与文档