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。创建密钥很简单,但有几个细节容易翻车。

创建 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 提供了两种方式:自动轮换和手动轮换。

我的建议:能自动就别手动。自动轮换不需要你改任何代码,KMS 背后会创建新的密钥材料,但保留旧的 KeyId 做解密。手动轮换一般用于“必须重设整个密钥”的场景,比如怀疑密钥泄露。

自动轮换的规则很简单:每年轮换一次,不能改频率。如果你需要按季度轮换,就得走手动路线。手动轮换的本质是新建一个 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 集成关系图 用户 / 应用 kms:Encrypt / Decrypt KMS 密钥 CMK / KMS Key CloudTrail 事件记录 API 调用 自动投递 数据事件(可选开启) 管理事件(默认开启) CloudWatch Logs 默认自动 需手动开启 事件聚合 可选投递 运维与排查用途 审计密钥使用 → 定位访问超时 / 权限拒绝 → 分析密钥轮换影响

从上图你能看出来,KMS 每次被调用都会自动向 CloudTrail 投递一条事件。如果开启了数据事件,就连每次加密/解密的请求都会被记录。我排查过很多“偶尔解密失败”的案例,最后答案往往就藏在 CloudTrail 的一条 Decrypt 事件里——不是 AccessDenied 就是 ExpiredTokenException。

记住这几点:
  • 密钥创建和策略变更属于管理事件,默认即记录。
  • 加密和解密属于数据事件,需要手动启用,且会产生大量日志,建议收窄到特定密钥或过滤条件。
  • CloudTrail 不记录密钥的明文内容,只记录元数据——这是设计如此,不是 Bug。

实用小技巧:在 CloudTrail 控制台里搜索 eventName: Decrypt AND resources.ARN: arn:aws:kms:ap-northeast-1:123:key/xxxx,能快速定位某个密钥在特定时间段内的所有解密请求。我每次做密钥轮换审计,都会先跑这个查询,看看有哪些应用还在用旧密钥。