IAM 实战案例

通过真实场景案例,学习如何设计和实现安全、高效的 IAM 权限方案。

案例 1:开发环境资源隔离

业务需求

场景描述:

公司有 20 名开发人员:
├─ 每人需要独立的开发环境
├─ 可以创建 EC2、RDS、S3 等资源
├─ 只能管理自己的资源
├─ 不能查看或修改他人资源
└─ 防止误删生产环境资源

解决方案

方案设计:

核心思路:基于标签的访问控制(Tag-based Access Control)

标签规范:
├─ Owner: ${aws:username}
├─ Environment: development
└─ Project: 项目名称

权限模型:
├─ 只能操作带有 Owner=自己用户名 的资源
├─ 创建资源时强制添加 Owner 标签
└─ 限制在开发区域(如 us-east-1)

IAM 策略实现:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowReadAllResources",
      "Effect": "Allow",
      "Action": [
        "ec2:Describe*",
        "rds:Describe*",
        "s3:List*",
        "s3:GetBucketLocation"
      ],
      "Resource": "*"
    },
    {
      "Sid": "AllowManageOwnResources",
      "Effect": "Allow",
      "Action": [
        "ec2:*",
        "rds:*",
        "s3:*Object"
      ],
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "ec2:ResourceTag/Owner": "${aws:username}",
          "rds:db-tag/Owner": "${aws:username}",
          "s3:ExistingObjectTag/Owner": "${aws:username}"
        }
      }
    },
    {
      "Sid": "RequireOwnerTagOnCreation",
      "Effect": "Allow",
      "Action": [
        "ec2:RunInstances",
        "ec2:CreateVolume",
        "rds:CreateDBInstance",
        "s3:PutObject"
      ],
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "aws:RequestTag/Owner": "${aws:username}",
          "aws:RequestTag/Environment": "development"
        }
      }
    },
    {
      "Sid": "AllowTagging",
      "Effect": "Allow",
      "Action": [
        "ec2:CreateTags",
        "rds:AddTagsToResource"
      ],
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "aws:RequestTag/Owner": "${aws:username}"
        }
      }
    },
    {
      "Sid": "DenyTagModification",
      "Effect": "Deny",
      "Action": [
        "ec2:DeleteTags",
        "rds:RemoveTagsFromResource"
      ],
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "aws:ResourceTag/Owner": "${aws:username}"
        },
        "ForAnyValue:StringEquals": {
          "aws:TagKeys": ["Owner", "Environment"]
        }
      }
    },
    {
      "Sid": "RestrictToDevRegion",
      "Effect": "Deny",
      "Action": "*",
      "Resource": "*",
      "Condition": {
        "StringNotEquals": {
          "aws:RequestedRegion": "us-east-1"
        }
      }
    }
  ]
}

实施步骤

1. 创建IAM 组:

# 创建 Developers 组
aws iam create-group --group-name Developers

# 附加策略
aws iam put-group-policy \
  --group-name Developers \
  --policy-name DevEnvironmentPolicy \
  --policy-document file://dev-policy.json

2. 添加用户到组:

# 为每个开发人员创建 IAM 用户
aws iam create-user --user-name alice
aws iam create-user --user-name bob

# 添加到 Developers 组
aws iam add-user-to-group --user-name alice --group-name Developers
aws iam add-user-to-group --user-name bob --group-name Developers

# 设置控制台密码
aws iam create-login-profile \
  --user-name alice \
  --password "TemporaryPassword123!" \
  --password-reset-required

3. 用户使用示例:

# Alice 创建 EC2 实例(成功)
aws ec2 run-instances \
  --image-id ami-0c55b159cbfafe1f0 \
  --instance-type t3.micro \
  --tag-specifications \
    'ResourceType=instance,Tags=[{Key=Owner,Value=alice},{Key=Environment,Value=development}]'

# Alice 尝试访问 Bob 的实例(失败)
aws ec2 stop-instances --instance-ids i-bob-instance-id
# Error: AccessDenied

# Alice 尝试删除 Owner 标签(失败)
aws ec2 delete-tags --resources i-alice-instance-id --tags Key=Owner
# Error: AccessDenied

效果验证

验证清单:

✓ 用户只能看到自己的资源
✓ 创建资源时必须打上正确的标签
✓ 不能修改或删除 Owner、Environment 标签
✓ 不能在开发区域外创建资源
✓ 不能操作他人的资源
✓ 可以查看所有资源列表(但不能操作)

案例 2:时间和网络访问控制

业务需求

安全要求:

合规部门要求:
├─ 仅允许工作时间(9:00-18:00)访问
├─ 必须来自公司网络(特定 IP 段)
├─ 敏感操作必须使用 MFA
├─ 生产环境操作需要额外审批
└─ 记录所有访问日志

解决方案

策略设计:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowDuringBusinessHours",
      "Effect": "Allow",
      "Action": "*",
      "Resource": "*",
      "Condition": {
        "DateGreaterThan": {
          "aws:CurrentTime": "2024-01-01T01:00:00Z"
        },
        "DateLessThan": {
          "aws:CurrentTime": "2024-12-31T10:00:00Z"
        },
        "IpAddress": {
          "aws:SourceIp": [
            "203.0.113.0/24",
            "192.0.2.0/24"
          ]
        }
      }
    },
    {
      "Sid": "DenyOutsideBusinessHours",
      "Effect": "Deny",
      "Action": "*",
      "Resource": "*",
      "Condition": {
        "DateLessThan": {
          "aws:CurrentTime": "2024-01-01T01:00:00Z"
        }
      }
    },
    {
      "Sid": "DenyOutsideBusinessHours2",
      "Effect": "Deny",
      "Action": "*",
      "Resource": "*",
      "Condition": {
        "DateGreaterThan": {
          "aws:CurrentTime": "2024-12-31T10:00:00Z"
        }
      }
    },
    {
      "Sid": "DenyFromUnauthorizedIP",
      "Effect": "Deny",
      "Action": "*",
      "Resource": "*",
      "Condition": {
        "NotIpAddress": {
          "aws:SourceIp": [
            "203.0.113.0/24",
            "192.0.2.0/24"
          ]
        },
        "Bool": {
          "aws:ViaAWSService": "false"
        }
      }
    },
    {
      "Sid": "RequireMFAForSensitiveOperations",
      "Effect": "Deny",
      "Action": [
        "ec2:StopInstances",
        "ec2:TerminateInstances",
        "rds:DeleteDBInstance",
        "rds:DeleteDBCluster",
        "s3:DeleteBucket",
        "s3:DeleteObject",
        "iam:DeleteUser",
        "iam:DeleteRole",
        "iam:DetachUserPolicy",
        "iam:DetachRolePolicy"
      ],
      "Resource": "*",
      "Condition": {
        "BoolIfExists": {
          "aws:MultiFactorAuthPresent": "false"
        }
      }
    },
    {
      "Sid": "DenyProductionAccessWithoutApproval",
      "Effect": "Deny",
      "Action": [
        "ec2:*",
        "rds:*",
        "lambda:*"
      ],
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "ec2:ResourceTag/Environment": "production",
          "rds:db-tag/Environment": "production"
        },
        "StringNotEquals": {
          "aws:PrincipalTag/ProductionAccess": "approved"
        }
      }
    }
  ]
}

MFA 配置

启用 MFA:

# 1. 用户启用虚拟 MFA
aws iam create-virtual-mfa-device \
  --virtual-mfa-device-name alice-mfa \
  --outfile QRCode.png \
  --bootstrap-method QRCodePNG

# 2. 绑定 MFA 设备到用户
aws iam enable-mfa-device \
  --user-name alice \
  --serial-number arn:aws:iam::123456789012:mfa/alice-mfa \
  --authentication-code-1 123456 \
  --authentication-code-2 789012

使用 MFA 获取临时凭证:

# 获取会话令牌
aws sts get-session-token \
  --serial-number arn:aws:iam::123456789012:mfa/alice-mfa \
  --token-code 123456 \
  --duration-seconds 3600

# 输出包含临时凭证
{
  "Credentials": {
    "AccessKeyId": "ASIA...",
    "SecretAccessKey": "...",
    "SessionToken": "...",
    "Expiration": "2024-01-11T12:00:00Z"
  }
}

# 使用临时凭证
export AWS_ACCESS_KEY_ID=ASIA...
export AWS_SECRET_ACCESS_KEY=...
export AWS_SESSION_TOKEN=...

监控和告警

CloudWatch 告警:

# 监控非授权 IP 访问尝试
aws cloudwatch put-metric-alarm \
  --alarm-name UnauthorizedIPAccess \
  --alarm-description "Alert on access from unauthorized IP" \
  --metric-name UnauthorizedIPCount \
  --namespace Custom/IAM \
  --statistic Sum \
  --period 300 \
  --evaluation-periods 1 \
  --threshold 1 \
  --comparison-operator GreaterThanThreshold \
  --alarm-actions arn:aws:sns:us-east-1:123456789012:security-alerts

CloudTrail 日志查询:

-- 查询非工作时间的访问
SELECT 
  userIdentity.userName,
  eventTime,
  eventName,
  sourceIPAddress
FROM cloudtrail_logs
WHERE eventTime NOT BETWEEN '09:00:00' AND '18:00:00'
  AND errorCode IS NULL
ORDER BY eventTime DESC;

案例 3:多环境权限分离

业务需求

环境划分:

三个环境:
├─ Development(开发)
│   ├─ 开发人员:完全访问
│   └─ 成本优先,可随意实验
├─ Staging(预发布)
│   ├─ 开发人员:读写访问
│   ├─ QA:完全访问
│   └─ 接近生产配置
└─ Production(生产)
    ├─ DevOps:完全访问
    ├─ 开发人员:只读访问
    └─ 严格变更控制

解决方案架构

多账户策略(推荐):

AWS Organizations
├─ Management Account(管理账户)
│   └─ 仅用于账单和组织管理
├─ Dev Account(开发账户)
│   ├─ 开发人员:AdministratorAccess
│   └─ SCP:限制昂贵服务
├─ Staging Account(预发布账户)
│   ├─ 开发人员:PowerUserAccess
│   ├─ QA:特定服务完全访问
│   └─ SCP:防止意外删除
└─ Production Account(生产账户)
    ├─ DevOps:管理员访问(有 MFA)
    ├─ 开发人员:ReadOnlyAccess
    └─ SCP:强制加密、标签、CloudTrail

单账户策略(备选):

基于标签和命名约定:
├─ 资源标签:Environment=dev/staging/prod
├─ 命名前缀:dev-*, staging-*, prod-*
└─ IAM 策略根据标签控制访问

跨账户访问实现

1. 在 Production 账户创建角色:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::111111111111:root"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "sts:ExternalId": "unique-external-id-12345"
        },
        "Bool": {
          "aws:MultiFactorAuthPresent": "true"
        }
      }
    }
  ]
}

2. 在 Dev 账户授权用户:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "sts:AssumeRole",
      "Resource": "arn:aws:iam::222222222222:role/ProductionReadOnlyRole"
    }
  ]
}

3. 用户切换角色:

# CLI 方式
aws sts assume-role \
  --role-arn arn:aws:iam::222222222222:role/ProductionReadOnlyRole \
  --role-session-name alice-prod-session \
  --external-id unique-external-id-12345 \
  --serial-number arn:aws:iam::111111111111:mfa/alice \
  --token-code 123456

# 使用 AWS CLI Profile
[profile prod-readonly]
role_arn = arn:aws:iam::222222222222:role/ProductionReadOnlyRole
source_profile = default
external_id = unique-external-id-12345
mfa_serial = arn:aws:iam::111111111111:mfa/alice

# 使用
aws s3 ls --profile prod-readonly

SCP 示例

开发账户 SCP(限制成本):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyExpensiveInstances",
      "Effect": "Deny",
      "Action": "ec2:RunInstances",
      "Resource": "arn:aws:ec2:*:*:instance/*",
      "Condition": {
        "StringNotLike": {
          "ec2:InstanceType": [
            "t3.*",
            "t4g.*"
          ]
        }
      }
    },
    {
      "Sid": "DenyExpensiveServices",
      "Effect": "Deny",
      "Action": [
        "redshift:*",
        "sagemaker:CreateNotebookInstance"
      ],
      "Resource": "*"
    }
  ]
}

生产账户 SCP(强制安全):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyDisableCloudTrail",
      "Effect": "Deny",
      "Action": [
        "cloudtrail:StopLogging",
        "cloudtrail:DeleteTrail"
      ],
      "Resource": "*"
    },
    {
      "Sid": "RequireEncryption",
      "Effect": "Deny",
      "Action": "s3:PutObject",
      "Resource": "*",
      "Condition": {
        "StringNotEquals": {
          "s3:x-amz-server-side-encryption": [
            "AES256",
            "aws:kms"
          ]
        }
      }
    },
    {
      "Sid": "RequireMandatoryTags",
      "Effect": "Deny",
      "Action": [
        "ec2:RunInstances",
        "rds:CreateDBInstance"
      ],
      "Resource": "*",
      "Condition": {
        "Null": {
          "aws:RequestTag/Owner": "true",
          "aws:RequestTag/CostCenter": "true",
          "aws:RequestTag/Environment": "true"
        }
      }
    }
  ]
}

案例 4:第三方 SaaS 集成

业务需求

场景:

授权第三方监控服务(如 Datadog)访问 AWS:
├─ 需要读取 CloudWatch 指标
├─ 需要列出 EC2、RDS 实例
├─ 需要读取 S3 日志
├─ 不能修改任何资源
└─ 确保安全(防止混淆代理攻击)

解决方案

1. 创建第三方访问角色:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::464622532012:root"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "sts:ExternalId": "generated-by-third-party-12345"
        }
      }
    }
  ]
}

External ID 重要性:

混淆代理攻击(Confused Deputy Problem):

没有 External ID 的风险:
1. 恶意用户 A 注册第三方服务
2. 第三方使用相同的 IAM 角色
3. 恶意用户 A 提供受害者的账户 ID
4. 第三方使用角色访问受害者账户
5. 恶意用户 A 获得受害者数据

使用 External ID 的保护:
1. 每个客户有唯一的 External ID
2. 第三方必须提供正确的 External ID
3. 角色信任策略验证 External ID
4. 防止跨客户访问

2. 权限策略(最小权限):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowListResources",
      "Effect": "Allow",
      "Action": [
        "ec2:Describe*",
        "rds:Describe*",
        "elasticloadbalancing:Describe*",
        "cloudwatch:ListMetrics",
        "cloudwatch:GetMetricStatistics",
        "logs:DescribeLogGroups",
        "logs:DescribeLogStreams",
        "s3:ListAllMyBuckets",
        "s3:GetBucketLocation"
      ],
      "Resource": "*"
    },
    {
      "Sid": "AllowReadLogs",
      "Effect": "Allow",
      "Action": [
        "s3:GetObject"
      ],
      "Resource": "arn:aws:s3:::my-logs-bucket/*"
    },
    {
      "Sid": "DenyWrite",
      "Effect": "Deny",
      "NotAction": [
        "ec2:Describe*",
        "rds:Describe*",
        "s3:Get*",
        "s3:List*",
        "cloudwatch:Get*",
        "cloudwatch:List*",
        "logs:Describe*"
      ],
      "Resource": "*"
    }
  ]
}

3. 集成步骤:

# 第三方提供的集成脚本
aws cloudformation create-stack \
  --stack-name datadog-integration \
  --template-url https://datadog-cloudformation-template.s3.amazonaws.com/aws/main.yaml \
  --parameters \
    ParameterKey=ExternalId,ParameterValue=generated-by-datadog-12345 \
  --capabilities CAPABILITY_IAM

# 验证角色创建
aws iam get-role --role-name DatadogIntegrationRole

# 测试角色承担
aws sts assume-role \
  --role-arn arn:aws:iam::123456789012:role/DatadogIntegrationRole \
  --role-session-name test-session \
  --external-id generated-by-datadog-12345

安全审计

定期审查:

# 查看第三方角色的使用情况
aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=ResourceName,AttributeValue=DatadogIntegrationRole \
  --max-results 50

# 检查异常访问
SELECT 
  userIdentity.sessionContext.sessionIssuer.userName as role_name,
  sourceIPAddress,
  eventName,
  eventTime,
  COUNT(*) as request_count
FROM cloudtrail_logs
WHERE userIdentity.type = 'AssumedRole'
  AND userIdentity.sessionContext.sessionIssuer.userName = 'DatadogIntegrationRole'
GROUP BY role_name, sourceIPAddress, eventName, eventTime
HAVING request_count > 100
ORDER BY eventTime DESC;

这些实战案例展示了 IAM 在真实场景中的应用,理解这些模式能够帮助你设计安全、高效的权限方案!