Christian Posta 是 Solo.io 的全球现场 CTO,也是 agentgateway 项目背后的主要推动者之一。2026 年 7 月到 11 月,他在博客上连发了四篇文章(Part 1、Part 2、Part 2.5、Part 3),讨论一个问题:AI Agent 要调 GitHub、Slack、Salesforce 这些外部 API,凭证到底该放哪?
这个系列难得的地方在于,作者没有在任何一个"看起来差不多行了"的地方停笔。每一篇的结尾都是下一篇的问题:Agent 不拿凭证了,那凭证集中到哪儿去了?加密存起来了,那被攻陷的进程自己不就能解密吗?用了 KMS Grant,配额撑不住怎么办?换成 OpenBao,又能玩出什么 KMS 做不到的花样?我顺着这四篇读下来,再加上 CB4A 草案、OpenBao 官方文档和一些行业数据,整理出这篇笔记。
先说结论,给不想读全文的人:最安全的凭证是 Agent 从未见过的凭证;但凭证不会消失,只会集中;集中之后真正的功夫在于把"解密权"从存密文的进程手里拿走;而 Control Groups------一项 Vault Enterprise 多年前就有、OpenBao 2.7 首次以 MPL-2.0 开源的能力------让这件事可以做成"应用侧没有任何组件持有能独立执行的常驻解密权限":控制器只能提议解密,Broker 只能批准,这是 AWS KMS 两条路线都给不出的授权平面原语。需要说清的是,它管的是"谁能触发解密",密钥材料仍在托管进程里;真正把密钥材料也移出进程的,是同版本另一个功能 External Keys。
一、为什么 Agent 手里的凭证格外危险
Posta 在 Part 1 里先把问题的根子挖出来:现在 Agent 用的凭证,绝大多数是长期有效、粒度粗糙的 bearer 凭证。"Bearer"这个词就是字面意思------谁拿着这串字节谁就能用,请求本身证明不了持有者是不是合法持有者。提示注入、凭证外泄、意外暴露,听起来是三种攻击,其实都是同一件事的三个入口:凭证的字节离开了 Agent,而且还照样能用(Part 1)。
更麻烦的是,泄露甚至不需要攻击者。Agent 是目标导向的随机系统,它可能主动把 API Key 递给另一个 Agent------"帮我办这件事,这是我的 key"------并且真心认为自己在解决问题。对付这种局面,手段只有三种:让凭证快速过期,把泄露后的可用窗口压到几分钟;让凭证不可转让,比如 DPoP 那种绑定私钥的持有者约束;或者干脆不给 Agent 任何凭证。前两种是缩小爆炸半径,第三种是把会炸的东西直接撤掉(Part 1)。
这不是杞人忧天。OWASP 2026 年版的 LLM Top 10 把提示注入连续第三年放在第一,"过度代理权"从第 6 蹿到第 3------榜单历史上最大的一次爬升。这一版第一次引入了真实事件数据加权:分析了 7,714 起公开 AI 安全事件(Aembit、CSA)。与此同时,机器身份的数量早就失控了:各家调查口径不一,ESG 2024 年说是 20:1,CyberArk 2025 年说 82:1,Entro Labs 2025 年上半年报告 144:1,Palo Alto Networks 2026 年的数据是 109:1,其中还包含每个人类对应 79 个 AI Agent 身份------而 78% 的组织连 AI 身份的创建和注销都没有成文策略(Identra、Palo Alto Networks 分析、Stig)。GitGuardian 的 2026 年报告说,2025 年公开 GitHub 提交里新泄露的硬编码密钥有 2,865 万个,同比涨了 34%(Identity Security Research)。

真正把这个问题钉死的,是 2026 年 3 月的 LiteLLM 供应链事件------CB4A 草案拿它当现实动机,Posta 在系列里反复引用。攻击组织 TeamPCP 先利用 Trivy 扫描器里配置不当的 pull_request_target 工作流偷了 Aqua Security 的凭证,污染 trivy-action 的版本标签;LiteLLM 的 CI 跑到被投毒的 Trivy 时,恶意载荷从 Runner 环境变量里掏走了 PyPI 长期发布令牌,然后直接往 PyPI 传了带毒的 1.82.7 和 1.82.8。GitHub 源码全程是干净的,pip install --require-hashes 也能过,因为攻击者把 RECORD 哈希文件一起重新生成了。带毒版本被下载约 4.7 万次,专偷 SSH 密钥、云凭证、AI API Key 和 Kubernetes 密钥(CVE-2026-33634,CVSS 9.4)(Ruh.ai、MintMCP)。
故事还有续集:2026 年 10 月 21 日,LiteLLM 又被攻破了一次。一枚 5 月份泄露后始终没吊销的 master key 被用来提取组织所有者的 GitHub CLI OAuth 令牌,劫持发布流水线推了个带凭证窃取后门的 1.107.0,在 PyPI 上活了约两小时(Microsoft Security、LiteLLM 官方)。一个专职替几十家厂商保管 API Key 的 AI 网关,一年之内被以两种方式打穿------CB4A 草案那句话就是这么应验的:AI 基础设施把长期凭证集中在单个进程里,那个进程一旦被拿下,就是灾难性的访问。所以 Posta 反复强调的那点值得记住:问题从来不是"有没有加密存储",而是被攻陷的进程自己手里就握着解密能力。
二、CB4A 的三种模型,以及 Posta 为什么把推荐顺序倒了过来
CB4A(Credential Broker 4 Agents)是 Kenneth G. Hartman 2026 年 3 月提交给 IETF 的草案。骨架很清晰:策略决策点(PDP)和凭证投递点(CDP)必须分开------拍板的组件不碰凭证,发凭证的组件不参与拍板;身份层直接用 SPIFFE/SPIRE 的工作负载身份,不搞自建;令牌通过 DPoP(RFC 9449)做持有者约束,偷走令牌没有配套私钥也用不了(IETF CB4A)。
草案给了凭证中介三种模型:Model A 是代理网关,Agent 从头到尾不见凭证,网关在出口方向注入;Model B 铸造一个短期、窄范围的令牌交给 Agent 直连目标;Model C 直接把长期凭证交出去再事后吊销,被草案标为最弱、不推荐。草案的推荐主模式是 Model B,Model A 只是回退选项。
Posta 明确唱反调:对今天的企业来说,顺序应该倒过来,Model A 才是主模式 。理由有两个。第一个很现实:DPoP 要两头配合------授权服务器得签发密钥绑定的令牌,资源服务器得逐请求校验持有证明,缺任何一半就退回 bearer。而现实世界里,GitHub PAT、各种 API Key、绝大多数 SaaS OAuth 令牌,全都不校验持有者约束(Part 1、RFC 9449)。第二个是草案自己承认的:Model B 的弱点写着"Agent 在内存里持有真实(尽管是临时的)凭证"。短期比长期好,但它还是一个放在"我们刚说了不信任的随机系统"里的可复用秘密------缩短窗口不等于关上窗口。
Model A 换来的东西则很实在:Agent 永不见凭证;Broker 可以实时检查、记录、过滤每一个请求和响应;目标 API 不需要任何配合,什么都能接。草案给 Model A 的定位本来是"高安全环境的首选",Posta 的意思无非是:现在哪个企业不是高安全环境(Part 1)。
落到实现上,agentgateway(Solo.io 创建的 Rust 数据面网关,2025 年 8 月捐给 Linux 基金会,后来成为 Agentic AI Foundation 的第四个托管项目)把跨身份域的凭证中介拆成两段。连接建立时,MCP 客户端走完 IdP 登录拿到 JWT------这一步只在连接时做一次,所以叫"eager auth";之后如果某次工具调用需要的上游 OAuth 凭证还没有,网关不转发请求,而是回一个授权 URL,用户在自己浏览器里完成授权,网关接住 access/refresh token 存进 STS,按(用户身份, 资源)做键------Solo 管这一步叫 elicitation。往后该用户对该资源的每次调用,网关在出口方向把令牌注入 Authorization 头。MCP 服务器不知道这一切发生过,Agent 也不知道,它只拿到 API 的响应(Part 1、Yahoo Tech)。
读到这里多半会有人想问:ID-JAG 不就是干这个的吗?ID-JAG(Identity Assertion JWT Authorization Grant,Cross-App Access 的核心)让企业 IdP 把用户身份断言直接换成目标授权服务器认的访问令牌,用户连同意页面都不用看。Posta 的回答分两层。采用度上,ID-JAG 要企业 IdP 和资源方授权服务器两头都实现才行,目前覆盖有限------GitHub 不会收你 Okta 租户签的 ID-JAG。更关键的是,ID-JAG 并不消灭令牌库 :流程跑完你手里照样是一个 provider 作用域的访问令牌,总得有个组件存着它,不然每次工具调用都得把整个交换重放一遍。用他的话说,"同意屏幕消失了,令牌库没有。"ID-JAG 目前还是 Internet-Draft(2026 年 5 月更新到 -04),MCP 在 2026 年 6 月把基于它的企业托管授权收成了官方扩展,Okta、Auth0、Anthropic 已经实现,但生态还在早期(IETF ID-JAG、Auth0)。
于是矛盾转移了:Agent 那头干净了,STS 的数据库现在装着每个用户的每个上游凭证。CB4A 的威胁模型对这一点毫不客气------Broker 沦陷(TM-1)评为 CRITICAL,"Broker(CDP)是整个架构里价值最高的目标"。Part 2 就是从这句话往下写的(IETF CB4A)。
三、令牌库怎么守:把"同意解密"的权力从存密文的进程手里拿走
Part 2 先立了个所有方案共用的地基:信封加密。每条凭证裹两层 AES-256-GCM------内层用每次写入新生成的 DEK 加密令牌 JSON,外层用 KEK 把这 32 字节的 DEK 包起来。因为 KEK 只加密 32 字节的小东西、从不碰凭证数据,它就可以放在 STS 够不着的地方。两层密文都用 AAD 绑上下文:AAD 不加密也不保密,但混进 GCM 的认证标签里,解密时给的 AAD 不对就校验失败------密文没法跨行、跨用户挪窝。外层 AAD 里最要紧的一项是 owner = HMAC(sub),把每一行密文焊死在特定用户身上(Part 2)。
三种模式的差别,说到底就是一句话:KEK 在谁手里,解开一个 DEK 需要谁点头。
Mode 1,KEK 放 Kubernetes Secret。 这是最顺手的默认项,也是灾难。KEK 和要保护的东西在同一个信任域里,任何能读那个 Secret 的角色------ServiceAccount、cluster-admin、etcd 备份------都能拿走主密钥,离线、永久地解密整个令牌库,而且进程内解密没有任何审计痕迹。Posta 的原话是除了演示环境哪儿都别用(Part 2)。
Mode 2,KEK 进 AWS KMS。 主密钥永不离开 KMS,控制器调 kms:Encrypt/kms:Decrypt 完成包裹和解包,每次解包都是 IAM 门控、逐次、可审计、可吊销的事件。但这里有个容易被误读的地方,Posta 说得非常直白:KMS 拦不住被攻陷的控制器解密你的令牌库,它只能让对方没法静默地、批量地、离线地干 。控制器握着常驻 kms:Decrypt,可以坐在那儿循环调用直到把整张表走完。KMS 把"砸了就跑"变成了"在线、可限速、可观测"的操作------配上 CloudTrail 解密速率告警和几行蜜罐行,对很多部署来说这已经是诚实的终点了(Part 2)。
Mode 3,Broker 门控的 KMS Grant。 这一步把常驻解密权从控制器手里彻底收走:控制器只剩密文和 KMS 被授权方(grantee)身份,每次解密都得有独立 Broker 当场铸造的短期、凭证作用域 Grant。Broker 握着 kms:CreateGrant,但没有数据库、没有密文、没有任何解密能力------解密从此需要两个独立信任域同时到场 。IAM 策略钉住 Grant 的形状(被授权方=控制器角色、操作=Decrypt、约束=EncryptionContextSubset),owner 的值则由 Broker 重新验证用户主体令牌后自己算出来填进去,所以给 Alice 铸的 Grant 解不开 Bob 的行。代价是延迟、代码量、运维面,以及 Broker 成了新的受信组件(Part 2)。
为什么不让 KMS 直接按调用鉴权?因为 KMS 认证的永远是控制器的 IAM 角色,它看不见终端用户;密钥策略是静态的,表达不了"owner 必须等于这次请求的认证者"------这条链路上没有任何组件知道那是谁。Grant 是 KMS 唯一能做动态、逐请求、逐用户作用域的机制,而铸 Grant 的主体必须先重新验证用户,否则作用域就是空话。Broker 不是外挂,是"逐用户作用域"这个需求推导到底的必然产物(Part 2)。
三种模式在"控制器沦陷"下的表现
| Mode 1:K8s Secret | Mode 2:KMS 常驻解密 | Mode 3:KMS Grant | |
|---|---|---|---|
| 数据库被拖库 | 仅密文,安全 | 仅密文,安全 | 仅密文,安全 |
| 控制器沦陷 | 全库离线永久解密 | 在线期间可批量解密,可观测可吊销 | 只能解开 Broker 当下为该用户该行授权的那一份 |
| 审计 | 无 | CloudTrail 全量记录 | CloudTrail + Broker 决策日志,都在控制器之外 |
| 撤销能力 | 密钥泄了收不回 | 撤 IAM 权限或禁用密钥即止血 | 控制器根本没有可泄的权限 |
四、Part 2.5:Grant 撞上天花板,还有"在用凭证"这个盲区
Part 2 发出去之后,Stephane Nangue 在评论里指出 Mode 3 扩展不了------Posta 验证完承认他是对的,专门写了 Part 2.5。核心问题是 AWS KMS 的配额结构:CreateGrant 和 RetireGrant 默认每账户每区域 50 次/秒,而共享对称 Decrypt 配额是 10,000 到 100,000 次/秒。Mode 3 下每次令牌交换是三次 KMS 调用(CreateGrant、Decrypt、RetireGrant),铸造速率就是全系统的吞吐上限 ------而且这个配额还得跟账户里所有替你铸 Grant 的 AWS 服务分(EBS 每挂一个加密卷就铸一个)。一个每次工具调用都做交换的企业级共享网关,摸到每秒 50 次是很快的事(Part 2.5)。

更隐蔽的是 Grant 不会自己过期,得有人退休它,每个密钥最多挂 50,000 个。Broker 崩了、RetireGrant 被限流、退休路径出 bug,都会留下滞留 Grant------按铸造上限算,17 分钟就能把密钥挂满,之后 CreateGrant 直接返回 LimitExceededException,全环境的令牌交换瘫痪。而每一个没退休的 Grant 都是一行"控制器随时能解密"的隐藏后门------退休失败意味着系统悄悄退回 Mode 2,甚至更糟(Part 2.5)。
解法是不发 Grant 了,翻转谁调 Decrypt :Broker 拿常驻 kms:Decrypt 但永不碰密文,控制器留密文但完全没有 kms:Decrypt。控制器把主体令牌、新鲜度证明、资源、elicitation id 和包好的 DEK 发给 Broker------注意不含加密载荷,Broker 永远看不到凭证本身。Broker 跑完和 Mode 3 相同的校验,用自己算的 owner 构造 EncryptionContext 调 Decrypt,把明文 DEK 经 mTLS 还给控制器。KMS 照样通过 AAD 强制 owner 绑定:控制器递来 Bob 的 DEK 却出示 Alice 的令牌,AAD 校验一样失败。CloudTrail 现在记在 Broker 的角色名下,和 Broker 自己的决策日志对接------审计叙事反而更干净了,因为控制器身份发起的任何 Decrypt 调用都可以直接当攻击看(Part 2.5)。
Part 2.5 还修正了 Part 2 一个说过头的结论。拿下控制器的攻击者根本不需要批量解密令牌库------控制器就在每次合法交换的热路径上,每个活跃用户的 GitHub/Slack/Jira 令牌都以明文从它手里过,蹲几个小时就收齐了。更糟的是 DEK 是"按写入"不是"按请求"的,控制器拿到 Alice 某行的 DEK 之后可以一直重开那行,直到下次刷新重写;而 DEK 解开的是整个令牌响应------access token、refresh token、id token 全在。再配上控制器持有的 OAuth client secret,攻击者被赶出去之后还能继续铸新访问令牌(Part 2.5)。
对策是把 refresh token 和 client secret 整体搬进第四个组件,Refresher。它是完成授权码交换的 OAuth 客户端,令牌库同样是信封加密加 Broker 门控,唯一的输出是某一行的新 access token ------refresh token 永远不离开 Refresher。这样控制器库里只剩 access token 行,而 access token 会自己死掉(GitHub App 用户令牌 8 小时、Slack 12 小时、Google 1 小时)。Refresh 路径的调用频率大约是活跃用户数除以 access token TTL,比热路径低几个数量级,所以负担得起最严格的门控------Posta 甚至说,如果哪天要把这套系统的一部分塞进 Nitro Enclave 这种飞地,Refresher 是最值得放进去的那个。盲区也很明确:没有 refresh 半边的长期 PAT、API Key,只能继续待在控制器库里(Part 2.5)。
五、Part 3:换 OpenBao,怎么把 Mode 3 重做一遍
Part 2 和 2.5 都长在 AWS KMS 上,但最关心凭证集中的组织,往往恰恰不能、或者不愿把守护所有用户凭证的钥匙交给超大规模云厂商。Posta 列了选 Vault/OpenBao 的四类理由:运行位置(本地、气隙、主权云,AWS KMS 够不着的地方);多云与可移植性(一套密钥服务、一套策略模型、一条审计轨迹横跨各云和本地,密钥跟着工作负载走);既有投资(已经有 Vault/OpenBao 集群、团队和审计管线);托管权(KEK 自持、源码可读、授权配置进 Git)(Part 3)。
OpenBao 本身值得交代两句。HashiCorp 2023 年 8 月把 Vault 从 MPL 2.0 转成 BSL 之后,社区从最后一个 MPL 版本分叉出 OpenBao,交给 Linux 基金会(OpenSSF)治理,保持与 Vault 的 API 兼容。它不是个停更的快照:2.5 加了水平读扩展,2.6 加了按命名空间封印(每个命名空间独立的 Shamir 密钥材料,可以单独吊销某个租户)和工作流引擎,2.7(2026 年 10 月发布)把 Control Groups 和外部 HSM/KMS 密钥托管(External Keys)开源,并加入后量子算法------Control Groups、命名空间这类在 Vault 里属于 Enterprise 付费档的功能,在 OpenBao 免费。GitLab 19.0 已把 OpenBao 收作原生密钥引擎,Broadcom 在 vSphere Supervisor 里集成,NVIDIA 也在采用者名单里(OpenBao Blog、DevToolLab、iThub)。

在 OpenBao 方案里,扮演 KMS 角色的是 Transit 秘密引擎 :它提供"加密即服务",不存送进来的数据,支持密钥派生------同一把密钥可以基于调用方给的 context 值派生出不同子密钥。Part 3 利用这一点给每个用户建一把 Transit 密钥(derived=true),每行的 context 派生不同子密钥,包好的 DEK 只有拿本行的 context 才解得开------这和 KMS 用 EncryptionContext 做 AAD 是同一个效果(OpenBao Transit 文档、Part 3)。Posta 给了两种实现 Mode 3 的设计,分歧点在于谁来扮演"用户验证者兼授权者"。
设计一:让网关替用户登录 OpenBao。 出发点是,Broker 之所以存在,是因为 KMS 认证不了终端用户(它永远只看到控制器的 IAM 角色);而 OpenBao 的 JWT 认证方法可以直接认证用户。那就让 OpenBao 一人分饰两角:每次实时请求,网关拿用户的主体令牌调 auth/jwt/login,OpenBao 对照 IdP JWKS 验签,返回一个策略模板化到该用户 Transit 密钥的短期解密令牌;网关把它转给控制器,控制器带着某行的包好的 DEK 和 context 调 transit/decrypt,拿回明文 DEK 后本地解开上游令牌------OpenBao 全程不见凭证。这个设计来自 Mesut Özdil 在 Part 2 发布后寄来的可运行演示,有些地方(比如令牌 TTL、失败即关闭的审计)比 KMS 版做得还好(Part 3)。
但它有两个绕不过去的问题。第一个是登录路径的活体性:JWT 登录证明的是"用户是谁",不是"用户此刻正在发请求"------Agent 出口场景里用户可能正在睡觉,机器在重放他的令牌。控制器必须持有主体令牌(它是 RFC 8693 交换的输入),如果它够得着登录路径,被攻陷的控制器可以拿这一小时收集的所有 JWT 逐用户登录、解密。token_bound_cidrs 帮不上忙,因为登录和使用校验的是同一份列表;管用的做法是只允许网关经 mTLS 调 auth/jwt/login,其余一律拒绝------这样控制器能见到的 OpenBao 令牌,只剩网关在实时请求中递给它的那些。第二个是粒度:登录返回的令牌以用户为作用域,而 context 不是秘密(就摆在控制器库里),所以控制器可以拿 Alice 的任何一行配上它自己的 context 通过策略校验。可以用 token_num_uses=1 封顶、并在解密路径的 allowed_parameters 里排除 batch_input------一次实时请求最多开一行,但开哪一行还是控制器说了算 ,攻击者会把 Alice 的每次实时请求都花在她最值钱的凭证上。另外设计一丢掉了 Broker 的逐请求检查能力(CEL 规则、蜜罐行、拒绝原因日志),因为 auth/jwt/login 只评估 JWT 声明,看不到请求上下文(Part 3)。
设计二(Mode 3b):Broker 加上 OpenBao 2.7 的 Control Groups。 Mode 3b 保留 Part 2 的 Broker 做用户验证者兼授权者,OpenBao 只当密钥托管者,而且 Broker 什么都不铸造。基座是 Control Groups。这里要先纠正一个容易产生的误解:它不是 OpenBao 的首创------这个功能在 Vault Enterprise 里早在 1.8(2020 年 7 月)之前就已存在(1.8 增补的是按 capability 粒度触发),OpenBao 自己的 RFC 也明言 "The api is following Vault Enterprise";2.7 的贡献是第一次把它以 MPL-2.0 开源,让不付企业版授权费的部署者也能用上。机制是这样的:在策略路径上声明 control_group 之后,对该路径的请求不会直接执行------OpenBao 存下请求副本,回给调用方一个 wrapping token(可以理解成"一个还没执行的请求的取件凭证")和它的 accessor(别的组件可以用这个 ID 引用令牌、但用不了令牌);策略指定的审批组成员通过 sys/control-group/request 查看请求详情,通过 sys/control-group/authorize 批准;然后调用方拿 wrapping token 调 sys/wrapping/unwrap,这一刻 OpenBao 才真正执行解密 。批之前 unwrap 报错,TTL 内没人批就过期作废(OpenBao Control Groups 文档、Part 3)。
2.7 这个开源实现的能力边界也要如实交代:审批因子只能用 Identity Groups 表达;self_authorization 默认关闭(请求者不能批自己的请求);OpenBao 不会主动向审批人推送通知,审批方得自己轮询待批请求;也没有 Vault Enterprise Sentinel 那样的策略语言,复杂的审批规则只能写在 Broker 自己的代码里(OpenBao Control Groups 文档)。
在 Mode 3b 里,控制器对 transit/decrypt/user-* 持一个静态策略,control group 因子把审批组指定为只含 Broker 一个成员的 cb4a-broker,TTL 30 秒,禁止自批准。Broker 用自己的 SVID 登录(比如 cert auth),全部策略只有 sys/control-group/request 和 sys/control-group/authorize 的 update 权限------没有 Transit 路径、不能铸令牌、不能写策略。控制器把 accessor、主体令牌、网关的新鲜度证明转给 Broker;Broker 读出 OpenBao 暂存的请求原文,自己算期望的 context(owner 来自刚验过的主体令牌、resource 来自网关证明、elicitation_id 由控制器指认),只有 request_path 对得上该用户的密钥、request_data.context 完全匹配,才批(Part 3)。
这套设计达成的特性,要分两个平面说清楚。授权平面上,应用侧不再有任何组件持有能独立执行的常驻解密权限 :控制器只能"提议"一次解密;Broker 只能批准控制器已经提出的请求(它只见 accessor,永不见 wrapping token)。Broker 被攻陷,自己一个人解不开任何东西;控制器被攻陷,没有网关的实时证明也拿不到任何批准。密钥平面上则要保持诚实:KEK 仍在 OpenBao 进程里,它才是那个真正执行解密的组件------"无人持有常驻解密"说的是应用组件,托管者本身不在此列。如果威胁模型连托管进程也不信任,同版本 2.7 的 External Keys(GH-3956)可以把 Transit/PKI 的密钥材料挪进外部 HSM 或 KMS,OpenBao 只当密码学操作的代理------对应的 Vault Enterprise 功能是 Managed Keys。所以完整的"存密文的没有解密能力、托管密钥的不能独自授权"链条,是 Control Groups 与 External Keys 两个正交功能拼出来的,Control Groups 一个功能扛不下这句话。对比之下,Part 2 的 Broker 握着 CreateGrant,Part 2.5 的 Broker 握着常驻 kms:Decrypt。双层绑定依然成立:密文在包裹时绑定到行(derived=true 加逐行 context 派生子密钥),调用者在审批时绑定到行(Broker 的 context 校验)------张冠李戴要么过不了审批,要么过不了派生。而且 OpenBao 侧没有逐行状态,唯一的状态就是那个会随 TTL 过期的暂存请求(Part 3)。
两种 OpenBao 设计摆在一起
| 设计一:网关代登录 | Mode 3b:Broker + Control Groups | |
|---|---|---|
| OpenBao 的角色 | 用户验证者 + 授权者 + 密钥托管者 | 只做密钥托管者 |
| 解密粒度 | 用户级:一次请求可开该用户任一行(封顶一行,但选行权在控制器) | 行级:Broker 逐行审批 OpenBao 暂存的那个请求 |
| 逐请求策略(CEL/蜜罐/决策日志) | 没有,要做得挪进网关 | 在 Broker 里,网关被攻陷也波及不到 |
| 多出的组件 | 无 | Broker,请求路径上多一跳 |
| 每次解密的 OpenBao 调用 | 2 次 | 4 次 |
| 常驻解密权限(应用侧) | 控制器没有;解密令牌只在实时请求中存在 | 控制器与 Broker 都没有;KEK 仍在 OpenBao 进程内 |
| 依赖 | JWT auth(成熟) | Control Groups(OpenBao 实现发布仅数周;Vault Enterprise 早有此功能) |
怎么选,Posta 给的决策路径挺干脆:逐行作用域重要,就在 OpenBao 2.7 上跑 Control Groups 审批模式,Broker 策略严格限制在 sys/control-group/*,不装 JWT auth mount,也绝不给 Broker 任何策略写权限(那会让它变成事实上的管理员);在用 Vault 或者暂时升不了 2.7,就移植 Part 2.5 的 Broker 代解密变体(Broker 持静态 transit/decrypt/user-* 策略、自算 context、返回 DEK),接受 Broker 持有常驻解密这个和 KMS 版一样的妥协;逐行粒度不重要,就从设计一入手,把登录路径锁到只有网关能进。不管选哪条,审计日志都要送到所有组件都够不着的地方(Part 3)。

六、几点横向的看法
OpenBao 在这盘棋里的位置,是"密钥托管者"这一层的中立化和主权化。 KMS 方案把托管权交给单一云商,换来成熟的 IAM 集成和 CloudTrail 审计;OpenBao 方案用自托管换来多云可移植、源码可审、配置入 Git,以及 Control Groups 这种"请求级双人审批"原语------它在 Vault Enterprise 里存在多年、AWS KMS 没有对应物,OpenBao 2.7 把它开源了,要密钥材料也不落在托管进程就再叠加同版本的 External Keys。两者共享同一套架构骨架:信封加密、逐行 AAD/context 绑定、决策与密文分离、解密能力不落单。企业在两者之间迁移的成本主要在 Broker 和托管层的适配,不是架构重写(Part 2、Part 3)。
也要如实说 OpenBao 的现实约束:相对 Vault Enterprise,它缺灾难恢复、性能复制这类企业特性,Sentinel 那样的策略能力还在规划(CEL 化的 EGP/RGP 仍是开放 RFC),没有官方托管 SaaS,生态和"凌晨三点排障"的经验积累都更年轻。Part 3 的 Control Groups 路径直接依赖 2.7 版本(2026 年 10 月 5 日发布),对保守的生产环境来说,"等它再长一两个小版本"和"先用设计一或 Part 2.5 变体过渡"都是合理节奏(GSI 笔记、Inferensys)。
凭证中介正在收敛成行业共识,Posta 不是孤例。 Anthropic 的托管 Agent 基础设施描述过一个"工具束永不感知凭证"的专用代理;Vercel 在沙箱层做凭证注入;Cloudflare 的 Outbound Workers for Sandboxes 在出口层附加凭证;LangChain 提供沙箱认证代理;Infisical 的 Agent Vault 用 HTTPS_PROXY 重定向加占位符替换(把 __github_token__ 在 Broker 侧换成真令牌)做出同样效果,开源预览版拿过 2,300 多颗星(Infisical)。CB4A 草案自己也指出,这套架构和 CASB/SASE 平台的能力高度重叠,天然适合演进成托管服务(IETF CB4A)。身份侧的标准化同样在加速:SPIFFE/SPIRE 给 Agent 签发短期、自动轮换的 SVID,撤销就是注销注册项,没有可泄的长期秘密,正好给 Broker 的"新鲜度证明"和 Agent 身份当地基(NHI Governance、TrueFoundry);ID-JAG/Cross-App Access 则把"跨应用代用户访问"从逐用户 OAuth 同意收敛为 IdP 管理员集中授权,并被 MCP 收为企业托管授权扩展(Ping Identity、Proofpoint)。但就像 Posta 强调的,这些标准解决的是令牌怎么被授权,不解决令牌存在哪------令牌库怎么守,还是得每个部署者自己回答。
这套架构没消掉的洞,也值得摆出来。 网关沦陷是令牌库加固盖不住的:gateway 必须把明文令牌放上线路,天然处在该令牌的机密性边界内,Posta 在 Part 3 结尾明说这是他下一步要研究的问题------比如让网关完成最终解包,让"网关沦陷"成为整个系统爆炸半径的上限。热路径的明文滴漏也还在:Mode 3/3b 保护的是"未在用"的行,在用行随使用速度泄露,Refresher 只能把 refresh token 挪出热路径,access token 仍必经控制器。没有 refresh 半边的长期 PAT、API Key,防护上限就是 Mode 3 的"逐请求授权"。Broker 自己成了新的高价值目标------CB4A 的威胁模型本来就把它评成 CRITICAL,这套架构做的是风险再集中而不是消除,所以 Broker 的最小权限(只有 sys/control-group/*)、无数据库、不碰密文,每一样都不是装饰。最后是操作面:每次解密四次 OpenBao 调用、控制组 TTL 内没人批就失败、Refresher 宕机后以 access token TTL 为降级窗口------这些都要真实的容量规划和降级演练,不是画完架构图就完事(Part 2.5、Part 3)。
如果要落地,我会按这个顺序来。 先止血:盘一遍 Agent 手里的全部凭证,停掉一切"KEK 和应用同信任域"的做法,把令牌库迁进信封加密加独立密钥托管,先到 Mode 2 的"可检测、可吊销"基线。再收权:摘掉控制器/STS 的常驻解密权限,按上面的决策树在 OpenBao 设计一(简单场景)和 Mode 3b(要行级粒度)之间选;已经在 AWS 且流量不大的话,KMS Grant 版 Mode 3 仍可用,但拿 50 次/秒当红线做容量评估。然后固本:拆 Refresher,把 refresh token 和 client secret 挪出热路径,给上游开可过期的令牌类型(GitHub App、Slack 轮换这些),审计日志外送,蜜罐行上线。最后跟踪演进:盯 ID-JAG 的生态覆盖,逐步替换 elicitation 流程;盯 IETF WIMSE 的密钥绑定工作负载凭证,替代可重放的 JWT-SVID;评估把 Refresher 塞进可信执行环境的收益。
结尾
这四篇文章真正的价值,是把"AI Agent 凭证安全"这个常被一句"用 Vault 存起来"带过的议题,拆成了一条追问链:Agent 不持凭证(Part 1),凭证集中后怎么防一锅端(Part 2),方案撞上限额和在用凭证盲区怎么修(Part 2.5),不靠单一云商怎么拿到同等甚至更强的保证(Part 3)。OpenBao 在这条链里的角色要准确地说:它不是这套能力的首创------Control Groups 在 Vault Enterprise 里存在多年------而是第一次把"每次解密都需要第二方对这一次请求点头"的托管层能力以 MPL-2.0 开源,让不付企业版授权费的部署者也能拿到。在这个落点上,Mode 3 的最终形态是:控制器只能提议,Broker 只能批准,应用侧没有人持有常驻解密权限;密钥材料本身仍在托管进程手里,要再进一步就叠加 External Keys。即便如此,这仍是整条技术路线里信任集中度最低的一个落点。至于它还盖不住的网关沦陷和热路径明文,Posta 自己也没说解决完了------那大概是 Part 4 的事。
本文是基于公开资料的技术分析,不构成专业安全、合规或采购建议。生产环境的密钥管理架构请结合自身威胁模型,咨询安全专业人员。