4. Condition条件键:常见条件键及其用法
Condition条件键,说白了就是密钥边界策略里的“精细控制器”。
一个策略看起来可能很宽泛,但在Condition的加持下,能变得非常精准。你想想看,你不能只告诉KMS“允许所有用户操作所有密钥”,那太粗暴了。你得说“允许张三,用特定的标签,在特定的时间,创建特定类型的密钥”。这里的“特定”二字,全靠Condition来实现。
4.1 条件键的分类
我个人习惯把KMS的条件键分成三大类:
- 资源绑定类条件键:跟密钥本身属性相关的。比如密钥的标签(
kms:RequestTag)、密钥的创建时间(kms:CreationDate)、密钥的ID(kms:KeySpec)等。这类条件键往往在kms:*Key动作中使用,用来限定“什么样的密钥才能被操作”。 - 请求属性类条件键:跟API请求参数相关的。比如
kms:EncryptionAlgorithm(加密算法)、kms:KeyUsage(密钥用途)。它们直接检查你发送的请求里带了什么参数。 - 上下文类条件键:跟调用环境相关的。比如
kms:CallerPrincipal(调用者主体)、kms:ViaService(通过哪个服务发起)。这类条件键我用的最多,因为在排查问题时,经常发现是某个服务误调用了密钥,通过kms:ViaService就能直接拦住。
| 分类 | 常见条件键 | 用途 | 注意事项 |
|---|---|---|---|
| 资源绑定类 | kms:RequestTag, kms:CreationDate, kms:KeySpec |
限定密钥的标签、创建时间、规格 | kms:CreationDate 只能用 DateGreaterThan 或 DateLessThan |
| 请求属性类 | kms:EncryptionAlgorithm, kms:KeyUsage |
限定加密算法、密钥用途 | 注意区分 kms:EncryptionAlgorithm 和 kms:DecryptionAlgorithm |
| 上下文类 | kms:CallerPrincipal, kms:ViaService |
限定调用者主体、调用来源服务 | kms:ViaService 常用于隔离不同云服务对密钥的使用 |
4.2 深度解析:kms:RequestTag
这个条件键很实用。它检查创建密钥时是否带了指定的标签。如果你做了一个策略,只有带Environment=Production标签的密钥才能被创建,那么用户就无法创建无标签或错误标签的密钥。
我在项目中遇到过一个问题:开发环境不小心创建了一个“生产环境”标签的密钥,导致测试数据落盘后被误认为生产数据。嗯,后来我用kms:RequestTag强制要求创建密钥时必须带上Environment标签,且值必须是Dev或Test,这样开发环境的人就再也创建不了Production标签的密钥了。
用法示例:
{
"Effect": "Allow",
"Action": "kms:CreateKey",
"Resource": "*",
"Condition": {
"StringEquals": {
"kms:RequestTag/Environment": "Production"
}
}
}
注意,kms:RequestTag只在CreateKey动作中生效。对于已存在的密钥,如果想基于标签来控制访问,需要用kms:ResourceTag,这个我们后面会讲到。
kms:RequestTag支持多值匹配。如果你需要允许多种标签,可以用 ForAnyValue:StringEquals 条件。
4.3 深度解析:kms:CreationDate
这个条件键控制的是密钥的创建时间。简单来说,你可以限制:只有某一天之后创建的密钥,才能执行某些操作。
为什么需要这个?我之前帮客户排查过一个异常:他们发现某个老密钥被意外删除。原因是一条旧策略过于宽泛,允许了所有密钥的删除操作。我建议他们用kms:CreationDate加一个DateGreaterThan条件,只允许对2024年1月1日之后创建的密钥执行删除。这样,老密钥就自动处于“只读不可删”的状态。避坑效果立竿见影。
用法示例:
{
"Effect": "Deny",
"Action": "kms:ScheduleKeyDeletion",
"Resource": "*",
"Condition": {
"DateLessThan": {
"kms:CreationDate": "2024-06-01T00:00:00Z"
}
}
}
这里用DateLessThan表示:如果密钥创建时间早于2024年6月1日,则拒绝删除。注意,日期格式必须是ISO 8601的UTC时间。
kms:CreationDate 和 kms:KeyRotationStatus 混在一起用。我曾经想把“未开启自动轮转的老密钥”都禁止使用,结果发现策略生效时机不对,建议分两条策略来做。
4.4 深度解析:kms:KeySpec 和 kms:KeyUsage
这两个条件键绑定得很紧。kms:KeySpec决定了密钥的算法和长度(比如AES_256、RSA_2048、EC_P256等),kms:KeyUsage决定了密钥的用途(比如ENCRYPT_DECRYPT、SIGN_VERIFY)。
你想想看如果一个密钥既用来加密,又用来签名,一旦泄露风险极大。所以最好强制限定:KeyUsage必须是ENCRYPT_DECRYPT,而且KeySpec必须是AES_256。
用法示例:
{
"Effect": "Allow",
"Action": "kms:CreateKey",
"Resource": "*",
"Condition": {
"StringEquals": {
"kms:KeySpec": "AES_256",
"kms:KeyUsage": "ENCRYPT_DECRYPT"
}
}
}
4.5 实战:组合使用条件键
很多时候,我们需要把多个条件键组合起来。比如:只允许创建带Environment=Production标签的、AES_256算法、用于加密解密的密钥。这在边界策略里非常常见。
不过有个细节要留心:条件键之间是“与”关系,也就是说必须同时满足所有条件。没有“或”的关系,除非你用ForAnyValue:StringEquals这样的集合条件符。
4.6 使用注意事项
最后说几个我踩过的坑,希望能帮你避开:
- 条件键不适用于所有动作:比如
kms:ListKeys这个动作不支持条件键。你在策略里写了kms:RequestTag也不管用,因为ListKeys不传标签参数。我曾见过有人在这个动作上加了kms:RequestTag,结果策略总是匹配失败。 - 大小写敏感:条件键名称和值都区分大小写。
StringEquals就是严格的匹配,Environment和environment不一样。嗯,这个我吃过亏。 - 不要滥用
ForAllValues:ForAllValues要求请求中的所有标签都在策略允许的范围内。如果你的策略只写了一个允许标签,但用户请求带了两个标签,就会失败。我建议只用ForAnyValue,它更宽容。
核心总结: 条件键是边界策略的灵魂。只要你想把策略做得细,就必须玩转kms:RequestTag、kms:CreationDate、kms:KeySpec、kms:KeyUsage这几个。我个人觉得,加一个条件键,比写一大段Action排除列表要安全得多。试试看,你会爱上这种精确控制的感觉。