IAM 故障排查与调试
掌握 IAM 权限问题的诊断技巧和调试工具。
常见错误类型
Access Denied 错误
错误表现:
CLI 输出:
An error occurred (AccessDenied) when calling the <Operation>:
User: arn:aws:iam::123456789012:user/alice is not authorized
to perform: <service>:<action> on resource: <resource-arn>
控制台显示:
You are not authorized to perform this operation
可能原因分析:
1. 缺少显式 Allow:
问题:没有任何策略授予所需权限
排查:
├─ 检查用户直接附加的策略
├─ 检查用户所属组的策略
├─ 检查资源策略(如 S3 Bucket Policy)
└─ 验证策略中的 Action 和 Resource
解决:
└─ 添加包含所需操作的策略
2. 存在显式 Deny:
问题:某个策略显式拒绝了操作
排查优先级:
1. SCP(组织策略)- 最高优先级
2. 权限边界(Permissions Boundary)
3. 会话策略(Session Policy)
4. 身份策略或资源策略中的 Deny
解决:
├─ 找到 Deny 语句的来源
├─ 评估是否可以修改或删除
└─ 如果是 SCP,需要组织管理员协助
3. 权限边界限制:
问题:实际权限超出了权限边界
原理:
实际权限 = 身份策略 ∩ 权限边界
排查:
aws iam get-user --user-name alice
# 查看 PermissionsBoundary 字段
aws iam get-policy --policy-arn <boundary-arn>
aws iam get-policy-version \
--policy-arn <boundary-arn> \
--version-id <version>
解决:
├─ 扩大权限边界(如果合理)
└─ 或调整身份策略以符合边界
4. SCP 限制:
问题:组织策略拒绝操作
排查:
aws organizations list-policies-for-target \
--target-id <account-id> \
--filter SERVICE_CONTROL_POLICY
aws organizations describe-policy \
--policy-id <policy-id>
解决:
├─ 联系组织管理员
├─ 评估 SCP 的合理性
└─ 可能需要修改 SCP 或更换账户
5. 资源策略冲突:
问题:资源策略拒绝访问
典型场景:
├─ S3 Bucket Policy 拒绝
├─ KMS Key Policy 未授权
├─ Lambda 资源策略限制
└─ SQS Queue Policy 限制
排查:
# S3 示例
aws s3api get-bucket-policy --bucket my-bucket
# KMS 示例
aws kms get-key-policy \
--key-id <key-id> \
--policy-name default
解决:
└─ 修改资源策略以允许访问
策略语法错误
JSON 格式错误:
// ❌ 错误:缺少逗号
{
"Version": "2012-10-17"
"Statement": [...]
}
// ✅ 正确
{
"Version": "2012-10-17",
"Statement": [...]
}
// ❌ 错误:多余的逗号
{
"Version": "2012-10-17",
"Statement": [
{...},
]
}
// ✅ 正确
{
"Version": "2012-10-17",
"Statement": [
{...}
]
}
Action 名称错误:
// ❌ 错误:拼写错误
{
"Action": "s3:GetObjet"
}
// ❌ 错误:大小写错误
{
"Action": "s3:getobject"
}
// ✅ 正确
{
"Action": "s3:GetObject"
}
// ❌ 错误:通配符位置错误
{
"Action": "s3*:GetObject"
}
// ✅ 正确
{
"Action": "s3:Get*"
}
Resource ARN 错误:
// ❌ 错误:缺少分区
{
"Resource": "arn:s3:::my-bucket/*"
}
// ✅ 正确
{
"Resource": "arn:aws:s3:::my-bucket/*"
}
// ❌ 错误:S3 ARN 包含区域和账户ID(不需要)
{
"Resource": "arn:aws:s3:us-east-1:123456789012:my-bucket/*"
}
// ✅ 正确
{
"Resource": "arn:aws:s3:::my-bucket/*"
}
// ❌ 错误:缺少资源类型
{
"Resource": "arn:aws:ec2:us-east-1:123456789012:i-1234567890abcdef0"
}
// ✅ 正确
{
"Resource": "arn:aws:ec2:us-east-1:123456789012:instance/i-1234567890abcdef0"
}
Condition 错误:
// ❌ 错误:条件运算符拼写错误
{
"Condition": {
"StringEqual": {
"aws:username": "alice"
}
}
}
// ✅ 正确
{
"Condition": {
"StringEquals": {
"aws:username": "alice"
}
}
}
// ❌ 错误:条件键不存在
{
"Condition": {
"StringEquals": {
"aws:UserName": "alice"
}
}
}
// ✅ 正确
{
"Condition": {
"StringEquals": {
"aws:username": "alice"
}
}
}
角色承担失败
错误类型:
1. 信任策略配置错误:
错误信息:
User: arn:aws:iam::123456789012:user/alice is not authorized
to perform: sts:AssumeRole on resource: arn:aws:iam::123456789012:role/MyRole
原因:
└─ 角色的信任策略未授权该用户
检查信任策略:
aws iam get-role --role-name MyRole \
--query 'Role.AssumeRolePolicyDocument'
正确的信任策略:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:user/alice"
},
"Action": "sts:AssumeRole"
}
]
}
2. External ID 不匹配:
错误信息:
An error occurred (AccessDenied) when calling the AssumeRole operation:
External ID does not match
原因:
└─ 提供的 External ID 与信任策略要求的不一致
解决:
# 使用正确的 External ID
aws sts assume-role \
--role-arn arn:aws:iam::123456789012:role/MyRole \
--role-session-name my-session \
--external-id "correct-external-id-12345"
3. MFA 要求未满足:
错误信息:
MultiFactorAuthentication failed
原因:
└─ 信任策略要求 MFA,但未提供
信任策略:
{
"Condition": {
"Bool": {
"aws:MultiFactorAuthPresent": "true"
}
}
}
解决:
# 先获取 MFA 会话令牌
aws sts get-session-token \
--serial-number arn:aws:iam::123456789012:mfa/alice \
--token-code 123456
# 使用临时凭证 assume role
4. 会话持续时间超限:
错误信息:
DurationSeconds exceeds the MaxSessionDuration set for this role
原因:
└─ 请求的会话时长超过角色配置的最大值
检查角色配置:
aws iam get-role --role-name MyRole \
--query 'Role.MaxSessionDuration'
解决:
# 使用较短的持续时间
aws sts assume-role \
--role-arn arn:aws:iam::123456789012:role/MyRole \
--role-session-name my-session \
--duration-seconds 3600 # 1小时
调试工具
IAM Policy Simulator
功能:
模拟器能做什么:
├─ 测试用户/角色的权限
├─ 模拟 API 调用
├─ 查看策略评估结果
├─ 识别拒绝原因
└─ 无需实际执行操作(安全)
使用方法:
Web 控制台:
1. 访问:https://policysim.aws.amazon.com/
2. 选择用户或角色
3. 选择服务和操作
4. 指定资源(可选)
5. 添加条件(可选)
6. 点击"Run Simulation"
7. 查看结果:
├─ Allowed(允许)
├─ Denied(拒绝)
└─ Implicitly Denied(隐式拒绝)
CLI 方式:
# 模拟单个操作
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::123456789012:user/alice \
--action-names s3:GetObject \
--resource-arns arn:aws:s3:::my-bucket/file.txt
# 输出示例
{
"EvaluationResults": [
{
"EvalActionName": "s3:GetObject",
"EvalResourceName": "arn:aws:s3:::my-bucket/file.txt",
"EvalDecision": "allowed",
"MatchedStatements": [
{
"SourcePolicyId": "UserPolicy1",
"StartPosition": {...},
"EndPosition": {...}
}
]
}
]
}
# 模拟多个操作
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::123456789012:user/alice \
--action-names s3:GetObject s3:PutObject s3:DeleteObject \
--resource-arns arn:aws:s3:::my-bucket/*
# 添加条件上下文
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::123456789012:user/alice \
--action-names ec2:RunInstances \
--resource-arns "*" \
--context-entries \
"ContextKeyName=aws:RequestedRegion,ContextKeyValues=us-east-1,ContextKeyType=string" \
"ContextKeyName=aws:CurrentTime,ContextKeyValues=2024-01-11T10:00:00Z,ContextKeyType=date"
解读结果:
EvalDecision 值:
├─ allowed
│ └─ 有明确的 Allow,无 Deny
├─ explicitDeny
│ └─ 存在明确的 Deny 语句
└─ implicitDeny
└─ 没有 Allow(默认拒绝)
MissingContextValues:
└─ 表示需要额外的上下文信息才能完整评估
IAM Access Analyzer
功能:
持续分析和发现:
├─ 对外共享的资源
├─ 公开访问的资源
├─ 跨账户访问
├─ 未使用的访问权限
├─ 过度授权
└─ 策略验证
启用 Access Analyzer:
# 创建 Analyzer
aws accessanalyzer create-analyzer \
--analyzer-name my-analyzer \
--type ACCOUNT
# 或组织级别
aws accessanalyzer create-analyzer \
--analyzer-name org-analyzer \
--type ORGANIZATION
查看发现:
# 列出所有发现
aws accessanalyzer list-findings \
--analyzer-arn arn:aws:access-analyzer:us-east-1:123456789012:analyzer/my-analyzer
# 过滤特定类型
aws accessanalyzer list-findings \
--analyzer-arn <analyzer-arn> \
--filter 'resourceType={eq:["AWS::S3::Bucket"]}'
# 过滤活跃的发现
aws accessanalyzer list-findings \
--analyzer-arn <analyzer-arn> \
--filter 'status={eq:["ACTIVE"]}'
# 查看详细信息
aws accessanalyzer get-finding \
--analyzer-arn <analyzer-arn> \
--id <finding-id>
发现类型示例:
类型 1:Public Access(公开访问)
资源:S3 Bucket
问题:Bucket 策略允许所有人访问
建议:移除 Principal: "*" 或添加条件限制
类型 2:Cross-Account Access(跨账户访问)
资源:IAM Role
问题:信任策略允许外部账户承担角色
建议:验证是否合理,添加 External ID
类型 3:Unused Access(未使用的访问)
资源:IAM User
问题:Access Key 180 天未使用
建议:删除或停用未使用的凭证
类型 4:Overly Permissive(过度授权)
资源:IAM Policy
问题:策略使用 Action: "*", Resource: "*"
建议:应用最小权限原则
策略验证:
# 验证策略语法和安全性
aws accessanalyzer validate-policy \
--policy-document file://policy.json \
--policy-type IDENTITY_POLICY
# 输出包含:
# - ERRORS: 语法错误
# - SECURITY_WARNINGS: 安全问题
# - SUGGESTIONS: 改进建议
# - WARNINGS: 潜在问题
# 示例输出
{
"findings": [
{
"findingType": "SECURITY_WARNING",
"issueCode": "PASS_ROLE_WITH_STAR_IN_RESOURCE",
"learnMoreLink": "https://...",
"locations": [...],
"findingDetails": "Using wildcards (*) in the resource can be overly permissive."
}
]
}
CloudTrail 日志分析
查找权限拒绝事件:
# CLI 方式
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=EventName,AttributeValue=AccessDenied \
--max-results 50
# 查看特定用户的拒绝事件
aws cloudtrail lookup-events \
--lookup-attributes \
AttributeKey=Username,AttributeValue=alice \
--start-time 2024-01-10T00:00:00Z \
--end-time 2024-01-11T00:00:00Z \
| jq '.Events[] | select(.CloudTrailEvent | fromjson | .errorCode == "AccessDenied")'
CloudTrail Insights 查询(Athena):
-- 创建表(首次)
CREATE EXTERNAL TABLE cloudtrail_logs (
eventversion STRING,
useridentity STRUCT<
type:STRING,
principalid:STRING,
arn:STRING,
accountid:STRING,
invokedby:STRING,
accesskeyid:STRING,
userName:STRING,
sessioncontext:STRUCT<
attributes:STRUCT<
mfaauthenticated:STRING,
creationdate:STRING>,
sessionissuer:STRUCT<
type:STRING,
principalid:STRING,
arn:STRING,
accountid:STRING,
username:STRING>>>,
eventtime STRING,
eventsource STRING,
eventname STRING,
awsregion STRING,
sourceipaddress STRING,
useragent STRING,
errorcode STRING,
errormessage STRING,
requestparameters STRING,
responseelements STRING,
requestid STRING,
eventid STRING,
resources ARRAY<STRUCT<
ARN:STRING,
accountId:STRING,
type:STRING>>,
eventtype STRING,
apiversion STRING,
readonly BOOLEAN,
recipientaccountid STRING
)
PARTITIONED BY (region string, year string, month string, day string)
ROW FORMAT SERDE 'com.amazon.emr.hive.serde.CloudTrailSerde'
STORED AS INPUTFORMAT 'com.amazon.emr.cloudtrail.CloudTrailInputFormat'
OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION 's3://your-cloudtrail-bucket/AWSLogs/123456789012/CloudTrail/';
-- 查询 Access Denied 事件
SELECT
eventtime,
useridentity.username,
eventname,
eventsource,
errorcode,
errormessage,
sourceipaddress,
requestparameters
FROM cloudtrail_logs
WHERE errorcode = 'AccessDenied'
AND year = '2024'
AND month = '01'
AND day = '11'
ORDER BY eventtime DESC
LIMIT 100;
-- 分析特定用户的拒绝操作
SELECT
eventname,
eventsource,
COUNT(*) as denial_count,
array_agg(DISTINCT errormessage) as error_messages
FROM cloudtrail_logs
WHERE errorcode = 'AccessDenied'
AND useridentity.username = 'alice'
AND year = '2024'
AND month = '01'
GROUP BY eventname, eventsource
ORDER BY denial_count DESC;
-- 识别异常 IP 访问
SELECT
sourceipaddress,
useridentity.username,
COUNT(*) as request_count,
array_agg(DISTINCT eventname) as operations
FROM cloudtrail_logs
WHERE year = '2024'
AND month = '01'
AND day = '11'
AND sourceipaddress NOT LIKE '203.0.113.%' -- 公司 IP 段
GROUP BY sourceipaddress, useridentity.username
HAVING request_count > 10
ORDER BY request_count DESC;
Credential Report
生成和下载:
# 生成报告
aws iam generate-credential-report
# 等待生成完成
while true; do
STATUS=$(aws iam get-credential-report --query 'ReportFormat' 2>&1)
if [[ $STATUS != *"ReportNotPresent"* ]]; then
break
fi
sleep 5
done
# 下载报告
aws iam get-credential-report \
--query 'Content' \
--output text \
| base64 --decode > credential-report.csv
# 查看报告
cat credential-report.csv | column -t -s ','
报告字段:
user,arn,user_creation_time,password_enabled,password_last_used,
password_last_changed,password_next_rotation,mfa_active,
access_key_1_active,access_key_1_last_rotated,access_key_1_last_used_date,
access_key_2_active,access_key_2_last_rotated,access_key_2_last_used_date,
cert_1_active,cert_2_active
分析未使用的凭证:
# 使用 csvkit 分析
pip install csvkit
# 查找 90 天未使用的 Access Key
csvgrep -c access_key_1_last_used_date -r "^(19|20)\d{2}" credential-report.csv \
| csvgrep -c access_key_1_active -m TRUE \
| awk -F',' 'NR==1 || (systime() - mktime(gensub(/T/, " ", "g", gensub(/:/, " ", "g", $11))) > 7776000)'
# 查找未启用 MFA 的用户
csvgrep -c mfa_active -m false credential-report.csv \
| csvcut -c user,arn,mfa_active
排查流程
系统化排查步骤
Step 1:确认错误详情
收集信息:
├─ 完整的错误消息
├─ 发生时间
├─ 尝试的操作
├─ 目标资源
├─ 执行的主体(用户/角色)
└─ 源 IP 地址
Step 2:使用 Policy Simulator
# 模拟相同的操作
aws iam simulate-principal-policy \
--policy-source-arn <principal-arn> \
--action-names <service>:<action> \
--resource-arns <resource-arn>
# 分析结果
# - 如果 allowed:检查其他因素(条件、资源策略)
# - 如果 explicitDeny:找到 Deny 来源
# - 如果 implicitDeny:添加 Allow 策略
Step 3:检查 CloudTrail
# 查找具体的拒绝事件
aws cloudtrail lookup-events \
--lookup-attributes \
AttributeKey=EventName,AttributeValue=<operation> \
--start-time <timestamp>
# 查看详细信息
echo $EVENT_JSON | jq '.errorCode, .errorMessage, .userIdentity, .requestParameters'
Step 4:检查策略层级
检查顺序:
1. SCP(如果在 Organizations 中)
aws organizations list-policies-for-target --target-id <account-id>
2. 权限边界
aws iam get-user/get-role --user-name/role-name <name>
3. 会话策略(如果使用 AssumeRole)
检查 assume-role 调用时传递的策略
4. 身份策略
aws iam list-attached-user-policies --user-name <name>
aws iam list-user-policies --user-name <name>
5. 资源策略
aws s3api get-bucket-policy --bucket <name>
aws kms get-key-policy --key-id <id>
Step 5:使用 Access Analyzer
# 检查资源是否对外暴露
aws accessanalyzer list-findings \
--analyzer-arn <analyzer-arn> \
--filter 'resourceArn={eq:["<resource-arn>"]}'
# 检查策略问题
aws accessanalyzer validate-policy \
--policy-document file://suspect-policy.json \
--policy-type IDENTITY_POLICY
Step 6:临时提权测试
# 临时授予 AdministratorAccess 测试
aws iam attach-user-policy \
--user-name alice \
--policy-arn arn:aws:iam::aws:policy/AdministratorAccess
# 测试操作
# 如果成功:说明是权限不足
# 如果失败:说明是其他问题(SCP、资源策略等)
# 移除临时权限
aws iam detach-user-policy \
--user-name alice \
--policy-arn arn:aws:iam::aws:policy/AdministratorAccess
常见问题解决方案
问题:无法列出 S3 bucket 中的对象
症状:
aws s3 ls s3://my-bucket/
An error occurred (AccessDenied)
可能原因:
1. 缺少 s3:ListBucket 权限
2. Bucket 策略拒绝
3. S3 Block Public Access 设置
解决:
# 检查权限
aws iam simulate-principal-policy \
--policy-source-arn <user-arn> \
--action-names s3:ListBucket \
--resource-arns arn:aws:s3:::my-bucket
# 检查 Bucket 策略
aws s3api get-bucket-policy --bucket my-bucket
# 检查 Block Public Access
aws s3api get-public-access-block --bucket my-bucket
问题:KMS 加密操作失败
症状:
Error: AccessDeniedException: User is not authorized to perform:
kms:Decrypt on resource
解决:
1. 检查 KMS Key Policy
aws kms get-key-policy --key-id <key-id> --policy-name default
2. 确保 Key Policy 允许账户访问
{
"Sid": "Enable IAM User Permissions",
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::123456789012:root"},
"Action": "kms:*",
"Resource": "*"
}
3. 检查用户的 IAM 策略
需要 kms:Decrypt 权限
掌握这些排查技巧和工具,能够快速定位和解决 IAM 权限问题!