人员、工作负载与机器身份联邦:用短期凭证替代共享密钥
一次普通的 CI 调试把 AWS_ACCESS_KEY_ID 和长期 secret 打进了构建日志。团队轮换密钥后以为事故结束,几天后却发现同一份密钥还存在于旧 runner 镜像、开发者下载的构建包和第三方缓存中。密钥属于一个名为 ci-deployer 的共享账号,云审计只能看到账号,无法判断是哪次流水线、哪个仓库、哪条分支获得了权限,更无法只撤销其中一个任务。
另一个系统为了省事,让后台作业复用员工登录得到的 refresh token。员工离职后作业停止,团队于是延长 token 生命周期;从此人员会话、服务身份和生产发布绑定在一起。人员身份需要交互认证、MFA 与入离职管理,工作负载需要可验证的运行上下文和短期授权,机器身份还可能依赖设备、进程或信任域证明。把三者塞进同一 service account,只会让凭证活得更久、权限更宽、责任更模糊。
三类主体使用不同的信任根
workforce identity 对应员工、承包商和合作伙伴等人。它通常由企业目录或外部 IdP 管理,通过 OIDC 或 SAML 建立交互会话,结合 MFA、设备、风险与组织属性,再映射到云或 SaaS 权限。它的关键控制是人员权威、入转离、重新认证、会话终止和个人可追责性,而不是把员工复制成每个系统里的永久本地账号。
workload identity 对应应用、CI 作业、Pod、函数和批处理任务。它需要证明“这段软件正在被哪个可信运行环境执行”,再交换短期云凭证或服务 token。常见证明包括 CI 平台签发的 OIDC JWT、Kubernetes service account token、云实例身份以及 X.509 证书。machine identity 是更广的非人主体集合,还包括代理、节点、设备和进程;它可能通过 OIDC 交换,也可能直接使用 PKI、TPM 或 SPIFFE SVID 建立身份。
service account 只是权限主体或目录对象的一种表达,不等于安全凭证方案。一个 Kubernetes ServiceAccount 可以获得短期投射 token,也可能被错误地绑定长期静态 token;一个云 service account 可以由外部主体临时 impersonate,也可能泄露永久 JSON key。架构师要分开设计“主体是谁”“它用什么证明运行环境”“交换后得到什么短期 credential”“资源策略允许什么”。
human -> enterprise IdP -> workforce session -> role/policy -> resource
CI/Pod -> external OIDC assertion -> cloud STS/token exchange
-> short-lived credential -> role/service account -> resource
process -> node attestation -> SPIFFE Workload API -> SVID
-> mTLS/JWT verification or exchange -> service/resource联邦只替换信任建立方式。错误的 issuer、过宽 subject、通配 audience、宽泛 role 和无 owner 的 service account 仍会造成越权;短期凭证降低静态 secret 扩散与轮换负担,却不会自动实现最小权限。
先设计一份可拒绝的联邦合同
一份工作负载合同至少包含外部 issuer、允许的 audience、可匹配的 subject 或属性、目标 role/service account、最大会话时长、资源权限和审计字段。issuer 表示谁能签发外部证明,audience 防止 token 被错误服务接收,subject 和 claims 把权限缩到仓库、分支、命名空间、service account 或环境。目标云签发的 credential 还要受到 role policy、session policy、资源策略和组织级控制共同约束。
CI 场景可以把合同写成仓库内不含秘密的声明,平台控制面再把它编译到各云信任策略:
federationContract:
owner: platform-release
issuer: https://token.ci.example.test
audiences:
- cloud-token-exchange
subjects:
- repo:payments/checkout:ref:refs/heads/main
target:
environment: production
role: checkout-release
permissions:
- artifact.read
- deployment.update
maxSessionDuration: ${FEDERATED_SESSION_MAX}
denyPullRequestsFromForks: truesubject 若只匹配 repo:payments/*,任意仓库都可能借用生产角色;若把可变分支名直接拼进宽泛通配,攻击者可能创建相似引用。目标系统支持属性条件时,应把仓库、分支、环境和任务类型映射为独立字段,并在服务端做精确或受控模式匹配。任何扩权都要同时经过信任合同和资源权限评审。
正向实验让受保护主分支的测试作业请求外部 OIDC token,交换最低权限 credential,并读取一个隔离资源。反向实验从未授权分支、fork、错误 audience、不同仓库和过期 token 发起交换;预期均在签发云 credential 前失败。审计必须关联外部 issuer、subject、目标 role、session name、流水线 run ID 和资源动作,但不得记录完整 assertion 或 credential。
Microsoft Entra 把外部 token 绑定到应用或托管身份
Microsoft Entra workload identity federation 使用 federated identity credential,把外部 IdP token 的 issuer、subject 和单一 audience 绑定到 app registration 或 user-assigned managed identity。外部工作负载用受信 token 请求 Entra token endpoint,成功后获得访问目标资源的短期 token;应用对象或托管身份在 Azure 资源上的实际权限仍由角色分配和资源策略决定。
创建信任前先确定对象 owner、外部 issuer 的稳定性、唯一 subject、目标 audience 与最小 Azure role。官方的创建信任指南和限制与注意事项应作为变更依据。控制台名称、可用云环境、限制和预览能力会变化,自动化应以 Microsoft Graph 或受支持 CLI 的当前 schema 为准,并在部署前验证目标租户。
下面是声明式对象的结构示意,值均来自部署环境:
{
"name": "checkout-main-release",
"issuer": "https://token.ci.example.test",
"subject": "repo:payments/checkout:ref:refs/heads/main",
"audiences": ["api://AzureADTokenExchange"],
"description": "Protected main branch release identity"
}正向验证应先读取外部 token 的非敏感 header/claim 摘要,确认 issuer、subject、audience,再请求 Entra token 并访问隔离资源。反向验证分别修改 subject、audience 与 issuer;预期 token exchange 被拒绝,Azure 资源端没有副作用。若交换成功而资源返回 403,说明联邦信任成立但目标角色不足;若交换阶段失败,应检查 FIC 对象、issuer 可达性、claim 精确匹配和时间声明,不能先扩大 Azure role。
清理时先停止流水线使用,删除或禁用 FIC,撤销目标对象角色分配,再确认旧外部 assertion 无法换取新 token。短期 access token 可能在自身有效期内继续存在,高风险退出要结合有效期、资源端控制和应用对象停用处理,而不是只删除一条联邦记录。
AWS 用 OIDC 与 STS 颁发角色会话
AWS 的典型链路是创建 IAM OIDC provider,在 IAM role 的 trust policy 中允许受控外部主体调用 sts:AssumeRoleWithWebIdentity,STS 再签发临时安全凭证。AWS 提供 OIDC federation 指南和 AssumeRoleWithWebIdentity API。对人类 workforce 访问,优先评估 IAM Identity Center;对外部非 AWS 工作负载,也可根据证书基础设施评估 IAM Roles Anywhere,而不是强行把所有机器转换成 Web OIDC 客户端。
信任策略必须约束 issuer 对应的 aud 与 sub。下面的结构突出条件关系,实际 provider ARN、claim key、分区和资源 ARN应由目标账号生成:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Federated": "${OIDC_PROVIDER_ARN}"},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.ci.example.test:aud": "cloud-token-exchange",
"token.ci.example.test:sub": "repo:payments/checkout:ref:refs/heads/main"
}
}
}]
}角色 trust policy 决定谁能进入角色,permission policy 决定进入后能做什么,资源策略与组织控制还可能进一步限制。只检查 trust policy 会漏掉资源越权,只检查 permission policy 又解释不了谁能获得 session。role session name 应带可审计的 run ID,但不能塞入 token、邮箱或其他敏感值。
正向实验通过受控作业交换临时 credential,调用 sts get-caller-identity 确认账号和 role session,再访问隔离资源。反向实验使用错误分支 subject、错误 audience 和未受信 issuer,预期 STS 拒绝且 CloudTrail 中可看到失败上下文。随后让正确主体尝试未授权资源,预期资源端 AccessDenied;这一步证明身份信任与权限策略是两道独立门。
Google Cloud 区分 workforce pool 与 workload pool
Google Cloud 明确区分 Workforce Identity Federation 与 Workload Identity Federation。workforce pool 面向员工和合作伙伴等人,使外部 IdP 主体获得 Google Cloud 会话;workload identity pool 面向部署在 Google Cloud 之外或多云环境中的工作负载,通过外部 OIDC、SAML 或云提供方证明换取短期访问。两者的主体 URI、属性映射、授权方式和支持服务不同,不能只因为都叫 pool 就复用配置。
Workforce Identity Federation和Workload Identity Federation的当前官方页面列出配置入口与服务边界。工作负载常先经 Security Token Service 交换 federated token,再直接访问支持联邦主体的资源,或 impersonate 受控 service account 获得短期 access token。service account impersonation 不是必选步骤,也不能用来掩盖过宽 pool 权限。
属性映射应保留稳定、不可歧义的外部身份。一个抽象配置如下:
provider:
issuerUri: https://token.ci.example.test
allowedAudiences:
- cloud-token-exchange
attributeMapping:
google.subject: assertion.sub
attribute.repository: assertion.repository
attribute.ref: assertion.ref
attributeCondition: >-
assertion.repository == 'payments/checkout' &&
assertion.ref == 'refs/heads/main'
target:
serviceAccount: ${RELEASE_SERVICE_ACCOUNT}
resourceRole: ${MINIMUM_RELEASE_ROLE}正例应检查 external account 配置中的 audience、subject token type、token URL 与 impersonation URL 是否属于目标项目,再在隔离环境交换 credential 并读取一个资源。反例依次替换仓库、分支、audience 与 provider,预期属性条件或 IAM 绑定拒绝。若 STS 交换成功而 service account impersonation 失败,应检查 workloadIdentityUser 类授权和目标账号;若 impersonation 成功但资源失败,则回到资源 IAM,不能放宽 pool provider。
SPIFFE 用 SVID 把进程放进信任域
SPIFFE 不是云 IAM 产品,而是一组工作负载身份规范。SPIFFE ID 以 URI 标识工作负载;SVID 可以是 X.509-SVID 或 JWT-SVID;Workload API 让本地工作负载在不保存静态私钥文件的情况下获取和轮换身份材料;trust bundle 提供验证根。SPIRE 是常见实现之一,但服务授权、人员目录和云资源角色仍需其他系统完成。
SPIFFE ID 与 SVID、Workload API和联邦规范共同定义了信任域内签发与跨域验证。节点先通过受控 attestation 加入,工作负载再按进程、容器、Kubernetes 或平台属性匹配注册条目。错误 selector 会把身份发给错误进程,过宽 trust bundle 会让不必要的域被接受;因此注册条目与 bundle 分发都应版本化和审计。
开发环境可先用 Workload API 客户端观察身份轮换,不把 SVID 写入仓库:
set -eu
export SPIFFE_ENDPOINT_SOCKET="${SPIFFE_ENDPOINT_SOCKET:?set Workload API socket}"
workload-api-client validate
workload-api-client x509 -write /tmp/svid-check
test -s /tmp/svid-check/svid.0.pem
test -s /tmp/svid-check/bundle.0.pem
rm -rf /tmp/svid-check预期客户端能连接受控 socket,证书链的 SPIFFE ID 与目标注册条目一致,且到期时间随轮换更新。反向实验以不匹配 selector 的进程访问 Workload API,预期得不到目标 SVID;再让服务端移除对外部 trust bundle 的信任,跨域 mTLS 应失败。临时写盘只适合隔离验证,生产应用优先直接消费 Workload API,避免短期私钥在文件系统留下额外副本。
把联邦接入流水线而不是复制 token
CI 作业应在需要访问云资源时临时请求 OIDC assertion,立即交换目标环境的短期 credential,并在进程结束时清空环境变量和临时 credential 文件。不要把外部 token 上传为 artifact、写进缓存、通过命令行参数暴露给进程列表,也不要让一个 job 把 credential 传给另一个 job。每个环境使用独立 audience、目标 role 或 service account,生产授权再绑定受保护环境与审批。
下面的 shell 骨架只展示生命周期控制,云交换命令由对应平台 SDK 或凭证提供器完成:
set -eu
umask 077
tmp="$(mktemp -d)"
cleanup() {
unset EXTERNAL_ID_TOKEN CLOUD_ACCESS_TOKEN AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN
rm -rf "$tmp"
}
trap cleanup EXIT INT TERM
EXTERNAL_ID_TOKEN="$(ci-identity token --audience cloud-token-exchange)"
export EXTERNAL_ID_TOKEN
cloud-credential-helper exchange \
--subject-token-env EXTERNAL_ID_TOKEN \
--target "${FEDERATED_TARGET}" \
--output "$tmp/credential.json"
cloud-cli --credential-file "$tmp/credential.json" resource describe "${TEST_RESOURCE}"工具必须支持从内存、受限文件描述符或权限收紧的临时文件读取 token。若只能接收环境变量,需评估崩溃转储、子进程继承和调试日志;若生成 credential 文件,确保目录不在 workspace、缓存或 artifact 路径,并在异常退出时清理。runner 镜像中不应内置长期云 key,仓库变量也不应保留备用 secret 作为永久逃生通道。
失败证据要区分信任、交换与资源授权
外部 token 获取失败,先检查 CI 或平台是否允许该任务申请指定 audience,以及作业来源是否满足受保护环境规则。云 token exchange 失败,检查 issuer 可达性、签名 key、audience、subject、属性映射、时钟和 provider 状态。交换成功但资源 403,检查目标 role/service account、session policy、资源 IAM 和组织级拒绝。不要在最后一步失败时回头扩大 issuer subject 条件。
审计事件至少记录外部 issuer、subject 摘要、audience、provider、目标主体、session/run ID、资源、动作、决定和 correlation ID。对 AWS 关联 STS 与 CloudTrail,对 Azure 关联 token 获取与资源活动,对 Google Cloud 关联 STS、service account delegation 与资源审计,对 SPIFFE 关联节点证明、注册条目、SVID 签发和服务端验证。完整 OIDC JWT、SVID 私钥、云临时 secret 和员工属性不进入日志。
反向矩阵应持续进入 CI:错误 audience、错误仓库、fork、非保护分支、过期 token、被删除 provider、被撤销角色、错误 trust bundle 和重放旧 credential。每个反例要在预期边界失败,并确认资源没有副作用。只测试“能交换成功”,会让最小权限与撤销能力长期不可见。
短期凭证也有容量和可用性成本
从静态 key 切换到短期 credential,会增加外部 issuer、JWKS、STS、策略和审计链路。大批 CI 作业在整点启动可能造成 token exchange 峰值;过短会话会放大刷新,过长会扩大失陷窗口。容量模型要包含并发作业、交换 QPS、issuer/JWKS 缓存、云限流、SVID 轮换、trust bundle 分发与上游故障恢复后的重试。
客户端应采用有上限的指数退避和随机抖动,缓存公开 JWKS 时按 issuer 隔离,并避免未知 kid 触发所有实例同步刷新。凭证只在足够接近过期时更新,但不能拖到业务请求已失败。SPIFFE 控制面还要规划服务器、agent、数据存储、签名密钥和跨信任域 bundle 的高可用;云联邦则要考虑外部 IdP 不可用时是否允许已有短期会话继续,以及允许多久。
成本不仅是平台账单。还包括身份平台运行、HSM 或 KMS、审计留存、跨云出口、流水线等待、连接器维护、事件响应和迁移。联邦通常减少静态 key 盘点与轮换成本,但会增加信任策略和可观测性投入。具体配额、支持服务和收费边界随平台变化,上线评审应回到目标云的当前官方文档和账号配置核对。
权限、数据与团队责任必须一起收敛
平台团队维护 issuer、provider、属性合同、交换入口和可观测性;云资源团队维护目标 role/service account 与资源策略;CI 团队保证 token 只向受控作业签发;安全团队维护允许的信任根、TTL、异常检测与撤销流程;应用 owner 对主体、环境和权限仍然负责。任何 federated credential、OIDC provider、workload pool 或 SPIFFE 注册条目都要有 owner 和到期复审日期。
人员属性与工作负载 claim 都可能泄露敏感信息。subject 应稳定且足够区分,不必塞入邮箱、姓名或仓库内部秘密;attribute mapping 只保留授权所需字段。日志、trace 与 metric label 不记录完整 token、云 credential、证书私钥或高基数主体值。故障工单使用摘要、事件 ID 和受控查询链接,不粘贴凭证。
权限收敛从两端同时做:外部信任只允许必要主体,目标权限只允许必要资源动作。再加入环境审批、session 时长、资源条件和组织级边界。这样即使外部 issuer 错签一枚 token,或某个工作负载被攻破,攻击面也受 audience、subject、role 与资源策略多层限制。
迁移、回滚与退出要消灭旧密钥路径
迁移前先盘点长期 key 的所有副本、使用者、资源权限、调用频率和 owner。创建联邦 provider 与最低权限目标主体,让单个非生产作业并行使用短期 credential;验证正反矩阵和审计关联后,再按仓库与环境灰度。生产作业稳定后撤销旧 key,并持续搜索日志、镜像、变量、缓存和 artifact 中的残留引用。
回滚不是恢复共享长期 key。可保留经过审批、受限时长和隔离存储的应急身份,但必须独立 owner、强审计和使用告警。联邦故障时优先停止高风险发布、恢复 provider 或 issuer 可用性;若业务必须继续,临时授权也要缩小资源和时长,并在事件结束后立即撤销。
退出某个 provider 时,先禁止新交换并观察调用归零,移除 CI 配置和资源绑定,再删除 provider、federated credential、role trust、workload pool 或 trust bundle。确认旧 assertion 不能换取新 credential,旧短期 credential 到期或被资源端拒绝,service account/role 没有其他未知信任,审计和保留数据按策略核销。最后扫描仓库、runner、镜像和 secret manager,证明静态 key 没有因为迁移失败而悄悄回归。
