策略结构:读透 IAM 策略的六要素

策略这东西,说白了就是一组权限规则的集合。

我刚接触云安全那会儿,总觉得策略很玄乎。后来发现,只要把它的六个组成部分拆开看,其实非常好理解。就像写代码有语法规范一样,IAM 策略也有固定的结构体。

来看一个最基础的策略骨架:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "ec2:DescribeInstances",
            "Resource": "*",
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": "10.0.0.0/8"
                }
            }
        }
    ]
}

嗯,这里要注意。策略本质上是一个 JSON 文档。我见过不少人在 JSON 末尾多写个逗号导致策略不生效,白白排查半天。

Version:策略语言的版本标尺

Version 就一个作用:告诉 IAM 服务用哪个版本的语法来解析你的策略。目前的版本号只有两个选择:

版本号 说明
2012-10-17 当前稳定版本,强烈建议使用
2008-10-17 早期版本,不支持 Condition 等高级功能

避坑指南:我记得早期有一批策略直接用空字符串 "" 作为 Version。这在旧版本中可能通过,但现在会导致策略完全失效。我曾经排查过一个诡异的权限问题,最后发现就是 Version 没写对。

Statement:策略的核心容器

Statement 是一个数组,里面每个元素是一条独立的权限规则。说实话,很多初学者把多条规则揉成一个 Statement,结果权限逻辑混乱得不行。我的建议是:每个 Statement 只做一件事。

// 推荐写法:权限拆开
"Statement": [
    {
        "Effect": "Allow",
        "Action": "s3:GetObject",
        "Resource": "arn:aws:s3:::my-bucket/*"
    },
    {
        "Effect": "Deny",
        "Action": "s3:DeleteObject",
        "Resource": "arn:aws:s3:::my-bucket/*"
    }
]

你想想看,把 Allow 和 Deny 拆到不同的 Statement 里,后续维护时一眼就能看明白。

Effect:允许还是拒绝

Effect 就两个值:Allow 和 Deny。但它的判定逻辑有个坑:Deny 的优先级永远高于 Allow。

理解这个规则:如果同一个 Action 同时出现在了 Allow 和 Deny 里,最终结果一定是 Deny。我在项目中遇到过不止一次,有人因为父级资源上有 Deny,子级怎么 Allow 都没用。

为什么这样设计?说白了,Deny 是一种安全锁。哪怕管理员不小心给用户加了 FullAccess,只要 Deny 策略在,也能守住底线。

Action:授什么权

Action 就是你要授权的具体操作。格式是:服务前缀:操作名。

服务 Action 示例
EC2 ec2:StartInstances, ec2:StopInstances
S3 s3:GetObject, s3:ListBucket
IAM iam:CreateUser, iam:PassRole

一个小习惯:我习惯把 Action 按服务分组写,中间加注释。比如 EC2 的写在前面,S3 的写在后面。这样可读性会好很多。

Resource:作用在谁身上

Resource 指定策略作用的对象。它是一个 ARN(Amazon Resource Name),格式类似这样:arn:partition:service:region:account-id:resource-type/resource-id。

这里有个容易出错的地方:Resource 支持通配符,但通配符只能用一次。我曾经看到有人写 arn:aws:ec2:*:*:instance/*,结果发现某些 API 根本不支持这种写法。

具体来说,不同服务的 ARN 格式不一样:

  • S3 的对象:arn:aws:s3:::bucket-name/*
  • EC2 的实例:arn:aws:ec2:us-east-1:123456789012:instance/i-abcdef
  • IAM 的用户:arn:aws:iam::123456789012:user/devops

说白了,Resource 就是你权限的边界。不写清楚 Resource,用户可能操作到不该碰的资源。

Condition:精细化控制的杀手锏

Condition 是策略中最灵活的部分。它允许你在授权时附加更多的判断条件。格式是:"条件运算符": { "条件键": "条件值" }。

// 只有来自公司内网的请求才允许
"Condition": {
    "IpAddress": {
        "aws:SourceIp": "203.0.113.0/24"
    }
}

// 必须启用 MFA 才能删除资源
"Condition": {
    "Bool": {
        "aws:MultiFactorAuthPresent": "true"
    }
}

我曾经踩过一个坑:在 Condition 里写 aws:SourceIp 时,如果你用的是某个服务的内部调用,比如 SNS 触发 Lambda,这时候 SourceIp 其实是服务的 IP,不是你预想的客户端 IP。结果调试了半天才发现是条件写错了。

常见的条件运算符有:

  • StringEquals:字符串完全匹配
  • StringLike:字符串模糊匹配
  • IpAddress:IP 地址范围匹配
  • Bool:布尔值匹配
  • DateLessThan:时间比较
  • ArnEquals:ARN 精确匹配

每个条件键都对应一个特定的场景。你想想看,有了 Condition,你可以做到:只允许某个时间段操作、只允许从某个 IP 访问、只允许通过某个服务调用……这些精细化控制在生产环境中非常实用。

整体结构回顾

最后把六个要素串起来看看:

IAM 策略六要素结构图 策略 (Policy) Version Statement Resource Condition Effect Action 六要素组合后:Version 定义语法 → Statement 承载规则 → Effect 定允许/拒绝 → Action 定操作 → Resource 定范围 → Condition 定条件

我个人习惯在写策略时,先从 Statement 开始。确定好 Effect 后,再填 Action,接着写 Resource,最后用 Condition 做精细化控制。Version 放在最前面,但它基本不用改。

总结一下:策略结构不复杂,复杂的是理解每个元素之间的交互逻辑。当你遇到权限问题时,别急着翻文档,先把策略的六个要素拆开看,问题往往出在其中某个点上。