3. SCP与权限边界实战
服务控制策略(Service Control Policy,SCP)是 AWS Organizations 中作用于成员账号的权限护栏。它本身不授予任何权限,只负责在账号根、OU 或账号层级上划定“允许的最大权限集合”。真正让某个 IAM 主体获得操作能力的,仍然是身份策略(Identity-based Policy)。因此理解 SCP 的关键在于:最终有效权限 = SCP 允许范围 ∩ IAM 身份策略允许范围 − 显式拒绝。本节围绕网络服务、区域与资源类型三类典型限制,编写可落地的 SCP,并用 IAM 策略验证多账号治理效果。
一、SCP 的作用位置与生效逻辑
SCP 只能附加到组织根、OU 或成员账号,不能附加到 IAM 用户、角色或组。一个成员账号的 SCP 生效集合,是从根到该账号路径上所有 SCP 的交集。例如根上有一条“禁止所有区域外操作”的 SCP,某 OU 上有一条“禁止删除 VPC”的 SCP,则该 OU 下账号同时受两条约束。默认情况下,组织根会带有一条 FullAWSAccess 策略,它允许一切,因此新账号在未附加限制前不受额外约束。
SCP 的语法与 IAM 策略一致,包含 Version、Statement、Effect、Action、Resource、Condition。区别在于 SCP 中的 Resource 通常写 "*",因为 SCP 不针对具体资源实例做精细授权,而是控制“能不能调用某类 API”。
二、限制网络服务:只允许受控的 VPC 与网络 API
多账号环境中,网络服务是最容易失控的部分:成员账号可能自行创建 VPC、对等连接、Transit Gateway 挂载或开放安全组。治理思路是“白名单式放行 + 显式拒绝高风险动作”。下面这条 SCP 允许常规的 VPC 读取与受控创建,但拒绝删除 VPC、修改路由表和创建对等连接:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowNetworkReadAndControlledCreate",
"Effect": "Allow",
"Action": [
"ec2:Describe*",
"ec2:CreateVpc",
"ec2:CreateSubnet",
"ec2:CreateTags"
],
"Resource": "*"
},
{
"Sid": "DenyRiskyNetworkOps",
"Effect": "Deny",
"Action": [
"ec2:DeleteVpc",
"ec2:CreateVpcPeeringConnection",
"ec2:ModifyRouteTable",
"ec2:CreateTransitGatewayVpcAttachment"
],
"Resource": "*"
}
]
}
注意:SCP 中一旦出现 Deny,即使 IAM 身份策略允许,操作也会被拒绝。因此上例中成员账号的 IAM 角色即便拥有 ec2:*,也无法删除 VPC 或建立对等连接。这就是“护栏”与“授权”分离的价值。
三、限制区域:用 aws:RequestedRegion 锁定部署范围
区域限制是成本与合规治理的常用手段。通过条件键 aws:RequestedRegion,可以只允许在指定区域调用 API。下面这条 SCP 仅允许在 cn-north-1 与 cn-northwest-1 两个区域操作,其余区域一律拒绝:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyOutsideApprovedRegions",
"Effect": "Deny",
"Action": "*",
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:RequestedRegion": [
"cn-north-1",
"cn-northwest-1"
]
}
}
}
]
}
需要特别说明:全局服务(如 IAM、Organizations、CloudFront、Route 53)的请求区域通常表现为 us-east-1 或空值,直接套用上述策略可能误伤这些服务。稳妥做法是在条件中排除全局服务,或使用 NotAction 列出不受区域限制的服务。例如把 Action 改为 NotAction 并列出 iam:*、organizations:*、
四、限制资源类型:用标签与资源 ARN 约束创建行为
资源类型限制常用于防止成员账号创建高成本或不合规的资源。例如只允许创建带 CostCenter 标签的 EC2 实例,禁止创建特定实例族:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyUnlabeledEC2",
"Effect": "Deny",
"Action": "ec2:RunInstances",
"Resource": "arn:aws:ec2:*:*:instance/*",
"Condition": {
"Null": {
"aws:RequestTag/CostCenter": "true"
}
}
},
{
"Sid": "DenyExpensiveInstanceFamily",
"Effect": "Deny",
"Action": "ec2:RunInstances",
"Resource": "arn:aws:ec2:*:*:instance/*",
"Condition": {
"StringLike": {
"ec2:InstanceType": "p*"
}
}
}
]
}
这里 aws:RequestTag/CostCenter 为 true 表示请求中未携带该标签,因此拒绝。第二条用 ec2:InstanceType 条件键匹配 p* 系列,阻止 GPU 实例被随意创建。两条策略叠加后,成员账号只能创建带成本中心标签、且非 p 系列的实例。
五、结合 IAM 策略验证治理效果
SCP 写完后必须验证,否则容易“策略看似正确、实际误伤或漏放”。验证分两步:先确认 SCP 是否按预期裁剪权限,再确认 IAM 身份策略在裁剪后仍能完成必要操作。
假设成员账号中有一个角色 NetworkOperator,其身份策略允许 ec2:*。在附加了前述三条 SCP 后,我们期望:
- 在
cn-north-1创建带CostCenter=Net标签的t3.medium实例:应成功。 - 在
us-east-1创建任意实例:应被区域 SCP 拒绝。 - 在
cn-north-1创建p3.2xlarge实例:应被实例族 SCP 拒绝。 - 删除已有 VPC:应被网络 SCP 拒绝。
验证时可用 IAM Policy Simulator 或直接调用 API 观察返回。例如用 CLI 测试区域限制:
aws ec2 run-instances \
--image-id ami-xxxxxxxx \
--instance-type t3.medium \
--region us-east-1 \
--tag-specifications 'ResourceType=instance,Tags=[{Key=CostCenter,Value=Net}]'
预期返回 UnauthorizedOperation,错误信息中会提示请求被 SCP 拒绝。若返回成功,说明区域 SCP 未正确附加或条件键写错。再测试标签约束:去掉 --tag-specifications 后在 cn-north-1 执行,应同样被拒绝,提示缺少 CostCenter 标签。
AccessDenied 事件中的 errorMessage,确认是 SCP 拒绝还是 IAM 策略拒绝。六、多账号治理的落地要点
第一,SCP 要分层设计:根上放全局底线(区域、禁止关闭 CloudTrail),OU 上放业务线约束(网络、资源类型),账号上放个别例外。第二,SCP 与 IAM 职责分离:SCP 由中心治理团队维护,IAM 由账号所有者维护,二者通过“交集”协作。第三,所有 Deny 语句都要写 Sid,便于在 CloudTrail 和错误信息中定位是哪条策略拦截。第四,区域限制务必排除全局服务,否则 IAM、Organizations、Billing 等基础功能会一并失效。第五,每次变更后按“允许用例 + 拒绝用例”成对验证,确保策略既不过松也不过紧。
通过本节的三类 SCP 与 IAM 策略的组合验证,可以看到多账号权限治理的核心并不是“把权限收得越小越好”,而是让 SCP 精确表达组织底线,让 IAM 策略在底线内完成业务授权,最终用可复现的测试用例证明治理效果符合预期。