为什么需要护栏
OpenClaw 能直接执行 shell、读写文件、调用外部接口。能力越大,风险越大:一个没加约束的定时任务,可能因为一条错误的 rm -rf 把工作区清空;一段包含密钥的命令,可能被原样写进聊天记录或日志里。
这不是吓唬人。本工作区自己就跑着每天 03:00 的自动发文 cron,如果它哪天被改坏,会静默发出错误内容甚至误操作。所以 OpenClaw 从设计上就分了三道闸:
- 命令权限分级(普通 / 需审批 / 提权)
- 人工审批
/approve(命令级的一次性放行) - 密钥托管
secrets(密钥不进对话、不进日志)
下面逐一拆开讲怎么用。
一、exec 的三档权限
OpenClaw 执行命令时,按风险把权限分成三档。理解它们是配好护栏的前提。
| 权限档 | 触发条件 | 行为 |
|---|---|---|
| 普通(normal) | 低风险命令、在允许的主机/沙箱 | 直接执行,无需确认 |
| 需审批(ask) | 命中审批策略、或调用方要求 | 暂停,等用户在聊天里发 /approve |
| 提权(elevated) | 需要 root / 写系统目录 / 改系统配置 | 必须显式 elevated:true 且通过审批 |
关键点:elevated 不会自动发生。即使一个命令"看起来需要 root",只要调用时没有声明 elevated,它要么按普通权限跑(失败),要么被 ask 拦住,绝不会 silently 提权。这是第一道硬保护。
实战:手动跑一条需要 root 的命令
# 错误示范:不声明 elevated,遇到需要 root 的命令会直接失败
exec command="systemctl restart nginx"
# 正确示范:显式声明提权(仍需人工审批放行)
exec command="systemctl restart nginx" elevated=true
审批时,界面会给出完整命令预览(包括所有链式、多行参数),你确认无误再放行,不会只给一个模糊的 "allow command"。
二、/approve:命令级的单次放行
当一条命令被 ask 拦下,你会在聊天里看到审批请求。两种放行方式:
/approve:对当前这条待审批命令放行一次。- 指定 slug 审批:如果同批有多个待审,可针对某个命令 id 放行。
重要规则:
- 一次
/approve只放行一条命令。下一条新的提权命令,需要重新审批。这避免了"批一次、之后全放开"的隐患。 - 审批预览里看到的是完整命令,不是别名、不是摘要。任何拼接、引号、管道都原样呈现,方便你核对。
- 绝不要用脚本本身当审批 id,审批用的 slug 与命令文本分离。
经验:把
/approve当成"门禁刷卡",而不是"开门后一直开着"。每次刷都是一次独立授权。
三、secrets:密钥不进聊天、不进日志
很多人图省事,把 API Key 直接写在命令参数或环境变量里:
# 危险:密钥明文出现在命令、进程列表、日志里
curl -H "Authorization: Bearer sk-xxxxxxx" https://api.example.com
OpenClaw 的 secrets 机制解决这个问题:
# 列出已有密钥的元信息(看不到值)
secrets action=list
# 向用户申请一个缺失的密钥(用户_masked 录入,值直送托管存储)
secrets action=request name=STRIPE_API_KEY reason="支付回调需要" allowedHosts=["api.stripe.com"]
要点:
secrets list只返回元数据(名称、类型),值不可读回,连模型本身都拿不到明文。- 录入走宿主侧 mask 输入,不经过聊天转录,因此不会出现在对话或 transcript 里。
- 调用时通过返回的
SecretRef引用,配置字段里只存引用,不存值。 - 网关出口还需要开启代理 + 精确
allowedHosts,没有主机白名单时密钥不会被外发替换,防止密钥被误投到不该去的域名。
实战:在网关命令里用 SecretRef
{
"env": {
"API_KEY": "@secret:STRIPE_API_KEY"
}
}
运行时由宿主注入不透明哨兵变量,命令执行时能用到,但日志里只看到变量名,看不到值。
四、给 cron 定时任务配最小权限
自动发文这类无人值守任务,最该守规矩。几条可落地的做法:
脚本白名单化:cron payload 只调用一个固定脚本路径,而不是把一长串 shell 拼进 schedule。脚本内容受版本管理,改了有记录。
{ "kind": "cron", "expr": "0 3 * * *", "tz": "Asia/Shanghai", "payload": { "kind": "script", "script": "python3 /opt/halo-migration/publish_static.py <file> openclaw", "timeoutSeconds": 600 } }失败要出声,别静默:任务配置
failureAlert,连续执行失败就告警。静默失败比报错更危险——你以为它在跑,其实早挂了。不把密钥写进脚本:脚本里用
SecretRef引用,如上节。即便脚本被cat出来,也看不到真值。改动前先备份配置:改
openclaw.json前先cp一份带时间戳的备份(本机就是这么干的,openclaw.json.bak-*一堆)。出问题能秒回滚。
五、常见坑
- 把
/approve当万能钥匙:它只放行当前命令,不要指望"审批一次管全程"。需要持续授权时,应该改策略配置,而不是反复手点。 - elevated 命令进 cron 要谨慎:无人值守 + 提权 = 最高风险组合。能用普通权限完成的,绝不声明 elevated。
- allowedHosts 留空:这样密钥不会被外发替换,但也可能让本该走的请求失败。按真实需要的域名精确填写,别图省事全开。
- 日志里打印返回值:
secrets保证的是"值不泄露",但你自己在脚本里echo $API_KEY照样会写进日志。别手动打印。
小结
OpenClaw 的安全不是靠"信任 AI 不会乱来",而是靠三层硬机制:
- exec 分级——elevated 必须显式声明,不会自动提权;
- /approve——命令级单次放行,预览完整命令;
- secrets——密钥托管、mask 录入、引用不暴露值。
把这三点用对,你就能让 AI 放心地每天自动干活,又不必担心它哪天"手滑"。
扫码关注公众号,获取更多 OpenClaw 实操技巧:
