策略结构:读透 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 访问、只允许通过某个服务调用……这些精细化控制在生产环境中非常实用。
整体结构回顾
最后把六个要素串起来看看:
我个人习惯在写策略时,先从 Statement 开始。确定好 Effect 后,再填 Action,接着写 Resource,最后用 Condition 做精细化控制。Version 放在最前面,但它基本不用改。
总结一下:策略结构不复杂,复杂的是理解每个元素之间的交互逻辑。当你遇到权限问题时,别急着翻文档,先把策略的六个要素拆开看,问题往往出在其中某个点上。