切换深色模式
第 7 章:服务不保存密钥,启动时怎么证明“我就是我”?
本章主线:从 Git 仓库里的云密钥出发,走到工作负载证明、短期身份、SPIFFE/SPIRE、云 IAM 与零信任策略。
开场:删掉 Secret 后服务还能启动吗
安全扫描发现订单服务镜像里有长期云密钥。小安准备把它移到 Kubernetes Secret,评审却问:只是换了保存位置,泄漏和轮换问题真的解决了吗?
更好的问题是:平台能否依据 Pod、节点、镜像、命名空间等不可轻易伪造的证据确认工作负载,并动态发放短期凭证?这就是工作负载身份的起点。
学习目标
能盘点机器身份;区分节点、Pod、服务和任务;配置短期云联邦;解释 SPIFFE attestation;设计 mTLS 后的授权;制定静态密钥迁移与回滚。
身份层次不要混用
| 层次 | 示例 | 能证明什么 | 不能自动证明什么 |
|---|---|---|---|
| 节点 | VM instance identity | 哪台受管主机 | 主机上哪个进程 |
| 编排工作负载 | Kubernetes ServiceAccount | 哪类 Pod/命名空间 | 具体业务权限 |
| 服务身份 | SPIFFE ID | 哪个逻辑服务 | 当前代表哪个用户 |
| 任务身份 | CI job/run ID | 哪次构建/部署 | 制品一定可信 |
| Agent 实例 | agent run ID + workload | 哪次 Agent 运行 | 用户委托范围 |
7.1 为什么静态密钥不适合云原生
容器快速创建、销毁和迁移,IP 不稳定;长期 API Key 容易进代码、镜像、CI 日志或备份,泄漏后难定位实例和撤销。目标是让运行环境证明“我是什么工作负载”,自动获得短期、受限凭证。
7.2 云 IAM 心智模型
云授权通常由身份策略、资源策略、组织边界、会话策略和显式 deny 组合。必须理解实际有效权限,而非只看某个角色页面。
- 长期用户密钥:尽量消除。
- Role/Service Account:代表工作负载或任务。
- Assume Role/Federation:用已有可信身份换短期会话。
- Resource Policy:资源端限制谁可访问。
- Permission Boundary/SCP:限制最大权限,不主动授予。
跨账号信任需限制 principal、外部 ID/条件、source identity、session duration 和可承担链。
7.2.1 有效权限计算
不要假设“角色策略允许即允许”。资源策略、身份策略、组织级限制、permission boundary、会话策略和显式 deny 共同决定。建立 policy simulator/访问分析,并用真实拒绝日志验证。
示例风险:信任策略允许任意仓库的 CI OIDC subject 承担生产角色;攻击者在自己仓库构造合法平台 token 即可进入。必须约束 organization/repository/ref/environment 与 audience。
7.3 Kubernetes 身份
Pod 通常通过 ServiceAccount 获得投射 token。控制点:每工作负载独立 SA;关闭不必要自动挂载;短期、audience-bound token;最小 RBAC;限制 secret 读取;使用 admission policy 防危险配置。
Kubernetes RBAC 对 namespaced/cluster resource 授权,不等于云 IAM 或业务 API 授权。三层身份需要明确映射,不能让一个节点角色代表所有 Pod。
7.3.1 投射 ServiceAccount Token
Token 应有目标 audience 和短有效期,由 kubelet 轮换。应用通过标准文件读取并处理轮换,不把 token 打进日志。目标服务验证 issuer、audience、时间,并将 SA 映射到明确工作负载,而非把整个 namespace 当同一身份。
7.3.2 最小 RBAC
避免 cluster-admin、通配 verb/resource 和读取全命名空间 Secret。使用权限检查与审计日志验证实际能力;控制谁能创建 Pod,因为能创建使用高权 SA 的 Pod 常等于获得该身份。
7.4 SPIFFE/SPIRE
SPIFFE 定义工作负载身份标准:
- SPIFFE ID:
spiffe://trust-domain/workload-path; - SVID:可验证身份文档,常见 X.509 或 JWT;
- Workload API:工作负载本地获取和更新身份;
- Trust Bundle:验证信任域签发者;
- Federation:跨信任域建立互信。
图 7-1:7.4 SPIFFE/SPIRE · 可编辑 Mermaid 源码
工作负载无需携带 bootstrap secret;Agent 通过内核/平台属性证明调用者。生产设计要保护 SPIRE Server/Agent、注册权限、信任域、联邦 bundle 和轮换。
7.4.1 Selector 与注册
Kubernetes selector 可组合 namespace、ServiceAccount、Pod label、container image 等。选择过宽会让同节点/同 namespace 的恶意 workload 获得身份;选择过细会导致部署变更后身份中断。把 selector schema 当 API 管理并测试。
7.4.2 Trust domain 设计
生产与开发、不同监管区域或安全管理边界通常分 trust domain。跨域 federation 只交换 bundle,不自动授权;双方仍按对方 SPIFFE ID 和资源策略决定。
7.5 mTLS 不等于授权
mTLS 回答连接方持有什么证书;业务仍需判断该 SPIFFE ID 能否调用 POST /payments。把证书 DN 硬编码在代码会造成迁移困难,应统一 identity mapping 与策略。
7.5.1 代理终止问题
若 sidecar 终止 mTLS,应用收到的是本地代理连接。代理必须通过受保护元数据传递已验证身份,应用只信任本地受控通道;防止外部请求直接注入同名头。高风险服务可让授权在代理和应用双层执行。
7.6 CI/CD 与供应链身份
流水线应以 OIDC/工作负载联邦换取短期云权限;每个仓库、分支、环境限制 trust claims。生产部署要求受保护分支、审批、构建证明、制品签名和部署者身份审计。
不要给所有 CI job 一个组织级云 secret。Fork PR、第三方 Action、缓存和日志都是泄漏面。
7.6.1 发布证明链
text
开发者身份 → 受保护代码评审 → CI 工作负载身份
→ 构建 provenance → 签名制品 → 部署工作负载身份
→ 目标环境策略验证身份不仅决定“谁能部署”,还用于证明“哪个源码、哪个构建器产生了当前制品”。部署系统验证证明和签名,不能只信镜像标签。
7.7 零信任原则
零信任不是“永不信任”,而是不因网络位置或资产归属隐式信任;每次访问基于主体、设备/工作负载、资源、策略和动态信号做明确判断。
图 7-2:7.7 零信任原则 · 可编辑 Mermaid 源码
落地顺序:盘点主体和资源;建立强身份;定义分层策略;把 PEP 放在不可绕过处;接入遥测;逐步缩小隐式网络信任;持续验证。
7.8 服务网格与 API Gateway
Service Mesh 适合东西向服务身份、mTLS 和粗粒度策略;API Gateway 适合南北向认证、限速、路由。对象级权限仍在服务或统一授权层执行。
Sidecar/ambient 模式有不同运维和攻击面。任何代理代为传递身份时,都要确保下游身份头不可由外部伪造,并绑定受保护连接。
7.9 密钥与 Secret 迁移路线
- 盘点 secret、owner、使用和权限;
- 集中保管与自动轮换;
- 应用启动时动态获取,不写镜像/仓库;
- 用工作负载身份换短期凭证;
- 删除旧 secret 并监控复用;
- 阻止新增长期 secret 的策略门禁。
7.9.1 双轨迁移
先让应用同时支持静态 secret 和工作负载身份;影子验证新身份;小流量切换;监控拒绝和延迟;确认后撤销旧 secret。回滚只能在短窗口恢复受控旧凭证,不能长期“双轨永久化”。
记录每个 secret 的最后使用时间,撤销后监控任何复用尝试,这往往能发现遗漏的消费者。
7.10 本章实验与验收
部署两个 Kubernetes 服务与 SPIRE,使其用 X.509-SVID mTLS;策略只允许 frontend 调用 orders 的只读接口;轮换 SVID、撤销注册、模拟 Agent 故障。
验收:说明节点身份与 Pod 身份差异;为何 JWT-SVID 更易重放;怎样无长期云密钥部署;零信任为何不是 VPN 替代品。
7.10.1 生产验收清单
- 工作负载重建后自动获得相同逻辑身份,但实例凭证不同;
- 私钥不可从 Workload API 导出到其他进程使用;
- SVID 自动轮换不中断连接;
- 删除注册后新凭证停止签发,旧凭证暴露窗口有界;
- 错 trust domain、错 SPIFFE ID 和过期证书被拒;
- 业务授权日志保留 caller workload ID 与动作。
工程复盘:把云密钥移进 Vault,为什么还不能宣布完成
小安把代码仓库里的长期云密钥迁入 Vault,扫描告警清零了。但服务仍在启动时取出同一把长期密钥,多个副本共享,轮换还需要人工重启。密钥只是换了存放位置,身份、最小权限和撤销问题都没有消失。
第二版方案让平台根据命名空间、ServiceAccount、镜像和节点证明工作负载,签发短期、限定 audience 的凭证。迁移没有一步切换:先让旧密钥与新身份双轨运行,在影子模式比较授权结果;按服务灰度;监控签发失败、凭证年龄和权限差异;确认回滚路径后再撤销旧 secret。
真正的验收证据包括:仓库和镜像中无静态密钥;凭证寿命符合策略;错误工作负载无法领取;目标 API 校验身份后仍执行资源授权;节点或工作负载被撤销后,暴露时间有明确上界。
零信任不是“用了 mTLS”这一行配置,而是每次访问都能回答主体从哪里被证明、凭证为何仍有效、当前动作为什么被允许。
面试与设计评审
- 把 secret 放进 Vault 是否就完成 NHI 治理?没有;仍需 owner、用途、最小权限、动态获取、轮换和撤销。
- SPIFFE 怎样避免 bootstrap secret?说明本地 Workload API、平台 selectors、注册条目和短期 SVID。
- mTLS 与零信任是什么关系?mTLS 提供连接身份,策略仍结合资源、动作和动态信号授权。
本章小结
静态密钥的根本替代是“可证明运行环境 + 自动短期凭证 + 资源端授权”,而不是把秘密从代码搬到另一个盒子。零信任让这种身份成为每次访问决策的一项输入。