3、策略组成部分详解:策略结构(Version, Statement)、Effect、Principal、Action、Resource、Condition

3.1 开篇:别被JSON吓到

策略本质上就是一段JSON。我刚开始接触KMS的时候,看到一长串JSON头都大了。但拆开来看,其实就六个核心元素:Version、Statement、Effect、Principal、Action、Resource、Condition。今天咱们一个一个说清楚。

你想想看,写策略就像写一个「门禁规则」。Version是规则版本号,Statement是具体条款。每个条款里,Effect决定「放行还是禁止」,Principal指定「谁」,Action定义「做什么」,Resource限定「对哪个钥匙」,Condition加上「附加条件」。搞明白这些,你就能自己写策略了。

3.2 Version — 版本号,别乱填

Version字段只有一个值:1(字符串形式)。早期阿里云还有过其他版本,但现在已经统一成 "1"。

我要提醒你:千万别写成数字 1,必须是字符串 "1"。我曾经在一个自动化脚本里传了数字,结果策略死活不生效,排查了半天。所以,养成习惯,写 "Version": "1"。

3.3 Statement — 策略的“条款清单”

Statement是一个数组,里面可以放多个策略条款。每个条款是一个对象,包含Effect、Principal、Action、Resource、Condition。多个条款之间是「OR」关系——只要命中任意一条,就生效。

注意顺序:如果一条策略是Deny,另一条是Allow,Deny优先。这是阿里云IAM的规则,不是KMS独有的。我在做跨账号授权时,就踩过这个坑:Allow写在前面,Deny写在后面,结果Deny盖过了Allow,权限死活开不起来。

3.4 Effect — 允许还是拒绝?

Effect只有两个取值:Allow 和 Deny。

  • Allow:明确允许某个操作
  • Deny:明确拒绝某个操作(优先级高于Allow)

我习惯把Deny放在所有语句的最前面,这样一眼就能看到「禁区」。有一次我在生产环境误把Effect写成了「Deny」本该是Allow,导致整个部门无法使用KMS密钥。嗯,从那以后我每次部署前都会人工检查Effect的值。

3.5 Principal — 谁可以?

Principal指定策略对谁生效。在KMS密钥边界策略里,Principal通常跟RAM用户、RAM角色、阿里云服务账号相关。

Principal类型写法示例说明
RAM用户"RAM" : "acs:ram::1234567890:user/admin"精确指定某个用户
RAM角色"RAM" : "acs:ram::1234567890:role/admin"精确指定某个角色
阿里云服务"Service" : "ecs.aliyuncs.com"允许ECS服务使用
所有人"*"慎用!等于全公开

我曾经见过有人直接在Principal写 "*",以为只是测试用,结果上线后密钥被非预期的账号使用了。所以我的建议是:除非你明确需要开放给所有RAM账号,否则一定要写具体的ARN。

3.6 Action — 能做什么?

Action指定允许或拒绝的操作。KMS的Action非常多,比如 kms:Encrypt、kms:Decrypt、kms:GenerateDataKey 等等。可以用通配符 * 匹配所有操作,但粒度越细越安全。

小技巧: 如果你不确定某个业务需要哪些Action,可以先用 kms:DescribeKey 这种只读操作测试,然后通过审计日志观察实际调用的API,再逐步收窄。

Action支持 List 和 NotAction。NotAction的意思是「除了这些操作之外的所有操作」,配合Deny效果很好。比如你只想禁止 kms:ScheduleKeyDeletion(计划删除密钥),其他操作都允许,就可以用Deny + NotAction。

3.7 Resource — 对哪个密钥?

Resource指定策略作用的KMS密钥。格式是 "acs:kms:${region}:${account}:key/${keyId}"。

  • 精确指定:"acs:kms:cn-hangzhou:1234567890:key/1234abcd-..."
  • 通配符:"acs:kms:*:*:key/*" 匹配所有密钥(谨慎使用)
  • 多密钥:可以用数组写多个ARN

我在项目中遇到过一位同事,Resource写了同一个区域所有密钥(用了 *),本来只打算给一个密钥授权,结果所有密钥都暴露了。所以,Resource一定要精确到keyId,除非你确实需要全局策略。

3.8 Condition — 附加限制条件

Condition是策略的「精细调节器」。它可以在Allow/Deny的基础上再附加条件,比如来源IP、是否使用HTTPS、是否使用临时凭证等。

常见条件操作符:

  • StringEquals — 字符串相等
  • IpAddress — 来源IP匹配
  • DateLessThan — 时间条件(比如只允许在某个时间段内操作)
  • Bool — 布尔值条件(比如 "kms:CallerIsKeyOwner")

注意: Condition写错可能导致策略永远不生效,或者永远生效。我曾经设置过一个 StringNotEquals 条件,本意是排除某个IP,结果写反了,变成了只允许那个IP访问。排查了两个小时才发现。

Condition的写法比较灵活,可以组合多个条件(AND、OR)。我个人习惯把条件写成 "Condition": { "IpAddress": { "acs:SourceIp": ["10.0.0.0/8"] } },这样限制来源网段,安全性高很多。

3.9 知识总结:策略结构图

我把上面的关系画成一张SVG图,方便你对照记忆。

KMS 密钥边界策略核心结构 策略 (Policy) Version : "1" Statement[] Effect Principal Action Resource Condition 说明: 一个策略包含一个Version和一个Statement数组。每个Statement是一个对象,包含Effect(允许/拒绝)、Principal(谁)、Action(操作)、Resource(密钥)、Condition(条件)。 多个Statement之间是OR关系,Deny优先级高于Allow。Condition是可选的,但强烈建议使用。

3.10 实战对照:一条完整策略的拆解

我拿一条我常用的策略例子,咱们把每个部分对上。

{
  "Version": "1",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "RAM": ["acs:ram::1234567890:role/ecs-app-role"]
      },
      "Action": [
        "kms:Encrypt",
        "kms:Decrypt"
      ],
      "Resource": [
        "acs:kms:cn-hangzhou:1234567890:key/abcd-1234"
      ],
      "Condition": {
        "IpAddress": {
          "acs:SourceIp": ["10.0.0.0/8"]
        }
      }
    }
  ]
}

解读:允许RAM角色 ecs-app-role 对指定密钥进行加密和解密,但只允许来自内网 10.0.0.0/8 的请求。这就是一个最小权限且附带网络限制的典型策略。

核心原则: Effect + Principal + Action + Resource + Condition 缺一不可,任何一个写错都可能造成权限泄露或业务中断。写完后,用RAM策略模拟器测试一下,我每次都会这么做。