当前位置:首页 > 知识wiki > 正文内容

OpenClaw 生产升级血泪史:3097 个 root 文件与 exec-approvals.json 死锁的完整拆解

Openclaw48分钟前知识wiki3
OpenClaw 权限问题排查

一段真实的修复实录:3097 个文件归属错乱、exec-approvals.json 死锁、systemd 权限盲区——每一个自建 OpenClaw 用户迟早都会遇到的事情。


🔴 写在前面:一个问题全景

如果你的 OpenClaw 是跑在云服务器上的(比如阿里云 OpenCloudOS),并且你用的还是 root 用户安装的 Nginx、Node.js、npm,那你几乎一定会遇到本文要讲的问题——区别只是早晚。

简单一句话概括这场事故:

npm 全局安装目录归 root → openclaw 用户无法执行 openclaw update → 被迫用 sudo 安装 → root 在新版启动时污染了 openclaw 的状态目录 → 遗留 3097 个 root 归属文件 → exec-approvals.json 被 root 锁定 → 整个网关瘫痪,修这个文件需要执行命令,但执行命令需要这个文件可读,死锁形成

这不是一个 bug,而是一整套权限设计惯性在真实环境下的共振。每个环节单独看都合理,串在一起就成了灾难。


🧬 根因分析链条

第一环:npm global 的「所有权陷阱」

这是整件事的起点。绝大多数云服务器的 node 都是由 root 通过 nvm 或包管理器安装到 /www/server/nodejs/ 目录下的——这些目录的 owner 是 root。npm 的全局包安装到 /www/server/nodejs/v24.18.0/lib/node_modules/,同样是 root 所有。

# 查看所有权
$ ls -ld /www/server/nodejs/v24.18.0/lib/node_modules/
drwxr-xr-x 7 root root 4096 Jul 16 14:22 /www/server/nodejs/.../node_modules/

$ ls -ld /www/server/nodejs/v24.18.0/bin/node
-rwxr-xr-x 1 root root 76M Jul 16 14:22 /www/server/nodejs/.../bin/node

但 OpenClaw 网关通过 systemdopenclaw 用户运行:

User=openclaw
Group=openclaw

openclaw 用户对 npm 全局目录没有写权限。于是 openclaw update 执行时——这是内置的升级命令——在尝试 npm install -g openclaw@latest 时直接报错:

Error: EACCES: permission denied, mkdir '/www/server/nodejs/v24.18.0/lib/node_modules/.staging'

第二环:sudo npm i -g 的「用户切换盲区」

遇到 EACCES 之后,正常的 ops 思维是「那我用 sudo 装」。于是:

sudo systemctl stop openclaw-gateway
sudo npm i -g openclaw@latest         # ✅ 安装成功
sudo systemctl start openclaw-gateway

版本安装了,但你仔细想想——sudo npm iHOME 是谁的?

默认 sudo 保留的 HOME 是 root 的 /root。当 OpenClaw 新版本网关第一次启动时,它做了一系列状态库迁移——全写到了 /root/.openclaw/。而 systemd 的 WorkingDirectory=/home/openclaw 只是工作目录,不决定状态库位置。

💡 关键洞察:OpenClaw 的状态库路径由 $HOME 环境变量决定,而不是 WorkingDirectory。systemd 中虽然配置了 WorkingDirectory=/home/openclaw,但并未设置 Environment=HOME=/home/openclaw(直到后续修复才加上)。因此新版启动时,$HOME 仍然是 systemd 默认的 / 或旧配置继承的值,造成配置库分裂。

此时的状态:

  • /root/.openclaw/ — 新版生成的空配置(没有对话记录、没有技能、没有 approvals)
  • /home/openclaw/.openclaw/ — 旧版的完整配置(但新网关没在读它)

网关启动了,但是「裸奔」——没有任何配置,相当于一个全新的安装。没有模型配置,没有联系人,没有历史对话。

第三环:SSH 修复引入了 3097 个 root 文件

发现配置丢失后,SSH 进去手动修正:

# 强制指向正确的状态目录
export OPENCLAW_STATE_DIR=/home/openclaw/.openclaw

# 重新启动——这一步实际上是 root shell 干的
sudo systemctl restart openclaw-gateway

但关键问题是——这些修复命令本身是在 root shell 里执行的。当新版网关在升级流程中写入新的状态文件时,owner 全是 root:

# 后续发现的受灾情况
$ find /home/openclaw/.openclaw/ -user root | wc -l
3097

# 问题文件分布
$ find /home/openclaw/.openclaw/ -user root -type f | head -10
/home/openclaw/.openclaw/exec-approvals.json        # ← 最致命的
/home/openclaw/.openclaw/gateway.sqlite
/home/openclaw/.openclaw/config.yaml
/home/openclaw/.openclaw/state.json
/home/openclaw/.openclaw/plugins/skill-manager/...
/home/openclaw/.openclaw/sessions/...

升级完成了,网关工作了,但 3097 个文件埋着雷——等着在某个深夜引爆。


☠️ 死锁场景深度解析

这是整件事的高潮。大部分 root 文件虽然麻烦但不致命——真正杀死网关的是 exec-approvals.json

exec-approvals.json 是什么?

OpenClaw 的 exec 工具在执行命令前会先检查 exec-approvals.json,这里记录着哪些命令被允许以什么权限执行。它是一个安全白名单。网关启动时会读取它,Agent 每次要执行命令时也要读它来校验权限。

死锁的形成

exec-approvals.json 的 owner 是 root 而网关进程以 openclaw 用户运行时:

# 权限 $ ls -la /home/openclaw/.openclaw/exec-approvals.json -rw-r--r-- 1 root root 18342 Jul 16 19:32 exec-approvals.json # ^^^^ root 所有

这不是写入失败的小问题——这是读取级别的失败:

[ERROR] exec command rejected: EACCES: permission denied
  path: /home/openclaw/.openclaw/exec-approvals.json
  user: openclaw (uid=1001)
  action: read

# Agent 的每次命令尝试都返回这个错误
# 包括"修复这个文件"所需要的 chown、cp、chmod 等命令

这就是真正的 死锁(Deadlock)——一个自指的矛盾锁:

你没法通过 Agent 执行命令来修复这个文件

因为执行命令需要读取这个文件

而读取这个文件需要先修复权限

死锁在日志中的表现

网关日志可以看到这个循环:

2026-07-16 19:33:12 [exec] Attempting to run: chown openclaw:openclaw exec-approvals.json
2026-07-16 19:33:12 [exec] Checking approvals from /home/openclaw/.openclaw/exec-approvals.json
2026-07-16 19:33:12 [exec] ERROR: EACCES: permission denied, open '/home/openclaw/.openclaw/exec-approvals.json'
2026-07-16 19:33:12 [approval] Command rejected: chown (no approvals file available)

2026-07-16 19:33:15 [exec] Attempting to run: cp /tmp/backup-exec-approvals.json ...
2026-07-16 19:33:15 [exec] ERROR: EACCES ... [same story]

2026-07-16 19:33:20 [system] Critical: 5 commands rejected in succession.
  Possible causes: approvals file missing, permissions broken, or disk full.

每一个 Agent 给出的修复方案都完美地撞死在同一个门上——你确实知道怎么办,但你没法让那个「怎么办」被执行。


✅ Step-by-Step 修复步骤

紧急修复:SSH 直连破局

死锁的唯一解是绕过执行管道——直接 SSH 到服务器,用 root shell 操作,不经过 Agent 和 approvals:

🔑 核心原则:当 exec-approvals.json 本身是受损文件时,任何通过 OpenClaw exec 管道发起的修复都会失败。必须从外部(SSH)操作。

# Step 1: SSH 登录
ssh root@your-server

# Step 2: 停网关(避免写入竞争)
systemctl stop openclaw-gateway

# Step 3: 修复 exec-approvals.json —— 这是最关键的一步
chown openclaw:openclaw /home/openclaw/.openclaw/exec-approvals.json
chmod 644 /home/openclaw/.openclaw/exec-approvals.json

# Step 4: 验证这步确保可读
sudo -u openclaw test -r /home/openclaw/.openclaw/exec-approvals.json && echo "✅ 可读"

# Step 5: 批量修复所有 root 文件
find /home/openclaw/.openclaw/ -user root -exec chown openclaw:openclaw {} \;

# Step 6: 验证已无 root 残留
find /home/openclaw/.openclaw/ -user root | wc -l
# 期望输出:0

# Step 7: 确认必要文件的权限
chmod -R u+rwX /home/openclaw/.openclaw/

# Step 8: 重启网关
systemctl start openclaw-gateway

# Step 9: 确认网关正常运行
systemctl status openclaw-gateway
journalctl -u openclaw-gateway --no-pager -n 20 | grep -i error

验证恢复

网关恢复后,在 WebChat 或任意终端确认 exec 功能已恢复:

# 在 Agent 对话中尝试一个无害的命令
执行: echo "permissions fixed"

# 期望回应:命令正常执行,不再报 EACCES
# 如果这一步通过,死锁已解除

🛡️ 如何避免再次发生

1. 固化环境变量到 systemd

最根本的修复——在 service 文件中显式锁定 $HOME

[Service]
User=openclaw
Group=openclaw
WorkingDirectory=/home/openclaw
Environment=HOME=/home/openclaw          # ← 显式锁定 HOME
Environment=OPENCLAW_STATE_DIR=/home/openclaw/.openclaw  # ← 可选,双重保险
Environment=PATH=/www/server/nodejs/v24.18.0/bin:/usr/local/bin:/usr/bin:/bin

加了 Environment=HOME=/home/openclaw 之后,无论怎样启动,状态库永远指向正确的路径。这是所有修复中最重要的一行。

2. 升级脚本模板

不要手敲升级命令。把这个脚本存在服务器上,每次升级跑一遍:

User=openclaw
Group=openclaw
1

3. 权限审计钩子

作为日常巡检的一部分,每周或每次重启后检查一次:

User=openclaw
Group=openclaw
2

⚙️ systemd 配置优化建议

基于这次血的教训,最终的 systemd 配置应该是这样——每行都有它的意义:

[Unit]
Description=OpenClaw Gateway
After=network-online.target
Wants=network-online.target
StartLimitBurst=5
StartLimitIntervalSec=60

[Service]
Type=simple
ExecStart=/www/server/nodejs/v24.18.0/bin/node /www/server/nodejs/v24.18.0/lib/node_modules/openclaw/dist/index.js gateway --port 18789
Restart=always
RestartSec=5
RestartPreventExitStatus=78
TimeoutStopSec=30
TimeoutStartSec=30
SuccessExitStatus=0 143
KillMode=control-group

# === 用户与权限 ===
User=openclaw
Group=openclaw
WorkingDirectory=/home/openclaw

# === 环境变量(必须显式设置 ===
# HOME 决定状态库路径,不设置就可能是 /root 下生成空配置
Environment=HOME=/home/openclaw

# OPENCLAW_STATE_DIR 是双重保险,覆盖所有可能的状态读取路径
Environment=OPENCLAW_STATE_DIR=/home/openclaw/.openclaw

# PATH 确保能找到 npm、node 和其他工具
Environment=PATH=/www/server/nodejs/v24.18.0/bin:/usr/local/bin:/usr/bin:/bin

# TMPDIR 确保临时文件写入可控目录
Environment=TMPDIR=/tmp

# === 安全与清理 ===
# PrivateTmp 隔离 /tmp 访问,防止其他进程读取敏感数据
PrivateTmp=false

# 关键:StateDirectory 告诉 systemd 这个目录是状态目录
# systemd 会自动创建并设置权限(需要 systemd 240+)
StateDirectory=openclaw

# 可选:限制能力集,不要给网关不必要的特权
CapabilityBoundingSet=
NoNewPrivileges=true

# 文件系统访问控制
ProtectSystem=strict
ReadWritePaths=/home/openclaw

[Install]
WantedBy=multi-user.target

💡 配置要点解读:

  • Environment=HOME=/home/openclaw — 这是整个事故的「一票否决」行。没有它,任何依赖 $HOME 的路径都会飘到意想不到的地方。
  • StateDirectory=openclaw — systemd 会管理这个目录的生命周期和权限,但前提是你的 systemd 版本 ≥ 240(OpenCloudOS 8/9 满足)。
  • NoNewPrivileges=true — 防止进程通过 setuid 等方式提升权限,增加安全性。
  • ProtectSystem=strict + ReadWritePaths — 严格限制可写范围,防止意外写扩散。

应用新配置

User=openclaw
Group=openclaw
4

📝 最后的反思

回顾整个事件,真正的问题不是某个具体的 bug,而是一种最小权限原则的盲目区:

  • npm 的全局安装需要 root,但运行时进程应该用普通用户——这是一个经典的「安装时特权 vs 运行时无特权」的矛盾。Node.js 社区对此没有好的原生解法。
  • sudo 保留了 root 的 HOME,但没有人提醒你——这是 sudo 的行为惯性,不是 OpenClaw 的设计失误。
  • exec-approvals.json 变成了自己的守卫——任何用它来保护自身修复的尝试都是不可能的。这是一个不该存在的自指循环。如果未来 OpenClaw 能对这个文件做特例处理(比如 fallback 到默认批准的维护命令集),或者提供一个「维护模式」入口绕过 approvals 校验,这个死锁就不会发生。

对于自建 OpenClaw 的用户,我的建议很简单:

  1. 升级前 — 打开 /etc/systemd/system/openclaw-gateway.service,确认 Environment=HOME=/home/你的用户名 这一行存在且正确。
  2. 升级后 — 跑一遍 find /home/你的用户名/.openclaw/ -user root | wc -l,确认数字为 0。
  3. 日常 — 把审计脚本放到 cron 里,每周跑一次。

这两个小时的血泪,最终就换来了那三行 systemd 配置、一个审计脚本、和一个永远不会忘记的经验:权限问题不会自己消失,它只是等着在凌晨两点给你一个惊喜。

2026年7月 · 可亓

扫描二维码推送至手机访问。

版权声明:本文由点度点度金讯时代-BLOG发布,如需转载请注明出处。

本文链接:https://lmwmm.com/post/2661.html

分享给朋友:

“OpenClaw 生产升级血泪史:3097 个 root 文件与 exec-approvals.json 死锁的完整拆解” 的相关文章

OpenClaw AI 助手平台使用指南

OpenClaw AI 助手平台使用指南

OpenClaw 是一款功能强大的AI助手平台,支持部署在自有服务器上,通过微信、企业微信、Telegram等多种渠道交互。本文详细介绍其核心功能、模型配置、技能系统、记忆机制、子代理系统和cron任务等使用要点。…

什么是AI Agent:定义、分类与核心能力

什么是AI Agent:定义、分类与核心能力

AI Agent是能自主感知、决策并执行任务的智能程序。本文介绍Agent的核心定义、术语表、发展简史、分类方式、典型架构、关键组件和主要应用场景,适合快速建立对AI Agent的系统认知。…

Browser Agent:浏览器自动化智能体架构解析

Browser Agent:浏览器自动化智能体架构解析

Browser Agent是能够自主操控浏览器完成网页交互任务的AI智能体系统。本文从定义出发,解析其观察-推理-行动闭环架构、感知层(DOM解析/视觉截图/无障碍树)与动作空间设计,对比ReAct、CoT等决策策略,梳理Playwrigh…