第3章 IAM策略语法:从结构到条件的一站式拆解

说实话,刚接触IAM策略那会儿,我也被那一堆JSON搞得头晕。Version、Statement、Effect……这都什么鬼?后来踩的坑多了,慢慢就摸清了门道。今天咱们就把IAM策略语法从头到尾捋一遍。嗯,我保证,看完这章你也能自己写出一手漂亮策略。

3.1 策略长什么样?

先看一个最简策略,感受一下骨架:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::my-bucket"
    }
  ]
}

你看,核心就两个键:Version 和 Statement。Statement 是一个数组,里面放一条或多条具体的权限规则。每条规则又由 Effect、Action、Resource、Condition 这四个基本元素组成。

说白了,IAM 策略本质上就是一个权限声明清单。你告诉 AWS:“谁,能对什么资源,做什么操作,在什么条件下。”就这么简单。

3.2 Version:版本不是摆设

目前官方推荐的版本号只有一个:"2012-10-17"。为什么非要写这个?因为 AWS 不同时期支持的策略语法有差异。比如早期版本不支持 Condition 里的某些操作符。我个人习惯每次都写最新版,省得以后出兼容性问题。

提示: 千万别写 "2008-10-17" 那个老版本。很多新特性(比如跨账户信任策略)根本不认它。我见过有人从旧项目里复制策略,忘了改版本号,结果折腾了一下午。

3.3 Statement:权限规则的核心集合

每个 Statement 是一条独立的权限声明。你可以写多条,它们之间是“或”的关系——只要有一条匹配,权限就生效。不过要注意,Effect 为 Deny 的优先级永远高于 Allow。这个后面会细说。

Statement 内部的结构是这样的:

字段 是否必填 说明
Effect 是 Allow 或 Deny
Action 是 允许或拒绝的操作列表,支持通配符
Resource 大部分情况是 操作针对的资源 ARN,支持通配符
Condition 否 附加条件,进一步缩小权限范围
Sid 否 可选标识符,方便日志审计时定位

你看,Sid 不是必须的。但我建议你每条 Statement 都加上一个可读的 ID,比如 "AllowS3ReadOnly"。为什么?我曾在排错时看到 CloudTrail 日志里只有策略 ARN 和 Sid,没有 Sid 你根本不知道哪条规则触发的。加一行 Sid,排障效率翻倍。

3.4 Effect:点明是允许还是拒绝

就两个值:Allow 和 Deny。逻辑很简单——但陷阱在默认规则。当一个请求没有匹配到任何显式的 Allow 时,默认就是 Deny(隐式拒绝)。而显式的 Deny 可以覆盖任何 Allow。

举个例子:你给一个用户附加了管理员权限(Allow *),然后又单独加了一条策略 Deny 删除 S3 存储桶。那么那个用户就确实无法删除存储桶。显式 Deny 是铁律,谁都绕不过。

警告: 我曾经见过有人把 Deny 写反成 Allow,结果开放了所有权限。另一种常见错误是:在类似“拒绝非加密访问”的场景里,Deny 的条件写得太宽,把正常请求也拦了。所以写 Deny 时务必仔细检查 Condition。

3.5 Action:定义允许或拒绝的操作

Action 是一个数组,每个元素是一个服务操作标识符。格式是 服务前缀:操作名,比如 s3:GetObject、ec2:DescribeInstances。支持通配符 *,比如 s3:* 表示所有 S3 操作。

写 Action 时我有一条经验:尽量用最小权限原则。不要一上来就写 "*:*"。你想想看,一个只需读取日志的 Lambda 执行角色,给它 logs:GetLogEvents 就够了,没必要给 logs:*。你不知道哪天 AWS 推出个新操作,万一那个操作能写日志呢?

另外要注意,有些 Action 是“资源级”的,有些是“服务级”的。比如 iam:CreateUser 不支持指定资源 ARN(因为它创建的是全局资源),你只能写成 "Resource": "*"。这属于设计上的限制,记住就行。

3.6 Resource:指定操作目标

Resource 的值是 ARN(Amazon Resource Name)。ARN 的格式是:arn:partition:service:region:account-id:resource-type/resource-id。比如一个 S3 存储桶的 ARN 是 arn:aws:s3:::my-bucket。

支持通配符 * 和 ?。比如 arn:aws:s3:::my-bucket/* 表示该桶下的所有对象。但注意,桶本身和桶内对象是两个不同的资源,策略里需要分别授权。

我踩过一个坑:想给用户列出某个桶里所有对象的权限,写了 "arn:aws:s3:::my-bucket/*",但忘了加 "arn:aws:s3:::my-bucket"(不带斜杠的版本)。结果用户能看对象列表,但无法执行 s3:ListBucket 操作——因为 s3:ListBucket 作用于桶本身,不是对象。这个细节很容易被忽略。

3.7 Condition:精细化的条件魔法

Condition 是我认为最强大也最容易翻车的部分。它让你在 Allow 或 Deny 的基础上增加额外限制。比如只允许从特定 IP 访问、只允许使用 HTTPS、只允许在指定时间段操作。

Condition 的写法是:

"Condition": {
  "条件运算符": {
    "条件键": "条件值"
  }
}

举个实际例子:

"Condition": {
  "IpAddress": {
    "aws:SourceIp": "203.0.113.0/24"
  }
}

这条条件表示只有当请求来源 IP 属于 203.0.113.0/24 网段时,规则才生效。

常见的条件运算符有这些:

运算符 作用 典型场景
StringEquals 字符串精确匹配 限制只能使用某个特定标签
StringLike 字符串通配符匹配 限制只能访问以 prod- 开头的资源
IpAddress IP 地址匹配(CIDR) 只允许公司内网访问
Bool 布尔值判断 检查是否使用了 MFA
DateLessThan 日期/时间比较 设置临时访问有效期
ArnEquals ARN 精确匹配 限制只能从特定角色访问

条件键分两类:aws 全局条件键(比如 aws:SourceIp、aws:RequestedRegion)和服务特定条件键(比如 s3:x-amz-server-side-encryption)。全局键对所有服务都有效,服务键只对该服务生效。

核心要点: 写 Condition 时要记住运算符的逻辑。比如 ForAnyValue:StringEquals 和 ForAllValues:StringEquals 的区别很大,用错了会放行非法请求或误拦合法请求。我的习惯是先做正向测试(只允许什么),再做逆向测试(拒绝什么),保证不遗漏。

3.8 一张图看懂策略结构

下面这张 SVG 把策略的层级关系画清楚了,看一眼就能记在脑子里:

IAM策略结构总览 Version Statement [ ] Effect Action Resource Condition 条件运算符 StringEquals IpAddress / Bool … Allow / Deny "s3:GetObject" ARN 或通配符 关键记忆点 Deny 优先于任意 Allow | 隐式 Deny 即未匹配则拒绝 | Condition 写在同一 Statement 内

3.9 综合示例:一条带条件的只读策略

咱们把上面讲的知识点串起来,写一个真实场景下的策略:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowS3ReadOnlyInOffice",
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::company-docs",
        "arn:aws:s3:::company-docs/*"
      ],
      "Condition": {
        "IpAddress": {
          "aws:SourceIp": "10.0.0.0/8"
        },
        "Bool": {
          "aws:SecureTransport": "true"
        }
      }
    },
    {
      "Sid": "DenyDeleteForAll",
      "Effect": "Deny",
      "Action": "s3:DeleteObject",
      "Resource": "arn:aws:s3:::company-docs/*"
    }
  ]
}

这个策略做了两件事:第一,允许用户在办公网段(10.x.x.x)内通过 HTTPS 读取 company-docs 桶里的对象和列表;第二,明确禁止任何人删除该桶里的任何对象。注意 Deny 没有加 Condition,所以全局生效,包括管理员(除非有更高优先级的策略绕过)。

小技巧: 写 Condition 里的 IP 地址时,记得用 aws:SourceIp 而不是 aws:SourceVpc。后者用于 VPC Endpoint 场景,容易搞混。我曾经把这两个键用反了,结果策略死活不生效,查了半天才发现是键名写错了。

好了,以上就是 IAM 策略语法的全貌。Version、Statement、Effect、Action、Resource、Condition——每个都有自己的脾气。但只要你多写几遍,把这些概念像搭积木一样组合起来,就能构建出既安全又灵活的权限体系。嗯,我现在写策略已经很少翻文档了,希望你也早日达到这个手感。