Appearance
11个子Agent(财务、HR、行政等)完全隔离保密,但都要读写同一个webs目录和读取全局skills。经历6个方案5个失败,最终靠反向软链接搞定。
业务背景
需求目的:
- 所有子Agent都能读写同一个webs静态服务目录
- 所有子Agent都能读取全局skills(技能文件)
- 零同步开销,改一处全局生效
- 保持容器隔离安全,绝对保密
架构演进:经历了3个阶段
阶段1:简单配置workspace(❌不安全)
每个Agent分配同一个workspace目录。
问题:每个子Agent都能看到宿主机上所有其他Agent的工作区文件,没有隔离。
阶段2:Docker sandbox隔离(❌隔离了,但共享不通)
给每个子Agent配独立Docker sandbox容器。
json
{
"sandbox": {
"mode": "all",
"scope": "agent",
"workspaceAccess": "rw"
}
}效果:
- ✅ 每个agent只能看到自己的/workspace/,隔离成功
- ❌ 公共webs目录访问不到
- ❌ 全局skills访问不到
阶段3:dangerouslyAllowExternalBindSources(✅ webs共享解决)
json
{
"sandbox": {
"mode": "all",
"docker": {
"dangerouslyAllowExternalBindSources": true,
"binds": ["/opt/openclaw-workspaces/shared/webs:/webs:rw"]
}
}
}效果:webs共享解决,但全局skills还是读不到。
踩坑之路(6个方案)
❌ 尝试1:直接bind mount共享目录
json
"binds": ["/opt/shared/webs:/webs:rw"]💣 报错:Sandbox security: bind mount source is outside allowed roots
📝 教训:OpenClaw sandbox默认只允许挂载agent自己workspace内的路径
❌ 尝试2:Docker Named Volume
json
"volumes": ["shared-webs:/webs:rw"]💣 报错:Unrecognized key: "volumes"
📝 教训:OpenClaw 2026.4.8不支持volumes配置项
❌ 尝试3:配置allowedRoots
json
"allowedRoots": ["/opt/...", "/opt/..."]💣 报错:Unrecognized key: "allowedRoots"
📝 教训:allowedRoots是openclaw内部自动派生的,用户不可以手动配置
❌ 尝试4:软链接
bash
ln -s /opt/shared/webs /agent/workspace/webs💣 结果:容器内断链,目标路径不存在
📝 教训:Docker容器是隔离的文件系统,软链接的绝对路径在容器内不存在
❌ 尝试5:bind mount /root路径
json
"binds": ["/root/.openclaw/skills:/openclaw-skills:ro"]💣 报错:targets blocked path "/root". Mounting system directories is not allowed.
📝 教训:OpenClaw禁止从/root路径挂载到sandbox容器
❌ 尝试6:反向软链接到/root
bash
ln -s /opt/shared/skills /root/.openclaw/skills💣 结果:仍然被拦截
📝 教训:OpenClaw会解析软链接的最终目标路径,照样拦截
✅ 最终方案:反向软链接 + dangerouslyAllowExternalBindSources
核心思路:既然OpenClaw禁止从/root挂载,那就把真实skills目录放在安全路径下,然后让/root/.openclaw/skills软链接指向它。
目录结构
/opt/openclaw-workspaces/shared/skills/ ← 真实skills(安全路径)
/root/.openclaw/skills/ ← 软链接 → shared/skills配置文件(每个agent)
json
{
"sandbox": {
"mode": "all",
"docker": {
"network": "bridge",
"dangerouslyAllowExternalBindSources": true,
"binds": [
"/opt/openclaw-workspaces/shared/webs:/webs:rw",
"/opt/openclaw-workspaces/shared/skills:/openclaw-skills:ro"
]
}
}
}容器内效果
| 路径 | 说明 |
|---|---|
| /workspace/ | agent自己的workspace |
| /openclaw-skills/ | 18个共享全局skills |
| /webs/ | 公共webs项目 |
完整操作清单
Step 1:创建共享目录并复制skills
bash
mkdir -p /opt/openclaw-workspaces/shared/skills
cp -a /root/.openclaw/skills/* /opt/openclaw-workspaces/shared/skills/Step 2:创建反向软链接
bash
rm -rf /root/.openclaw/skills
ln -s /opt/openclaw-workspaces/shared/skills /root/.openclaw/skillsStep 3:更新每个agent的配置
json
{
"dangerouslyAllowExternalBindSources": true,
"binds": [
"/opt/openclaw-workspaces/shared/webs:/webs:rw",
"/opt/openclaw-workspaces/shared/skills:/openclaw-skills:ro"
]
}Step 4:重启+清理缓存
bash
openclaw gateway restart
# 删除旧sandbox容器缓存,让新配置生效方案对比
| 方案 | 可行性 | 同步开销 | 安全性 |
|---|---|---|---|
| 定时同步cron | ❌ 有延迟 | 高 | 中 |
| 软链接指向/root | ❌ 安全拦截 | 零 | 高 |
| 反向软链接+binds | ✅ 已验证 | 零 | 高 |
核心收获
1️⃣ 不要假设配置字段存在
OpenClaw的sandbox docker配置只支持6个字段:
- network、binds、setupCommand、image、user、dangerouslyAllowContainerNamespaceJoin
其他字段(volumes、allowedRoots)都不存在,一定要先查文档确认。
2️⃣ 安全策略会解析软链接
不要以为换个软链接就能绕过安全检查,OpenClaw会追踪最终目标路径。
3️⃣ 反向思维是关键
既然不能从/root挂载到容器,那就把文件放在安全路径,让/root软链接过去:
- 主agent通过软链接透明访问 ✅
- sandbox容器通过bind mount安全挂载 ✅
4️⃣ 备份!备份!备份!
这次改了openclaw.json、删除了重复skills、重建了容器...每一步都有备份,出问题随时回滚。
完整工作流验证
konglong01生成报告全流程:
| 步骤 | 操作 | 结果 |
|---|---|---|
| 1 | 读取skills(grafana-data-query等) | ✅ |
| 2 | Grafana查询数据 | ✅ |
| 3 | 生成HTML报告 | ✅ enrollment-2026-05-01.html |
| 4 | 更新reports-index.json | ✅ |
| 5 | vite build | ✅ |
| 6 | 网页访问 | ✅ HTTP 200 |
总结
从6个失败方案到最终成功,核心突破点就两个:
dangerouslyAllowExternalBindSources: true——放开bind mount源路径限制- 反向软链接——真实文件放安全路径,/root通过软链接跟随
✅ 零同步开销,改一处全生效 ✅ 安全隔离不受影响 ✅ 11个子Agent全部配置完成
