4. 密钥策略详解:如何编写和管理CMK的访问策略
咱们搞 KMS,说白了,密钥策略就是 CMK 的“门禁系统”。你策略写错了,哪怕 IAM 里配得再花哨,照样调用失败。我见过不少团队,一上来就卡在这个地方——明明权限都给了,KMS 就是拒你,查半天发现策略里少写了一个 Action。
这一节,我把密钥策略的要点掰开来讲。你跟着我的思路走,基本不会再踩坑。
4.1 为什么需要密钥策略?跟 IAM 有啥区别?
先理清两个概念:IAM 策略是“全局通行证”,密钥策略是“本地门禁”。你对一个 CMK 发起请求时,KMS 的鉴权逻辑是这样走的:
- 检查请求者是否有 IAM 权限(用户/角色策略)
- 检查该 CMK 的密钥策略是否明确允许该操作
- 如果密钥策略有明确的
Deny,直接拒绝,IAM 允许也没用
我在项目中遇到过一例:某团队给开发人员配了 IAM 的 kms:Encrypt,结果开发还是报权限错。最后发现密钥策略里只写了“允许管理员用户使用”,没写开发角色——密钥策略没放行,上层 IAM 再允许也没用。
4.2 密钥策略的结构长什么样?
密钥策略本质上是一个 JSON 文档,遵循标准的 IAM 策略语法。我习惯把它拆成五个要素来看:
| 要素 | 说明 | 常见写法 |
|---|---|---|
| Version | 策略语法版本,目前固定 2012-10-17 |
"Version": "2012-10-17" |
| Statement | 声明列表,每个声明是一条规则 | 数组形式,至少一条 |
| Effect | 允许 Allow 或拒绝 Deny |
二选一,不能既允许又拒绝 |
| Principal | 谁可以执行操作(IAM 用户/角色/根用户) | 可以使用 ARN 或通配符 * |
| Action / Resource / Condition | 操作、资源 ARN、条件限制 | 类似 IAM 策略写法 |
举个例子,一个最基本的、允许根用户完全控制的策略:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:root"
},
"Action": "kms:*",
"Resource": "*"
}
]
}
嗯,这里要注意:Principal 是密钥策略特有的字段,IAM 策略里没有它。你写密钥策略时,必须明确指定“谁”能操作。
4.3 实战编写:一个典型的生产环境策略
假设我们要创建一个 CMK,让应用服务(IAM 角色 app-role)能加密解密数据,同时让安全工程师(IAM 用户 sec-admin)能管理密钥的别名和标签。完整策略如下:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Enable IAM User Permissions",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:root"
},
"Action": "kms:*",
"Resource": "*"
},
{
"Sid": "Allow app-role to encrypt/decrypt",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:role/app-role"
},
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:GenerateDataKey"
],
"Resource": "*"
},
{
"Sid": "Deny non-HTTPS requests",
"Effect": "Deny",
"Principal": "*",
"Action": "kms:*",
"Resource": "*",
"Condition": {
"Bool": {
"aws:SecureTransport": "false"
}
}
}
]
}
这段策略里,我做了三件事:
- 第 1 条:保留根账户的完全控制,这是 KMS 的强制要求,删了会报错。
- 第 2 条:只给
app-role加密、解密、生成数据密钥的权限,管理员能加别名,但应用角色不需要。 - 第 3 条:拒绝所有非 HTTPS 的请求——我曾经遇到过明文调用的情况,虽然概率低,但加上这个条件更安全。
Sid 字段给每条声明起个名字,方便后续审计和排除。我习惯用“动作-主体”的命名方式,比如 AllowEncrypt-app-role。
4.4 如何更新密钥策略?三种方式
你可以在 CMK 创建时一次性配好策略,也可以事后修改。我个人最常用的是前两种:
- AWS 管理控制台:进入 KMS → 选择 CMK → “密钥策略” → 直接编辑 JSON,保存即可。适合快速验证。
- AWS CLI:用
put-key-policy命令替换整份策略,或者用create-key-policy添加语句(需逐个添加)。 - CloudFormation / Terraform:基础设施即代码方式,推荐生产环境使用。
注意,每次更新策略,都会完全覆盖旧策略。我建议你在修改前先 get-key-policy 保存一份备份。别问为什么——我曾经手滑删了根用户权限,导致整把 CMK 无人能管,最后只好联系客服。
4.5 常见陷阱与避坑指南
陷阱 1:Principal 写错 ARN
写 Principal 时,IAM 用户要用 arn:aws:iam::account-id:user/username,IAM 角色用 arn:aws:iam::account-id:role/rolename。我见过有人写成 arn:aws:iam::123456789012:service-role/app-role,结果角色是 app-role 但路径不同,死活不生效。
陷阱 2:同时给多个主体写允许,却忘了 Deny 的优先级
密钥策略里,Deny 永远优先于 Allow。即使你写了多个 Allow,只要有一条 Deny 匹配,请求就被拒绝。我建议:尽量用 Allow 来精确放行,少用 Deny。如果非得用 Deny,务必配上 Condition 缩小范围。
陷阱 3:跨账户访问时,两边都要配
想让自己账户的 CMK 被其他账户的 IAM 角色使用,你的密钥策略必须把那个角色的 ARN 写在 Principal 里,同时,对方账户的 IAM 策略也要加上相应权限。缺一不可。我记得有个客户折腾了三天,最后发现对方账户的 IAM 策略写的是 kms:Decrypt,但 CMK 策略里只给了 kms:Encrypt,双向不对齐。
4.6 密钥策略决策流程(一图胜千言)
下面这张图总结了 KMS 鉴权的完整路径。你顺着箭头走一遍,就能明白策略怎么生效。
这个流程你看懂了吧?IAM 和密钥策略是“与”的关系,缺一个都不行。我建议你把这张图存下来,每次写策略时对照着过一遍,能省不少排查时间。
4.7 管理策略的最佳实践
- 最小权限原则:只给需要的 Action,不要给
kms:*。比如加密密钥不需要ScheduleKeyDeletion权限。 - 使用条件键:通过
kms:ViaService限制只能从特定 AWS 服务调用,比如只允许从 S3 加密对象。 - 定期审计:用
get-key-policy配合脚本检查策略变更,避免意外的 Deny 生效。 - 策略版本控制:KMS 本身不保存历史版本,我习惯每次变更前将当前策略另存为 JSON 文件,丢到 Git 里管理。
核心总结:密钥策略不是你想象的 IAM 的“子集”。它是 CMK 自身的独立门禁。写策略时,先想清三个问题:谁(Principal)?做什么(Action)?什么条件下做(Condition)?想清楚再动笔,一般不会出错。