IAM 策略深入
全面理解 IAM 策略的类型、语法、评估逻辑和高级用法。
策略类型
基于身份的策略(Identity-based Policies)
托管策略(Managed Policies):
AWS 托管策略:
特性:
├─ AWS 预先创建和维护
├─ 涵盖常见使用场景
├─ 自动更新(添加新服务/功能)
├─ 无法修改内容
├─ 橙色图标标识
└─ 可附加到多个实体
常用 AWS 托管策略:
├─ AdministratorAccess
│ └─ 完全访问权限(*:*)
├─ PowerUserAccess
│ └─ 完全访问(除 IAM 和 Organizations)
├─ ReadOnlyAccess
│ └─ 所有服务的只读权限
├─ SecurityAudit
│ └─ 安全审计所需权限
├─ Billing
│ └─ 查看账单信息
└─ 服务特定策略
├─ AmazonS3FullAccess
├─ AmazonEC2ReadOnlyAccess
└─ AWSLambdaExecute
命名规范:
├─ [Service]FullAccess - 完全访问
├─ [Service]ReadOnlyAccess - 只读访问
├─ [Service]PowerUser - 高级用户
└─ AWS[Service]Role - 服务角色策略
客户托管策略:
特性:
├─ 你创建和管理
├─ 可自定义内容
├─ 支持版本控制(最多 5 个版本)
├─ 可复用到多个实体
├─ 蓝色图标标识
└─ 推荐用于组织标准策略
使用场景:
├─ AWS 托管策略过于宽泛
├─ 需要精确的权限控制
├─ 组织特定的权限模板
├─ 合规要求的自定义策略
└─ 跨多个用户/角色共享
版本管理:
├─ 每次修改创建新版本
├─ 可以回滚到旧版本
├─ 设置默认版本
├─ 最多保留 5 个版本
└─ 删除旧版本释放空间
内联策略(Inline Policies):
特性:
├─ 直接嵌入到实体(用户/组/角色)
├─ 一对一严格关系
├─ 删除实体时自动删除
├─ 无法复用
└─ 无版本控制
使用场景:
├─ 严格的一对一策略关系
├─ 确保策略与实体同生死
├─ 临时、特殊的权限
├─ 避免策略被误附加到其他实体
└─ 例外情况的权限授予
何时使用:
✅ 用于特定实体的独特权限
✅ 测试和开发环境的临时权限
❌ 不要用于标准、可复用的权限
❌ 不要用于需要审计的生产权限
基于资源的策略(Resource-based Policies)
支持资源策略的服务:
存储:
├─ S3 Bucket(Bucket Policy)
├─ S3 Glacier Vault(Vault Policy)
├─ EFS File System
└─ AWS Backup Vault
计算:
├─ Lambda Function(Resource Policy)
└─ ECR Repository(Repository Policy)
消息和队列:
├─ SQS Queue(Queue Policy)
├─ SNS Topic(Topic Policy)
└─ EventBridge Event Bus
密钥和密码:
├─ KMS Key(Key Policy)
├─ Secrets Manager Secret
└─ ACM Private CA
API 和网络:
├─ API Gateway REST API
├─ VPC Endpoint
└─ CloudWatch Logs
其他:
├─ AWS Organizations SCP
├─ CodeArtifact Repository
└─ IoT Policy
资源策略 vs 身份策略对比:
| 维度 | 身份策略 | 资源策略 |
|---|---|---|
| 附加位置 | IAM 实体 | 资源本身 |
| Principal | 不需要 | 必须指定 |
| 跨账户 | 需要两步(角色信任) | 直接支持 |
| 可见性 | IAM 控制台 | 资源服务控制台 |
| 使用场景 | 定义"谁能做什么" | 定义"谁能访问我" |
| 评估 | 在主体侧 | 在资源侧 |
资源策略示例:
S3 Bucket 策略(跨账户访问):
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowCrossAccountRead",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111111111111:root"
},
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::my-bucket",
"arn:aws:s3:::my-bucket/*"
]
}
]
}
Lambda 资源策略(API Gateway 调用):
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowAPIGatewayInvoke",
"Effect": "Allow",
"Principal": {
"Service": "apigateway.amazonaws.com"
},
"Action": "lambda:InvokeFunction",
"Resource": "arn:aws:lambda:us-east-1:123456789012:function:my-function",
"Condition": {
"ArnLike": {
"AWS:SourceArn": "arn:aws:execute-api:us-east-1:123456789012:api-id/*"
}
}
}
]
}
策略语法详解
JSON 结构
完整策略结构:
{
"Version": "2012-10-17",
"Id": "PolicyID",
"Statement": [
{
"Sid": "StatementID",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:user/alice"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-bucket/*",
"Condition": {
"StringEquals": {
"s3:x-amz-server-side-encryption": "AES256"
}
}
}
]
}
元素说明:
Version(必需):
作用:策略语言版本
值:"2012-10-17"(当前版本,也是唯一推荐值)
历史:"2008-10-17"(旧版本,功能受限)
注意:
├─ 必须是策略的第一个元素
├─ 使用新版本获得完整功能
└─ 不是策略的修订版本
Statement(必需):
作用:策略的主体,包含一个或多个语句
类型:对象数组
每个语句是一个完整的权限声明
多语句示例:
{
"Statement": [
{
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::bucket1/*"
},
{
"Effect": "Allow",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::bucket2/*"
}
]
}
Sid(可选):
作用:Statement ID,语句的唯一标识符
类型:字符串
用途:
├─ 便于识别和引用
├─ 日志和错误消息中显示
├─ 策略编辑器中的标签
└─ 文档说明
命名建议:
├─ 使用描述性名称
├─ CamelCase 或 kebab-case
├─ 说明语句的目的
└─ 例如:"AllowS3ReadAccess"
Effect(必需):
作用:允许或拒绝访问
值:
├─ "Allow" - 允许
└─ "Deny" - 明确拒绝
优先级:
Deny > Allow > 默认 Deny
注意:
├─ 显式 Deny 始终优先
├─ 默认情况下所有请求都被拒绝
└─ 至少需要一个 Allow 才能访问
Principal(条件必需):
作用:指定被允许或拒绝的主体
使用场景:
├─ 资源策略(必需)
├─ 信任策略(必需)
└─ 身份策略(不使用)
Principal 类型:
├─ AWS 账户和 IAM 实体
├─ AWS 服务
├─ 联合用户
├─ Canonical User ID(S3)
└─ 通配符(*)表示所有人
Principal 示例:
{
"Principal": {
"AWS": [
"arn:aws:iam::123456789012:user/alice",
"arn:aws:iam::123456789012:role/MyRole",
"123456789012"
],
"Service": [
"ec2.amazonaws.com",
"lambda.amazonaws.com"
],
"Federated": "arn:aws:iam::123456789012:saml-provider/MyProvider",
"CanonicalUser": "79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be"
}
}
// 所有人(公开访问)
{
"Principal": "*"
}
// 所有 AWS 账户
{
"Principal": {
"AWS": "*"
}
}
Action(必需):
作用:指定允许或拒绝的操作
格式:service:action
单个操作:
"Action": "s3:GetObject"
多个操作:
"Action": [
"s3:GetObject",
"s3:PutObject"
]
通配符:
"Action": "s3:*" # S3 所有操作
"Action": "s3:Get*" # 所有 Get 操作
"Action": "*" # 所有服务的所有操作
NotAction(排除):
"NotAction": "s3:DeleteBucket" # 除了删除 bucket 的所有操作
Resource(必需):
作用:指定策略应用的资源
格式:ARN(Amazon Resource Name)
ARN 格式:
arn:partition:service:region:account-id:resource-type/resource-id
示例:
"Resource": "arn:aws:s3:::my-bucket/*"
"Resource": "arn:aws:ec2:us-east-1:123456789012:instance/*"
"Resource": "*" # 所有资源
多个资源:
"Resource": [
"arn:aws:s3:::bucket1/*",
"arn:aws:s3:::bucket2/*"
]
通配符:
"Resource": "arn:aws:s3:::my-bucket/home/${aws:username}/*"
"Resource": "arn:aws:ec2:*:*:instance/*"
NotResource(排除):
"NotResource": "arn:aws:s3:::sensitive-bucket/*"
策略条件
条件运算符完整列表
字符串条件:
StringEquals - 精确匹配(区分大小写)
StringNotEquals - 不等于
StringLike - 模式匹配(支持 * 和 ?)
StringNotLike - 不匹配模式
StringEqualsIgnoreCase - 忽略大小写匹配
StringNotEqualsIgnoreCase - 忽略大小写不等于
示例:
"StringEquals": {
"aws:username": "alice"
}
"StringLike": {
"s3:prefix": "home/*"
}
数值条件:
NumericEquals - 等于
NumericNotEquals - 不等于
NumericLessThan - 小于
NumericLessThanEquals - 小于等于
NumericGreaterThan - 大于
NumericGreaterThanEquals - 大于等于
示例:
"NumericLessThan": {
"s3:max-keys": "10"
}
日期条件:
DateEquals - 日期等于
DateNotEquals - 日期不等于
DateLessThan - 日期早于
DateLessThanEquals - 日期早于等于
DateGreaterThan - 日期晚于
DateGreaterThanEquals - 日期晚于等于
格式:ISO 8601
示例:
"DateGreaterThan": {
"aws:CurrentTime": "2024-01-01T00:00:00Z"
}
布尔条件:
Bool - 布尔值比较
示例:
"Bool": {
"aws:SecureTransport": "true",
"aws:MultiFactorAuthPresent": "true"
}
IP 地址条件:
IpAddress - IP 地址匹配(CIDR)
NotIpAddress - IP 地址不匹配
示例:
"IpAddress": {
"aws:SourceIp": [
"203.0.113.0/24",
"2001:DB8:1234:5678::/64"
]
}
ARN 条件:
ArnEquals - ARN 精确匹配
ArnLike - ARN 模式匹配
ArnNotEquals - ARN 不等于
ArnNotLike - ARN 不匹配模式
示例:
"ArnLike": {
"AWS:SourceArn": "arn:aws:s3:::my-bucket/*"
}
Null 条件:
Null - 检查键是否存在
示例:
"Null": {
"aws:TokenIssueTime": "false" # 键必须存在
}
条件键(Condition Keys)
全局条件键:
aws:CurrentTime - 当前时间
aws:EpochTime - Unix 时间戳
aws:SecureTransport - 是否使用 HTTPS
aws:SourceIp - 请求源 IP
aws:SourceVpc - 源 VPC ID
aws:SourceVpce - 源 VPC 端点 ID
aws:UserAgent - HTTP User-Agent
aws:username - IAM 用户名
aws:userid - 唯一用户 ID
aws:PrincipalType - 主体类型
aws:PrincipalArn - 主体 ARN
aws:PrincipalAccount - 主体账户 ID
aws:PrincipalOrgID - 组织 ID
aws:PrincipalTag/<key> - 主体标签
aws:RequestedRegion - 请求的区域
aws:MultiFactorAuthPresent - 是否使用 MFA
aws:MultiFactorAuthAge - MFA 认证时长
aws:TokenIssueTime - 临时令牌签发时间
服务特定条件键:
S3 条件键:
s3:prefix - 对象键前缀
s3:max-keys - 列出的最大对象数
s3:x-amz-acl - ACL 设置
s3:x-amz-server-side-encryption - 加密类型
s3:x-amz-server-side-encryption-aws-kms-key-id - KMS 密钥
s3:ExistingObjectTag/<key> - 对象标签
s3:VersionId - 对象版本 ID
示例:
"StringEquals": {
"s3:x-amz-server-side-encryption": "AES256"
}
EC2 条件键:
ec2:InstanceType - 实例类型
ec2:Region - 区域
ec2:ResourceTag/<key> - 资源标签
ec2:Vpc - VPC ID
ec2:AvailabilityZone - 可用区
ec2:EbsOptimized - 是否 EBS 优化
ec2:Tenancy - 租户类型
示例:
"StringEquals": {
"ec2:InstanceType": "t3.micro"
}
策略变量
变量语法:
${variable-name}
使用位置:
├─ Resource 元素
├─ Condition 元素
└─ 不能在 Principal 元素中使用
常用变量:
${aws:username} - IAM 用户名
${aws:userid} - 用户 ID
${aws:PrincipalTag/TagName} - 主体标签值
${aws:SourceIp} - 源 IP 地址
${aws:CurrentTime} - 当前时间
${aws:RequestedRegion} - 请求的区域
示例:用户专属 S3 文件夹
"Resource": "arn:aws:s3:::my-bucket/home/${aws:username}/*"
标签变量(ABAC):
主体标签:
${aws:PrincipalTag/Department}
${aws:PrincipalTag/Project}
资源标签:
${ec2:ResourceTag/Owner}
${s3:ExistingObjectTag/Classification}
请求标签:
${aws:RequestTag/Environment}
示例:
"Condition": {
"StringEquals": {
"ec2:ResourceTag/Owner": "${aws:username}"
}
}
策略评估逻辑
评估流程
完整决策流程:
1. 默认拒绝(Implicit Deny)
↓
2. 收集所有适用的策略
├─ 身份策略(用户、组、角色)
├─ 资源策略
├─ 权限边界
├─ SCP(组织策略)
└─ 会话策略
↓
3. 评估 SCP(如果有)
├─ SCP 拒绝 → 最终拒绝
└─ SCP 允许 → 继续
↓
4. 评估权限边界(如果有)
├─ 超出边界 → 最终拒绝
└─ 在边界内 → 继续
↓
5. 评估身份和资源策略
├─ 有显式 Deny? → 最终拒绝
├─ 有显式 Allow? → 继续
└─ 无 Allow? → 最终拒绝
↓
6. 评估会话策略(如果有)
├─ 超出会话限制 → 最终拒绝
└─ 在限制内 → 最终允许
结果:允许或拒绝
同账户 vs 跨账户评估
同账户访问:
评估逻辑:身份策略 OR 资源策略
场景 1:仅身份策略
IAM 用户 → S3 Bucket(无 Bucket 策略)
评估:用户的 IAM 策略
场景 2:仅资源策略
Lambda 函数 → S3 Bucket(有 Bucket 策略允许 Lambda)
评估:Bucket 策略
场景 3:同时存在
IAM 用户 → S3 Bucket(都有策略)
评估:任一策略允许即可(OR 关系)
跨账户访问:
评估逻辑:身份策略 AND 资源策略
必须同时满足:
├─ 源账户的身份策略允许
└─ 目标账户的资源策略允许
示例:
账户 A 用户访问账户 B 的 S3:
✓ 账户 A:用户策略允许 s3:GetObject
✓ 账户 B:Bucket 策略允许账户 A 访问
→ 最终:允许
如果任一策略拒绝:
✗ 账户 A:允许
✗ 账户 B:未明确允许
→ 最终:拒绝
策略冲突处理
Deny 优先原则:
情况 1:显式 Deny
策略 A:Allow s3:*
策略 B:Deny s3:DeleteBucket
结果:允许所有 S3 操作,除了 DeleteBucket
情况 2:多个 Allow
策略 A:Allow s3:GetObject on bucket1
策略 B:Allow s3:GetObject on bucket2
结果:可以访问两个 bucket(权限合并)
情况 3:无 Allow
没有任何策略授予权限
结果:默认拒绝(Implicit Deny)
情况 4:Deny 覆盖 Allow
策略 A:Allow *:*(管理员权限)
SCP:Deny cloudtrail:StopLogging
结果:允许一切,除了停止 CloudTrail
理解策略的类型、语法和评估逻辑是精确控制 AWS 权限的关键!