openclaw

关注公众号 jb51net

关闭
AI > openclaw >

云端 OpenClaw 远程执行本地进程原理机制详解:Gateway、approvals 与 system.run 到底

脱脱克克

云端 OpenClaw 远程执行本地进程原理机制详解:Gateway、approvals 与 system.run 到底谁在判定、谁在执行?

前言

在使用 OpenClaw 搭建“云端 Gateway + 本地 Windows 节点”的远程执行体系时,最容易混淆的一件事,不是命令本身怎么写,而是命令到底是谁决定能不能执行,谁又真正负责把它跑起来

很多人在刚接触这套架构时,脑子里往往会把几个概念混在一起:

实际上,这些理解都只看到了局部,没有把 OpenClaw 的控制面、执行面、安全联锁、协议分层放在一个统一的框架里看。

这篇文章就围绕一个核心问题展开:

OpenClaw 的远程命令执行,到底是如何从“云端发起请求”一步步走到“本地机器真正启动进程”的?

如果要用一句最直白的话概括整套机制,那就是:

Gateway 负责调度和下令,node 负责暴露本机能力,本地 approvals 负责决定这台机器最后是否同意执行,而 system.run 负责真正把命令跑起来。

这四者不是一个东西,也不是同一层逻辑。只有把它们彻底拆开,很多现象才能解释得通。

一、先把最容易混淆的三层拆开

在 OpenClaw 的远程执行体系里,最值得先建立的不是某个具体命令,而是“分层思维”。

从原理上看,至少要把下面三层分开理解:

  1. 云端 Gateway 的默认执行策略层
  2. 本地执行主机上的 approvals / allowlist 层
  3. 真正落到主机上执行的 system.run 层

很多问题之所以越排越乱,根本原因就在于把这三层混成了一层,以为“能连上节点 = 节点一定能执行 = system.run 已经有权限跑命令”。

实际上并不是这样。

更准确地说,OpenClaw 的设计是一个典型的“控制平面与执行平面分离”架构:

所以,要真正读懂“再结合 approvals 去调用 system.run”这句话,首先就要知道:

system.run 不是先执行再审批,而是前面所有策略都通过之后,才有机会真正启动进程。

二、第一层:云端 Gateway 的默认执行策略到底在管什么

先看第一层,也就是最上层的Gateway 默认执行策略

在你的这套部署中,像 tools.exec.hosttools.exec.securitytools.exec.asktools.exec.node 这几个配置,实际上都属于这一层。它们定义的不是“某台主机上的本地规则”,而是控制面默认想怎么发起执行请求

1.1tools.exec.host:决定默认执行位置

这个配置决定的是:

也就是说,它解决的是一个最顶层的问题:

默认把执行请求发到哪里。

它只是“调度意图”,不是最终执行结果。

1.2tools.exec.security:决定默认安全模式

这个配置不是在本地机器上直接执行白名单,而是定义 Gateway 在形成执行请求时默认遵循什么安全策略,比如:

它表达的是控制面对于执行请求的默认安全态度

1.3tools.exec.ask:决定默认是否弹审批

这个配置决定在控制面层面,遇到执行请求时要不要交互确认。例如:

本质上它解决的是:

在请求进入执行面之前,控制面是否希望引入用户交互式审批。

1.4tools.exec.node:决定默认目标节点

当执行目标是 node 时,这个配置负责指定默认发给哪一台节点。

它解决的不是权限问题,而是路由问题

1.5 这一层的本质:定义“默认执行意图”

因此,把这几个配置合起来看,就能发现一个很重要的结论:

tools.exec.* 本质上是在定义 Gateway 这一侧的默认执行意图,而不是对执行主机作最终裁决。

也就是说,即便你已经把云端 Gateway 配成“默认发到 node、默认走 allowlist、默认不 ask”,它也只代表:

但这还远远没有走到“命令一定会在 Windows 上成功执行”那一步。

三、第二层:什么是本地 approvals,它为什么才是主机侧最后一道闸门

理解了 Gateway 的默认策略之后,接下来要看第二层:执行主机本地的 approvals

这是很多人第一次接触时最容易忽略,但实际上最关键的一层。

3.1 approvals 不是云端全局规则,而是执行主机本地规则

所谓“本地 approvals”,最直接的理解就是:

真正负责执行命令的那台机器,自己本地保存的一份命令运行规则文件。

如果你的执行主机是 Windows 节点,那么这份文件通常就在 Windows 用户目录下,例如:

C:\Users\你的用户名\.openclaw\exec-approvals.json

这说明一件非常重要的事:

approvals 不是放在云端 Gateway 上给所有机器通用的全局白名单,而是每一台执行主机各自持有的一份本地规则。

换句话说,谁负责真正落地执行,谁就有自己的审批文件。

3.2 approvals 管的不是“模型想不想执行”,而是“这台机器愿不愿意执行”

这层的意义必须说透。

大模型、CLI、Gateway 都可以形成“执行请求”,但真正承担风险的是执行主机本身。因为一旦命令落地,它影响的是那台机器上的:

所以 approvals 的本质不是模型侧行为约束,而是资产拥有者视角下的主机安全联锁

可以把它理解成一句非常白的话:

云端可以“建议执行”,但本地机器有权说“这条命令我不接”。

3.3 为什么 approvals 必须放在执行主机本地

这是 OpenClaw 这套设计最值得理解的地方之一。

如果所有审批都只放在云端,那就意味着:

而把 approvals 放在执行主机本地,就实现了一个非常典型、也非常合理的机制:

最后一道生死线不在控制面,而在实际承担风险的宿主机一侧。

这就是为什么说 approvals 是 node host 的 guardrail,是主机侧的 safety interlock。

3.4 approvals 里真正判定什么

这一层一般会包含类似下面这些维度:

其中最敏感的,就是 allowlist

因为它直接决定:

哪些命令入口、哪些脚本、哪些可执行程序,允许在这台机器上被远程触发。

如果一个命令没有命中 allowlist,又没有可用的审批 UI,那它就会停在 approvals 这一层,而不是继续往下进入真正的执行层。

这也就是很多人会碰到的那种现象:

approval required (approval UI not available)

这个报错的真正含义不是“system.run 坏了”,而是:

命令在本地 approvals 这一关被拦住了,根本还没有机会真正落到 system.run。

四、第三层:system.run 到底是什么,它是不是“最终权限拥有者”

讲到这里,才轮到第三层,也就是很多人最先注意到、但最容易误解的 system.run

4.1 system.run 的本质:真实主机上的命令执行器

如果用一句话给它定性,那么最准确的说法是:

system.run 是执行器,不是裁判器。

它的职责非常单纯:

你可以把它类比成“远程桌面环境里的启动程序动作”,也可以把它理解成“节点主机暴露出来的 CreateProcess 接口入口”。

它负责的是把命令跑起来,不是负责决定“这条命令是否应当被允许跑起来”。

4.2 为什么说 system.run 很敏感

因为一旦它被真正调用,落地的就不再是 OpenClaw 自己的内部逻辑,而是宿主机上的真实命令执行。

它理论上可能启动的对象包括但不限于:

这就是为什么 system.run 看似只是一个“命令执行能力”,但它的权限边界非常敏感。因为它一旦真的被触发,本机真实资源就可能被访问或修改。

4.3 它不是“无限授权”,而是“继承宿主机运行身份的能力”

这里必须纠正一个很常见的误区:

很多人会把 system.run 想象成一个天然拥有高权限的系统级执行器,仿佛只要通过了它,就等于自动拿到了这台机器的全部控制权。

这并不准确。

更严谨的理解应该是:

system.run 本身并不凭空创造权限,它继承的是 node host 运行账户在宿主机上的权限边界。

这意味着:

所以,system.run 的权限上限,大体上就等于:

启动 node host 的那个 Windows 用户本来能做什么。

4.4 为什么最佳实践不是直接放开 PowerShell,而是只放固定脚本入口

从工程实践角度看,最稳妥的做法通常不是 allowlist 一个功能极强的通用解释器,而是只允许固定用途的脚本入口,例如:

paper_scan.cmd
paper_rename.cmd
paper_classify.cmd

这样做的好处非常明显:

所以,真正安全的思路从来不是“给 system.run 更多权力”,而是:

system.run 只被允许走你定义好的、边界清晰的受控入口。

五、把整句话讲透:什么叫“再结合 approvals 去调用 system.run”

这是最值得拆开讲透的一句话。

原话听起来容易让人误解成一种线性直觉:

但真实机制恰恰不是这样。

更准确的理解应该是:

控制面先决定原则上怎么发,本地主机再决定这条请求是否允许落地,只有两边都通过之后,system.run 才会被真正调用。

换句话说:

不是先跑了再审批,而是审批通过之后才真正执行。

六、按时序看完整判定链:一条命令是怎么一步步落地的

如果把整个过程按时间顺序展开,通常可以拆成下面 5 步。

第 1 步:模型、CLI 或自动化流程形成执行请求

一切的起点,都是某个控制面主体发起了一个“希望执行命令”的请求。这个主体可能是:

这一阶段还没有真正碰到执行主机,只是生成了一个执行意图

第 2 步:Gateway 根据tools.exec.*看默认策略

接下来,Gateway 会先根据自己的默认配置来判断:

注意,这一步仍然只是控制面判断。

它解决的是“原则上应该怎么发”,不是“执行主机是否最终同意执行”。

第 3 步:Gateway 把请求转发给目标 node

当目标是某台 node 时,Gateway 会把这条请求通过与 node 的连接通道发过去。

这一步体现的是控制面与能力宿主之间的转发关系。

也就是说,Gateway 并不是自己替 node 执行命令,而是:

把控制面形成的执行请求,交给具备相应能力的 node host。

第 4 步:node 在本机读取exec-approvals.json再判一次

到了这一步,事情才真正进入主机侧安全联锁。

node 会根据本机的 approvals 规则再做一次判断,包括:

这一关非常关键,因为它代表的是:

即使 Gateway 愿意发,本地执行主机也仍然可以拒绝。

第 5 步:前面都通过后,node 才真正调用system.run

只有在前面所有条件都通过后,才会真正落到最终执行层。

也就是:

这一步才叫“命令真正落地”。

因此,整条链路最准确的白话版可以概括成一句:

先在控制面决定原则上允不允许发,再在执行主机决定这台机器到底准不准跑,最后才真正启动进程。

七、为什么node.list能成功,不代表nodes run一定成功

这是远程执行场景里最常见、也最容易让人误判的问题之一。

很多人看到:

就会自然得出一个错误结论:

既然节点在线,那执行命令肯定没问题。

其实这个推理跳过了很多层。

7.1node.list成功,只能说明“节点注册与连接层”是通的

node.list 正常返回时,它最多只能说明:

但这些都还只是“能看到节点”,不等于“能成功调起节点能力执行命令”。

7.2nodes run失败,可能卡在更后的任意一层

nodes run 真正失败的位置,可能在后面很多层里:

因此,node.listnodes run 根本不是同一个层次的成功标准。

一个是在看:

我能不能看到节点。

另一个是在看:

我能不能穿过整条执行链,最终让命令在主机上真的跑起来。

这两者之间隔着:

所以,node.list 能成功,只能证明“前几层没问题”,绝不能直接推出“命令执行链已经完全打通”。

八、所谓“远距离通信协议层级需要对应”,到底是什么意思

很多人在排查远程节点问题时,会说一句很抽象的话:

协议层级需要对应。

这句话如果不拆开,很容易流于空泛。其实它背后讲的是一整套分层链路。

要把它说清楚,可以从下往上分成六层来看。

九、第一层:网络可达层——先解决“能不能连到”

这是最底层。

例如你的架构里,可能是这样的:

这一层的职责只有一个:

让本地 node 看起来像是在连本地端口,实际上流量通过 SSH 转发到云端 Gateway。

注意,这一层只解决了“TCP 路能不能打通”。

它解决不了下面这些问题:

所以网络通,不代表执行通。

十、第二层:WebSocket 连接层——不是裸 TCP,而是承载会话语义的连接

node host 并不是向某个随便的 TCP 端口发送字符串,而是要连接到 Gateway WebSocket

这意味着 SSH 隧道里承载的,不是“裸 TCP 上随便发点字节”,而是:

WebSocket over forwarded TCP

也就是说,网络可达只是基础,真正进入 OpenClaw 通信语义的起点是 WebSocket 连接建立成功。

如果这一层失败,哪怕端口是通的,node 也没法变成一个真正挂接到 Gateway 上的能力宿主。

十一、第三层:Gateway 协议层——连上了,不代表会说“同一种话”

即使 WebSocket 连上了,也还不够。

因为在 WebSocket 之上,OpenClaw 还有自己的一套消息协议。

从抽象上看,这一层会涉及类似这样的消息结构:

也就是说,SSH 隧道和 WebSocket 只是通信管道,而 Gateway 协议层才是真正约定:

所以,“协议层级需要对应”在这里的第一重含义就是:

链路打通了,不代表双方已经能用同一种协议语义正常通信。

十二、第四层:角色与作用域层——为什么 CLI 和 node 同样连着 Gateway,干的事却完全不同

继续往上,就进入角色层

在这套体系中,虽然很多东西都连接到同一个 Gateway,但它们并不是同一种身份。

例如可以粗略区分为两类:

这个区分非常关键,因为它决定了:

所以,即使 CLI 和 node 同样都“连着 Gateway”,两者在体系里的角色完全不同:

这也是为什么不能简单地把“连上 Gateway”理解成“所有连接对象都拥有同样的能力”。

十三、第五层:节点能力声明层——节点在线,不代表什么命令都能接

再往上,是很多人经常忽视的一层:节点能力声明

node 被 Gateway 看到,只代表这台机器已经作为一个能力宿主接入了系统。但它真正能做什么,还要看它对外声明了哪些能力和命令。

例如某个节点可能声明:

这表示 Gateway 不是“想给它发什么就发什么”,而是只能调用它明确声明过的命令入口

因此,如果某个节点只声明了浏览器代理能力,却没有声明 system.run,那它即使在线,也不具备执行本地命令的能力。

所以这里又出现一个非常关键的判断:

节点在线 ≠ 节点具备目标能力。

要真正能执行命令,还必须满足:

十四、第六层:主机审批层——这才是“能不能真的落地”的最后裁决

在前面所有层都通过后,最后仍然要回到主机本地 approvals。

这一步的意义可以总结成一句:

即使控制面通了、协议通了、角色对了、能力也存在,本地机器仍然保有最终拒绝权。

这是整套架构最稳的地方,也是最容易被忽略的地方。

因为在很多远程控制系统里,人们习惯把“控制面已经授权”理解成“执行面必然服从”。但 OpenClaw 这里并不是这样的逻辑。

它保留了一条非常明确的原则:

宿主机对自己的命令执行拥有最终同意权。

也正因为有这一层,系统才能做到在远程调度和本地主机安全之间取得平衡。

十五、把整条链浓缩成一句真正准确的话

如果把前面六层再压缩成一句最准确的话,那就是:

隧道层、WebSocket 层、Gateway 协议层、角色/作用域层、节点能力层、主机审批层,必须一层层都通过,最后 system.run 才会真正落地执行。

这句话看似长,但它恰恰解释了远程执行中最常见的误区:

只有把这些层分开,排障思路才会变得清晰。

十六、最容易出现的几个误区,一次讲透

误区 1:Gateway 配成host=node就等于节点一定能执行

错误原因在于把“调度目标”当成了“执行结果”。

host=node 只能说明默认往节点发,不代表本地 approvals 一定放行,也不代表节点一定声明了正确能力,更不代表目标命令一定存在。

误区 2:system.run 自己决定权限

system.run 不是自带无限权限的神秘接口。它只是在 node host 的宿主环境里启动进程,权限边界取决于运行 node host 的那个本地账户。

误区 3:云端安全策略能替代本地 approvals

不能。云端策略解决的是“控制面怎么发”,本地 approvals 解决的是“宿主机愿不愿意接”。这两者不是重复关系,而是串联关系。

误区 4:SSH 隧道通了,远程执行就没问题了

SSH 隧道只解决网络可达,后面还有 WebSocket、协议、角色、能力、审批等层次。

误区 5:node.list正常,nodes run失败一定是架构错了

不一定。很多情况下更可能是:

十七、从安全设计角度看,这套机制为什么合理

如果把 OpenClaw 这套设计放到更一般的软件架构视角去看,会发现它其实非常像成熟系统里常见的“多层联锁”思想。

17.1 控制面与执行面分离

这样做的好处是:

17.2 本地宿主机保有最终决定权

这避免了“远端控制面一旦出错,所有本地主机被一锅端”的风险。

17.3 白名单而不是全开放

从安全角度看,远程自动化系统最怕的不是“功能不够强”,而是“边界不清”。

system.run 收缩为少量固定入口脚本,实际上是在把“通用执行器”改造成“受控流程触发器”。这比直接开放一个通用 shell 要稳得多。

17.4 多层串联比单层放权更可审计

当系统分成:

每一层都可以成为排障点、审计点、日志点。这对线上系统稳定性和安全性都非常重要。

十八、实战建议:如何把 system.run 用得既稳又可控

如果目标是让 OpenClaw 在本地 Windows 节点上执行自动化流程,又不想把风险放大,比较推荐的思路不是“尽可能开放”,而是“尽可能收敛”。

18.1 不要优先开放通用解释器

例如不要一上来就把 PowerShell、cmd 的任意执行能力完全放开。

因为这等于把 system.run 变成一个泛用远程 shell,安全边界会非常大。

18.2 优先只开放固定用途脚本

更好的做法是围绕任务设计稳定入口,例如:

paper_scan.cmd
paper_rename.cmd
paper_classify.cmd

每个脚本只干一类事,参数也尽可能收敛。这样既方便 allowlist 管控,也方便后续扩展自动化流程。

18.3 把复杂逻辑放进脚本,不要放进远程拼接命令

让远端只触发入口,把真正复杂的业务逻辑固化在本地脚本里,会比远程拼接长命令稳定得多,也安全得多。

18.4 排障时一定分层看问题

不要一看到执行失败就只盯着 system.run。正确做法应该是按层往下排:

  1. 网络层通不通;
  2. WebSocket 是否已连上;
  3. Gateway 是否能看到节点;
  4. 节点是否已 paired / connected;
  5. 节点是否声明了目标 command;
  6. 本地 approvals 是否命中 allowlist;
  7. 目标命令入口在宿主机上是否真实存在;
  8. node host 运行身份是否具备足够权限。

只有这样,问题定位才会高效。

十九、一个帮助理解的总流程图

下面用一个简单的 ASCII 流程图,把这整套机制串起来:

[模型 / CLI / 自动化]
          |
          v
[Gateway 控制面]
  - 读取 tools.exec.host
  - 读取 tools.exec.security
  - 读取 tools.exec.ask
  - 读取 tools.exec.node
          |
          v
[将请求转发给目标 node]
          |
          v
[node host 本机]
  - 读取 exec-approvals.json
  - 校验 security / ask / allowlist
          |
   通过?  |----------------------否-------------------->
          |                                          [approval required / reject]
          v
[调用 system.run]
          |
          v
[Windows 本地真实进程启动]
          |
          v
[返回执行结果]

这个图最重要的意义,是把“谁负责什么”彻底分开:

二十、结语:真正应该记住的不是命令,而是判定链

很多人学习远程执行系统时,最先关注的是命令怎么写、端口怎么转发、节点怎么配对。但从长期来看,真正决定你是否能把系统用稳、排障排准、边界管住的,不是这些表层操作,而是对整条执行判定链的理解。

OpenClaw 这套机制的关键,不在于某一个点多么复杂,而在于它把“调度”“能力暴露”“本地审批”“真实执行”明确拆成了不同层。

也正因为这种分层足够清晰,它才具有两个非常重要的优点:

一方面,控制面可以统一调度云端与本地节点,具备良好的扩展性;另一方面,本地宿主机又始终保有最后一道安全控制权,不会因为远端一个配置就把执行权完全放飞。

所以,最后只需要记住一句最核心的话:

Gateway 负责“调度和下令”,node 负责“暴露本机能力”,本地 approvals 负责“这台机器最后是否同意执行”,而 system.run 负责“真的把命令跑起来”。其中任意一层没过,命令都不会真正落地。

这句话理解透了,OpenClaw 远程执行的大部分原理和排障逻辑,基本也就通了。

附:一段最简明的总结,适合作为文章摘要或开头导语

在 OpenClaw 的远程执行架构中,云端 Gateway 负责根据 tools.exec.* 配置形成默认执行策略,决定请求原则上发往哪里、按什么安全模式处理、是否需要审批;目标 node 负责作为能力宿主接收请求,并在本机读取 exec-approvals.json 做最后一道主机侧判定;只有当控制面策略与本地 approvals 同时放行后,节点才真正调用 system.run 在宿主机上启动本地进程。因此,system.run 是最终执行者而不是最终裁判者,SSH 隧道打通、节点在线、node.list 成功都不等于命令一定能落地执行,真正的执行链必须依次通过网络层、WebSocket 层、Gateway 协议层、角色/作用域层、节点能力层和主机审批层。

到此这篇关于云端 OpenClaw 远程执行本地进程原理机制详解:Gateway、approvals 与 system.run 到底谁在判定、谁在执行?的文章就介绍到这了,更多相关OpenClaw 远程执行本地进程内容请搜索脚本之家以前的文章或继续浏览下面的相关文章,希望大家以后多多支持脚本之家!