Tools
Dingtalk Isolation Kit
OpenClaw/Hermes DingTalk DWS multi-user identity isolation kit with per-user auth state, audit logging, policy controls, and plugin scaffolds.
Configuration Example
{"service":"drive","args":["file","list"],"confirmed":true}
README
# OpenClaw / Hermes 钉钉 DWS 多用户身份隔离套件
> 目标:在多人共用同一个 OpenClaw 或 Hermes 智能体容器时,避免所有钉钉能力共用同一个 `dws auth login` 登录账号。
## 1. 这套文件解决什么问题
如果 OpenClaw / Hermes 容器里只安装了一份 `dws`,并且只登录了某一个人的钉钉账号,那么默认所有用户调用钉钉能力时都会使用这个账号,风险包括:
- 普通用户间接访问登录账号可见的日程、文档、审批、邮箱、云盘等;
- 操作审计显示的是登录账号,而不是实际发起请求的人;
- Agent 可能越权读取或发送钉钉数据;
- 后续很难满足企业安全审查。
本套件用三层解决:
```text
OpenClaw / Hermes 当前对话用户
↓
插件自动读取 current_user_id,禁止模型伪造 user_id
↓
openclaw-dws wrapper 选择该用户独立的 DWS_CONFIG_DIR / DWS_CACHE_DIR / DWS_KEYCHAIN_DIR / DWS_AUTH_IDENTITY
↓
dws CLI 以该用户自己的配置、缓存和 keychain 授权执行
↓
SQLite 审计日志记录:谁、何时、调用了什么、是否成功
```
## 2. 目录结构
```text
openclaw-dingtalk-isolation-kit/
bin/
openclaw-dws.py # 核心 wrapper:按用户隔离 dws 认证目录
plugins/
dingtalk_identity_router.py # OpenClaw / Hermes 插件/工具适配层示例
openclaw-plugin.example.yaml # 插件注册模板
hermes-plugin.example.yaml # Hermes 注册模板
skills/
dingtalk-multi-user-isolation/
SKILL.md # Agent 行为规范:禁止裸 dws,必须走隔离工具
config/
policy.example.yaml # Agent / service 白名单策略模板
docker-compose.patch.example.yaml# 容器挂载与环境变量示例
sql/
schema.sql # 绑定表和审计表
install.sh # 安装 wrapper 到 /usr/local/bin 的示例脚本
```
## 3. 推荐部署路径
容器内建议挂载:
```text
/data/openclaw/
dws/
users/
user_xxx/config/
user_xxx/cache/
user_xxx/keychain/
service/
db/
identity.db
logs/
```
不要继续让所有人使用默认的:
```text
~/.dws
~/.dws/cache
```
## 4. 安装 wrapper
在容器里:
```bash
cd /path/to/openclaw-dingtalk-isolation-kit
bash install.sh
```
或者手动:
```bash
install -m 0755 bin/openclaw-dws.py /usr/local/bin/openclaw-dws
```
确认:
```bash
openclaw-dws --help
```
## 5. 配置环境变量
建议在 OpenClaw 容器环境里设置:
```bash
export OPENCLAW_DWS_BASE=/data/openclaw/dws
export OPENCLAW_DWS_DB=/data/openclaw/db/identity.db
export DWS_BIN=/usr/local/bin/dws
export DWS_TENANT=company_default
export DINGTALK_AGENT=openclaw
export OPENCLAW_DWS_ALLOWED_SERVICES=auth,calendar,chat,contact,doc,drive,minutes,oa,todo,wiki,report,mail,sheet,aitable,attendance,aisearch,ding
export DINGTALK_POLICY_PATH=/data/openclaw/config/dingtalk-policy.yaml
export OPENCLAW_DWS_REQUIRE_CONFIRMATION=chat,ding,mail,oa,attendance,drive,doc,minutes
```
Hermes 容器可以使用同一 wrapper,也可以用 Hermes 前缀别名:
```bash
export HERMES_DWS_BASE=/data/hermes/dws
export HERMES_DWS_DB=/data/hermes/db/identity.db
export HERMES_DWS_WRAPPER=/usr/local/bin/openclaw-dws
export DINGTALK_AGENT=hermes
export HERMES_DWS_ALLOWED_SERVICES=auth,calendar,chat,contact,doc,drive,minutes,oa,todo,wiki,report,mail,sheet,aitable,attendance,aisearch,ding
export DINGTALK_POLICY_PATH=/data/hermes/config/dingtalk-policy.yaml
export HERMES_DWS_REQUIRE_CONFIRMATION=chat,ding,mail,oa,attendance,drive,doc,minutes
```
如果你把原始 `dws` 移到了私有路径,例如 `/usr/local/lib/dws-bin/dws`,则设置:
```bash
export DWS_BIN=/usr/local/lib/dws-bin/dws
```
## 6. 首次用户绑定流程
假设当前 runtime 用户 ID 是 `oc_user_001` 或 `hm_user_001`。
### 6.1 检查绑定状态
```bash
openclaw-dws --user oc_user_001 --binding-status
```
### 6.2 用户扫码登录自己的钉钉
```bash
openclaw-dws --user oc_user_001 auth login --format json
```
智能体容器、SSH、CI 等无头环境建议用 device flow:
```bash
openclaw-dws --user oc_user_001 auth login --device --no-browser --format json
```
该命令会在容器终端输出钉钉授权链接或 user code。把链接发给当前用户,用户在自己的浏览器里完成授权,容器内的 CLI 会轮询并在授权成功后写入该 runtime 用户自己的隔离目录。
插件工具 `dingtalk_auth_login` 也是同一条链路:它后台启动 `dws auth login --device --no-browser`,先把 `authorization_url` / `user_code` 返回给 Agent,后台进程继续等待用户完成授权。若 OpenClaw 当前只有 `exec` 工具,Agent 直接执行上面的 `openclaw-dws` 命令并转发返回链接,不要求用户进入容器或终端。
登录成功后,wrapper 会把该 runtime 用户的绑定状态标记为 `bound`。
### 6.3 执行业务命令
```bash
openclaw-dws --user oc_user_001 calendar event list --format json
```
实际内部会注入:
```bash
DWS_CONFIG_DIR=/data/openclaw/dws/users/user_<hash>/config
DWS_CACHE_DIR=/data/openclaw/dws/users/user_<hash>/cache
DWS_KEYCHAIN_DIR=/data/openclaw/dws/users/user_<hash>/keychain
DWS_AUTH_IDENTITY=user_<hash>
```
不同 runtime 用户会得到不同的 `user_<hash>`,从而隔离 token、缓存和配置。
## 7. 插件接入 OpenClaw / Hermes
`plugins/dingtalk_identity_router.py` 是一个“适配层示例”,提供三个工具:
| 工具 | 说明 |
|---|---|
| `dingtalk_auth_status` | 通过 `dws auth status` 检查当前 runtime 用户钉钉认证 |
| `dingtalk_auth_login` | 后台启动 `dws auth login --device --no-browser`,返回授权链接给当前用户 |
| `dingtalk_run` | 以当前 runtime 用户身份执行白名单内的 dws 命令 |
关键原则:
> `user_id` 不应该由模型传入,而应该由 OpenClaw / Hermes runtime 从渠道事件里的真实发送人身份注入。
OpenClaw 的钉钉会话渠道应注入钉钉发送人 ID。Hermes 的飞书会话渠道应注入飞书发送人 ID;`conversation_id` / `chat_id` 只能用于审计关联,不能当个人身份绑定。
如果 OpenClaw 2026.6.5 的插件接口与本示例不同,请保留这个原则,把 `handle_tool_call()` 接到你们实际的 tool runtime 即可。
Hermes 默认识别这些 Feishu/Lark 上下文字段:
```text
用户:feishu_user_id/lark_user_id/hermes_user_id,也兼容 event.sender.sender_id.open_id/user_id/union_id
会话:conversation_id/thread_id/chat_id/hermes_session_id,也兼容 event.message.chat_id
消息:turn_id/message_id/request_id/event_id,也兼容 event.message.message_id
```
如果 Hermes 实际字段名不同,用环境变量覆盖即可:
```bash
export DINGTALK_CONTEXT_USER_KEYS=principal_id
export DINGTALK_CONTEXT_SESSION_KEYS=conversation_id
export DINGTALK_CONTEXT_MESSAGE_KEYS=turn_id
```
示例 CLI 调试:
```bash
python3 plugins/dingtalk_identity_router.py --tool dingtalk_auth_status --context-json '{"user_id":"oc_user_001","session_id":"s1","message_id":"m1"}' --args-json '{}'
```
Hermes 调试:
```bash
python3 plugins/dingtalk_identity_router.py --tool dingtalk_auth_status --context-json '{"hermes_user_id":"hm_user_001","conversation_id":"c1","turn_id":"t1"}' --args-json '{}'
```
执行业务命令:
```bash
python3 plugins/dingtalk_identity_router.py --tool dingtalk_run --context-json '{"user_id":"oc_user_001","session_id":"s1","message_id":"m1"}' --args-json '{"service":"calendar","args":["event","list","--format","json"]}'
```
## 8. 安全要求
### 8.1 禁止 Agent 直接调用裸 `dws`
正确:
```bash
openclaw-dws --user <current_user_id> calendar event list --format json
```
错误:
```bash
dws calendar event list --format json
```
### 8.2 最好不要把原始 `dws` 暴露在 PATH
更安全的部署方式:
```text
/usr/local/lib/dws-bin/dws # 原始 dws,只给 wrapper 用
/usr/local/bin/openclaw-dws # 暴露给 OpenClaw / Hermes / Agent
```
### 8.3 最终权限模型
```text
最终权限 = OpenClaw / Hermes 当前用户身份
∩ 用户自己的钉钉授权
∩ Agent 工具白名单
∩ 当前任务授权范围
```
### 8.4 系统账号与个人账号分离
- 个人资源:必须用当前用户自己的钉钉授权。
- 系统通知:可以用 service account,但只能开放 `chat`、`ding` 等低风险服务。
### 8.5 服务白名单与二次确认
wrapper 优先读取统一命名:
```bash
OPENCLAW_DWS_ALLOWED_SERVICES=auth,calendar,chat,...
HERMES_DWS_ALLOWED_SERVICES=auth,calendar,chat,...
```
为兼容早期模板,也会接受 `OPENCLAW_DINGTALK_ALLOWED_SERVICES` / `HERMES_DINGTALK_ALLOWED_SERVICES`。
敏感服务或具体命令需要当前用户明确确认后才能执行。可通过环境变量设置:
```bash
OPENCLAW_DWS_REQUIRE_CONFIRMATION=chat,ding,mail,oa,attendance,drive,doc,minutes
# 或
HERMES_DWS_REQUIRE_CONFIRMATION=chat,ding,mail,oa,attendance,drive,doc,minutes
```
也可以通过 `DINGTALK_POLICY_PATH` 指向类似 `config/policy.example.yaml` 的策略文件。当前内置解析支持 `sensitive_services: [...]` 和 `require_confirmation: [...]` 这种行内列表。
确认后的插件调用应传:
```json
{"service":"drive","args":["file","list"],"confirmed":true}
```
wrapper 侧会收到 `DINGTALK_CONFIRMED=1`。没有确认时,敏感服务会返回 `dws command requires explicit confirmation`。
### 8.6 `--mark-bound` 安全边界
`--mark-bound` 不再无条件标记绑定。它必须满足其中之一:
1. 当前隔离身份的 `dws auth status --format json` 成功;
2. 受控迁移时显式设置 `OPENCLAW_DWS_ADMIN_OVERRIDE=1` 或 `HERMES_DWS_ADMIN_OVERRIDE=1`。
普通生产流程应让用户通过 `auth login --device --no-browser` 完成自己的钉钉授权,而不是手动 mark。
## 9. Skill 使用
把 `skills/dingtalk-multi-user-isolation/SKILL.md` 安装到 OpenClaw / Hermes 的技能目录中,让 Agent 在涉及钉钉能力时加载。
但要注意:
> Skill 是行为规范,不是安全边界。真正的隔离必须靠 wrapper / 插件强制实现。
## 10. MVP 验收清单
- [ ] 用户 A 登录后,`/data/openclaw/dws/users/user_A_hash/` 下有独立配置与缓存;
- [ ] 用户 B 登录后,目录不同;
- [ ] 用户 A 查日程不会返回用户 B 的日程;
- [ ] 未绑定用户调用非 `auth` 服务会被拒绝;
- [ ] `identity.db` 中有绑定记录;
- [ ] 每次调用 `dingtalk_audit_logs` 都有审计记录;
- [ ] Agent 无法直接执行裸 `dws`;
- [ ] 写操作仍需要二次确认。
## 11. 后续增强建议
1. 在 wrapper 中加入更细粒度策略:哪些 Agent 可以调用哪些 dws 服务;
2. 对 `mail`、`oa`、`attendance`、`drive`、`doc` 等敏感服务增加二次确认;
3. 把审计日志接入企业日志系统;
4. 增加管理员后台,管理用户绑定和解绑;
5. 增加 token 过期提醒和重新授权流程;
6. 增加“代表当前用户,但禁止永久保存敏感结果”的数据治理规则。
tools
Comments
Sign in to leave a comment