一、引言
OpenClaw 作为 AI Agent 框架,需要与各类外部服务交互——LLM 提供商(OpenAI、GLM)、消息通道(Telegram、Discord、QQ)、密钥管理器(Vault)等。这些交互都需要凭据(credentials)。
OpenClaw 提供了两种凭据配置方式:
- 明文凭据:直接在配置文件中写入 API Key、Token 等敏感信息
- SecretRef:在配置文件中只写引用,凭据存储在外部(环境变量、文件、密钥管理器)
本文以实战视角,详细讲解两种配置方式的操作方法、适用场景和验证手段。
二、凭据配置文件全景
OpenClaw 的凭据分布在多个配置文件中,每个文件有特定职责。
2.0 配置文件截图
以下是三个核心配置文件的实际内容:

openclaw.json — 全局主配置文件

auth-profiles.json — Agent 级别认证配置

models.json — 运行时自动生成,不要手动编辑
2.1 为什么配置文件是分离的?
OpenClaw 将配置分为 openclaw.json 和 auth-profiles.json 两个文件,这是有意为之的架构设计,原因如下:
多 Agent 支持
OpenClaw 支持同时运行多个独立的 Agent 实例,每个 Agent 可以有自己的认证配置:
| |
使用场景:
| Agent | 用途 | 可用模型 |
|---|---|---|
main | 日常对话、通用任务 | GLM-5、GPT-4o |
coder | 代码生成、技术问答 | Claude、GPT-4o |
writer | 写作、翻译 | GLM-5 |
职责分离
| 文件 | 职责 | 修改频率 | 共享范围 |
|---|---|---|---|
openclaw.json | 全局基础设施配置 | 低 | 所有 Agent 共享 |
auth-profiles.json | 单个 Agent 的认证凭据 | 中 | 仅该 Agent |
models.json | 运行时快照 | 自动生成 | 仅该 Agent |
为什么不合并成一个文件?
- 隔离性:修改一个 Agent 的认证配置不会影响其他 Agent
- 安全性:可以为不同 Agent 分配不同权限的 Token(如只读 vs 完整访问)
- 灵活性:不同 Agent 可以使用不同的模型提供商(main 用 GLM,coder 用 Claude)
- 可维护性:认证配置频繁变动,与相对稳定的基础设施配置分开更易管理
配置优先级
当同一凭据在多处配置时,优先级如下:
flowchart LR
A["auth-profiles.json
key/token"] --> B["最高优先级"]
C["auth-profiles.json
keyRef/tokenRef"] --> D["次高优先级"]
E["openclaw.json
models.providers.*.apiKey"] --> F["基础优先级"]
style A fill:#B22222,color:#fff
style C fill:#228B22,color:#fff
style E fill:#1565C0,color:#fff
实际含义:
- 如果
auth-profiles.json中有profiles.zai:default.key,则使用此明文值 - 否则,如果
auth-profiles.json中有profiles.zai:default.keyRef,则解析此引用 - 否则,使用
openclaw.json中models.providers.zai.apiKey的值
这种设计允许:
- 全局默认配置(openclaw.json)
- Agent 级别覆盖(auth-profiles.json)
- 同一 OpenClaw 实例中运行使用不同凭据的多个 Agent
flowchart TB
subgraph 用户配置
A1["openclaw.json
主配置文件"]
A2["auth-profiles.json
认证配置文件"]
end
subgraph 生成文件
B1["models.json
模型配置快照"]
end
subgraph 外部存储
C1["环境变量"]
C2["secrets.json"]
C3["Vault 或 1Password"]
end
A1 --> B1
A2 --> B1
C1 --> D["运行时解析"]
C2 --> D
C3 --> D
D --> B1
style A1 fill:#1565C0,color:#fff
style A2 fill:#1565C0,color:#fff
style B1 fill:#9E9E9E,color:#fff
style C1 fill:#228B22,color:#fff
style C2 fill:#228B22,color:#fff
style C3 fill:#228B22,color:#fff
2.1 主配置文件:openclaw.json
路径:~/.openclaw/openclaw.json
作用:OpenClaw 的核心配置文件,包含:
| 配置块 | 作用 | 典型凭据 |
|---|---|---|
models.providers.*.apiKey | LLM 提供商 API Key | OpenAI、GLM、Anthropic |
channels.* | 消息通道凭据 | Telegram Bot Token、Discord Token |
gateway.auth.token | Gateway 认证 Token | 本地/远程 Gateway 访问 |
tools.web.search.*.apiKey | Web 搜索 API Key | Brave、Gemini、Perplexity |
secrets.providers | SecretRef 提供者定义 | Vault、1Password 连接配置 |
示例:
| |
2.2 认证配置文件:auth-profiles.json
路径:~/.openclaw/agents/main/agent/auth-profiles.json
作用:为特定 Agent 定义认证配置,优先级高于 openclaw.json 中的 auth.profiles。
| 配置块 | 作用 |
|---|---|
profiles.*.key | 明文 API Key |
profiles.*.keyRef | SecretRef 引用(推荐) |
profiles.*.token | 明文 Token |
profiles.*.tokenRef | SecretRef 引用(推荐) |
示例:
| |
2.4 生成文件:models.json
路径:~/.openclaw/agents/main/agent/models.json
作用:运行时模型配置快照,由 OpenClaw 自动生成。
⚠️ 不要手动编辑此文件,修改会被覆盖。
此文件是 openclaw.json 中 models 配置的解析结果,用于运行时快速查找模型信息。
⚠️ 常见误解:models.json 是"所有凭据的汇总"
一个容易产生的误解是:每个 agent 的 models.json 包含该 agent 能访问的所有明文凭据。这是不准确的。
models.json 的实际范围:
| ✅ 包含 | ❌ 不包含 |
|---|---|
| 模型提供商的 apiKey | 消息通道凭据(telegram、discord 等) |
| baseUrl、模型 ID/名称 | Gateway 认证 Token |
| (如果是 SecretRef,保留引用格式) | auth-profiles.json 中的凭据 |
| 其他非模型类凭据 |
SecretRef 凭据不会在 models.json 中变成明文:如果配置使用 SecretRef,models.json 中仍然保留引用格式,凭据在运行时从外部存储(Vault/env/file)解析后存入内存。
凭据解析与存储全景图
以下流程图展示了凭据从配置文件到运行时内存的完整流程:
flowchart TB
subgraph 配置文件层
A1["openclaw.json
models.providers.*.apiKey"]
A2["openclaw.json
channels.*.botToken"]
A3["openclaw.json
gateway.auth.token"]
A4["auth-profiles.json
profiles.*.key/keyRef"]
end
subgraph 快照文件层
B1["models.json
(仅模型 apiKey)"]
end
subgraph 外部存储层
C1["环境变量"]
C2["secrets.json"]
C3["Vault / 1Password"]
end
subgraph 运行时内存
D1["Agent 内存空间
所有明文凭据的最终归宿"]
end
A1 --> B1
B1 -->|"SecretRef 保留引用格式"| D1
B1 -->|"明文直接复制"| D1
A2 -->|"不在 models.json 中"| D1
A3 -->|"不在 models.json 中"| D1
A4 -->|"优先级最高"| D1
C1 -->|"运行时解析"| D1
C2 -->|"运行时解析"| D1
C3 -->|"运行时解析"| D1
style A1 fill:#1565C0,color:#fff
style A2 fill:#1565C0,color:#fff
style A3 fill:#1565C0,color:#fff
style A4 fill:#1565C0,color:#fff
style B1 fill:#9E9E9E,color:#fff
style C1 fill:#228B22,color:#fff
style C2 fill:#228B22,color:#fff
style C3 fill:#228B22,color:#fff
style D1 fill:#B22222,color:#fff
多 Agent 场景下的凭据隔离
flowchart TB
subgraph 全局配置
G["openclaw.json
所有 Agent 共享"]
end
subgraph Agent: main
M1["auth-profiles.json"]
M2["models.json"]
M3["运行时内存
GLM-5 API Key
Telegram Token"]
end
subgraph Agent: coder
C1["auth-profiles.json"]
C2["models.json"]
C3["运行时内存
Claude API Key
(无 Telegram Token)"]
end
subgraph Agent: writer
W1["auth-profiles.json"]
W2["models.json"]
W3["运行时内存
GLM-5 API Key
(无 Telegram Token)"]
end
G --> M2
G --> C2
G --> W2
M1 --> M3
M2 --> M3
C1 --> C3
C2 --> C3
W1 --> W3
W2 --> W3
style G fill:#1565C0,color:#fff
style M3 fill:#B22222,color:#fff
style C3 fill:#B22222,color:#fff
style W3 fill:#B22222,color:#fff
关键点:
- models.json 是每个 Agent 独立的:
main、coder、writer各有自己的 models.json - 凭据隔离在运行时内存:每个 Agent 的内存空间独立,互不访问
- auth-profiles.json 提供覆盖能力:可以为不同 Agent 配置不同的模型提供商凭据
- 非模型凭据不进入 models.json:如
channels.telegram.botToken直接从 openclaw.json 读入内存
如何查看 Agent 实际可访问的凭据?
| 方法 | 能看到什么 | 局限性 |
|---|---|---|
| 查看 models.json | 模型 apiKey(明文或引用) | 不包含 channels、gateway 等凭据 |
| 查看 openclaw.json | 所有配置的明文或引用 | 需要手动追踪 SecretRef |
| 运行时内存 | 所有明文凭据 | 需要调试工具,生产环境不推荐 |
结论:如果想要"查看某个 agent 能访问的所有明文凭据",正确的方式是看运行时内存,而不是看 models.json 文件。这也是 SecretRef 的安全意义:配置文件中不存明文,明文只在内存中出现。
三、明文凭据配置
3.1 配置方法
在配置文件中直接写入凭据明文:
| |
3.2 适用场景
| 场景 | 是否推荐 | 原因 |
|---|---|---|
| 本地开发测试 | ⚠️ 可接受 | 配置简单,但需注意不要提交到 Git |
| 个人机器部署 | ⚠️ 可接受 | 单用户,风险可控 |
| 多人协作项目 | ❌ 不推荐 | 凭据共享风险 |
| 生产环境 | ❌ 不推荐 | 安全合规要求 |
| 需要凭据轮换 | ❌ 不推荐 | 修改配置文件繁琐 |
3.3 风险分析
配置文件泄露:
| |
Git 历史污染:
| |
备份/传输风险:
| |
四、SecretRef 支持的凭据类型
并非所有凭据都支持 SecretRef。在讲解如何配置之前,先明确哪些凭据可以使用 SecretRef,哪些不能。
4.0 明文配置 vs SecretRef 配置对比
在深入讲解之前,先通过一个实际例子直观感受两种配置方式的区别:

openclaw.json 中 channels 配置对比:qqbot 使用明文 clientSecret,telegram 使用 SecretRef 引用 botToken
两种配置方式的区别:
| 维度 | 明文配置(qqbot) | SecretRef 配置(telegram) |
|---|---|---|
| 配置格式 | "clientSecret": "32oM****" | "botToken": {"source": "exec", "provider": "vault", "id": "..."} |
| 凭据存储位置 | 直接写在配置文件中 | 存储在外部(Vault) |
| 安全性 | ❌ 配置文件泄露 = 凭据泄露 | ✅ 配置文件只有引用,无明文 |
| Git 提交风险 | ⚠️ 明文进入历史记录 | ✅ 历史记录只有引用 |
| 轮换便利性 | ❌ 需修改配置文件 | ✅ 只需更新外部存储 |
为什么 qqbot 使用明文? 因为 channels.qqbot.clientSecret 是插件实现,不在 OpenClaw 内置 SecretRef 支持列表中(详见 4.3 不支持的凭据类型)。
4.1 OpenClaw 凭据分类与依赖关系
在讨论 SecretRef 支持范围之前,先理解 OpenClaw 涉及的凭据类型及其依赖关系。
三层凭据架构(概念性框架)
⚠️ 说明:本节提出的"三层凭据架构"是本文作者的概念性抽象,用于帮助理解 OpenClaw 凭据流转的依赖关系。它不是 OpenClaw 代码中的正式概念或架构术语。代码中没有"第一层"、“第二层”、“第三层"的任何体现。
OpenClaw 的凭据流转可以分为三个概念层次,每一层都有凭证和使用者:
flowchart TB
subgraph L1["第一层:基础凭证"]
direction TB
subgraph L1_Cred["凭证"]
E1["环境变量
VAULT_TOKEN 等"]
E2["本地文件
~/.openclaw/.vault-token"]
end
subgraph L1_User["使用者"]
U1["Resolver 脚本
vault-resolver.sh"]
end
end
subgraph L2["第二层:SecretRef 存储"]
direction TB
subgraph L2_Cred["凭证(存储后端)"]
V1["exec 模式
Vault / 1Password"]
V2["file 模式
secrets.json"]
V3["env 模式
环境变量"]
end
subgraph L2_User["使用者"]
U2["secrets.providers
(openclaw.json 配置)"]
end
end
subgraph L3["第三层:应用凭证"]
direction TB
subgraph L3_Cred["凭证(业务凭据)"]
C1["LLM API Key"]
C2["Telegram Token"]
C3["Gateway Token"]
C4["Skills API Key"]
end
subgraph L3_User["使用者"]
U3["应用模块
LLM/Telegram/Gateway/Skills"]
end
end
L1_Cred -->|"读取"| U1
U1 -->|"调用"| V1
L1_Cred -->|"直接读取"| V2
L1_Cred -->|"直接读取"| V3
L2_Cred -->|"配置引用"| U2
U2 -->|"SecretRef 声明"| C1
U2 -->|"SecretRef 声明"| C2
U2 -->|"SecretRef 声明"| C3
U2 -->|"SecretRef 声明"| C4
C1 -->|"注入"| U3
C2 -->|"注入"| U3
C3 -->|"注入"| U3
C4 -->|"注入"| U3
style E1 fill:#228B22,color:#fff
style E2 fill:#228B22,color:#fff
style U1 fill:#E65100,color:#fff
style V1 fill:#5E35B1,color:#fff
style V2 fill:#5E35B1,color:#fff
style V3 fill:#5E35B1,color:#fff
style U2 fill:#B22222,color:#fff
style C1 fill:#1565C0,color:#fff
style C2 fill:#1565C0,color:#fff
style C3 fill:#1565C0,color:#fff
style C4 fill:#1565C0,color:#fff
style U3 fill:#9E9E9E,color:#fff
凭证与使用者对照表:
| 层级 | 凭证 | 使用者 | 使用方式 |
|---|---|---|---|
| 第一层 | VAULT_TOKEN、VAULT_ADDR | Resolver 脚本 | 脚本读取环境变量/文件,调用 Vault |
| 第二层 | Vault 存储的凭据 | secrets.providers | openclaw.json 中定义 provider |
| 第三层 | LLM API Key、Telegram Token | 应用模块 | SecretRef 引用,运行时注入 |
机制说明(通用规则,与具体存储无关):
| 层级 | OpenClaw 机制 | 说明 |
|---|---|---|
| 第一层 | 不通过 SecretRef 配置 | “启动钥匙”,使用者是 Resolver 脚本 |
| 第二层 | 通过 secrets.providers 定义 | 使用者是 openclaw.json 配置 |
| 第三层 | 通过 SecretRef 引用 | 使用者是具体的应用模块 |
完整的凭据流转链路
flowchart LR
subgraph 输入
A["基础凭证
VAULT_TOKEN"]
end
subgraph 处理
B["Resolver 脚本
使用基础凭证"]
C["Vault
存储应用凭证"]
D["secrets.providers
配置引用"]
E["SecretRef
声明路径"]
end
subgraph 输出
F["应用凭证
运行时注入"]
G["应用模块
使用凭证"]
end
A -->|1. 读取| B
B -->|2. 认证| C
C -->|3. 返回| D
D -->|4. 解析| E
E -->|5. 获取| F
F -->|6. 注入| G
style A fill:#228B22,color:#fff
style B fill:#E65100,color:#fff
style C fill:#5E35B1,color:#fff
style D fill:#B22222,color:#fff
style E fill:#1565C0,color:#fff
style F fill:#1565C0,color:#fff
style G fill:#9E9E9E,color:#fff
6 步流转说明:
| 步骤 | 动作 | 执行者 | 数据流 |
|---|---|---|---|
| 1 | 读取基础凭证 | Resolver 脚本 | VAULT_TOKEN → 脚本内存 |
| 2 | 认证到 Vault | Resolver 脚本 | VAULT_TOKEN → Vault |
| 3 | 返回存储的凭据 | Vault | openclaw/zai/apiKey → 脚本 |
| 4 | 解析 SecretRef | OpenClaw Gateway | provider + id → 凭据值 |
| 5 | 获取应用凭证 | OpenClaw Gateway | 凭据值 → 内存 |
| 6 | 注入应用模块 | OpenClaw Gateway | 内存 → LLM/Telegram/Gateway |
关联载体详解
载体 ①:Resolver 脚本(第一层使用者)
Resolver 脚本是第一层凭证的使用者,负责:
- 读取基础凭证(如 VAULT_TOKEN)
- 调用外部服务(如 Vault CLI)
- 将凭据值返回给 OpenClaw
| |
载体 ②:openclaw.json 配置(第二层使用者)
openclaw.json 是第二层凭证的使用者,通过 secrets.providers 配置引用存储后端:
| |
完整的凭据解析流程:
sequenceDiagram
participant User as 用户
participant Config as openclaw.json
participant Gateway as OpenClaw Gateway
participant Resolver as Resolver 脚本
participant Vault as Vault / 外部存储
User->>Config: 1. 配置 secrets.providers
User->>Config: 2. 配置 SecretRef 引用
User->>Resolver: 3. 编写 resolver 脚本
User->>Vault: 4. 存储凭据到外部服务
Gateway->>Config: 5. 启动时读取配置
Gateway->>Resolver: 6. 调用 resolver(传入 id)
Resolver->>Vault: 7. 使用 VAULT_TOKEN 访问 Vault
Vault-->>Resolver: 8. 返回凭据值
Resolver-->>Gateway: 9. 返回 JSON 响应
Gateway->>Gateway: 10. 解析 SecretRef,注入应用凭证
第一层:基础凭证(引导凭证)
OpenClaw 机制:基础凭证不通过 SecretRef 配置,而是直接通过环境变量或文件提供。
具体内容取决于你选择的存储后端:
| 存储后端 | 需要的基础凭证 | 配置方式 |
|---|---|---|
| HashiCorp Vault | VAULT_TOKEN + VAULT_ADDR | 环境变量 或 ~/.openclaw/.vault-token |
| 1Password CLI | OP_SESSION_* | 环境变量(op signin 后自动设置) |
| AWS Secrets Manager | AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY | 环境变量 或 IAM 角色 |
| GCP Secret Manager | GOOGLE_APPLICATION_CREDENTIALS | 环境变量 指向服务账号 JSON |
| file 模式 | 无额外凭证 | 直接读取 ~/.openclaw/secrets.json |
| env 模式 | 无额外凭证 | 直接读取进程环境变量 |
以 Vault 为例:
| |
以 1Password 为例:
| |
第二层:SecretRef 存储层
OpenClaw 机制:通过 secrets.providers 定义存储后端,支持三种 source:
| source | OpenClaw 机制 | 依赖的基础凭证(举例) |
|---|---|---|
"env" | 直接读取环境变量 | 无(环境变量本身就是凭据) |
"file" | 直接读取本地 JSON 文件 | 无(文件内容就是凭据) |
"exec" | 执行外部命令,从 stdout 读取 | Vault Token / 1Password 会话 / AWS 凭证 等 |
机制 vs 举例:
| 维度 | OpenClaw 机制 | 举例 |
|---|---|---|
| source 类型 | env / file / exec | — |
| provider 名称 | 用户自定义(如 vault、onepassword) | — |
| exec 命令 | 用户自定义脚本 | vault-resolver.sh、op read |
| 基础凭证 | 取决于 exec 命令的需要 | VAULT_TOKEN、OP_SESSION_* |
第三层:应用凭证层(支持 SecretRef)
OpenClaw 机制:以下配置路径支持 SecretRef,可以在 openclaw.json 中使用引用格式:
| 类别 | 凭证路径 | 说明 |
|---|---|---|
| LLM 凭证 | models.providers.*.apiKey | OpenAI、GLM、Anthropic 等 |
| 消息通道 | channels.telegram.botToken | Telegram Bot Token |
channels.discord.token | Discord Bot Token | |
channels.slack.botToken | Slack Bot Token | |
channels.feishu.appSecret | 飞书 App Secret | |
| Gateway | gateway.auth.token | Gateway 访问 Token |
gateway.remote.token | 远程 Gateway Token | |
| Skills | skills.entries.*.apiKey | 技能专用 API Key |
| Web 工具 | tools.web.search.*.apiKey | 搜索 API Key |
| TTS | messages.tts.*.apiKey | 语音合成 API Key |
完整列表见下一节 4.2 支持的凭据。
Skills 凭证存放详解
Skills 是 OpenClaw 扩展能力的核心,每个 Skill 可以有自己的 API Key,支持通过 SecretRef 管理。
Skills 凭证的三个层次:
flowchart LR
subgraph Vault存储["存储层(以 Vault 为例)"]
A["openclaw/skills/eet/apiKey"]
B["openclaw/skills/weather/apiKey"]
end
subgraph openclaw.json["配置层(OpenClaw 机制)"]
C["skills.entries.eet.apiKey"]
D["skills.entries.weather.apiKey"]
end
subgraph Skill代码["运行时(OpenClaw 机制)"]
E["eet Skill 读取 apiKey"]
F["weather Skill 读取 apiKey"]
end
A -->|"SecretRef 解析"| C
B -->|"SecretRef 解析"| D
C -->|"运行时注入"| E
D -->|"运行时注入"| F
style A fill:#5E35B1,color:#fff
style B fill:#5E35B1,color:#fff
style C fill:#1565C0,color:#fff
style D fill:#1565C0,color:#fff
style E fill:#228B22,color:#fff
style F fill:#228B22,color:#fff
配置步骤:
存储到 Vault(以 Vault 为例,其他存储后端同理):
1 2vault kv put openclaw/skills/eet apiKey="your-eet-api-key" vault kv put openclaw/skills/weather apiKey="your-weather-api-key"配置 openclaw.json:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22{ skills: { entries: { "eet": { enabled: true, apiKey: { source: "exec", provider: "vault", id: "openclaw/skills/eet/apiKey" } }, "weather": { enabled: true, apiKey: { source: "exec", provider: "vault", id: "openclaw/skills/weather/apiKey" } } } } }Skill 代码中读取:
OpenClaw 在 Skill 执行时会:
- 解析 SecretRef,从 Vault 获取 apiKey 的实际值
- 将值注入到 SKILL.md 中
metadata.openclaw.primaryEnv声明的环境变量 - Skill 代码通过
process.env.MY_API_KEY读取
完整流程示例:
1 2 3 4 5 6# SKILL.md 中的 frontmatter --- name: my-skill description: My custom skill metadata: { "openclaw": { "primaryEnv": "MY_SKILL_API_KEY" } } ---1 2 3 4 5 6 7 8 9 10 11 12 13 14 15// openclaw.json 中的配置 { skills: { entries: { "my-skill": { enabled: true, apiKey: { source: "exec", provider: "vault", id: "openclaw/skills/my-skill/apiKey" // ← Vault 路径 } } } } }1 2# Vault 中存储的凭据 vault kv put openclaw/skills/my-skill apiKey="sk-xxxxx"执行时的数据流:
sequenceDiagram participant Config as openclaw.json participant Vault as Vault participant Gateway as OpenClaw Gateway participant Skill as Skill 进程 Config->>Gateway: 1. 读取 apiKey SecretRef Gateway->>Vault: 2. 解析 SecretRef,获取实际值 Vault-->>Gateway: 3. 返回 "sk-xxxxx" Gateway->>Gateway: 4. 读取 primaryEnv = "MY_SKILL_API_KEY" Gateway->>Skill: 5. 注入 process.env.MY_SKILL_API_KEY = "sk-xxxxx" Skill->>Skill: 6. 代码读取 process.env.MY_SKILL_API_KEYSkill 代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14import os def call_external_service(): # 从环境变量读取 API Key(由 OpenClaw 注入) api_key = os.environ.get("MY_SKILL_API_KEY") if not api_key: raise ValueError("MY_SKILL_API_KEY not set") # 使用 API Key 调用外部服务 response = requests.get( "https://api.example.com/data", headers={"Authorization": f"Bearer {api_key}"} ) return response.json()
Skills 凭证的安全边界:
| 隔离层级 | 说明 |
|---|---|
| 配置文件隔离 | Skill 的 apiKey 只在 skills.entries.<name> 中定义 |
| 运行时隔离 | 每个 Skill 执行时只能访问自己的 apiKey |
| Vault 路径隔离 | 不同 Skill 的凭据存储在不同 Vault 路径 |
用户自定义服务的凭证放哪里?
假设你有一个自定义服务 aps.cipherhub.cloud/mcp,需要一个 API Token,有以下几种方案:
方案一:通过 Skills 接入(推荐)
如果这个服务需要被 OpenClaw Agent 调用,可以封装成 Skill:
| |
然后在 Skill 代码中通过环境变量或配置读取这个 apiKey。
方案二:不通过 OpenClaw 管理
如果这个服务独立运行(不在 OpenClaw 框架内),则:
- OpenClaw 的 SecretRef 机制无法管理它
- 需要自行实现凭据管理(直接读取 Vault、使用环境变量等)
不支持 SecretRef 的凭证
以下凭证不支持 SecretRef,原因各不相同:
| 凭证 | 原因 | 替代方案 |
|---|---|---|
channels.qqbot.clientSecret | 第三方插件:QQBot 是社区插件 @sliverp/qqbot,不在 OpenClaw 核心凭据管理范围内。插件自行管理凭据解析。 | 明文配置在 openclaw.json(注意权限) |
channels.matrix.accessToken | 运行时生成:由 Matrix 登录流程动态产生,无法预先配置。 | 由 OpenClaw 运行时自动管理 |
auth-profiles.oauth.* | 运行时轮换:OAuth 的 accessToken/refreshToken 会自动续期,静态 SecretRef 无法满足轮换需求。 | 由 OpenClaw 运行时自动管理 |
discord.threadBindings.*.webhookToken | 运行时生成:创建线程时动态生成。 | 自动管理 |
whatsapp.creds.json | 运行时管理:WhatsApp Web.js 持久化凭据。 | 自动管理 |
关键区别:
| 类型 | 示例 | 为什么不支持 SecretRef |
|---|---|---|
| 第三方插件 | qqbot.clientSecret | 插件独立管理凭据,不在 OpenClaw 核心体系 |
| 运行时生成 | matrix.accessToken | 凭据动态产生,无法预先静态配置 |
| 运行时轮换 | oauth.accessToken | 凭据会自动续期,SecretRef 只读模式无法满足 |
qqbot 的特殊情况:如果你确实需要保护 clientSecret,可以:
- 在插件层面实现 SecretRef 支持(需要修改插件代码)
- 使用文件权限保护(
chmod 600 openclaw.json) - 将整个
openclaw.json纳入 Git 忽略(不提交到版本控制)
凭据依赖关系总结
| 层级 | OpenClaw 机制 | 具体内容 |
|---|---|---|
| 基础凭证 | 环境变量 / 文件(明文) | 取决于存储后端(VAULT_TOKEN、OP_SESSION_* 等) |
| SecretRef 存储 | secrets.providers 定义 | 取决于用户选择(Vault、1Password、file 等) |
| 应用凭证 | SecretRef 引用 或 明文 | 取决于业务需求(LLM、Telegram、Skills 等) |
核心原则:
- 三层架构是 OpenClaw 的通用机制,与具体存储后端无关
- 基础凭证的具体内容由存储后端决定,不是 OpenClaw 定义的
- 基础凭证是"启动钥匙”,必须明文配置
- SecretRef 保护的是应用凭证,不是基础凭证
- 所有 SecretRef 引用的凭据最终在运行时内存中是明文
- Skills 的凭证通过
skills.entries.*.apiKey管理,支持 SecretRef
4.2 支持的凭据(openclaw.json)
以下配置路径支持 SecretRef:
| 类别 | 凭据路径 | 说明 |
|---|---|---|
| 模型提供商 | models.providers.*.apiKey | OpenAI、GLM、Anthropic 等 API Key |
models.providers.*.headers.* | 自定义请求头 | |
| Skills | skills.entries.*.apiKey | 技能专用 API Key(支持自定义服务) |
| 网关认证 | gateway.auth.token | Gateway 访问 Token |
gateway.auth.password | Gateway 访问密码 | |
gateway.remote.token | 远程 Gateway Token | |
gateway.remote.password | 远程 Gateway 密码 | |
| Web 工具 | tools.web.search.apiKey | Brave 搜索 API Key |
tools.web.search.gemini.apiKey | Gemini 搜索 | |
tools.web.search.grok.apiKey | Grok 搜索 | |
tools.web.search.kimi.apiKey | Kimi 搜索 | |
tools.web.search.perplexity.apiKey | Perplexity 搜索 | |
tools.web.fetch.firecrawl.apiKey | Firecrawl API Key | |
| 消息 TTS | messages.tts.elevenlabs.apiKey | ElevenLabs TTS |
messages.tts.openai.apiKey | OpenAI TTS | |
| 定时任务 | cron.webhookToken | Cron webhook Token |
| Telegram | channels.telegram.botToken | Bot Token |
channels.telegram.webhookSecret | Webhook 密钥 | |
channels.telegram.accounts.*.botToken | 多账号 Bot Token | |
| Discord | channels.discord.token | Bot Token |
channels.discord.pluralkit.token | PluralKit Token | |
| Slack | channels.slack.botToken | Bot Token |
channels.slack.appToken | App Token | |
channels.slack.userToken | User Token | |
channels.slack.signingSecret | 签名密钥 | |
| 飞书 | channels.feishu.appSecret | App Secret |
channels.feishu.encryptKey | 加密密钥 | |
channels.feishu.verificationToken | 验证 Token | |
| Matrix | channels.matrix.password | 密码 |
| IRC | channels.irc.password | 密码 |
channels.irc.nickserv.password | NickServ 密码 |
4.2 支持的凭据(auth-profiles.json)
| 凭据路径 | 说明 |
|---|---|
profiles.*.keyRef | API Key 引用(type: “api_key”) |
profiles.*.tokenRef | Token 引用(type: “token”) |
示例:
| |
4.3 不支持的凭据类型
以下凭据不支持 SecretRef,必须使用明文配置:
| 凭据 | 原因 |
|---|---|
channels.matrix.accessToken | 运行时生成的 Token |
channels.qqbot.clientSecret | 插件实现(非内置 channel) |
auth-profiles.oauth.* | OAuth 持久化凭据 |
hooks.token | Webhook Token |
discord.threadBindings.*.webhookToken | 运行时生成 |
whatsapp.creds.json | 运行时管理 |
为什么这些不支持? 它们属于「运行时生成或轮换」的凭据类型,不适合通过只读的外部 SecretRef 解析。
4.5 SecretRef 三种来源概览
| source | 适用场景 | 外部存储 |
|---|---|---|
"env" | 个人开发、容器化部署 | 环境变量 |
"file" | 多凭据集中管理 | 本地 JSON 文件 |
"exec" | 企业级密钥管理 | Vault、1Password、AWS Secrets Manager |
下一章将详细讲解这三种来源的配置方法。
五、SecretRef 配置
SecretRef 是一种引用对象,配置文件中只存引用,凭据存储在外部。
5.1 SecretRef 基本结构
| |
5.2 前置配置:定义 secrets.providers
在使用 SecretRef 之前,需要在 openclaw.json 中定义提供者:
| |
5.3 source: “env” — 环境变量
适用场景:个人开发、简单部署、容器化环境
工作原理:从进程环境变量中读取凭据
配置示例:
| |
设置环境变量:
| |
验证规则:
| 字段 | 规则 | 示例 |
|---|---|---|
provider | ^[a-z][a-z0-9_-]{0,63}$ | default、my_provider |
id | ^[A-Z][A-Z0-9_]{0,127}$ | OPENAI_API_KEY、MY_SECRET_123 |
容器化部署示例:
| |
| |
5.4 source: “file” — 本地文件
适用场景:企业级密钥管理、多凭据集中管理
工作原理:从本地 JSON 文件中读取凭据,使用 JSON Pointer 定位
配置示例:
secrets.json 文件内容:
| |
openclaw.json 配置:
| |
JSON Pointer 规则(RFC 6901):
| JSON Pointer | JSON 路径 |
|---|---|
/providers/openai/apiKey | providers.openai.apiKey |
/gateway/authToken | gateway.authToken |
/a~0b~1c | a~b/c(转义:~0 → ~,~1 → /) |
file 后端的安全限制 ⚠️
OpenClaw 对 file 后端实施了严格的安全检查,以下条件必须全部满足,否则会拒绝读取:
| 安全限制 | 检查方式 | 错误提示 |
|---|---|---|
| 文件权限 | 必须是 0600(仅所有者可读写) | File permissions must be 0600 |
| 禁止符号链接 | 使用 lstat 检查,拒绝 symlink | Symbolic links are not allowed |
| 属主校验 | 文件 UID 必须与进程 UID 匹配 | File must be owned by the current user |
| 绝对路径要求 | 必须是绝对路径 | Path must be absolute |
常见错误排查:
| |
mode: “singleValue” 模式:
如果文件只存储单个值:
| |
| |
安全注意事项:
| |
5.5 source: “exec” — 外部命令(Vault)
适用场景:企业级密钥管理系统(HashiCorp Vault、1Password、AWS Secrets Manager)
工作原理:执行外部命令,从 stdout 读取凭据
5.5.1 协议规范
请求格式(OpenClaw → Resolver,通过 stdin):
| |
响应格式(Resolver → OpenClaw,通过 stdout):
| |
错误格式:
| |
5.5.2 Vault Resolver 脚本示例
OpenClaw 的 exec 后端需要执行一个外部脚本来解析 SecretRef。以下是推荐的实现方式:
推荐架构:.sh 包装器 + .py 解析脚本
| |
vault-resolver.sh(包装器):
| |
vault-resolver.py(解析脚本):
| |
| |
⚠️ 安全注意事项:
| 问题 | 错误做法 | 正确做法 |
|---|---|---|
| 环境变量泄露 | env={**os.environ, ...} | 只传递 passEnv 中声明的变量 |
| 包装器设计 | 直接调用 .py | 使用 .sh 包装器,便于扩展 |
| 权限控制 | 忽略文件权限 | chmod 700 或 600 |
5.5.3 openclaw.json 配置
| |
5.5.4 Vault KV 存储配置
KV v2 示例:
| |
实际 Vault 配置截图:

vault kv list openclaw — 列出所有 OpenClaw 凭据路径

vault kv get openclaw/gateway — 查看具体凭据内容
策略配置:
| |
| |
5.5.5 1Password CLI 示例
| |
5.5.6 验证规则
| 字段 | 规则 |
|---|---|
id | ^[A-Za-z0-9][A-Za-z0-9._:/-]{0,255}$ |
| 禁止 | . 或 .. 作为路径段(防止路径遍历) |
5.5.7 exec 后端完整安全机制
OpenClaw 的 exec 后端实施了多层安全防护:
| 安全机制 | 配置参数 | 说明 |
|---|---|---|
| 禁用 shell | shell: false(默认) | 直接执行命令,不经过 shell |
| 可信目录白名单 | trustedDirs | 只有白名单目录下的脚本才能执行 |
| 路径遍历防护 | id 字段校验 | 拒绝 . 和 .. 作为路径段 |
| 输出大小限制 | maxOutputBytes | 防止内存溢出(默认 1MB) |
| 超时控制 | timeoutMs / noOutputTimeoutMs | 防止脚本无限挂起 |
| 符号链接控制 | allowSymlinkCommand | 是否允许符号链接(默认 false) |
| 环境变量白名单 | passEnv | 只传递声明的环境变量 |
shell: false — 为什么禁用 shell?
机制说明:
OpenClaw 默认使用 shell: false,这意味着直接执行命令,而不是通过 /bin/sh -c "command" 包装。
如果允许 shell 会发生什么?
| 场景 | shell: true | shell: false |
|---|---|---|
| 命令注入风险 | ❌ 攻击者可以注入 ; rm -rf / | ✅ 参数被当作字面值 |
| 特殊字符解析 | ❌ $VAR、`cmd`、` | ` 会被解析 |
| 性能开销 | ⚠️ 需要启动 shell 进程 | ✅ 直接 fork+exec |
命令注入攻击示例(假设 shell: true):
| |
shell: false 时的防护:
| |
结论:shell: false 是防止命令注入的第一道防线。
timeoutMs / noOutputTimeoutMs — 为什么需要防止脚本挂起?
机制说明:
| 参数 | 作用 | 默认值 |
|---|---|---|
timeoutMs | 脚本总执行时间上限 | 30000ms (30秒) |
noOutputTimeoutMs | 无输出时自动终止 | 5000ms (5秒) |
为什么需要超时控制?
- 资源耗尽攻击:恶意脚本可能故意无限循环,占用 CPU/内存
- 死锁/阻塞:脚本可能等待一个永远不会完成的操作(如网络请求)
- 僵尸进程累积:大量挂起的子进程会耗尽系统资源
- 用户体验:用户等待凭据解析时不应无限等待
攻击示例:
| |
无超时保护的后果:
| |
有超时保护时:
| |
passEnv — 环境变量白名单机制
机制说明:
默认情况下,子进程会继承父进程的所有环境变量。这是 Unix 的默认行为,但也是安全隐患的来源。
| 模式 | 行为 | 风险 |
|---|---|---|
| 无白名单 | execve(cmd, args, process.env) | ❌ 所有环境变量都被继承 |
| 有白名单 | execve(cmd, args, filteredEnv) | ✅ 只传递 passEnv 中声明的变量 |
什么是"只传递声明的环境变量"?
| |
实际效果对比:
| |
为什么这很重要?
- 防止意外泄露:环境变量中可能包含其他系统的凭据(AWS、数据库密码等)
- 降低攻击面:即使 resolver 脚本被攻破,攻击者也只能访问白名单变量
- 最小权限原则:只给脚本完成任务所需的最小权限
真实攻击场景:
| |
配置示例:
| |
其他安全机制
| 机制 | 说明 |
|---|---|
可信目录白名单 (trustedDirs) | 只有 /Users/bowenerchen/.openclaw 等白名单目录下的脚本才能执行,防止执行任意路径的恶意脚本 |
| 路径遍历防护 | 拒绝 . 和 .. 作为路径段,防止目录穿越攻击 |
输出大小限制 (maxOutputBytes) | 默认 1MB,防止恶意脚本输出无限数据导致内存溢出 |
符号链接控制 (allowSymlinkCommand) | 默认禁止,防止攻击者通过符号链接指向恶意脚本 |
安全设计理念总结:
| 原则 | 实现方式 |
|---|---|
| 最小权限 | passEnv 只传递必要的环境变量 |
| 可信来源 | trustedDirs 只执行白名单目录下的脚本 |
| 防注入 | shell: false 直接执行,不经过 shell |
| 资源保护 | timeoutMs 和 maxOutputBytes 限制资源消耗 |
六、凭据生命周期验证命令
OpenClaw 提供了一组命令用于验证凭据配置和生命周期。
6.1 凭据审计:openclaw secrets audit
功能:扫描配置文件,检测明文凭据、未解析引用、优先级冲突等问题。
| |
输出示例:
| |
Finding 类型:
| Code | 含义 |
|---|---|
PLAINTEXT_SECRET | 发现明文凭据 |
UNRESOLVED_REF | SecretRef 无法解析 |
REF_SHADOWED | 引用被 auth-profiles 覆盖 |
LEGACY_RESIDUE | 旧版 auth.json 残留 |
6.2 交互式配置:openclaw secrets configure
功能:交互式引导配置 SecretRef。
| |
交互流程:
| |
6.3 凭据重载:openclaw secrets reload
功能:重新解析所有 SecretRef 并原子替换内存快照。
| |
使用场景:
- 在 Vault 中更新了凭据后
- 修改了
secrets.providers配置后 - 怀疑内存快照与外部存储不同步时
输出示例:
| |
6.4 凭据应用:openclaw secrets apply
功能:应用预生成的配置计划。
| |
计划文件格式:
| |
6.5 Gateway 状态检查:openclaw gateway status
功能:检查 Gateway 运行状态,包括凭据解析状态。
| |
输出示例:
| |
6.6 模型探测:openclaw models status –probe
功能:测试模型提供商的 API Key 是否有效。
| |
输出示例:
| |
6.7 完整验证流程
flowchart TB
A["配置 SecretRef"] --> B["openclaw secrets audit"]
B --> C{发现问题?}
C -->|是| D["openclaw secrets configure"]
D --> B
C -->|否| E["openclaw gateway restart"]
E --> F["openclaw gateway status"]
F --> G{Secrets healthy?}
G -->|否| H["检查日志"]
H --> I["openclaw secrets reload"]
I --> F
G -->|是| J["openclaw models status --probe"]
J --> K{API Key 有效?}
K -->|否| L["检查外部存储"]
L --> I
K -->|是| M["✓ 验证完成"]
style M fill:#228B22,color:#fff
推荐检查流程:
| |
七、实战案例
7.1 从明文迁移到 SecretRef(Vault)
场景:现有配置使用明文凭据,需要迁移到 Vault。
步骤 1:审计当前配置
| |
步骤 2:配置 Vault 提供者
| |
选择 exec 类型,配置 Vault resolver。
步骤 3:迁移凭据到 Vault
| |
步骤 4:映射凭据到 SecretRef
| |
交互选择要迁移的凭据。
步骤 5:验证迁移结果
| |
7.2 多环境配置(开发/生产)
场景:开发环境和生产环境使用不同的凭据。
开发环境:使用环境变量
| |
| |
生产环境:使用 Vault
| |
| |
7.3 CI/CD 集成
场景:在 CI 流水线中验证凭据配置。
| |
八、最佳实践
8.1 凭据管理策略
| 策略 | 适用场景 | 实现方式 |
|---|---|---|
| 全面 SecretRef | 生产环境、多人协作 | 所有凭据使用 SecretRef |
| 环境变量优先 | 容器化部署 | source: "env" + 容器环境变量 |
| Vault 集中管理 | 企业级部署 | source: "exec" + HashiCorp Vault |
| 定期轮换 | 高安全要求 | Vault 动态 Secret + 自动轮换 |
8.2 安全检查清单
-
openclaw secrets audit无明文凭据 -
~/.openclaw/openclaw.json在.gitignore中 -
~/.openclaw/secrets.json(如果使用)权限为600 - Vault Token 有最小权限策略
- 定期运行
openclaw models status --probe验证凭据有效性 - CI/CD 流水线集成
openclaw secrets audit --check
8.3 故障排查
| 问题 | 诊断命令 | 解决方案 |
|---|---|---|
| Gateway 启动失败 | openclaw gateway status | 检查 SecretRef 是否可解析 |
| API 调用 401 | openclaw models status --probe | 验证 API Key 有效性 |
| Vault 连接失败 | 手动运行 resolver 脚本 | 检查 VAULT_ADDR/VAULT_TOKEN |
| 配置不生效 | openclaw secrets reload | 重新解析 SecretRef |
九、总结
OpenClaw 的凭据配置从简单到复杂有多种选择:
| 配置方式 | 安全级别 | 复杂度 | 适用场景 |
|---|---|---|---|
| 明文凭据 | ⚠️ 低 | 简单 | 本地开发测试 |
| SecretRef + env | ✅ 中 | 中等 | 个人部署、容器化 |
| SecretRef + file | ✅ 中高 | 中等 | 多凭据集中管理 |
| SecretRef + Vault | ✅ 高 | 较高 | 企业级生产环境 |
核心命令速查:
| |
⚠️ 重要警告:SecretRef 保护的是配置文件,凭据最终会在运行时内存中以明文形式存在。这是 OpenClaw 当前架构的基础限制。