早先我写了不少 Agent 代码,但每次捏着模型服务商的 API Key 时,心态都很微妙:一边知道这串东西值钱得很,一边又确实没有一套正经的存放方式。环境变量、.env、配置文件,乃至聊天窗口里直接贴出来——都试过。后来公司做安全审计,我盯着 Git 历史里那几行清清楚楚的 sk- 开头,突然觉得这哪是审计,简直是在裸奔实况回放。
这篇文章想聊的,就是我怎么用 OpenClaw 这套开源方案,把 AI Agent 的 API Key 从"写在哪算哪"的状态,迁进 Secrets Manager,做加密存储,再配上一套自动轮转机制。适合谁看?手上有 Agent 项目、或者准备把个人助手变成团队基础设施的开发者都算。读完以后,你能得到一个可以直接抄作业的密钥管理方案,也能理解那些"为什么必须这么做"背后的设计逻辑。
1. 先搞清楚:Agent 场景下密钥裸奔为什么格外严重
很多人下意识觉得密钥泄露是"别人家的事",直到自己碰到才慌。但在 AI Agent 这个新赛道里,密钥裸奔几乎是必然发生的,因为整个开发范式天然适合把密钥逼到明面上。
传统后端服务里,密钥一般锁在部署平台的环境变量里,落盘都少见。Agent 不是这样:它经常跑在本地,跑在 CI 里,跑在多人协作的 Notebook 里,甚至跑在别人电脑上临时起的容器里。你没法用"部署平台统一管"这种老思路去约束它,于是每个人的第一反应都是先把 key 写进代码再说。
还有一个 Agent 特有的放大器:上下文窗口。为了让模型调用外部工具,你会把各类授权信息、token、甚至完整配置拼进 prompt 或者工具返回值里。我在一个项目里见过把 AWS(某云服务平台的替代说法,指代云服务商)访问密钥直接塞进工具描述的做法,模型还真的把它照抄进了下一轮请求的日志里。这种场景传统密钥管理完全覆盖不到,因为问题不在存储,而在"流转"。
1.1 密钥泄露的几条典型路径
我梳理了下自己亲眼见过和听同行聊过的泄露路径,基本集中在六个方向:
- Git 历史。这是最经典的。项目早期为了跑通 demo,把 key 写进配置文件,后来哪怕删了,提交历史里还在。尤其在公开仓库里,等于主动白送。
- 日志与调试输出。Agent 的 debug 模式很喜欢把完整请求体打出来,而请求头里就带着 Authorization。
- 聊天记录。很多人为了快速联调,会把 key 直接贴在团队群里、私聊里,甚至发给模型对话界面所在的网页控制台。
- 第三方依赖。某些插件、工具库会在上报异常的时候附带环境变量快照,key 就跟着飞出去了。
- 提示词注入。模型被恶意指令诱导,把当前会话中可见的密钥拼进返回内容。
- 镜像和构建缓存。Dockerfile、pip 缓存层、CI 缓存目录里都可能残留 key。
每一条路径单看都挺弱智,但它们组合在一起,就成了 Agent 项目的常态。最麻烦的是,泄露往往不是被"偷走",而是被"顺走"——一个本来就想看看数据的同事、一台中了马的开发机,都是以不经意的方式把 key 拿走的。
1.2 传统密码管理工具为什么救不了 Agent
我一开始的思路是:用密码管理器的 CLI 把 key 存起来,运行时手动取一下不就行了?试过之后发现完全不是那么回事。
密码管理工具的设计假设是"人工交互",打开客户端、解锁主密码、复制一条记录。而 Agent 的运行时是无人值守的,它需要在启动瞬间拿到 key,可能还要周期性地刷新。你不可能让一个为了调 API 的工具自己去弹密码框。再一个是审计能力,密码管理器的记录大多平铺直叙,没有"谁在什么时间用哪个 key 调用过哪个服务"这种细颗粒度。Agent 场景需要的是面向机器的密钥服务,不是面向人的密码柜。这就回到了那个老判断:工具选错了,再努力也是事倍功半。
2. OpenClaw 的定位与 Secrets Manager 的设计思路
OpenClaw 这个项目名字本身就挺有意思,取的是龙虾那个螯爪的意思,打算把散落各处的密钥"钳"住。它不是一个纯加密工具,而是围绕 Agent 运行时打造的一整套密钥生命周期管理方案。核心就三件事:安全存储、运行时注入、自动轮转。很多文章只讲前两件,但在我看来,轮转才是真正把安全水位提上去的关键。
2.1 整体架构和核心模块
我这边实际部署下来的架构大致分四层:
- Vault(密钥保险库):负责存储加密后的密钥本体,落盘之前必须经过加密。
- Agent Runtime Agent(运行时插件):负责在 Agent 进程启动时,把密钥解密并注入到对应环境变量或配置引用处。
- Rotation Worker(轮转工作器):监听轮转策略,到点向模型服务商申请新 key,同时把旧 key 标记为"退役中"。
- Audit Store(审计存储):记录每次存取、注入和轮转的行为日志。
这四个模块可以全部跑在本地,也可以拆成服务。个人项目我用单机模式,团队项目则把 Vault 独立部署。判断标准很简单:如果你的 Agent 会在一台机器之外被调用,那就别把 Vault 和运行时耦合在同一个进程里,否则等于把保险柜和财务室修在一个房间。
2.2 加密存储链路:从输入到落盘
OpenClaw 的加密思路是信封加密(Envelope Encryption),这是一套很成熟的实践:主密钥只用来加密数据密钥,数据密钥才用来加密真正要保护的密钥本体。
实际写入时流程是这样的:
- 用户调用
openclaw secret set时,明文密钥只存在于内存中。 - Vault 向主密钥系统请求一个一次性数据密钥(DEK)。
- 用 DEK 对明文加密,得到密文。
- 再用主密钥(KEK)加密 DEK,得到信封。
- 密文和信封一同落盘,明文随手销毁。
这样做的原因是性能与安全的折中。主密钥如果直接加密所有数据,任何一次数据写入都要经过它,一旦主密钥泄露,全库沦陷。而信封加密允许我们用一把主密钥管理很多把临时数据密钥,即使某个 DEK 泄露,也只会暴露对应的那一条密钥。
注意:主密钥不要放到和 Vault 同一台机器上。我见过有人图方便把主密钥文件放在 Vault 的配置目录里,这相当于给保险柜配了一把挂在旁边的钥匙。最起码要分成两个不同的存储位置,最好的方案是交给独立的主密钥管理系统托管。
3. 实操:把 API Key 迁进 Secrets Manager
光讲原理不落地是耍流氓。这部分我按自己的真实操作顺序,写一套可以直接照做的迁移流程。我的示例环境是 Ubuntu + Python 3.11 + Docker Compose,OpenClaw 版本以当时最新的稳定版为准。
3.1 初始化 Vault 并写入首批密钥
第一步是初始化一个 Vault 目录。这个命令会在本地生成主密钥引用配置、存储索引和权限策略的模板:
openclaw vault init --path ./vault --kms local--kms local表示先用本地模拟主密钥系统,方便本地开发。注意这只是开发模式,千万不能直接用到生产环境。生产环境应该指向一个独立的主密钥管理系统,比如你所在云平台提供的 KMS 服务,或者自建的 HSM。
初始化完成后,写入第一条密钥:
openclaw secret set provider_api_key --value 'sk-xxxxxx'也可以从文件读入,避免 key 出现在终端的 shell history 里:
openclaw secret set provider_api_key --from-file ./tmp_key.txt rm ./tmp_key.txt3.2 Agent 运行时的注入方式
OpenClaw 提供了两种注入方式。第一种是启动前解析,适合配置文件驱动的 Agent 框架。你在 Agent 的配置里声明一个密钥引用:
# agent.yaml model: provider: some_provider api_key: ${secret:provider_api_key}然后在启动命令前加一个预处理步骤:
openclaw agent exec --config agent.yaml -- python run_agent.pyopenclaw agent exec会解析配置文件里的${secret:...}占位符,临时拉取解密后的密钥,注入到进程环境中,然后才启动 python 进程。进程结束后,环境变量自然消失,不留太多痕迹。
第二种是运行时调用,适合你正在自己开发 Agent 框架、想要更细的控制。这时候直接用 Python SDK:
from openclaw import SecretClient client = SecretClient() api_key = client.get("provider_api_key") # 不要再打印、不要再写日志,直接用即可这里有个特别重要的实操心得:无论是环境变量注入还是 SDK 获取,拿到密钥后都不要做任何可能把它带进上下文的操作。有些人拿了 key 之后顺手print(key)调试,或者塞进一个包含全部请求参数的 dict 里,下一跳就可能进日志。我对自己的要求是:密钥变量一旦使用完毕,立刻del或者置空。
4. 自动轮转:让密钥“到期即换”
如果说加密存储是"锁门",那自动轮转就是"定期换锁芯"。密钥的最大敌人是时间:用得越久,泄露概率越高。轮转的核心价值不在于防止某一次泄露,而在于把"泄露后损失窗口"压缩到足够小。
4.1 三种轮转策略的选择
OpenClaw 支持三种轮转策略,我分别说说适用场景:
| 策略 | 触发方式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|---|
| 定时轮转 | 每天/每周固定时间 | 稳定运行的服务型 Agent | 简单可预期,方便配合维护窗口 | 无法应对突发泄露 |
| 用量轮转 | 累计调用次数达到阈值 | 高并发、账单敏感的 Agent | 能控制单把 key 的暴露面 | 需要精确的计数和状态同步 |
| 事件轮转 | 检测到异常、泄露告警 | 安全要求高的生产环境 | 响应最快,能紧急止血 | 依赖监控质量,告警噪声会带来成本 |
实际配置里,定时轮转是默认选项,因为模型服务商大多支持在后台生成多个 key,轮换本身没有额外成本。我的建议是:日常运行先上定时轮转,把周期设为 7 天。等基础设施稳定后,再叠加事件轮转作为应急手段。
在 OpenClaw 的配置里,定时轮转大概长这样:
rotation: provider: some_provider schedule: "0 3 * * 1" # 每周一凌晨三点 strategy: dual_window retention_days: 24.2 双活跃窗口机制与失败处理
轮转最怕什么?怕换到一半,Agent 还在拿旧 key 调接口,然后被服务商拒绝,服务直接中断。为了解决这个竞态问题,OpenClaw 默认用双活跃窗口策略:
- 在轮转时间点,先生成一把新 key,写入 Vault,状态标记为
active。 - 旧 key 暂时不销毁,标记为
grace,保留retention_days天。 - 在这段窗口期内,新老 key 都允许通过鉴权。
- 窗口结束后,OpenClaw 调用服务商接口主动禁用旧 key,并从 Vault 删除密文。
这个机制的核心思想是"先提升、再收缩",宁可让旧 key 多活两天,也不能让服务断档。前两次我用 OpenClaw 时没注意retention_days配得太短,结果轮转后正好赶上 Agent 的缓存请求还在用旧 key,直接报 401。把这个值从 0 改成 2 之后,再也没复发过。
实践提示:就算有双活跃窗口,轮转器本身也要做失败重试。OpenClaw 默认重试 3 次,间隔分别是 30 秒、2 分钟、10 分钟。如果你对接的服务商接口偶尔抽风,可以适当加大重试间隔。我见过为了图快把重试调到 1 秒的,结果服务商限流,反而拖慢了轮转。
5. 真实环境里的常见问题与排错实录
用得越深,坑越真实。这部分是我在真实环境里逐个踩过、并记录下来的排查经验,直接整理成表,省得你走弯路。
5.1 权限、证书与时钟类问题
| 症状 | 根因 | 排查方向 | 解决办法 |
|---|---|---|---|
启动时报permission denied | Agent 运行用户没有 Vault 目录读权限 | 检查 Vault 目录 owner 和运行用户 | 把运行用户加入 Vault 组,或调整 systemd 的 User 配置 |
| 轮转时证书校验失败 | 自建主密钥系统证书未信任 | 看轮转工作器日志里的 TLS 错误 | 把自建系统 CA 加到系统信任链,而不是临时跳过校验 |
| 时间戳校验失败 | 机器时钟偏差过大 | 对比 Vault 服务器与 Agent 机器的时间 | 部署 chrony 或 ntpd,确保时间同步在 30 秒内 |
| 获取 key 返回空值但无报错 | 密钥写入的 namespace 与读取的不一致 | 检查secret set和agent exec是否指定了同一个 namespace | 统一在配置里写明 namespace |
权限问题最常见。我早期在一个团队环境里部署,所有 Agent 进程都用同一个低权限用户跑,但 Vault 目录是初始化时用管理员身份生成的,owner 直接变成了 root。解决办法很简单,chown -R一把梭,但更规范的做法是初始化时就用将来运行 Agent 的用户。
时钟偏差是另一个隐蔽坑。信封加密里对 DEK 的授权往往带有效期,如果 Agent 机器的时间比 Vault 慢了十几分钟,解密操作会因为"授权尚未生效"而失败。这类问题特别容易伪装成网络或权限问题,因为报错信息可能只是一句含糊的operation failed。
5.2 轮转竞态、日志泄露与迁移技巧
轮转竞态的问题我在前面提过,这里再补充一个经典场景:多个 Agent 实例同时启动,都去轮转一把 key。OpenClaw 默认没有分布式锁,如果你的 Agent 以多副本模式运行,必须在部署层面保证同一时间只有一个轮转工作器在跑。我是用单一 cron 副本解决的,团队模式下也可以把它独立成一个服务,避免和 Agent 主进程耦合。
日志泄露这事,很多人觉得"我打印的时候只打了 key 的后四位",但问题在于日志系统的检索和截图传播是防不住的。我的建议是三层防火墙:
- 代码层:在日志配置里加正则过滤器,凡是匹配密钥格式的字段一律打码。
- 框架层:调试模式默认关闭 HTTP body 输出,保留 header 字段名但不输出值。
- 流程层:定期用关键词扫描日志和 Git 历史,我一般扫
sk-、api[_-]?key这类模式。
迁移还有一个技巧:不要一次性把所有 Agent 切到 OpenClaw。我先挑了一个非核心的 Agent 做试点,配置、跑通、观察轮转行为一个月之后,再推广到其他项目。大迁移最容易出的问题就是"同一个 key 被多个 Agent 用了",这时候轮转会把所有 Agent 一起打挂。正确做法是让每个 Agent 拥有自己独立的 key,迁移过程里逐个分配和切换。
6. 除了存和转,Agent 密钥卫生还有哪些要注意的
把密钥从裸奔状态救回来只是第一步。我用 OpenClaw 这段时间最大的感触是:密钥管理不是工具问题,而是使用习惯问题。工具再强大,如果你把生产 key 和个人 key 混着用、没有审计、没有应急计划,最后还是会有漏洞。
6.1 密钥分级、最小权限与审计
我建议把所有密钥分成三个等级:
- 一级:生产环境 Agent 使用,涉及资金和核心数据,永不落入个人电脑。
- 二级:测试环境 Agent 使用,额度受限,可以进入开发机。
- 三级:一次性体验用途,用完即焚。
OpenClaw 支持同一个 Vault 下按 namespace 隔离不同等级的密钥,配置上很简单,但关键是申请流程要配套。我用下来最舒服的模式是:一级密钥必须通过内部工单申请,审批后自动写入 Vault,开发者拿到的是存储位置而不是明文。
审计日志不能只是"留着",要定期翻。我每周会花十分钟,看一眼有多少次 secret 读取、谁在深夜轮转了 key、有没有反复被拒的鉴权尝试。这些行为在周期内看不出什么,但坚持几周会形成一种直觉,异常数据一眼就能跳出来。
6.2 账单异常与应急处置
密钥泄露的最终表现往往是账单异常。模型服务商的调用量突然飙高、陌生地域的 IP 出现、凌晨时段的异常并发,这些都是信号。OpenClaw 的审计可以和账单数据做关联,帮你在几分钟内定位到"哪个 key 在什么时候被滥用"。
应急预案建议提前写好,别等出事再想。我的应急流程大致是:
- 立刻在 Vault 中把对应密钥标记为
compromised。 - 运行事件轮转,让服务商生成新 key 替换它。
- 确认旧 key 已禁用,再逐步排查泄露路径。
- 回顾审计日志,找到第一次异常调用的时间点,评估影响范围。
这套流程从 OpenClaw 视角看不算重,但越早练越熟练。我第一回遇到异常时手忙脚乱,光找哪个 key 出了问题就花了二十分钟,后来因为有了固定流程,五分钟以内就能完成止血。
最后再分享一个小技巧。OpenClaw 的审计日志里包含了每次轮转的新旧 key 指纹,我会把指纹前八位和模型服务商后台的 key 列表做对照,每月做一次全量核对。这个习惯帮我发现过两把"幽灵 key"——它们不在 Vault 里,却真实存在于服务商的账户下,大概率是早期测试时遗留下来的。清理掉之后,账单立刻少了零头。密钥管理的终极状态不是装一个工具,而是让每一把 key 都有来源、有归宿、有生命周期。