企业出海多云非人类身份(NHI)治理实战:机器身份盘点、长期密钥清零与工作负载身份联邦(2026)
Meta Description: 出海架构里人类身份有 SSO 与离职回收,机器身份却常裸奔。
- 前言:先把结论摆出来
- 为什么 NHI 是出海架构里最后一个没上锁的门
- 边界:NHI 治理与站内相邻主题的分工
- NHI 全景:五类身份 × 四层信任
- 第一步:盘点,把三朵云的机器身份拉成一张表
- 第二步:风险量化,决定先动哪一把钥匙
- 第三步:清零长期凭证,四条路径
- 工作负载身份联邦:让机器不再需要那把密钥
Meta Description: 出海架构里人类身份有 SSO 与离职回收,机器身份却常裸奔。本文给出非人类身份(NHI)治理三步法:三云盘点、风险量化、长期密钥清零,附工作负载身份联邦与密钥蔓延防护实操。
关键词: 非人类身份、NHI 治理、机器身份、长期密钥清零、工作负载身份联邦
前言:先把结论摆出来
如果你的出海架构已经做了统一 SSO、MFA、离职当天回收权限,那么剩下最大的一个洞不在人身上,而在机器身份上:CI/CD 流水线里的长期 AccessKey、Kubernetes 里挂进环境变量的 AK/SK、监控 Agent 的 API Token、对接客户 ERP 的固定密钥、三年前某位离职同事创建的集成账号。
人类身份和机器身份在治理上的差别,不是"多一点少一点",而是四个维度全部相反:人类身份数量有限、有 HR 事件驱动、登录会留痕、失效靠离职;机器身份数量是人类的几十倍、没有入离职事件、调用是"合法"的所以不告警、失效只能靠人主动删。这就导致一个很典型的现象——安全团队能说清公司有多少员工,却说不清公司有多少把钥匙。
非人类身份(Non-Human Identity,NHI)治理的完整闭环只有三步,顺序不能颠倒:
- 盘清:把三朵云 + K8s + CI + SaaS 里的机器身份拉成一张统一清单,每条都必须有 owner;
- 收敛:按风险打分排序,把"永久有效、权限很大、没人认领"的先处理;
- 消灭长期凭证:用 OIDC 联邦、实例角色、工作负载身份把静态密钥换成几十分钟就过期的临时凭证。
本文按这三步给出可直接执行的三云命令、评分模型、IAM 信任策略、K8s 工作负载身份配置、密钥蔓延扫描流水线,以及合规评审时怎么举证。全文的前提是多云——单云场景下厂商自带工具能覆盖大半,多云场景下"清单归一"和"凭证最后使用时间"这两件事必须自己拼。
一、为什么 NHI 是出海架构里最后一个没上锁的门
1.1 三个几乎每家公司都有的场景
场景一:CI 里的"临时"密钥,用了三年。 2023 年为赶一个版本,运维在 GitHub Secrets 里放了一对 AccessKey,配置成部署用。后来流水线改成了别的方案,但这对密钥没删——它还在,权限是 AdministratorAccess,最近一次调用是 47 天前的一次手工排查。这类密钥的危险在于它不在任何人的日常视野里,但一旦仓库或 runner 被攻破,攻击者拿到的就是管理员权限,而且不需要任何 MFA。
场景二:K8s 里挂进环境变量的云凭证。 为了让 Pod 能读对象存储,早期做法是把 AK/SK 写进 Secret 再注入环境变量。问题有三层:Secret 是 base64 不是加密;任何能 exec 进 Pod 的人都能读到;这把密钥的权限通常是整个桶甚至整个账号,而不是"这个前缀只读"。
场景三:无人认领的集成账号。 对接物流商、支付网关、客服系统时,对方要求一个固定 API Key。这类凭证往往登记在某个人的笔记里,人走了、项目下线了,Key 还活着。它不属于任何服务,所以没有任何监控会盯它。
1.2 人类身份 vs 机器身份:四个维度全部相反
| 维度 | 人类身份 | 机器身份(NHI) | 治理含义 |
|---|---|---|---|
| 数量与增长 | 与编制挂钩,可控 | 随服务、环境、集成数量指数增长,通常是人类的数十倍 | 靠人工登记必然漏,必须自动采集 |
| 生命周期驱动 | 入职、转岗、离职等 HR 事件 | 没有事件,只有"创建"这一个动作,没有"删除"动作 | 必须人为设定到期时间与复核周期 |
| 可见性 | 登录有日志、异常有告警 | 调用是设计内的,行为合法,不触发任何告警 | 只能靠"从未使用""异地使用"这类旁证发现异常 |
| 失效方式 | 离职即停用,MFA 兜底 | 无 MFA 可言,唯一的生命周期控制就是轮换与删除 | 短寿命凭证是唯一有效的架构级解法 |
1.3 三条结论
- NHI 不能靠"管人"的流程管。没有入离职事件,就必须给它装一个"到期时间"。
- NHI 的泄漏不会有告警。攻击者用一把合法密钥调用 API,在日志里和正常业务没有区别,唯一的破口是"使用地/使用服务/使用时间"偏离基线。
- NHI 的治理成本主要在盘点,不在技术。三朵云各有一套 API,字段口径完全不同,把清单归一这件事的工作量占整个项目的六成以上。
二、边界:NHI 治理与站内相邻主题的分工
这一节是为了避免和已有文章混读。站内已经写过统一身份认证、特权访问管理、KMS、PKI、供应链安全,它们各自解决一段,本文只解决"机器身份自身的数量、寿命、权限与归属"。
| 已有主题 | 它回答的问题 | 与本文的边界 |
|---|---|---|
| 多云统一身份认证(08-07) | 人怎么登录、怎么联邦、怎么同步组织架构 | 只管人类身份;离职回收是 HR 事件驱动,NHI 没有这个事件 |
| 特权访问管理 PAM(09-29) | 人怎么临时提权、会话怎么录屏审计 | 只管人类特权会话;本文管无人值守的机器凭证 |
| 密钥管理与 KMS(08-26) | 密钥怎么存、怎么加密、怎么轮换 | 管"密钥的保管",本文管"密钥的数量与权限",且明确要求把长期密钥清零 |
| PKI 与证书生命周期(10-13) | 证书怎么签发、怎么自动续期 | 证书是 NHI 的一种载体,本文只做衔接,不重复签发流程 |
| 软件供应链安全(08-11) | 制品怎么签名、怎么验签 | 管"产物可信",本文管"构建过程用的是什么身份" |
| 零信任网络架构(08-25) | 访问凭什么被信任 | 管访问决策,本文管被决策的那个主体本身 |
五个容易混的概念,一句话分清:
| 概念 | 定义 | 典型载体 |
|---|---|---|
| 人类身份 | 有具体自然人对应的账号 | SSO 账号、IAM 用户、RAM 子账号 |
| 机器身份(NHI) | 由软件代表自己行使权限的身份 | 服务账号、IAM 角色、API Key、证书、Token |
| 服务账号 | 一种被服务使用的账号实体,可能是长期凭证也可能是角色 | K8s ServiceAccount、数据库账号、SaaS 集成账号 |
| 工作负载身份 | 把"身份"绑定到运行中的工作负载而非密钥文件 | EKS Pod Identity、IRSA、SPIFFE SVID |
| 密钥(Secret) | 证明身份的一段字符串 | AccessKey、API Key、Token、私钥 |
一句话记法:人类身份靠"人"证明自己,机器身份靠"密钥"证明自己;工作负载身份的目的是让机器不再需要那把密钥。
三、NHI 全景:五类身份 × 四层信任
3.1 五类机器身份
| 类别 | 举例 | 主要风险 | 优先治理手段 |
|---|---|---|---|
| 云平台长期凭证 | IAM 用户的 AccessKey、RAM AccessKey、CAM API 密钥 | 永久有效、权限常过大、无法 MFA | 清零,改角色/联邦 |
| 云平台角色 | IAM Role、RAM 角色、CAM 角色 | 信任策略过宽(* 主体) | 收紧信任策略条件 |
| 工作负载身份 | K8s ServiceAccount 绑定的云角色、SPIFFE SVID | 绑定粒度过粗(整个 namespace 共用一个角色) | 一工作负载一角色 |
| CI/CD 与自动化令牌 | GitHub Actions Secrets、GitLab CI 变量、Jenkins 凭据 | 存在仓库/runner 中,随仓库扩散 | OIDC 联邦,不落地 |
| 第三方集成凭证 | 支付、物流、客服、监控 SaaS 的 API Key | 无主、无到期、无法轮换 | 登记 + 设到期 + 网关代理 |
3.2 四层模型
NHI 治理不是一张清单,而是四层同时成立才算闭环:发现层(有没有全部找到)、收敛层(权限是不是最小)、寿命层(能不能自动过期)、证据层(能不能向审计举证)。
graph TD
A[发现层:三云+ K8s + CI + SaaS 清单归一] --> B[收敛层:权限打分与最小化]
B --> C[寿命层:临时凭证 / 自动轮换 / 到期删除]
C --> D[证据层:清单快照 + 变更日志 + 复核记录]
D --> A
┌──────────────────────────────────────────────────────────────────┐
│ 证 据 层(可举证) │
│ 月度清单快照 │ 变更审计 │ 季度 owner 复核签署 │ 例外台账 │
├──────────────────────────────────────────────────────────────────┤
│ 寿 命 层(会过期) │
│ OIDC 联邦(≤1h) │ 实例角色 │ 工作负载身份 │ Secrets 轮换 │ 到期删除 │
├──────────────────────────────────────────────────────────────────┤
│ 收 敛 层(最小权限) │
│ 权限边界 │ 条件键 │ 会话标签 │ 一工作负载一角色 │ 只读优先 │
├──────────────────────────────────────────────────────────────────┤
│ 发 现 层(全都在册) │
│ AWS 凭据报告 │ 阿里云 RAM │ 腾讯云 CAM │ K8s SA │ CI Secrets │ SaaS │
└──────────────────────────────────────────────────────────────────┘
▲ 单向依赖:上面每一层都依赖下一层的准确性
三条铁律:
- 发现层不准,上面三层全是自欺。清单漏了 20%,剩下的治理动作只覆盖了 80% 的风险敞口,而攻击者只会挑那 20%。
- 没有 owner 的机器身份,等于没有治理。owner 不是"某个人名",必须是能收到告警、能审批轮换的团队。
- 寿命层的收益最大、成本最低。权限收敛需要理解业务,寿命收敛只需要改架构,而短寿命凭证把"泄漏后的窗口"从永久压缩到一小时。
四、第一步:盘点,把三朵云的机器身份拉成一张表
4.1 统一清单的 12 个字段
不管来自哪朵云,最终都要落成同一张表。字段少一个,后面的评分就会失真:
| 字段 | 说明 | 缺失后果 |
|---|---|---|
| id | 云平台内的唯一标识 | 无法回溯 |
| cloud | aws / aliyun / tencent | 无法分组统计 |
| type | user / role / sa / api_key / cert / token | 无法匹配治理手段 |
| name | 名称 | 人工核对困难 |
| arn_or_id | 完整资源名 | 无法自动处置 |
| owner | 负责团队 | 无人认领 |
| created_at | 创建时间 | 无法算寿命 |
| last_used_at | 最后使用时间 | 无法判断僵尸 |
| expires_at | 到期时间(若有) | 无法自动清理 |
| permissions | 权限摘要(策略/角色) | 无法算风险分 |
| exposure | 是否可公网使用 / 是否在代码库中 | 无法定优先级 |
| status | active / disabled / pending_delete | 无法统计进度 |
其中最难拿到的两个字段是 last_used_at 和 exposure,也是决定优先级的关键。
4.2 AWS:凭据报告是唯一全量入口
AWS 的 IAM 用户长期凭证,靠 generate-credential-report 一次拉全。这份报告是 CSV,以 base64 返回,包含每个 IAM 用户的密码状态、MFA 状态、两把 AccessKey 的创建时间、最后使用时间、最后使用区域与最后使用的服务。
aws iam generate-credential-report
aws iam get-credential-report --query Content --output text | base64 -d > /tmp/cred_report.csv
aws iam list-users --query "Users[].UserName" --output text
aws iam get-access-key-last-used --access-key-id AKIAXXXXXXXXXXXXXXXX
解析出三个关键列就能做第一轮筛选:access_key_1_active、access_key_1_last_used_date、access_key_1_last_rotated。空的使用日期 = 从未使用,这类密钥应当直接进入删除队列。
两个坑:
- 凭据报告有刷新频率限制,AWS 侧大约每 4 小时才重新生成一次,短时间内重复调用拿到的还是旧数据。做自动化时不要按"每次运行都生成"来设计,改成读缓存 + 每日一次。
- 凭据报告只覆盖 IAM 用户,不覆盖角色。角色没有"永久密钥",但角色有"信任策略",那才是角色侧的风险点,需要单独用
list-roles+ 解析AssumeRolePolicyDocument来做。
4.3 阿里云 RAM:AccessKey 与最后使用时间
aliyun ram ListUsers --MaxItems 100
aliyun ram ListAccessKeys --UserName svc-deploy --output cols=AccessKeyId,Status,CreateDate
aliyun ram GetAccessKeyLastUsed --UserAccessKeyId LTAI5tXXXXXXXXXXXX
aliyun ram ListPoliciesForUser --UserName svc-deploy
阿里云 RAM 的 GetAccessKeyLastUsed 返回的是"最后一次使用时间 + 最后使用的服务",字段比 AWS 简短,但足够判断僵尸密钥。注意分页:ListUsers 默认只返回 100 条,账号多的时候必须循环 Marker。
4.4 腾讯云 CAM:清单有了,最后使用时间要靠审计日志
tccli cam ListUsers
tccli cam ListAccessKeys --TargetUin 100000000001
tccli cloudaudit LookupEvents --LookupAttributes.0.AttributeKey AccessKeyId --LookupAttributes.0.AttributeValue AKIDxxxxxxxxxxxx --StartTime 2026-07-01T00:00:00Z --EndTime 2026-10-22T00:00:00Z
腾讯云 CAM 的 ListAccessKeys 返回密钥 ID、状态与创建时间,但最后使用时间不在这个接口里——这一点与 AWS、阿里云不同。要用云审计 LookupEvents 按 AccessKeyId 反查,把"有调用记录"当作活跃证据,查不到就归为疑似僵尸。这也是多云盘点最容易被低估的工作量:三家云的"最后使用时间"语义和取法都不一样,归一逻辑必须自己写。
4.5 K8s 与 CI 侧
工作负载身份不需要"凭据报告",但它需要两个额外采集:
- 集群侧:列出所有 ServiceAccount,以及绑定到 SA 的云角色注解。没有绑定角色的 SA 是安全的,绑了但整个 namespace 共用一个角色的才是问题。
- CI 侧:把仓库变量/Secrets 的名称(不是值)导出成清单,并按名称匹配"疑似长期云凭证"(如
AWS_ACCESS_KEY_ID、ALIYUN_ACCESS_KEY)。OIDC 方案落地的团队,这类变量应当为零。
五、第二步:风险量化,决定先动哪一把钥匙
清单拉出来之后,几千条记录不能靠感觉排序。用五维打分,每一项 1~5 分,加权后落到 P0~P3:
| 维度 | 权重 | 5 分(最危险) | 1 分(最安全) |
|---|---|---|---|
| 权限广度 | ×2 | 管理员/: | 单一资源只读 |
| 凭证寿命 | ×2 | 永久有效且从未轮换 | ≤1 小时自动过期 |
| 暴露面 | ×3 | 出现在代码库/公网可达/挂进环境变量 | 仅内网、由角色承载 |
| 活跃度 | ×1 | 长期静默但权限大(僵尸) | 持续使用且有基线 |
| 归属清晰度 | ×2 | 无 owner 或 owner 已离职 | 有团队且季度复核 |
总分 = 权限×2 + 寿命×2 + 暴露×3 + 活跃×1 + 归属×2,满分 50。
| 分数区间 | 等级 | 处置时限 | 处置动作 |
|---|---|---|---|
| ≥40 | P0 | 72 小时内 | 立即禁用 → 观察 → 删除;若在用则改为角色并轮换 |
| 30~39 | P1 | 2 周 | 收权限到最小集 + 设到期 + 补 owner |
| 20~29 | P2 | 1 个季度 | 纳入季度复核,逐步替换为临时凭证 |
| <20 | P3 | 半年 | 保持监控,随架构演进自然淘汰 |
为什么"暴露面"权重最高(×3):权限大但只在内网、由角色承载的凭证,攻击者要先进入网络才能用;而写在代码库里的密钥,任何拿到仓库读权限的人都能用,且Git 历史会永久保留它。同一个权限等级下,后者危险一个数量级。
为什么"活跃度"权重最低(×1):直觉上"没在用"最该删,但打分时它只是加分项而不是主项。因为一把天天在用的管理员密钥,比一把闲置的只读密钥危险得多——前者一旦泄漏立刻能用,后者泄漏了也做不了什么。先按"权限 × 暴露"排序,再用"活跃度"做同分时的裁决。
六、第三步:清零长期凭证,四条路径
"清零"不是把所有 AccessKey 删掉(那会把业务停掉),而是把每一条长期凭证替换成一条不需要长期凭证的路径。四条路径的适用面完全不同:
| 路径 | 适用场景 | 凭证寿命 | 迁移成本 | 备注 |
|---|---|---|---|---|
| OIDC 联邦 | CI/CD、外部系统调用云 API | 分钟级 | 低 | 主流 CI 均已支持,首选 |
| 实例/节点角色 | 跑在云主机上的服务 | 小时级,自动轮换 | 低 | 消除"配置文件里的 AK" |
| 工作负载身份 | K8s Pod、容器化服务 | 分钟级 | 中 | 需集群支持与 RBAC 配合 |
| 短期令牌换取 | 遗留系统(不支持角色) | 小时级 | 中 | 用一处长期凭证换多处短期凭证,收敛为一个高风险点 |
迁移顺序(很重要的经验):
- 先做实例/工作负载角色。这类迁移是"删掉环境变量 + 加一个角色绑定",业务代码几乎不用改,SDK 会自动从元数据服务取临时凭证。
- 再做CI 的 OIDC 联邦。这一步要改流水线配置和云侧信任策略,但收益最大——CI 是最容易泄漏长期密钥的地方。
- 最后处理遗留系统。它们往往没有 SDK、只能读固定配置文件,用"令牌代理"把长期凭证收敛到一处,再对这一处加网络限制与使用监控。
兜底策略:禁用优先于删除。
删除是不可逆的,而"这把密钥是否真的没人用"很难 100% 确认。正确做法是先禁用,观察 14 天:
- 禁用期间业务正常 → 删除;
- 禁用期间出现调用失败 → 立刻启用,说明还有隐藏调用方,改为"收权限 + 定期轮换"而不是删除。
这条规则的价值不在技术,而在组织——它让"删密钥"从一件需要开会讨论的事,变成一件可以先动手的事。团队愿意执行,治理才推得动。
七、工作负载身份联邦:让机器不再需要那把密钥
7.1 原理:从"证明我知道密钥"到"证明我是谁"
静态凭证的逻辑是"谁持有字符串谁就有权限",所以字符串一旦泄漏,身份就被复制了。联邦的逻辑是"由平台证明这个工作负载的身份",云侧只认平台的签名,没有任何可被复制的长期字符串。
在 AWS 上有两条路:OIDC 联邦(外部系统,如 GitHub Actions)和 EKS Pod Identity(集群内的 Pod)。
7.2 GitHub Actions 的 OIDC 信任策略
关键是信任策略里的三个条件:sub 限定到具体仓库与分支、aud 限定为 STS、以及不要用 "Principal": "*" 裸放。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "Federated": "arn:aws:iam::111122223333:oidc-provider/token.actions.githubusercontent.com" },
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:your-org/your-repo:ref:refs/heads/main"
}
}
}
]
}
三个常见错误:
sub写成repo:your-org/*,等于组织内任何仓库的任何分支都能AssumeRole——这和一个共享账号没区别;- 忘记加
aud条件,其他平台的 token 可能被复用; - 把
refs/heads/main换成pull_request事件却没意识到 PR 来自 fork 时可以触发。
7.3 EKS Pod Identity:三步取代 IRSA
EKS Pod Identity 的核心是把"角色 ↔ 命名空间 ↔ ServiceAccount"的映射做成集群内的一等资源,不再需要给集群配 OIDC Provider,这也是它比 IRSA 简单的根本原因。AWS 官方文档明确写了这一点,同时也写明一件常被忽略的事:容器不是安全边界,Pod Identity 不改变这个事实——同一个节点上的 Pod 共享内核,所以身份隔离必须配合网络策略与调度隔离。
aws eks create-pod-identity-association \
--cluster-name prod-sg \
--namespace payments \
--service-account payments-api \
--role-arn arn:aws:iam::111122223333:role/payments-s3-readonly
配套的 ServiceAccount 只需要普通声明,不打角色注解:
apiVersion: v1
kind: ServiceAccount
metadata:
name: payments-api
namespace: payments
Pod Identity 与 IRSA 的选型:
| 维度 | EKS Pod Identity | IRSA |
|---|---|---|
| 是否需集群 OIDC Provider | 不需要 | 需要 |
| 角色信任策略主体 | pods.eks.amazonaws.com | 集群 OIDC Provider |
| 跨集群复用同一角色 | 支持(关联多个集群) | 需为每个集群的 OIDC 分别授权 |
| 迁移成本 | 新建关联即可,可与 IRSA 并存 | 存量方案,注解已写死在清单里 |
| 建议 | 新集群一律用 Pod Identity | 存量集群逐步迁移,不必一次性切换 |
7.4 阿里云与腾讯云的对应能力
- 阿里云 ACK:RRSA(RAM Roles for Service Accounts),基于集群 OIDC 颁发方 + RAM 角色的信任策略,Pod 通过 ServiceAccount 绑定 RAM 角色拿到 STS 临时凭证。落地三步是:开启集群 RRSA → 创建 RAM 角色并把信任策略限定到该集群的 OIDC 主体与 namespace/SA → 给业务 ServiceAccount 绑定角色。
- 腾讯云 TKE:通过集群的服务账号与 CAM 角色绑定,让 Pod 以角色身份调用云 API,同样避免在容器里落长期密钥。注意腾讯云的 CAM 角色信任策略语法与 AWS 差异较大,迁移时不能照抄 JSON。
两家共同的一点:绑定粒度决定安全水位。一个 namespace 一个角色是最省事也最危险的做法,正确做法是一个工作负载一个角色,权限只覆盖它真正需要的那几个资源。
7.5 三朵云工作负载身份能力对比
| 能力 | AWS | 阿里云 | 腾讯云 |
|---|---|---|---|
| 集群内工作负载身份 | EKS Pod Identity / IRSA | ACK RRSA | TKE 服务账号绑定 CAM 角色 |
| 是否依赖 OIDC Provider | Pod Identity 不依赖 | 依赖集群 OIDC | 依赖集群 OIDC |
| 临时凭证有效期 | 由角色会话控制 | 由 STS 控制 | 由 STS 控制 |
| 外部系统联邦 | IAM OIDC / SAML | RAM SSO / OIDC | CAM SSO / OIDC |
| 权限边界机制 | Permissions Boundary | 角色权限策略 + 管控策略 | 权限边界(策略限制) |
| 条件键丰富度 | 最高(含 VPC/OrgID/源 IP) | 较高 | 中等 |
八、SPIFFE/SPIRE:跨云统一工作负载身份
8.1 什么时候需要它
工作负载身份在单朵云里已经够用(Pod Identity、RRSA、TKE 服务账号)。但出海架构常见的形态是"两朵云 + 自建 IDC",这时候会出现一个尴尬局面:每个集群有自己的 OIDC Provider,每个云有自己的信任策略,一个服务从 AWS 迁到阿里云,就要重写一遍身份配置。
SPIFFE 解决的正是这件事:它定义一个与云无关的工作负载身份格式(SVID,本质是一张短寿命 X.509 证书或 JWT),由 SPIRE Server 统一签发,各云的信任策略只需要信任同一个 trust domain。
判断要不要上 SPIFFE,看三条:
| 判断条件 | 上 SPIFFE | 不上 |
|---|---|---|
| 工作负载是否跨两朵以上云、且需要互相认证 | 是 | 否 |
| 是否有自建 IDC / 边缘节点参与服务间调用 | 是 | 否 |
| 团队是否已有统一 CA 与证书运维能力 | 是 | 否(先做 PKI 再做身份) |
第三条经常被忽略:SPIFFE 的底座是证书签发与轮换,如果团队还没把证书生命周期管起来(见 10-13 的 PKI 篇),上 SPIFFE 只会把"证书过期"这个故障模式复制到每一个服务上。
8.2 与 PKI 篇的边界
- PKI 篇解决"证书从哪来、怎么自动续、怎么盘点"——它是机制;
- 本文解决"哪个工作负载该拿到什么身份、这个身份对应什么权限"——它是主体与授权的映射。
SPIFFE 把两者接起来:SVID 是证书(机制来自 PKI),它的 trust domain 与 SPIFFE ID 是身份(语义来自本文)。
8.3 落地骨架
SPIRE 的部署形态是 Server(签发 + 注册表)+ Agent(每个节点一个,向工作负载发放 SVID)。关键配置只有两处:
apiVersion: v1
kind: ConfigMap
metadata:
name: spire-server-config
namespace: spire
data:
server.conf: |
trust_domain = "corp.example.com"
bind_address = "0.0.0.0"
bind_port = "8081"
注册条目(registration entry)把"选择器"映射到"SPIFFE ID",这是整个体系的权限边界所在:
spire-server entry create \
-parentID spiffe://corp.example.com/node/sg-01 \
-spiffeID spiffe://corp.example.com/payments/api \
-selector k8s:ns:payments \
-selector k8s:sa:payments-api
三条工程纪律:
- 选择器必须同时限定 namespace 与 serviceaccount,只写 namespace 等于该命名空间内所有 Pod 共享身份;
- trust domain 一旦确定不要改,改了所有下游信任策略都要跟着改;
- SVID 寿命设短(小时级),把"泄漏窗口"压到与业务容忍度一致,续期由 Agent 自动完成。
九、密钥蔓延治理:从"事后响应"变成"入口拦截"
9.1 五类泄漏源
| 泄漏源 | 典型形态 | 特点 | 拦截点 |
|---|---|---|---|
| 代码库硬编码 | 源码里的 AKIA...、配置文件里的 password | 随 Git 历史永久存在 | pre-commit + CI 扫描 |
| CI 变量外泄 | 流水线日志打印环境变量、fork PR 可读 | 影响面覆盖整个仓库 | 日志脱敏 + 权限收紧 |
| 镜像层残留 | Dockerfile 里 COPY .env、构建缓存 | 镜像公开即泄漏 | 构建期扫描 + 多阶段构建 |
| 配置文件分发 | 运维把 AK 写进 Ansible/Jenkins 配置 | 扩散到多台机器 | 改为角色 + 集中配置中心 |
| 第三方泄露 | SaaS 平台被攻破、供应商日志外泄 | 不受自己控制 | 缩短寿命 + 使用监控 |
9.2 扫描工具对比
| 工具 | 优势 | 局限 | 适用位置 |
|---|---|---|---|
| gitleaks | 规则成熟、速度快、退出码友好 | 只做正则与熵检测,误报需调 | pre-commit + CI |
| trufflehog | 支持验证凭证是否仍然有效 | 验证需出网,可能触发风控 | CI 定时全仓扫描 |
| detect-secrets | 基线机制适合存量仓库 | 需维护 baseline 文件 | 存量项目接入 |
| 云厂商原生 | 与平台告警联动 | 通常只覆盖本云凭证 | 运行时兜底 |
推荐组合:gitleaks 做入口拦截(提交时),trufflehog 做周期性验证扫描(每周全仓 + 全历史),云厂商原生能力做运行时兜底。三者不是替代关系——入口挡住新泄漏,周期扫描找存量,运行时兜住"已经泄漏但还在用"的情况。
9.3 入口拦截流水线
gitleaks protect --staged --redact --verbose
gitleaks detect --source . --report-format json --report-path /tmp/leaks.json
三个工程细节:
--redact必开。不加这个参数,CI 日志会把命中的密钥明文打印出来——扫描工具本身成了泄漏渠道。- pre-commit 只挡新增。
protect --staged只扫描本次暂存内容,存量历史必须靠detect全量扫。 - 退出码要真的阻断。很多团队把扫描放进 CI 但设成
continue-on-error,等于没有。
9.4 已经泄漏了怎么办:顺序不能错
正确顺序是"先轮换,再取证,最后清历史"。 反过来做会出事:
- 轮换/禁用凭证——这是唯一能立刻止损的动作,其他都是善后;
- 取证——用云审计日志查这把凭证在泄漏窗口内做了什么(调用了哪些 API、从哪些 IP、有没有创建新用户或新密钥);
- 清理 Git 历史——用
git filter-repo重写历史并强推,同时通知所有协作者重新克隆; - 补流程——把这次泄漏的路径写进 pre-commit 规则或 CI 门禁。
git filter-repo 是重写历史的操作,会改变所有 commit 哈希,必须在团队约定的时间窗内做,并提前通知。而且要说清一件事:清历史不能替代轮换。已经被克隆、被 fork、被 CI 缓存过的仓库,历史永远清不干净。
十、运行时防滥用:让"合法凭证"也做不了大事
短期凭证解决了"寿命",但没有解决"权限"。一个权限过大的角色,即使只有一小时有效期,被利用的后果依然严重。运行时这一层做三件事:缩小单次可做的事、限制能从哪里做、发现异常的做法。
10.1 权限边界:给 NHI 装一个天花板
AWS 的 Permissions Boundary 是专门为"委托创建角色"设计的:它规定"这个角色最多能有什么权限",即使附加了管理员策略,实际权限也不会超过边界。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject", "s3:ListBucket"],
"Resource": [
"arn:aws:s3:::payments-export-sg",
"arn:aws:s3:::payments-export-sg/*"
]
},
{
"Effect": "Deny",
"Action": ["s3:DeleteBucket", "s3:PutBucketPolicy"],
"Resource": "*"
}
]
}
边界里放 Deny 比放 Allow 更重要:显式 Deny 是唯一能压住"上层策略写错"的机制。无论业务角色后面被谁加了多少权限,这几条 Deny 都生效。
10.2 条件键:把凭证钉在它该在的地方
| 条件键 | 作用 | 典型用法 |
|---|---|---|
aws:SourceVpce | 限定只能通过指定 VPC 端点访问 | 对象存储只允许私网端点访问 |
aws:SourceVpc | 限定来源 VPC | 内部服务之间的调用 |
aws:PrincipalOrgID | 限定只能被本组织账号使用 | 防止资源被外部账号引用 |
aws:RequestedRegion | 限定可用区域 | 数据驻留合规,防止误调其他区域 |
aws:PrincipalTag/* | 按会话标签授权 | 一个角色服务多个租户时做隔离 |
条件键是"低成本高收益"的典型:改一条策略,就能把"密钥泄漏后可全球使用"变成"密钥泄漏后只能从公司 VPC 内使用"。对 CI 场景尤其有效——CI 的出口 IP 或 VPC 通常是固定的。
10.3 会话标签与角色链
当多个服务共用一个角色时(不推荐但常见),用 sts:TagSession 在 AssumeRole 时打标签,再在权限策略里用 aws:PrincipalTag 做区分。这样即使角色相同,不同服务拿到的会话权限也不同,审计日志里也能看出"这次调用是哪个服务发起的"。
10.4 异常检测:三类信号
机器身份的异常不体现在"失败",而体现在"偏离基线"。三类信号最值得配告警:
| 信号 | 具体表现 | 数据源 |
|---|---|---|
| 地理位置异常 | 凭证突然从非业务区域调用 | CloudTrail 的 sourceIPAddress + 地域基线 |
| 服务组合异常 | 一个只读服务突然调用 IAM、STS、EC2 相关 API | CloudTrail 的 eventName 白名单 |
| 凭证外带特征 | 出现 GetCallerIdentity 高频探测、批量 CreateAccessKey | GuardDuty 相关 finding |
GuardDuty 里与 NHI 直接相关的几类 finding 值得单独看:UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS(实例凭证在云外被使用,最典型的"AK 被打包带走")、CredentialAccess:IAMUser/AnomalousBehavior(凭证使用行为异常)、Recon:IAMUser/MaliciousIPCaller(来自恶意 IP 的侦察)。这三类 finding 的共同点是——它们描述的都是"合法凭证的非法使用",这正是 NHI 风险的核心形态。
十一、三朵云 NHI 治理能力对比
| 能力 | AWS | 阿里云 | 腾讯云 |
|---|---|---|---|
| 长期凭证全量清单 | 凭据报告(CSV) | RAM ListAccessKeys | CAM ListAccessKeys |
| 最后使用时间 | 凭据报告内置 | GetAccessKeyLastUsed | 需查云审计 |
| 未使用凭证分析 | Access Analyzer(unused access) | 安全中心 / 配置审计规则 | 云审计 + 自定义规则 |
| 权限最小化建议 | Access Analyzer 策略生成 | RAM 权限诊断 | CAM 访问分析 |
| 外部系统联邦 | IAM OIDC / SAML | RAM SSO / OIDC | CAM SSO / OIDC |
| 工作负载身份 | Pod Identity / IRSA | ACK RRSA | TKE 服务账号 |
| 权限边界 | Permissions Boundary | 角色策略 + 管控策略 | 权限边界 |
| 条件键丰富度 | 最高 | 较高 | 中等 |
| 凭证滥用检测 | GuardDuty | 云安全中心 | 主机/云审计告警 |
三条选型结论:
- AWS 的工具链最完整:清单、最后使用、未使用分析、策略生成四件事都在原生服务里,可以直接搭自动化;
- 阿里云的关键能力齐全但分散:清单与最后使用在 RAM,未使用分析在安全中心或配置审计,需要自己拼;
- 腾讯云缺"最后使用时间"这个原生字段,必须靠云审计反查——这意味着盘点脚本要为它单独写一段逻辑,不能指望三家同一套代码。
十二、合规映射:审计会问哪几条
NHI 治理之所以必须做,很大一部分动力来自审计。四个常见框架里,与机器身份直接相关的条款如下:
| 框架 | 条款方向 | 审计实际要什么 |
|---|---|---|
| SOC 2 | CC6.1 / CC6.2 / CC6.3(逻辑访问) | 谁能访问、权限如何授予与撤销、是否定期复核 |
| ISO 27001 | A.5.15 / A.5.16 / A.5.17 / A.8.2(访问控制与特权访问) | 访问控制策略、身份生命周期、特权访问限制 |
| 等保三级 | 身份鉴别、访问控制、安全审计 | 是否有唯一标识、是否最小权限、操作是否可追溯 |
| PCI DSS 4.0 | 8.6(应用与系统账号管理) | 系统账号是否受管、是否禁止交互式登录、密钥是否定期更换 |
审计真正想要的不是"我们做了治理",而是"我们能证明"。 三样材料最有效:
- 月度清单快照:证明"我们知道有多少机器身份",且数量在下降;
- 变更记录:证明"每一次创建、轮换、删除都有记录",而不是年底补材料;
- 季度 owner 复核签署:证明"每条身份都有人认领",这是审计最常挑的毛病——清单里没有 owner 的条目,审计会直接判定为失控。
十三、成本与组织:钱不是主要成本
13.1 直接成本
| 成本项 | 口径 | 量级 |
|---|---|---|
| 联邦与临时凭证 | STS / 角色调用 | 基本为零,按请求计费可忽略 |
| 未使用凭证分析 | Access Analyzer 类能力 | AWS 侧该类分析不额外收费 |
| 密钥托管 | KMS 密钥 | 约 1 美元/密钥/月 |
| Secrets 存储 | Secrets Manager 类服务 | 约 0.4 美元/密钥/月 + 调用费 |
| 扫描工具 | gitleaks / trufflehog | 开源免费 |
| 自建 SPIRE | 2~3 台小规格实例 | 每月几十美元量级 |
价格为公开定价的量级参考,具体以各厂商官网实时报价为准;不同区域、不同计费方式会有差异。
13.2 真实成本是人力
三条经验:
- 盘点占 60% 工作量。三家的字段口径不同、分页不同、最后使用时间的取法不同,这部分不可能靠买工具省掉。
- 迁移占 30%。真正改代码的地方不多,主要是改流水线、改清单、改信任策略,以及一轮轮沟通"这把密钥到底谁在用"。
- 持续运营占 10%,但它是唯一不能停的部分。清单会腐化——新服务上线、新集成接入,如果不做"上线即登记",三个月后清单又是不准的。
13.3 组织上的三条建议
- NHI 登记写进上线流程,和"申请域名""申请数据库"同级。不上线登记就拿不到权限,这是唯一能长期保鲜的机制。
- owner 必须是团队而不是个人。个人会离职、会转岗,团队才有告警接收人和审批能力。
- 每季度做一次"僵尸清理日",固定时间、固定范围(全部 P0/P1 + 抽样 P2),产出物是清单快照与处置记录。固定节奏比运动式治理有效得多。
十四、90 天落地路线
| 阶段 | 时间 | 交付物 | 验收标准 |
|---|---|---|---|
| 一、可见 | 第 1~3 周 | 三云 + K8s + CI 统一清单(12 字段) | 能回答"我们有多少把钥匙",且每条有 owner 字段 |
| 二、收敛 | 第 4~7 周 | 五维评分 + P0/P1 处置完成 | P0 清零,P1 全部收到最小权限或设到期 |
| 三、消灭 | 第 8~11 周 | CI 全量 OIDC、工作负载身份覆盖核心服务 | 新增长期凭证为零;核心服务不再持有静态 AK |
| 四、固化 | 第 12~13 周 | 上线登记流程 + 季度复核机制 + 扫描门禁 | 门禁能真的阻断;季度复核有签署记录 |
15 分钟验收四问(能连续答上来才算做完):
- 我们一共有多少个机器身份?分布在哪些云、哪些集群、哪些 CI?
- 有多少条是"永久有效 + 管理员权限"?它们分别属于哪个团队?
- 最近 30 天新增了几条长期凭证?为什么不是零?
- 如果明天发现一把 AK 泄漏,我们能在多长时间内定位它做过什么、影响哪些数据?
总结
- 人类身份的治理靠流程(入职、离职、MFA),机器身份没有这些事件,只能靠"到期时间"和"owner"这两个人为装置。
- NHI 治理的顺序是盘清 → 收敛 → 消灭长期凭证,顺序颠倒会做出"看起来很安全但漏了 20%"的假象。
- 三步里最值钱的一步是清零长期凭证:它把"泄漏后的窗口"从永久压缩到一小时,而成本只是改流水线和加角色绑定。
- 工作负载身份(Pod Identity / RRSA / SPIFFE)的本质不是"更安全的密钥",而是让密钥这件事不存在。
- 治理能不能长期有效,取决于一条流程:上线即登记。没有这条,清单三个月就会腐化。
🚀 企业出海需要云架构咨询?通过 7.chengzicloud.cloud 联系我们,获取专属方案。覆盖多云身份治理、密钥与凭证体系、合规举证材料准备。
相关阅读
- 企业出海多云统一身份认证实战 — 人类身份的 SSO、联邦与 SCIM 同步,本文的前置篇章
- 企业出海多云特权访问管理(PAM) — 人类特权会话的 JIT 提权与审计,与本文的机器凭证治理互补
- 企业出海多云密钥管理与加密策略 — 密钥怎么存、怎么轮换,本文在此基础上要求清零长期凭证
- 企业出海多云 PKI 与证书生命周期管理 — 证书签发与自动续期,是 SPIFFE 身份的机制底座
- 企业出海多云零信任网络架构 — 访问凭什么被信任,本文提供被信任主体的治理
- 企业出海软件供应链安全(cosign + SBOM + SLSA) — 构建过程用的是什么身份,与本文的 CI 凭证治理直接相关
本文由 7.chengzicloud.cloud 提供,点击访问首页了解更多
常见问题(FAQ)
Q1:NHI 治理和 IAM 治理到底有什么区别?
IAM 治理的对象是"权限",问的是"谁能做什么";NHI 治理的对象是"身份实体",问的是"我们一共有多少个身份、它们各自活多久、谁负责"。前者是策略层面的收敛,后者是资产层面的盘点与生命周期管理。实践中两者共用一套策略工具,但盘点口径与 owner 机制是 NHI 独有的——IAM 治理通常不会要求"每一个角色都必须有团队认领并季度复核"。
Q2:小团队最少要做哪三件事?
只做三件:① 拉一次三云凭据清单,把所有"从未使用"和"超过 180 天未轮换"的 AccessKey 禁用;② 把 CI 改成 OIDC 联邦,删掉流水线里的长期密钥;③ 给所有机器身份补 owner 字段。这三件事覆盖了绝大多数真实事故的成因,成本是几天人力,不需要买任何工具。
Q3:长期 AccessKey 一定要清零吗?遗留系统不支持角色怎么办?
"清零"的目标是把长期凭证的暴露面压到最小,不是教条地要求零。对确实不支持角色的遗留系统,正确做法是"收敛":把它需要的那把长期凭证集中到一处(一台跳板机、一个令牌代理服务),对这一处加网络限制、加使用监控、设固定轮换周期,并且只保留这一把。真正要禁止的是"同一权限等级的长期密钥散落在十几台机器和几个仓库里"。
Q4:EKS Pod Identity 和 IRSA 怎么选?
新集群直接用 Pod Identity,因为它不需要给集群配 OIDC Provider、跨集群可以复用同一个角色、绑定关系是集群内的一等资源。存量集群不必一次性切换——两者可以并存,按命名空间逐步迁移。判断迁移优先级看两点:这个 SA 绑定的角色权限有多大、这个服务是否跨集群部署。权限大又跨集群的,先迁。
Q5:SPIFFE 是不是必须上?
不是。单云或双云但工作负载不互相认证的场景,各云原生的工作负载身份就够了。SPIFFE 的价值出现在"跨云 + 自建 IDC 的服务间互认",以及"需要一个与云无关的身份标识来做授权"的时候。而且它的前置条件是团队已经具备证书签发与轮换能力——没有这个底座,先做 PKI。
Q6:发现密钥泄漏,第一步做什么?
立刻轮换或禁用,不要先查日志。 取证很重要,但泄漏窗口每多一分钟,攻击者能做的事就多一分。正确顺序是:禁用 → 用云审计查这把凭证在窗口内的全部调用 → 检查是否被用来创建了新的用户/密钥/角色(这是最容易被忽略的持久化手段)→ 清理 Git 历史 → 补门禁。特别注意第二和第三步之间要检查"横向扩散",攻击者拿到一把密钥后的第一件事往往是建一个自己的后门账号。
Q7:三朵云能不能用一套脚本统一盘点?
能,但不要指望"一套代码三家用"。三家在三个地方不一样:字段命名(last_used_date vs 最后使用时间 vs 需查审计)、分页机制、以及权限模型(AWS 的 user/role 分离,阿里云与腾讯云的子账号体系不同)。可行做法是"统一输出 schema + 三个适配器",输出层共用,采集层各写一份。这样清单口径一致,采集逻辑可以独立演进。
Q8:怎么向审计证明"我们控制了机器身份"?
三样东西:① 有日期戳的清单快照(月度,能看到数量趋势);② 每一次创建/轮换/删除的变更记录(来自云审计,不是 Excel);③ 季度复核的签署记录(每条身份有 owner、有复核结论)。审计不看你用了什么工具,看的是这三样能不能对上——清单里的条目、变更记录里的事件、复核记录里的签署,三者数量与时间要能互相印证。