摘要

  • draft-ietf-wimse-workload-identity-practices-06 总结了业界如何从直接管理长期秘密,转向由平台签发短期、受众受限的工作负载凭据。
  • 验证通过的凭据只证明定义内的认证事实;它本身不能证明出示者就是确切的在线实例、私钥从未复制、旧凭据已处处失效、具体操作得到授权,或预期结果已经发生。

一枚凭据不是一段完整履历

IETF WIMSE 工作组的《工作负载身份实践》草案没有把 Kubernetes、云元数据服务、SPIFFE、CI/CD 与服务网格伪装成同一种实现。它提炼的是共同路线:平台先观察工作负载环境和属性,再签发一个或多个凭据;工作负载可在平台内直接使用,也可向外部身份提供方或安全令牌服务兑换目标资源所需的访问凭据。

这条路线解决了真实问题。应用不再需要把静态客户端秘密复制进配置,再用另一套流程轮换。平台可以按较短期限和单一受众签发凭据,减少长期秘密失窃后的冒用窗口。

但安全改进很容易被写成过度承诺。签名凭据是签发者在某一政策下作出的断言,不是持有进程的完整传记。Datatracker 当前记录显示,06 版仍是活动 Internet-Draft,预期为 Informational,已提交 IESG,当前状态为“AD Evaluation::Revised I-D Needed”。它不是 RFC,不是认证标志,也不是任何运营者已经正确部署的结果证据。

真正有用的问题不是“工作负载身份是否先进”,而是每个产物究竟能证明哪一件事,在它成功以后还有哪些事实没有被证明。

第一重:平台当时看见了什么

通用流程从平台核验环境和属性开始。这里已经存在第一处控制面:平台看的是主机、IP、进程 UID、内核元数据、编排器标签,还是若干属性的组合?这些值何时采样?由哪个组件作出绑定决定?

SPIFFE把引导难题呈现得最清楚。Workload API 不必要求调用者先带一个秘密,而是根据进程属性、内核元数据或编排器标签识别调用者。它避免了“先有凭据才能取得凭据”的循环,却不会让环境观察天然无误。只按主机或 IP 区分,会让同一节点上的更多进程有机会取得同一身份;加入 UID、cgroup 或工作负载专属选择器,才能缩小表面。

因此第一份证据应是身份引导回执:签发者、观测属性、证明组件版本、政策版本、时间与决定。若签发时从未保存这些输入,日后只验证 JWT 签名无法还原当初为何把该身份交给该调用者。

第二重:身份绑定到哪个在线实例

Kubernetes 清楚区分了账号名称与运行实例。其服务账号管理文档描述了绑定令牌和 TokenRequest;WIMSE 草案则画出 kubelet 按 Pod 配置向 API server 请求令牌并把它交给容器的过程。令牌可带 namespace、service account、Pod 和 node 等 claim。

这些 claim 很重要,却不能冻结集群。原 Pod 可能终止,替代 Pod 继续使用同一个 service account,标签与调度可能变化,令牌也可能比实例活得更久。用公开密钥离线验签,可以证明令牌由相应签发者产生且 claim 通过本地规则;它不会询问关联对象现在是否仍存在。草案明确指出,对象删除后的失效只有在使用 TokenReview 时才能被发现。

运行时绑定必须有新鲜的实例级证据:具体 Pod 或进程、生命周期状态、调度与政策 epoch,以及依赖方核验的时间。多个副本共享的 service-account subject 不能代替实例身份。

第三重:签发了什么,又怎样交付

签发和交付不是一件事。签发者可以产生正确字节,错误进程仍可能读到它;文件更新也可能被旧消费者忽略。

草案比较三种交付方式。文件系统可让续期与应用解耦,但写入方需要原子替换并及时 flush。Unix socket、loopback 或 link-local 地址上的本地 API 可以按需签发或推送更新,但安全性取决于隔离和调用者识别。环境变量静态、可被监控、调试和进程检查观察,因此只要平台提供替代方式,生产环境就不应以环境变量承载身份凭据。

原子 rename 只能证明读者不会看到半截文件,不能证明所有进程已经重新打开路径、放弃旧 file descriptor 或清空内存副本。本地 API 的成功响应证明某条连接收到字节,不能证明 SSRF、重定向或受害应用没有把它送到别处。交付回执需要绑定通道、目标实例、凭据摘要和明确的接收语义。

第四重:此刻是谁在控制密钥

Bearer token 在剩余有效期内把“持有”直接变成使用能力。草案因此在平台和依赖方支持时优先建议持有证明。X.509 握手证明协议参与方在该次交换中控制相应私钥;key-bound JWT 也能让偷走 token 但没有第二秘密的攻击者难以复用。RFC 8705给出了以双向 TLS 证书约束 OAuth access token 的一种机制。

持有证明回答的是窄问题:本次协议参与者是否证明了它能使用指定密钥?它不能证明密钥从未被复制、只有一个进程能调用密钥,或该进程仍是最初被平台观察到的工作负载。sidecar、节点代理、HSM 接口或受侵运行时,都可能在不导出私钥的情况下使用它。

持有回执应把具体交换、凭据与 key identifier 绑定,并包含 freshness 与 anti-replay 状态。若业务要求独占保管,还需另行核验密钥生成、存储、调用路径与销毁。

第五重:凭据可以对谁说话

受众限制是草案最强的实践建议之一。用于向外部身份提供方联邦的凭据,应只把该提供方列为 audience。Kubernetes API 的令牌不应拿到平台外复用。工作负载访问多个资源时,应取得多个凭据,而不是携带一个多受众 bearer token。

这会缩小爆炸半径,但 audience 不是 permission。它回答“哪个验证者有资格接受这个 token”,不回答“该工作负载能否删除这条记录、签署这个发布或执行这笔支付”。RFC 7521定义 OAuth assertion framework,RFC 7523定义 JWT bearer assertion。断言处理成功可以认证客户端或支撑授权许可,目标服务仍须根据当前资源状态和具体请求作授权判断。

这也是本篇与 BTW 既有 Agentic OAuth 文章的精确分界。旧文研究的是谁来授权模型最终生成的工具参数;本篇不再讲 prompt、RAR 参数绑定或工具调用批准,而是核验更早也更长的身份引导、实例状态、凭据流转与失效链条。

第六重:轮换是否在所有位置结束

短有效期只会缩短被盗凭据可用的时间,不会产生即时撤销。草案建议文件凭据在旧值失效前先续发新值,给应用无中断切换的时间。这会刻意产生重叠窗口:两个凭据可能同时有效。即便新文件已经出现,应用仍可能在内存、连接池、sidecar cache 或 retry queue 中持有旧 token。

草案也提醒,持久存储、备份、快照和镜像会保留凭据副本。内存挂载可以减小风险,不能证明下游没有日志或缓存。平台签发者应对每个停止、暂停或被删除的实例分别失效,并向验证者提供状态查询;具体机制却明确不在本草案范围内。

所以“已轮换”至少需要四张回执:新凭据签发、预期消费者已采用、旧凭据已被拒绝或到期、已知缓存与实例已经排空。缺少这些维度,轮换只是控制面的声明,不是完成的分布式状态变更。

第七重:请求是否真的执行

WIMSE 的架构草案、工作负载凭据草案、HTTP 签名草案与双向 TLS 草案分别探索身份与认证的相邻部件,但没有任何一个把认证自动变成应用操作已发生的证明。

资源服务器可能依次验证 issuer、lifetime、audience、token type、key possession 与 custom claims,最后仍按本地政策拒绝请求。它也可能接受请求,却在 commit 前失败。共享节点代理若代表多个工作负载,代理到工作负载的委托又是一层独立边界。传输成功不是持久应用效果。

执行回执必须由真正产生效果的服务给出,包括规范化请求标识、授权决定、幂等或交易标识、commit 状态与相关政策 epoch。

第八重:预期结果是否出现

即使请求已经 commit,也未必等于承诺结果。带身份的任务可以成功入队而后失败;部署 API 可以接收 release,而 rollout 始终不健康;数据库可以写入中间记录,下游转移仍被回滚。身份日志说明谁被允许尝试,不能认证业务结局。

这正是 Heng Lu 对现实层与象征层的区分在工程上的用途。token、policy 与 success response 具有制度意义,但现实层是实际执行并被观察到的状态。最小初始规范与本地核验以及运行代码优先共同指向一条纪律:让每个 claim 尽量靠近能够核验它的系统。

最终结果回执应来自真正能观察目标状态的系统,而不是从认证日志推导。工作负载身份的价值,恰恰是让前几环更强;若强迫它替代整条链,它的可信度反而会被透支。

一条可审计的链

严肃部署可以用八个问题复核:平台看见什么?身份绑定哪个在线实例?哪些字节经何通道交付?本次交换证明了哪把密钥的持有?哪个受众和哪个具体操作获准?旧凭据是否在已知存储与实例中停止工作?目标是否执行请求?独立结果观察者是否看见预期状态?

这些问题互不否定。更安全的架构不是一枚万能身份 token,而是一串边界清楚、能够定位失败的窄回执。