AWS KMS 服务介绍:密钥创建、轮换、权限策略与 CloudTrail 集成
讲到加密,绕不开 AWS KMS(Key Management Service)。你想想看,KMS 就像是云上加密的“总钥匙管理员”。我个人习惯先把 KMS 和 CloudTrail 的关系理清楚,因为绝大多数加密故障排查,最后都会落到“谁在什么时候用了哪把钥匙”这个点上。
这一节我会把密钥创建、轮换、权限策略,以及和 CloudTrail 的集成方式串起来讲。记得有一次客户半夜报警说加密失败,我一看 CloudTrail 日志,发现密钥被轮换后没更新别名映射——嗯,这种坑踩过一次就忘不了了。
1. 密钥创建(CMK)——从入门到实战
KMS 的核心实体叫 KMS Key,官方叫法 Customer Master Key(CMK)。现在 AWS 已经改口叫 KMS Key 了,但大家还是习惯叫 CMK。创建密钥很简单,但有几个细节容易翻车。
- 对称 vs 非对称:对称密钥(AES-256)用于数据加密解密;非对称(RSA/ECC)用于数字签名或加密小数据。我一般建议99%的场景用对称,除非你非得做公钥分发。
- 密钥来源:AWS 生成(默认) vs 导入自己的密钥材料。曾经有一个项目因为合规要求必须用自己的密钥,结果导入后忘了设置有效期,半年后密钥自动过期,所有 S3 对象都读不出来了……
- 区域属性:CMK 是区域隔离的。跨区域用不了,得复制密钥或建立多区域密钥。
创建时你会遇到两种“型”:AWS 托管型(服务自动创建,你改不了策略)和客户托管型(你自己管,可以设置轮换、权限)。排查的时候一定要先确认密钥类型,不然策略调了半天发现没权限改动。
代码示例:通过 AWS CLI 创建对称 CMK
aws kms create-key \
--description "MyApp Encryption Key" \
--key-usage ENCRYPT_DECRYPT \
--origin AWS_KMS \
--tags TagKey=Environment,TagValue=Production
运行后返回 KeyId 和 Arn。这个 Arne 就是以后所有权限和日志记录的标识。
2. 密钥轮换——自动与手动策略
密钥轮换是个“不用就忘”的安全动作。AWS 提供了两种方式:自动轮换和手动轮换。
自动轮换的规则很简单:每年轮换一次,不能改频率。如果你需要按季度轮换,就得走手动路线。手动轮换的本质是新建一个 CMK,然后把别名指向新密钥,旧密钥留着解密历史数据。
| 轮换方式 | 优点 | 缺点 |
|---|---|---|
| 自动轮换 | 零运维,旧密钥材料自动保留 | 频率固定(1年),无法立即强制轮换 |
| 手动轮换 | 灵活控制时间点,可立即生效 | 需要手动更新别名,容易遗漏历史数据解密问题 |
避坑指南:我曾经遇到一个案例,某团队手动轮换了密钥,但没更新 Lambda 环境变量里的 KeyId。结果新写入的数据用了新密钥,旧数据用旧密钥解密,但 Lambda 拿到的 KeyId 还是旧的那个,导致部分数据解密时报错 InvalidCiphertextException。排查了整整一个下午才发现是别名没切过来。
3. 密钥权限策略——IAM 与密钥策略的区别
KMS 的权限模型有两个层面:IAM 策略和密钥策略。这两个东西搞混了,权限就全乱套了。
- IAM 策略:附加在用户/角色上,控制“谁”能调用 KMS API。比如允许某个 IAM 用户调用
kms:Encrypt。 - 密钥策略:直接写在 CMK 上,控制“哪些主体”能对这把密钥做什么操作。这是最高层级的“门禁”。
说白了,密钥策略是“钥匙上的锁”,IAM 是“人身上的门禁卡”。一个用户即便 IAM 给了全部 KMS 权限,如果密钥策略没把他加进去,照样打不开锁。反过来也一样,密钥策略允许了但 IAM 拒绝了,也不行。两者必须同时允许。
- 跨账号授权时,必须同时设置密钥策略(允许对方账号的根用户)和对方的 IAM 策略。
- 某些 AWS 服务(如 S3、Lambda)自带“服务角色”访问 KMS,你需要在密钥策略里显式 Allow 那个服务角色。
- 千万别给
kms:*加上Condition限制时写漏了kms:ViaService,否则可能无意中阻断控制台操作。
密钥策略的典型结构(JSON):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::123456789012:role/MyAppRole"},
"Action": [
"kms:Encrypt",
"kms:Decrypt"
],
"Resource": "*"
}
]
}
我个人习惯在排查解密失败时,先去 CloudTrail 里看到底是 AccessDenied 还是 InvalidCiphertextException。前者大概率是策略问题,后者通常是密钥或数据损坏。
4. KMS 与 CloudTrail 的集成方式
KMS 和 CloudTrail 的集成是全自动的,不需要你额外配置什么。只要你的账号启用了 CloudTrail(推荐开启管理事件和数据事件),所有 KMS 的 API 调用都会被记录下来。
但是——这里有一个“但是”——KMS 的加密/解密操作并不会记录原始数据内容,只会记录请求的元数据:谁、什么时间、调用了哪个 API、目标密钥的 ARN、请求来源 IP、用户代理等。你想想看,要是能把明文数据都记录下来,那还叫什么加密服务呢?
具体的集成方式:
- 管理事件(Management Events):记录密钥创建、轮换、删除、策略变更等控制平面操作。
- 数据事件(Data Events):记录加密、解密、重新加密等数据平面操作。默认不记录,需要手动开启。
我记得有一次排查一个“S3 上传后无法下载”的问题,就是靠开启数据事件才发现:某个 EC2 实例的临时安全凭证已经被回收了,但应用还在用旧凭证调用 KMS 解密,每次都是 AccessDenied。
- 务必开启数据事件记录,否则你只能看到“谁创建了密钥”,看不到“谁在用密钥加解密”。
- 在 CloudTrail 中创建专门的“KMS 事件”仪表盘,过滤
eventSource = kms.amazonaws.com。 - 如果密钥被大量调用,日志量会暴增,建议加到 CloudWatch Logs 中并设置告警。
来看一张关系图,帮你快速理解 KMS 操作如何流入 CloudTrail:
从上图你能看出来,KMS 每次被调用都会自动向 CloudTrail 投递一条事件。如果开启了数据事件,就连每次加密/解密的请求都会被记录。我排查过很多“偶尔解密失败”的案例,最后答案往往就藏在 CloudTrail 的一条 Decrypt 事件里——不是 AccessDenied 就是 ExpiredTokenException。
- 密钥创建和策略变更属于管理事件,默认即记录。
- 加密和解密属于数据事件,需要手动启用,且会产生大量日志,建议收窄到特定密钥或过滤条件。
- CloudTrail 不记录密钥的明文内容,只记录元数据——这是设计如此,不是 Bug。
实用小技巧:在 CloudTrail 控制台里搜索 eventName: Decrypt AND resources.ARN: arn:aws:kms:ap-northeast-1:123:key/xxxx,能快速定位某个密钥在特定时间段内的所有解密请求。我每次做密钥轮换审计,都会先跑这个查询,看看有哪些应用还在用旧密钥。