策略变量与条件键:让IAM策略活起来
讲完了角色和密钥的基础绑定,咱们得聊聊怎么让策略变得智能。说白了,就是让策略能根据上下文做决策。你不能给所有人都放行是吧?
我个人习惯把策略变量和条件键叫做IAM策略的“眼睛和大脑”。没有它们,策略就是死的——要么全放行,要么全禁止。那太粗暴了。
先理解策略变量:${}
你在策略里写过 ${aws:username} 吗?这就是个变量,运行时会被替换成真实的用户名。AWS 支持很多全局变量,我挑几个最常用的列出来。
| 变量名 | 含义 | 典型使用场景 |
|---|---|---|
${aws:username} |
当前IAM用户的用户名 | 限制用户只能操作自己名字命名的S3前缀 |
${aws:userid} |
用户的唯一ID | 与临时凭证联合使用 |
${aws:AccountId} |
当前账号ID | 跨账号策略里限制来源账号 |
${aws:SourceIp} |
请求来源IP | 条件键里配合IpAddress使用 |
${aws:CurrentTime} |
当前UTC时间 | 时间窗口控制 |
有个坑:${aws:username} 只对IAM用户有效。角色或联合身份会返回空。我在项目里一开始没注意,导致角色下的策略全废了——嗯,查了半天才发现。
条件键是决策开关
条件键写在 Condition 块里。它负责判断“请求环境是否满足某个条件”。条件满足,策略生效;不满足,Deny或跳过。
最常见的两个条件键:IpAddress 和 StringEquals。来看个例子。
{
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-bucket/*",
"Condition": {
"IpAddress": {
"aws:SourceIp": ["10.0.0.0/8", "192.168.1.0/24"]
},
"StringEquals": {
"s3:x-amz-server-side-encryption": "AES256"
}
}
}
这个策略只允许来自内网IP段且强制使用AES256加密的请求。一次匹配两个条件——说白了就是“AND”逻辑。
条件键类型对比
AWS 有几十种条件键。我帮你归个类,记这个表就够了。
| 类型 | 条件键举例 | 比较方式 | 常用运算符 |
|---|---|---|---|
| 字符串 | aws:username, s3:x-amz-acl |
精确匹配 / 通配符 | StringEquals, StringLike |
| IP地址 | aws:SourceIp |
CIDR匹配 | IpAddress, NotIpAddress |
| 数字 | s3:max-keys |
范围比较 | NumericEquals, NumericLessThanEquals |
| 日期/时间 | aws:CurrentTime |
时间范围 | DateGreaterThan, DateLessThan |
| 布尔 | aws:SecureTransport |
true/false | Bool |
| 二进制 | aws:SourceIdentity |
精确匹配 | BinaryEquals |
核心逻辑:Condition 块里的所有条件默认是 AND 关系——必须全部满足。如果你想用 OR,得把多个条件分到不同的 Condition 块里,或者用 ...IfExists 变体。
实战:用变量和条件键做精细权限隔离
我之前帮一个团队重构过多云存储权限。他们的需求很简单:每个开发人员只能操作自己的目录。你想想看,如果用静态策略,得为每个人单独写一条,那太蠢了。
用变量一行搞定:
{
"Effect": "Allow",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::dev-bucket/home/${aws:username}/*"
}
但是有个隐患——万一别人伪造 aws:username 呢?嗯,aws:username 来自IAM身份验证,无法伪造。不过要注意:如果用户通过角色代入,这个变量是空的。所以我通常会在条件里加一层校验:
{
"Effect": "Allow",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::dev-bucket/home/${aws:username}/*",
"Condition": {
"StringLikeIfExists": {
"aws:username": "*"
}
}
}
StringLikeIfExists 表示:如果 aws:username 存在,就检查是否匹配任意字符;如果不存在(比如角色),则跳过该条件。这样角色就不受影响。
我曾经遇到过一个线上事故:一位运维用 ${aws:username} 做资源前缀隔离,但没加 IfExists,导致所有通过角色拉取数据的服务全部报403。所以记住——变量可能为空,策略要兜底。
SVG:策略变量与条件键决策流程
下面这张图展示了策略引擎是如何利用变量和条件键做决策的。你可以看到变量替换、条件匹配、最终放行或拒绝的完整路径。
避坑指南:条件键的常见误区
- 大小写敏感:
IpAddress和ipaddress不是同一个东西。我见过有人手写小写,策略直接无效。 - 条件键作用域:有些条件键只对特定服务有效。比如
s3:x-amz-server-side-encryption只能用在S3上,别想着在EC2的Action里用。 - NotIpAddress 的坑:配合
Deny使用时要小心。我曾经用Deny + NotIpAddress限制只允许内网,结果把AWS服务自己(比如CloudFront回源)也禁了——后来加了个aws:SourceOwner条件才解决。 - 字符串通配符:
StringLike支持*和?,但StringEquals不支持。别搞混。
调试小技巧:用IAM策略模拟器(Policy Simulator)可以查看条件键的实际匹配结果。我把这个工具放在浏览器书签里,每次写复杂策略都先跑一遍。
总结一下关键点
变量和条件键让IAM策略从静态变成了动态。你可以用 ${aws:username} 做用户级隔离,用 IpAddress 做网络级控制,用 StringEquals 做属性级校验。三者组合起来,能实现非常精细的权限边界。
记住:条件键的匹配逻辑是AND,变量可能为空。写策略时多问一句“如果变量不存在怎么办?”——养成这个习惯,能少踩一半的坑。