第3章:IAM策略编写实践——最小权限原则

好了,聊完了IAM的角色和信任策略,咱们该动手写策略了。这一章我重点说说最小权限原则——说白了就是:给每个角色、每个用户刚刚好的权限,多一点都不行。听起来简单?我见过太多团队一开始图省事,直接挂个 AdministratorAccess,后来出事了才追悔莫及。

3.1 为什么最小权限这么重要?

你想想看,AWS 安全模型的核心是「共享责任」。云基础设施的物理安全亚马逊管,但你的数据、你的策略、你的密钥,那都得自己负责。权限给大了,一个不小心,开发者可能在测试环境把生产库删了。我有个客户,就因为给了一个实习生 s3:* 权限,结果他写脚本时把整个桶递归清空了……还好有版本控制,不然损失惨重。

最小权限原则的好处很明显:

  • 缩小攻击面——哪怕某个凭证泄露了,能做的操作也有限。
  • 减少误操作——没权限就干不了坏事,不管是恶意的还是手滑的。
  • 审计更清晰——每条策略都能说清楚“为什么需要这个权限”。

核心观点:权限应该是「按需最小」,而不是「先给够再说」。你给一个只看日志的脚本 logs:DescribeLogGroups 就够了,别顺手加上 logs:*。

3.2 编写策略的第一步:用托管策略还是自定义策略?

AWS 提供了很多托管策略,比如 AmazonS3ReadOnlyAccess。我个人建议:能用托管策略的优先用,省事。但是!托管策略往往偏宽泛,比如 ReadOnlyAccess 几乎能读所有服务。如果你的场景只需要读某个特定桶,千万别偷懒。

我的经验是:先查一下有没有合适的托管策略作为起点,然后基于它做裁剪,或者直接写自定义策略。举个例子:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::my-important-data",
        "arn:aws:s3:::my-important-data/*"
      ]
    }
  ]
}

你看,我只给了 GetObject 和 ListBucket,没给 PutObject,更没给 DeleteObject。这就是最小权限的体现。

小技巧:用 AWS 的 IAM 访问分析器来检查你的策略是不是过于宽松。它会提醒你哪些权限超出实际使用范围。

3.3 善用条件键,给权限加「围栏」

光控制 Action 和 Resource 还不够。你还可以用 Condition 来限制:比如只能从公司 VPN 访问,或者只能操作特定标签的资源。我曾经给一个开发团队配置过:只允许使用 t3.micro 实例类型,避免他们一时兴起开了个昂贵的 p3.16xlarge。

条件键用得好的话,能把权限控制得非常精细。下面是我常用的几个:

条件键 用途 示例
aws:SourceIp 限制来源 IP 只允许公司内网
aws:RequestedRegion 限制可操作的区域 只允许 ap-northeast-1
aws:ResourceTag 限制带特定标签的资源 只允许操作 env=production 的 EC2
s3:prefix 限制 S3 前缀路径 只允许访问 /logs/

举个实际策略:只允许从公司 IP 段访问,并且在东京区域操作 S3。

{
  "Effect": "Allow",
  "Action": "s3:ListBucket",
  "Resource": "arn:aws:s3:::my-bucket",
  "Condition": {
    "IpAddress": {
      "aws:SourceIp": "203.0.113.0/24"
    },
    "StringEquals": {
      "aws:RequestedRegion": "ap-northeast-1"
    }
  }
}

注意:条件键不是万能的。有些服务不支持某些条件键,你得去查服务特定的授权参考。写完之后最好用 IAM 策略模拟器验证一下,别想当然。

3.4 拒绝优先于允许——显式 Deny 是你的保险

IAM 的策略评估逻辑里有一条铁律:显式 Deny 永远覆盖任何 Allow。所以当你需要确保某些操作一定不能做时,就用 "Effect": "Deny"。比如,你希望所有用户都不得删除 S3 存储桶,就可以全局加一条:

{
  "Effect": "Deny",
  "Action": "s3:DeleteBucket",
  "Resource": "*"
}

我本人习惯在账户的权限边界策略里先设几条硬性 Deny,比如禁止修改 CloudTrail 配置、禁止关闭多因素认证等。这就像给房子装了防盗门——就算不小心把钥匙给了不该给的人,他也绕不过去。

3.5 从浮夸到精确:迭代式的权限最小化

别指望第一次写出去的策略就完美。现实是:你一开始可能低估了业务需求,给少了导致应用报错;或者给多了,又得回头收紧。我建议采用迭代式:

  1. 基于最小知识写出初始策略——只赋权和文档里明确需要的 Action。
  2. 放到预发布环境跑一遍——看 CloudTrail 日志里有没有 AccessDenied 错误。
  3. 根据日志加回确实需要的权限——注意,每次只加一个 Action,不要一次加一堆。
  4. 定期用 IAM 访问分析器复查——把未使用的权限去掉。

你可能会问:这样是不是太繁琐?嗯,一开始确实有点麻烦。但一旦形成习惯,你的策略会越来越干净。我经历过一个项目,一开始给了一个 Lambda 执行角色 s3:*,后来发现它只读几个配置文件。优化后,策略从 20 行缩到 4 行,心里踏实多了。

3.6 一张图看懂最小权限策略设计流程

下面这张图总结了从我拿到一个需求,到最终落地最小权限策略的思考路径。嗯,画得比较糙,但逻辑是清晰的:

最小权限策略设计流程 1. 分析业务需求 2. 选托管/自定义策略 3. 编写最小Action+Resource 4. 模拟验证 + CloudTrail迭代 发现缺少权限→回到步骤3 关键:每轮只加一个Action,拒绝用通配符除非有显式Deny兜底

3.7 避坑指南:我踩过的几个雷

写策略的时候,有几个坑特别容易掉进去。我用自己的血泪史给你提个醒:

  • 滥用通配符 *——我曾经给一个 DevOps 角色写了 "Action": "ec2:*",结果他顺手终止了一个正在跑批处理的实例。后来我全都改成精确 Action 列表,宁可多写几行。
  • 忽略 Resource 的层级——S3 的桶和对象资源是分开的。比如 ListBucket 针对桶 ARN,GetObject 针对对象 ARN。少写一个 /* 就会报错。
  • 条件键拼写错误——我有一次把 aws:SourceIp 写成了 aws:sourceIp(大小写不对),结果条件永远不生效,策略等于白写了。教训:多看官方文档的精确拼写。

建议:写完策略一定要用 aws iam simulate-custom-policy 或 IAM 策略模拟器跑一遍,用真实的请求参数验证。省钱省心。

3.8 小结:最小权限是习惯,不是一次性的工作

说了这么多,其实就一句话:权限越小,睡得越安稳。别管项目多赶,都要留出时间做权限评审。把最小权限原则嵌入到你的 CI/CD 流水线里,比如通过自动化工具扫描策略的宽松度。我自己现在每个新策略都必须经过两个步骤:同事 Code Review 加策略模拟器通过,才能合并到主分支。