OpenClaw 本地沙盒化运行(macOS)实施笔记

本文记录如何在 不创建新用户 的前提下,用 sandbox-exec(Seatbelt)对 OpenClaw(AI Agent)进行本地沙盒化运行。

一、背景与目标

运行 OpenClaw(AI Agent)存在潜在安全风险:

  • 可能被 Prompt 注入执行危险命令
  • 可能访问 SSH Key、Keychain 等私密数据
  • 可能误删/篡改系统文件
  • 可能污染 Homebrew 工具链

目标是在 不创建新用户 的前提下,把 OpenClaw 运行在一个 受限沙盒环境 中,实现:

  • ✅ 限制文件访问范围
  • ✅ 阻止系统级破坏
  • ✅ 防止密钥泄露
  • ✅ 保留正常运行能力

二、总体架构

launchd
   ↓
run_sandbox.sh (wrapper)
   ↓
sandbox-exec (Seatbelt)
   ↓
node → openclaw gateway

关键点:

  • launchd 启动,避免“手动运行绕过沙盒”
  • 强制通过 wrapper 进入 sandbox-exec
  • 使用 macOS Seatbelt(SBPL policy)进行隔离

三、目录结构规划

/opt/openclaw/
├── work/      # 工作目录(可选)
├── tmp/       # 临时文件
├── logs/      # 日志(可选;也可以继续使用 ~/.openclaw 内置日志目录)
├── bin/       # 启动脚本
└── policy/    # 沙盒规则

权限:

chmod -R 700 /opt/openclaw

仅当前用户可访问。

一键创建目录:

sudo mkdir -p /opt/openclaw/{work,logs,tmp,bin,policy}
sudo chown -R "$(id -un)":"$(id -gn)" /opt/openclaw
sudo chmod -R 700 /opt/openclaw

四、沙盒策略

路径:

/opt/openclaw/policy/openclaw.sb

核心思想:

  • 先用 默认允许 保证跑得起来(黑名单模式)
  • 再对高风险路径做明确 deny

实战建议:先把策略跑通,再逐步收紧;一上来就 deny default 往往会导致大量“正常依赖”被误伤。

示例结构:

ME="$(id -un)"
tee /opt/openclaw/policy/openclaw.sb > /dev/null <<SB
(version 1)
(allow default)
(allow network*)

;; allow homebrew read-only (可选;按需开启)
(allow file-read* (subpath "/opt/homebrew"))
(deny  file-write* (subpath "/opt/homebrew"))

;; allow openclaw workspace read/write
(allow file-read*  (subpath "/opt/openclaw"))
(allow file-write* (subpath "/opt/openclaw"))
;; OpenClaw 自身目录(如果它默认写到 ~/.openclaw,可放行写入)
(allow file-write* (subpath "/Users/$ME/.openclaw"))

;; deny system writes
(deny file-write* (subpath "/System"))
(deny file-write* (subpath "/usr"))
(deny file-write* (subpath "/bin"))
(deny file-write* (subpath "/sbin"))
(deny file-write* (subpath "/Applications"))
(deny file-write* (subpath "/Library"))
(deny file-write* (subpath "/private"))

;; deny secrets
(deny file-read* (subpath "/Users/$ME/.ssh"))
(deny file-read* (subpath "/Users/$ME/.aws"))
(deny file-read* (subpath "/Users/$ME/.gnupg"))
(deny file-read* (subpath "/Users/$ME/.config"))
(deny file-read* (subpath "/Users/$ME/Library/Keychains"))
(deny file-read* (subpath "/Users/$ME/Documents"))
(deny file-read* (subpath "/Users/$ME/Desktop"))
(deny file-read* (subpath "/Users/$ME/Downloads"))
SB

注意点:

  • 直接 deny (subpath "~/.config") 很“狠”,可能影响一些依赖读取配置(例如 git/ssh/各类 CLI)。如果发现 OpenClaw/插件读配置失败,可以先临时注释掉这一条,再逐项收紧。
  • Keychain 相关路径在不同系统/用户环境可能略有差异;如果你发现仍能触达敏感资源,建议用对照实验验证并调整 deny 范围。

五、启动 Wrapper 脚本

路径:

/opt/openclaw/bin/run_sandbox.sh

作用:

  • 清理敏感环境变量(避免进程继承 Token/Agent)
  • 固定 PATH(减少被注入替换二进制的概率)
  • 统一入口:所有执行都从 sandbox-exec 进入

示例:

#!/bin/zsh
set -euo pipefail

unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY
unset OPENAI_API_KEY GITHUB_TOKEN
unset SSH_AUTH_SOCK
unset GOOGLE_API_KEY
unset ANTHROPIC_AUTH_TOKEN

export TMPDIR=/opt/openclaw/tmp
export PATH=/usr/bin:/bin:/opt/homebrew/bin

POLICY=/opt/openclaw/policy/openclaw.sb

exec /usr/bin/sandbox-exec -f "$POLICY" "$@"

可选增强:

  • PATH 更保守:如果不需要 Homebrew,把 /opt/homebrew/bin 去掉。
  • 如果 OpenClaw 依赖 HOME 下的某些缓存/配置,务必结合策略放行最小必要路径,而不是整体放开。

六、LaunchAgent 集成

修改原始 plist:

~/Library/LaunchAgents/ai.openclaw.gateway.plist

关键改动:

ProgramArguments

原: https://blog-img-1259526442.cos.ap-nanjing.myqcloud.com/20260203090635288.png

/opt/homebrew/bin/node ...

改为: https://blog-img-1259526442.cos.ap-nanjing.myqcloud.com/20260203090725297.png

/opt/openclaw/bin/run_sandbox.sh
/opt/homebrew/bin/node
...

保留

  • 原有 OPENCLAW_* 变量
  • HOME=/Users/xxx(避免配置丢失)

建议

<key>WorkingDirectory</key>
<string>/opt/openclaw/work</string>

七、重载服务

launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/ai.openclaw.gateway.plist
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/ai.openclaw.gateway.plist
launchctl kickstart -k gui/$(id -u)/ai.openclaw.gateway

八、安全验证:A/B 对照实验

目的:

确认安全来自 sandbox,而不是程序自身。

A 组:无沙盒

使用无 sandbox wrapper 启动。

测试:

ls ~/.ssh/

结果:

✅ 成功读取

B 组:启用沙盒

恢复 sandbox wrapper。

测试:

ls ~/.ssh

结果:

❌ abort / deny

https://blog-img-1259526442.cos.ap-nanjing.myqcloud.com/20260203091754268.png

对照结论

场景 结果
无沙盒 可访问私钥
有沙盒 被阻断

结论:

OpenClaw 安全隔离来自 sandbox,验证成功。

九、后续演进方向

Phase B:容器隔离(高风险任务)

  • Apple Container / Lima
  • Docker/OCI
  • 高风险构建/下载任务分流

Phase C:密钥迁移

  • Token → Keychain / secrets 文件
  • 移出 plist 明文

Phase D:严格策略

  • deny default
  • 精细白名单
  • 按需授权

十、经验总结

  1. 不要一开始用 deny default
  2. 先 Smoke → 再 Harden
  3. 一定做对照实验
  4. HOME 改动要谨慎
  5. launchd 环境 ≠ shell 环境