4. 权限不足导致轮换失败:IAM角色、ACL、云服务权限检查要点
密钥轮换失败,十次里有四五次都是权限问题。这不是夸张——我排查过的轮换事故里,权限不足是最容易踩的坑。为什么?因为轮换不是一个人干活,它涉及好几个角色:轮换工具、密钥存储、目标服务,每个环节都得有对应的权限。
说白了,轮换动作就像接力赛。第一棒是触发轮换的那个人(或脚本),第二棒是密钥管理系统,第三棒是目标应用。任何一棒权限不够,接力棒就掉地上了。今天咱们就按这个思路,把权限检查的要点掰开揉碎。
核心逻辑图 —— 先给个全景,后面再拆细节。
4.1 IAM 角色权限:轮换执行者的「身份证」
轮换动作通常不是人按按钮,而是由某个程序触发的——比如 Lambda 函数、CronJob、或者 CI/CD 流水线。这些程序需要一个身份,在 AWS 里就是 IAM 角色。如果这个角色权限不够,第一步就卡住了。
检查信任策略 —— 谁可以扮演这个角色?我记得有次帮客户排查,他们用 Lambda 做密钥轮换,结果 Lambda 一直报 AssumeRole 失败。一看信任策略,Principal 写的是"root",但 Lambda 的服务主体是 "lambda.amazonaws.com",根本不匹配。你想想看,连门都进不去,后面权限再大也没用。
| 信任策略常见问题 | 现象 | 修复方向 |
|---|---|---|
| Principal 缺失或写错服务主体 | AssumeRole 被拒绝 (403) | 补上对应的服务主体,如 ecs-tasks.amazonaws.com |
| 条件键限制过严 | 特定触发条件下能扮演,其他条件失败 | 检查 aws:SourceArn 等条件,放宽或补全 |
| 角色嵌套链限制 | 跨账户轮换时角色链太长 | 限制链深度,或用服务管控策略 |
权限边界 (Permissions Boundary) —— 这个很多人容易忽略。就算角色本身给了 kms:RotateKeyOnDemand,如果角色的权限边界里没放开,那也没用。我个人习惯每次新建轮换角色时,先检查一下有没有绑定边界策略,有的话得确认边界里包含了轮换需要的操作。
// 示例:检查角色是否被权限边界限制了
aws iam get-role --role-name MyKeyRotationRole
// 看输出中的 PermissionsBoundary 字段
// 如果存在,再用 get-policy 查看具体策略
4.2 ACL / 资源策略:密钥和数据存储的「门禁」
权限不足不止出在 IAM 角色上。很多时候,密钥本身有资源级策略(比如 KMS 密钥策略),或者密钥关联的数据库、队列也有自己的访问控制。嗯,这里要注意,IAM 和资源策略是独立评估的,两者必须同时允许才能通过。
KMS 密钥策略 —— 轮换密钥时,轮换工具需要 kms:ScheduleKeyDeletion、kms:RotateKeyOnDemand、kms:CreateKey(如果自动生成新密钥)等权限。但密钥策略里如果只写死了 Principal 为某个 Root 账户,没有加上轮换角色的 ARN,那就会报 AccessDenied。
我在项目中遇到过一件事:客户写了一个轮换函数,用了一个自动化角色,但 KMS 密钥策略里只允许了两个管理员账号。每次轮换,日志里就出现 "The ciphertext refers to a customer master key that does not exist, or you do not have permission to use the key" —— 折腾了两天才发现是密钥策略没加上轮换角色。
S3 存储桶策略 —— 如果你的密钥文件存在 S3(比如 Secrets Manager 后端),或者轮换后需要把新密钥写入某个桶,那桶策略也得允许。尤其跨账户场景,桶策略里要明确写上允许轮换角色的 PutObject、GetObject。
还有 SQS 队列策略,Secrets Manager 有时用队列来异步通知轮换结果。队列策略如果拒绝轮换工具发送消息,那日志里就会有 Publish 超时之类的错误。排查的时候,记得把消息队列和事件通知服务也纳入检查范围。
| 资源类型 | 轮换涉及的操作 | 常见遗漏点 |
|---|---|---|
| KMS 密钥 | RotateKeyOnDemand, CreateKey, EnableKey, PutKeyPolicy | 未添加轮换角色 ARN 至密钥策略 |
| S3 存储桶 | PutObject, GetObject, ListBucket | 跨账户时缺少 Condition(如 aws:SourceArn) |
| SQS 队列 | SendMessage, ReceiveMessage, DeleteMessage | 队列策略拒绝非 VPC 端点的请求 |
| Secrets Manager 自身 | UpdateSecret, PutSecretValue, RotateSecret | 资源策略限制只读账户 |
4.3 云服务自身权限:轮换「执行环境」的额外限制
前面说的都是主角色和资源策略。但轮换函数跑在具体服务上——Lambda、ECS、或者 EC2,这些服务本身也有自己的权限模型。
Lambda 执行角色 —— 如果轮换函数是 Lambda,它的执行角色必须包含必要的 IAM 策略和信任关系。另外,Lambda 的 VPC 配置也可能导致网络不通,无法访问 KMS 端点。我记得有一次,Lambda 连上了 VPC,但没配置 S3 端点或 NAT,结果轮换到一半,需要读取 Secrets Manager 的备份时超时了。表面看是权限问题,实际上是网络隔离导致的 IO 失败。
ECS 任务角色 —— 在 ECS/Fargate 里跑轮换任务,需要给任务定义一个任务角色 (Task Role)。千万别只给了容器角色 (Execution Role) 而忘了给任务角色。我见过有人把轮换脚本放在 Fargate 上,结果死活连不上 KMS,折腾半天才发现用的是 ECS 服务角色,不是任务角色。
- 执行角色 (Execution Role) — 用于拉取镜像、写入 CloudWatch 日志。
- 任务角色 (Task Role) — 用于任务内部调用 AWS API,比如 kms:RotateKeyOnDemand。
轮换功能用的是 任务角色,检查时务必确认它有没有轮换相关策略。
EC2 实例角色 —— 如果轮换是跑在 EC2 上(比如 Ansible 脚本),那得给实例附加一个实例角色。实例角色默认会拿到临时凭证,但如果实例不在标准元数据服务范围内(比如使用了 IMDSv2 且配置了高跳数限制),也会拿不到正确的凭证。
4.4 快速排查清单
每次接到轮换失败的工单,我会按这个顺序查一遍,效率很高:
- 查日志 — CloudTrail 里搜 AccessDenied 或 UnauthorizedOperation,记下具体的 API 调用和资源 ARN。
- 确认谁在调用 — 从日志里找到调用者的角色 ARN / 主体。是不是预期角色?
- 模拟策略 — IAM Policy Simulator,用该角色模拟刚才失败的 API 调用。如果模拟失败,那就是策略问题。
- 检查资源策略 — 到对应资源(KMS、S3、SQS)查看资源策略,是否包含了该角色?
- 检查网络边界 — 如果用的是 VPC Endpoint,检查端点策略是否允许。
- 检查服务控制策略 (SCP) — 多账户场景,根组织级的 SCP 可能限制了 kms 操作。
4.5 总结一下
权限不足导致的轮换失败,本质上就三类问题:
- 谁的身份不对 — 信任策略没写对,角色扮演不了。
- 能做什么没给够 — IAM 策略缺少关键操作,或权限边界挡住了。
- 资源方不让进 — 密钥策略、桶策略等资源级策略没放行。
排查的时候,记住「身份→策略→边界」三步法,再结合 CloudTrail 日志,基本十分钟内能定位。别一上来就怀疑代码——我见过太多人改代码改了一周,结果只是密钥策略少写了一个 ARN。你想想看,多冤啊。