多云 IAM 权限收敛实战:最小权限策略配置

多云 IAM 权限收敛的目标,是把散落在长期访问密钥、通配符策略与过度授权角色中的权限,压缩到「每个身份只拥有完成本职工作所需的最小集合」。本文以 AWS IAM 与阿里云 RAM 为例,给出权限清单盘点、策略模拟、权限边界(permissions boundary)与服务控制策略(SCP)的完整配置流程,并附可直接执行的验证命令。

适用场景

  • 多云或多账号环境,账号数量增长后无法追踪谁拥有什么权限。
  • 等保、ISO 27001 或 SOC 2 审计前的权限整改与最小权限举证。
  • 员工离职、外包团队退场后的账号与密钥回收。
  • 发生访问密钥泄露或 AK 被提交到代码仓库后的紧急收口。
  • 为自动化脚本、CI/CD 流水线创建可控的专用角色,替代全权限子账号。

前置条件

  • AWS CLI v2,已配置具备 iam:Get*、iam:List*、access-analyzer:* 只读权限的管理员凭证。
  • 阿里云 aliyun CLI,以及 RAM 只读权限的 AccessKey。
  • 若使用 SCP,需要 AWS Organizations 已开启并拥有组织管理账号权限。
  • 一个独立的非生产测试账号,用于在收紧前验证策略不会中断业务。
  • jq,用于解析授权明细 JSON。

原理说明

云平台的授权判定不是「多个策略取并集」这么简单,而是一个逐层收窄的漏斗模型,理解这一点是权限收敛的前提:

  • 有效权限 = 身份策略(Identity Policy) ∩ 权限边界(Permissions Boundary) ∩ 服务控制策略(SCP),且任意一层出现 显式 Deny 都会直接否决,显式 Deny 的优先级永远最高。
  • 权限边界只做上限约束、本身不授予任何权限。它适合覆盖「允许自助授权但不得越界」的场景,例如让团队自行创建角色,但不允许角色获得 iam:* 之类的提权动作。
  • SCP 作用于账号内全部身份,是账号级的合规底线,例如禁止在非白名单区域创建资源。需要注意组织管理账号自身不受 SCP 约束,必须用独立的 root 保护策略处理。
  • 资源策略(如 S3 Bucket Policy)属于额外一层,当同一账号内请求同时命中身份策略与资源策略时按并集判定,跨账号请求才要求两者同时允许。

操作步骤

步骤一:导出全量授权明细与凭证报告

aws iam get-account-authorization-details > iam-details.json
aws iam generate-credential-report
sleep 5
aws iam get-credential-report --query Content --output text | base64 -d > credential-report.csv

授权明细包含所有用户、组、角色、内联策略与自定义策略的完整绑定关系,是后续所有分析的输入。凭证报告则用于定位长期未使用的密钥与口令。

步骤二:筛出高危通配符策略

jq -r '.Policies[] | select(.PolicyName | test("FullAccess|Admin|Wildcard")) | [.PolicyName, .Arn] | @tsv' iam-details.json

# 找出所有含 Action:* 且 Resource:* 的策略
jq -r '.Policies[] | .PolicyName as $n
  | .PolicyVersionList[-1].Document.Statement[]
  | select(.Effect=="Allow")
  | select((.Action|tostring|test("\\*")) and (.Resource|tostring|test("^\\*$")))
  | [$n, (.Action|tostring), (.Resource|tostring)] | @tsv' iam-details.json

输出中同时命中「Action 含通配符」与「Resource 为全星号」的条目,就是权限收敛的第一批整改对象。只读型策略(如 s3:Get* 且 Resource 精确到具体桶)优先级可以放后。

步骤三:基于真实访问活动生成策略

把「凭经验猜最小权限」换成「按 CloudTrail 实际调用生成」,可显著降低误伤:

aws accessanalyzer start-policy-generation \
  --policy-generation-details '{"principalArn":"arn:aws:iam::111122223333:role/AppRole"}' \
  --cloud-trail-details '{"trailArn":"arn:aws:cloudtrail:cn-north-1:111122223333:trail/org-trail","accessRole":"arn:aws:iam::111122223333:role/AccessAnalyzerMonitor","startTime":"2026-09-01T00:00:00Z","endTime":"2026-10-01T00:00:00Z"}'

# 等待任务完成后取出生成的策略文档
aws accessanalyzer list-policy-generations --query 'policyGenerations[0].jobId' --output text
aws accessanalyzer get-generated-policy --job-id <job-id> --include-resource-placeholders FINDING_DETAILS

观察窗口建议不少于 30 天,且必须覆盖月度批处理、报表导出等低频任务,否则生成的策略会漏掉这些动作。

步骤四:编写并绑定权限边界

先禁止提权动作,再列出允许集合。以下边界策略可直接复用,重点是第一段显式 Deny:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyPrivilegeEscalation",
      "Effect": "Deny",
      "Action": [
        "iam:CreatePolicyVersion",
        "iam:SetDefaultPolicyVersion",
        "iam:CreateAccessKey",
        "iam:AttachUserPolicy",
        "iam:PutRolePolicy",
        "iam:PassRole",
        "organizations:LeaveOrganization"
      ],
      "Resource": "*"
    },
    {
      "Sid": "AllowOnlyBusinessActions",
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "*"
    }
  ]
}
aws iam create-policy --policy-name AppBoundary \
  --policy-document file://boundary.json

aws iam put-role-permissions-boundary --role-name AppRole \
  --permissions-boundary arn:aws:iam::111122223333:policy/AppBoundary

步骤五:用 SCP 锁住账号级底线

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyNonApprovedRegions",
      "Effect": "Deny",
      "Action": "*",
      "Resource": "*",
      "Condition": {
        "StringNotEquals": {
          "aws:RequestedRegion": ["cn-north-1", "cn-northwest-1"]
        }
      }
    },
    {
      "Sid": "DenyCloudTrailTamper",
      "Effect": "Deny",
      "Action": [
        "cloudtrail:StopLogging",
        "cloudtrail:DeleteTrail"
      ],
      "Resource": "*"
    }
  ]
}
aws organizations create-policy --name DenyNonApprovedRegions \
  --type SERVICE_CONTROL_POLICY --content file://scp.json

aws organizations attach-policy --policy-id p-xxxxxxxx --target-id 111122223333

把审计日志的关闭动作纳入 SCP 是权限收敛里最容易被忽略的一环:即便攻击者拿到了高权限身份,也无法抹掉自己的操作痕迹。

步骤六:清理长期访问密钥与硬编码凭证

aws iam list-access-keys --user-name svc-app

# 先禁用观察一周,确认无调用后再删除
aws iam update-access-key --user-name svc-app \
  --access-key-id AKIAXXXXXXXXXXXXXXXX --status Inactive

aws iam delete-access-key --user-name svc-app \
  --access-key-id AKIAXXXXXXXXXXXXXXXX

长期密钥的正确替代是联合身份:EC2 上用实例角色,容器上用服务账号或 IRSA,CI/CD 上用 OIDC 短期令牌换取 STS 凭证。完成替换后,账号内的长期密钥数量应趋近于零。

步骤七:阿里云 RAM 侧同步收敛

{
  "Version": "1",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["oss:GetObject", "oss:ListObjects"],
      "Resource": ["acs:oss:*:*:app-data/*"]
    },
    {
      "Effect": "Deny",
      "Action": ["ram:*", "sts:AssumeRole"],
      "Resource": ["*"]
    }
  ]
}
aliyun ram CreatePolicy --PolicyName AppReadOnly \
  --PolicyDocument "$(cat ram-policy.json)" --Description "应用只读最小权限"

aliyun ram AttachPolicyToUser --PolicyName AppReadOnly \
  --PolicyType Custom --UserName dev01

配置验证

  • 高危动作模拟:应返回 implicitDeny 或 explicitDeny,而不是 allowed。
  • 业务动作模拟:应返回 allowed,用于确认收紧没有误伤正常调用。
  • 边界已生效:查询角色的边界字段应有值。
  • 密钥健康度:凭证报告中 90 天未使用的密钥数应为 0。
aws iam simulate-principal-policy \
  --policy-source-arn arn:aws:iam::111122223333:role/AppRole \
  --action-names s3:GetObject iam:CreateAccessKey \
  --resource-arns arn:aws:s3:::app-data/report.csv

aws iam get-role --role-name AppRole --query 'Role.PermissionsBoundary'

awk -F, 'NR>1 && $9=="true" {print $1, $10, $11}' credential-report.csv

常见问题

Q1:权限边界与 SCP 功能重叠,是不是二选一即可?

两者作用层级不同,不能互相替代。权限边界是身份级上限,适用于「允许团队自助授权,但硬性规定不得超越某个集合」的场景,粒度细、可按角色单独设置;SCP 是组织级上限,作用于账号内全部身份,适合承载合规底线(区域限制、日志防篡改)。管理账号不受 SCP 约束,因此高敏感账号必须单独设计 root 保护与 MFA 策略。

Q2:直接按 Access Analyzer 生成的策略上线安全吗?

不建议直接强制上线。生成策略只统计观察窗口内实际发生的调用,天然会遗漏低频操作与灾备路径。稳妥做法分两步:先用「只记录不拒绝」的模式再观察 2 到 4 周,把期间出现的 implicitDeny 告警逐条确认;确认清单清零后,再把策略切换为强制生效。同时保留一份回滚用的宽松策略,并记录切换时间点,便于出现故障时快速恢复。

Q3:策略里出现通配符 Action 就一定不安全吗?

不是绝对的。判定的关键是通配符是否跨越了权限提升边界:s3:Get* 配合精确到具体桶的 Resource,且策略中不含 iam:*、sts:AssumeRole、lambda:UpdateFunctionCode 等提权动作时,风险可控。真正需要立即整改的是 Action:* + Resource:*,以及任何能修改身份策略、创建新凭证或关闭审计的组合。收敛时应以「能否获得新权限」为判据,而不是以是否含星号为判据。

总结

多云 IAM 权限收敛不是一次性动作,而是一条可持续运行的闭环:先用授权明细与凭证报告建立现状基线,再用访问活动分析生成候选最小权限集,然后通过权限边界与 SCP 双层收窄,最后用策略模拟和历史调用对账验证效果。落地节奏建议按「只读审计 → 灰度记录 → 强制拒绝 → 定期复查」四步推进,每一步都保留可回滚的中间态,避免为了合规把线上业务一起收掉。