Skip to content

本页介绍 OpenClaw exec 审批机制——沙箱 agent 在宿主机(Gateway 或节点)运行命令时须通过的安全联锁。涵盖三种安全策略(deny/allowlist/full)、无提示 YOLO 模式配置、safe bins 与手动 allowlist 的区别、聊天频道审批转发(Discord、Telegram、Slack、Matrix),以及 macOS IPC 安全流程。

Exec 审批

Exec 审批是伴随应用 / 节点宿主机的安全联锁,让沙箱 agent 在真实宿主机(gatewaynode)上运行命令前须经审批把关。 命令只有当策略 + allowlist + (可选的)用户审批全部同意时才被允许。 Exec 审批是工具策略和提升权限限制之外的额外保障(除非提升权限设为 full,则跳过审批)。 有效策略取两者的严格者tools.exec.* 和审批默认值;如果审批字段未设置,使用 tools.exec 的值。

宿主机本地也会使用该机器上的审批状态。在 ~/.openclaw/exec-approvals.json 中本地配置的 ask: "always" 会保持持续提示,即使会话或配置默认要求 ask: "on-miss"。 使用 openclaw approvals getopenclaw approvals get --gatewayopenclaw approvals get --node <id|name|ip> 检查所请求的策略、宿主机策略来源及有效结果。

如果伴随应用 UI 不可用,任何需要提示的请求都由询问回退(默认:拒绝)解决。

如果命令“卡在审批”或“明明 allowlist 了还是不执行”,先用 openclaw approvals get --gatewayopenclaw approvals get --node <id|name|ip> 看最终生效策略。OpenClaw 同时看全局 tools.exec.* 和宿主机本地 exec-approvals.json,更严格的一层会赢。

原生聊天审批客户端还可以在待审批消息上提供频道特有的交互快捷方式。例如,Matrix 可以在审批提示上添加表情反应快捷键( 允许一次、 拒绝、♾️ 始终允许),同时保留 /approve ... 命令作为回退方案。

适用范围

Exec 审批在执行宿主机本地强制执行:

  • gateway 宿主机 → gateway 机器上的 openclaw 进程
  • 节点宿主机 → 节点运行器(macOS 伴随应用或无头节点宿主机)

信任模型说明:

  • Gateway 认证的调用者是该 Gateway 的受信任操作员。
  • 配对的节点将该受信任操作员的能力扩展到节点宿主机上。
  • Exec 审批降低意外执行风险,但不是每用户的认证边界。
  • 已批准的节点宿主机运行绑定规范执行上下文:规范 cwd、精确 argv、存在时的环境绑定以及可能时的固定可执行路径。
  • 对于 shell 脚本和直接解释器/运行时文件调用,OpenClaw 也会尝试绑定一个具体的本地文件操作数。若该绑定文件在审批后但执行前发生变更,运行将被拒绝,而不是执行已漂移的内容。
  • 这种文件绑定是尽力而为的,不是每种解释器/运行时加载器路径的完整语义模型。如果审批模式无法精确识别一个具体的本地文件来绑定,它会拒绝创建审批支持的运行,而不是假装提供了完整覆盖。

macOS 分层:

  • 节点宿主机服务通过本地 IPC 将 system.run 转发到 macOS 应用
  • macOS 应用强制执行审批 + 在 UI 上下文中执行命令。

设置与存储

审批保存在执行宿主机上的本地 JSON 文件中:

~/.openclaw/exec-approvals.json

示例结构:

json
{
  "version": 1,
  "socket": {
    "path": "~/.openclaw/exec-approvals.sock",
    "token": "base64url-token"
  },
  "defaults": {
    "security": "deny",
    "ask": "on-miss",
    "askFallback": "deny",
    "autoAllowSkills": false
  },
  "agents": {
    "main": {
      "security": "allowlist",
      "ask": "on-miss",
      "askFallback": "deny",
      "autoAllowSkills": true,
      "allowlist": [
        {
          "id": "B0C8C0B3-2C2D-4F8A-9A3C-5A4B3C2D1E0F",
          "pattern": "~/Projects/**/bin/rg",
          "lastUsedAt": 1737150000000,
          "lastUsedCommand": "rg -n TODO",
          "lastResolvedPath": "/Users/user/Projects/.../bin/rg"
        }
      ]
    }
  }
}

无审批 YOLO 模式

如果你想让宿主机 exec 无需审批提示直接运行,必须同时开放两层策略:

  • OpenClaw 配置中的 exec 策略(tools.exec.*
  • ~/.openclaw/exec-approvals.json 中的宿主机本地审批策略

这是 gateway + node 的默认宿主机行为(除非你主动收紧):

  • tools.exec.security: gateway/node 设为 full
  • tools.exec.ask: off
  • 宿主机 askFallback: full

重要区别:

  • tools.exec.host=auto 决定 exec 在哪里运行:有沙箱时在沙箱,否则在 gateway。
  • YOLO 决定宿主机 exec 如何被审批:security=full + ask=off
  • YOLO 模式下,OpenClaw 不会在配置的宿主机 exec 策略之上再添加额外的命令混淆启发式审批门。

持久化 gateway 宿主机"永不提示"配置:

bash
openclaw config set tools.exec.host gateway
openclaw config set tools.exec.security full
openclaw config set tools.exec.ask off
openclaw gateway restart

然后设置宿主机审批文件:

bash
openclaw approvals set --stdin <<'EOF'
{
  version: 1,
  defaults: {
    security: "full",
    ask: "off",
    askFallback: "full"
  }
}
EOF

对于节点宿主机,在该节点上应用同样的审批文件:

bash
openclaw approvals set --node <id|name|ip> --stdin <<'EOF'
{
  version: 1,
  defaults: {
    security: "full",
    ask: "off",
    askFallback: "full"
  }
}
EOF

仅会话级快捷方式:

  • /exec security=full ask=off 只改变当前会话。
  • /elevated full 是紧急快捷方式,也会为该会话跳过 exec 审批。

如果宿主机审批文件比配置更严格,更严格的宿主机策略仍然优先。

策略选项

安全模式(exec.security

  • deny:阻止所有宿主机 exec 请求。
  • allowlist:只允许 allowlist 中的命令。
  • full:允许一切(相当于提升权限)。

询问(exec.ask

  • off:从不提示。
  • on-miss:仅在 allowlist 不匹配时提示。
  • always:每次命令都提示。
  • allow-always 持久信任在有效询问模式为 always 时不会抑制提示。

询问回退(askFallback

如果需要提示但没有 UI 可达,回退决策:

  • deny:阻止。
  • allowlist:只有 allowlist 匹配时允许。
  • full:允许。

内联解释器 eval 加固(tools.exec.strictInlineEval

tools.exec.strictInlineEval=true 时,OpenClaw 将内联代码执行形式视为仅审批,即使解释器二进制文件本身已在 allowlist 中。严格模式下:

  • python -cnode -enode --evalnode -pruby -eperl -eperl -Ephp -rlua -eosascript -e 等仍需明确审批
  • allow-always 不会自动为它们持久化新的 allowlist 条目

这是对不能简洁映射到单个稳定文件操作数的解释器加载器的纵深防御。

Allowlist(每个 agent)

Allowlist 是每个 agent 独立的。如果有多个 agent,在 macOS 应用中切换要编辑的 agent。模式是大小写不敏感的 glob 匹配。 模式应解析为二进制路径(仅文件名的条目会被忽略)。旧版 agents.default 条目在加载时迁移到 agents.main

Shell 链(如 echo ok && pwd)仍然需要每个顶层段都满足 allowlist 规则。

示例:

  • ~/Projects/**/bin/peekaboo
  • ~/.local/bin/*
  • /opt/homebrew/bin/rg

每个 allowlist 条目记录:

  • id:用于 UI 标识的稳定 UUID(可选)
  • 上次使用时间戳
  • 上次使用的命令
  • 上次解析的路径

自动允许技能 CLI

启用自动允许技能 CLI 后,已知技能引用的可执行文件在节点(macOS 节点或无头节点宿主机)上被视为 allowlist 中的条目。这通过 skills.bins 通过 Gateway RPC 获取技能二进制列表。若要严格的手动 allowlist,请禁用此功能。

重要信任说明:

  • 这是隐式的便利 allowlist,与手动路径 allowlist 条目分开。
  • 仅适用于 Gateway 和节点在同一信任边界的受信任操作员环境。
  • 如果需要严格的显式信任,保持 autoAllowSkills: false 并只使用手动路径 allowlist 条目。

Safe Bins(仅 stdin)

tools.exec.safeBins 定义了一小组仅 stdin 的二进制文件(如 cut),它们可以在 allowlist 模式下无需显式 allowlist 条目运行。Safe bins 拒绝位置文件参数和类路径 token,因此只能操作输入流。将其视为流过滤器的窄通道快捷方式,而非通用信任列表。

不要将解释器或运行时二进制文件(如 python3noderubybashshzsh)添加到 safeBins。如果命令能评估代码、执行子命令或通过设计读取文件,请优先使用显式 allowlist 条目并保持审批提示启用。

自定义 safe bins 必须在 tools.exec.safeBinProfiles.<bin> 中定义显式配置文件。Safe bins 验证仅基于 argv 形状(不检查宿主文件系统),防止文件存在性差异被用于信息探测。

Safe bins 也从受信任的二进制目录中解析(系统默认加可选的 tools.exec.safeBinTrustedDirs)。PATH 条目永远不会自动受信。默认受信任的 safe-bin 目录仅为 /bin/usr/bin。如果你的 safe-bin 可执行文件在包管理器/用户路径(如 /opt/homebrew/bin/usr/local/bin),请显式添加到 tools.exec.safeBinTrustedDirs

Safe bins 也对 argv token 强制字面文本处理(无 glob 和 $VARS 展开),防止用 *$HOME/... 偷运文件读取。

默认 safe bins:cutuniqheadtailtrwc

grepsort 不在默认列表中。如果选择加入,为它们的非 stdin 工作流保留显式 allowlist 条目。

Safe bins 中的拒绝标志:

  • grep--dereference-recursive--directories--exclude-from--file--recursive-R-d-f-r
  • jq--argfile--from-file--library-path--rawfile--slurpfile-L-f
  • sort--compress-program--files0-from--output--random-source--temporary-directory-T-o
  • wc--files0-from

Shell 链式命令(&&||;)在每个顶层段都满足 allowlist(含 safe bins 或技能自动允许)时才被允许。重定向在 allowlist 模式下不支持。命令替换($() / 反引号)在 allowlist 解析期间被拒绝。

自定义配置文件示例:

json5
{
  tools: {
    exec: {
      safeBins: ["jq", "myfilter"],
      safeBinProfiles: {
        myfilter: {
          minPositional: 0,
          maxPositional: 0,
          allowedValueFlags: ["-n", "--limit"],
          deniedFlags: ["-f", "--file", "-c", "--command"],
        },
      },
    },
  },
}

openclaw security audit 会在解释器/运行时 safeBins 条目缺少显式配置文件时发出警告。openclaw doctor --fix 可以为缺失的自定义 safeBinProfiles.<bin> 条目脚手架 {}(之后需检查并收紧)。

Safe Bins 与 Allowlist 对比

主题tools.exec.safeBinsAllowlist(exec-approvals.json
目标自动允许窄范围的 stdin 过滤器显式信任特定可执行文件
匹配类型可执行文件名 + safe-bin argv 策略已解析的可执行路径 glob 模式
参数范围由 safe-bin 配置文件和字面 token 规则限制仅路径匹配;参数是你的责任
典型示例headtailtrwcjqpython3nodeffmpeg、自定义 CLI
最佳使用场景管道中的低风险文本转换有更广泛行为或副作用的任何工具

控制 UI 编辑

使用控制 UI → 节点 → Exec 审批卡片编辑默认值、每个 agent 的覆盖和 allowlist。选择作用域(默认值或某个 agent),调整策略,添加/删除 allowlist 模式,然后保存。UI 显示每个模式的上次使用元数据,方便维护。

目标选择器选择 Gateway(本地审批)或节点。节点必须声明 system.execApprovals.get/set(macOS 应用或无头节点宿主机)。如果节点尚未声明 exec 审批,直接编辑其本地 ~/.openclaw/exec-approvals.json

CLI:openclaw approvals 支持 gateway 或节点编辑(参见 审批 CLI)。

审批流程

需要提示时,gateway 向操作员客户端广播 exec.approval.requested。 控制 UI 和 macOS 应用通过 exec.approval.resolve 解决,然后 gateway 将批准的请求转发给节点宿主机。

对于 host=node,审批请求包含规范的 systemRunPlan 载荷。Gateway 在转发已批准的 system.run 请求时使用该计划作为权威的命令/cwd/会话上下文。

异步审批延迟的重要机制:

  • 节点 exec 路径预先准备一个规范计划
  • 审批记录存储该计划及其绑定元数据
  • 一旦批准,最终转发的 system.run 调用重用存储的计划,而不是信任后续调用方的编辑
  • 如果调用方在审批请求创建后更改了 commandrawCommandcwdagentIdsessionKey,gateway 将以审批不匹配为由拒绝转发的运行

确认对话框包含:

  • 命令 + 参数
  • cwd
  • agent id
  • 已解析的可执行路径
  • 宿主机 + 策略元数据

操作:

  • 允许一次 → 立即运行
  • 始终允许 → 添加到 allowlist + 运行
  • 拒绝 → 阻止

解释器/运行时命令

审批支持的解释器/运行时运行是保守的:

  • 始终绑定精确的 argv/cwd/env 上下文。
  • 直接 shell 脚本和直接运行时文件形式被尽力绑定到一个具体的本地文件快照。
  • 仍然解析到一个直接本地文件的常见包管理器包装形式(如 pnpm execpnpm nodenpm execnpx)在绑定前会被解包。
  • 如果 OpenClaw 无法为解释器/运行时命令识别出一个具体的本地文件(如包脚本、eval 形式、特定运行时加载链或模糊的多文件形式),审批支持的执行会被拒绝,而非声称不具备的语义覆盖。
  • 对于这类工作流,优先使用沙箱、单独的宿主机边界,或操作员接受更宽泛运行时语义的显式信任 allowlist/full 工作流。

当需要审批时,exec 工具立即返回审批 id。使用该 id 关联后续系统事件(Exec finished / Exec denied)。如果在超时前没有收到决策,请求被视为审批超时并以拒绝原因呈现。

跟进投递行为

已批准的异步 exec 完成后,OpenClaw 向同一会话发送一次跟进 agent 轮次。

  • 如果存在有效的外部投递目标(可投递频道 + 目标 to),跟进投递使用该频道。
  • 在纯 webchat 或无外部目标的内部会话流中,跟进投递保持仅会话(deliver: false)。
  • 如果调用方明确请求严格外部投递但没有可解析的外部频道,请求失败并返回 INVALID_REQUEST
  • 如果启用了 bestEffortDeliver 且没有外部频道可解析,投递降级为仅会话,而不是失败。

审批转发到聊天频道

你可以将 exec 审批提示转发到任意聊天频道(包括插件频道),并通过 /approve 审批。

配置:

json5
{
  approvals: {
    exec: {
      enabled: true,
      mode: "session", // "session" | "targets" | "both"
      agentFilter: ["main"],
      sessionFilter: ["discord"], // 子字符串或正则
      targets: [
        { channel: "slack", to: "U12345678" },
        { channel: "telegram", to: "123456789" },
      ],
    },
  },
}

在聊天中回复:

/approve <id> allow-once
/approve <id> allow-always
/approve <id> deny

/approve 命令同时处理 exec 审批和插件审批。如果 ID 与待处理的 exec 审批不匹配,它会自动检查插件审批。

插件审批转发

插件审批转发使用与 exec 审批相同的投递管道,但有独立的配置(approvals.plugin)。启用或禁用其中一个不会影响另一个。

json5
{
  approvals: {
    plugin: {
      enabled: true,
      mode: "targets",
      agentFilter: ["main"],
      targets: [
        { channel: "slack", to: "U12345678" },
        { channel: "telegram", to: "123456789" },
      ],
    },
  },
}

配置形状与 approvals.exec 完全相同:enabledmodeagentFiltersessionFiltertargets 的工作方式相同。

任意频道的同聊天审批

当 exec 或插件审批请求来自可投递的聊天界面时,该聊天默认可以通过 /approve 进行审批。这适用于 Slack、Matrix、Microsoft Teams 以及现有的 Web UI 和终端 UI 流。

此共享文本命令路径使用该对话的普通频道认证模型。如果原始聊天已经可以发送命令并接收回复,审批请求不再需要单独的原生投递适配器。

Discord 和 Telegram 也支持同聊天 /approve,但这些频道即使禁用了原生审批投递,仍使用其解析的审批者列表进行授权。

原生审批投递

部分频道还可以作为原生审批客户端。原生客户端在共享的同聊天 /approve 流之上增加了审批者 DM、原始聊天扇出和频道特有的交互审批 UX。

当原生审批卡片/按钮可用时,该原生 UI 是面向 agent 的主要路径。

通用模型:

  • 宿主机 exec 策略仍然决定是否需要 exec 审批
  • approvals.exec 控制是否将审批提示转发到其他聊天目标
  • channels.<channel>.execApprovals 控制该频道是否作为原生审批客户端

原生审批客户端在以下条件全部满足时自动启用 DM 优先投递:

  • 该频道支持原生审批投递
  • 审批者可以从显式 execApprovals.approvers 或该频道文档中的回退来源解析
  • channels.<channel>.execApprovals.enabled 未设置或为 "auto"

支持原生审批的频道:

  • Discord:channels.discord.execApprovals.*
  • Slack:channels.slack.execApprovals.*
  • Telegram:channels.telegram.execApprovals.*

共享行为:

  • Slack、Matrix、Microsoft Teams 等可投递聊天使用该对话的普通频道认证模型进行同聊天 /approve
  • 原生审批客户端自动启用时,默认投递目标为审批者 DM
  • 只有已解析的审批者才能审批或拒绝(Discord 和 Telegram)
  • Discord 审批者可来自显式 execApprovals.approvers 或从 commands.ownerAllowFrom 推断
  • Telegram 审批者可来自显式 execApprovals.approvers 或从现有 owner 配置(allowFrom,以及支持时的直接消息 defaultTo)推断
  • Slack 审批者可来自显式 execApprovals.approvers 或从 commands.ownerAllowFrom 推断
  • Slack 原生按钮保留审批 id 类型,plugin: id 可直接解析插件审批,无需额外的 Slack 本地回退层
  • Matrix 原生 DM/频道路由仅支持 exec 审批;Matrix 插件审批保持在共享的同聊天 /approve 和可选的 approvals.plugin 转发路径上
  • 请求者不需要是审批者
  • 原始聊天在已支持命令和回复时可直接通过 /approve 审批
  • 原生 Discord 审批按钮按审批 id 类型路由:plugin: id 直接转到插件审批,其他所有 id 转到 exec 审批
  • 原生 Telegram 审批按钮遵循与 /approve 相同的有界 exec 到插件回退
  • 当原生 target 启用原始聊天投递时,审批提示包含命令文本
  • 待处理的 exec 审批默认 30 分钟后过期
  • 如果没有操作员 UI 或已配置的审批客户端可以接受请求,提示会回退到 askFallback

Telegram 默认使用审批者 DM(target: "dm")。可通过设置 target: "channel""both" 让审批提示出现在 Telegram 群组频道。对于 Telegram 论坛主题,OpenClaw 保留主题用于审批提示和审批后跟进。

macOS IPC 流程

Gateway -> 节点服务 (WS)
                 |  IPC (UDS + token + HMAC + TTL)
                 v
             Mac 应用 (UI + 审批 + system.run)

安全说明:

  • Unix socket 模式 0600,token 存储在 exec-approvals.json 中。
  • 同 UID 对等检查。
  • 挑战/响应(nonce + HMAC token + 请求哈希)+ 短 TTL。

系统事件

Exec 生命周期以系统消息形式呈现:

  • Exec running(仅当命令超过运行通知阈值时)
  • Exec finished
  • Exec denied

这些消息在节点报告事件后发布到 agent 的会话。Gateway 宿主机 exec 审批在命令完成后(以及可选地运行时间超过阈值时)也会发出相同的生命周期事件。审批门控的 exec 使用审批 id 作为这些消息中的 runId,便于关联。

被拒绝审批的行为

当异步 exec 审批被拒绝时,OpenClaw 阻止 agent 在该会话中重用同一命令之前运行的任何输出。拒绝原因会附带明确说明,指出没有命令输出可用,这会阻止 agent 声称有新输出或用先前成功运行的陈旧结果重复被拒绝的命令。

最佳实践

  • full 权限很强大;尽可能优先使用 allowlist。
  • ask 让你保持对审批的掌控,同时仍允许快速审批。
  • 每个 agent 的 allowlist 防止一个 agent 的审批泄漏到其他 agent。
  • 审批只适用于来自授权发送者的宿主机 exec 请求。
  • /exec security=full 是授权操作员的会话级便利,设计上跳过审批。 要硬性阻止宿主机 exec,将审批安全模式设为 deny 或通过工具策略拒绝 exec 工具。

相关文档

常见问题

Q: gateway 和 node 上的 exec 默认会提示审批吗?

A: 不会。gateway + node 的默认行为是 YOLO 模式(security=fullask=off),exec 直接运行无需提示。如果你想要审批/allowlist 行为,需要同时收紧 tools.exec.* 配置 ~/.openclaw/exec-approvals.json 中的宿主机策略。两层策略以严格者优先。

Q: allow-always 和手动添加 allowlist 条目有什么区别?

A: allow-always 是在审批对话框中选择后自动将该可执行路径写入 allowlist 的快捷方式,效果与手动添加一样。区别在于:当 tools.exec.strictInlineEval=true 时,内联 eval 形式(python -c 等)即使 allow-always 也不会自动持久化,每次都仍需明确审批。

Q: 如何让 Telegram 审批提示出现在群组频道而不是 DM?

A: 设置 channels.telegram.execApprovals.target: "channel""both"(同时发 DM 和频道)。还需确认 execApprovals.enabled 未被显式禁用。