首页 AWS Control Tower Guardrail 绕过 — AWSControlTowerExecution 角色实战
文章
取消

AWS Control Tower Guardrail 绕过 — AWSControlTowerExecution 角色实战

背景

在做多云安全架构的 Landing Zone 对比中,重点研究了各云的组织治理服务——AWS 的 Control Tower、阿里云的 CGC、腾讯云的 Control Center、华为云的 RGC。在实际落地 AWS Control Tower 时,遇到了一个 IaC 场景下的典型冲突:Mandatory Guardrail 生成的 SCP 拦截了 Terraform 的正常操作

具体表现为:CT 的部分 Mandatory Guardrail 使用 Resource = "*" 的 SCP 拦截操作(如 SSE 加密、生命周期配置),且只豁免了 AWSControlTowerExecution 这一个角色。Terraform 默认使用的 OrganizationAccountAccessRole 不在豁免名单中,直接导致 Apply 失败。

本文记录这个问题的完整排查过程和已验证的解决方案。


问题复现

执行 Terraform 配置 S3 生命周期策略时报错:

1
2
Error: error putting S3 Bucket Lifecycle Configuration:
AccessDenied: User: ... is not authorized to perform ...

根源是 CT 的一条 Mandatory Guardrail——AWS-GR_AUDIT_BUCKET_RETENTION_POLICY,其底层 SCP 策略类似:

1
2
3
4
5
6
7
8
9
10
{
  "Effect": "Deny",
  "Action": "s3:PutLifecycleConfiguration",
  "Resource": "*",
  "Condition": {
    "ArnNotLike": {
      "aws:PrincipalARN": "arn:aws:iam::*:role/AWSControlTowerExecution"
    }
  }
}

简化理解:AWSControlTowerExecution 角色外,所有主体都被禁止执行该操作


排查过程

关键发现:AWSControlTowerExecution 在 Management 账号不存在,仅存在于 Control Tower 自动创建的成员账号(Log-Archive、Audit 等)中。

尝试在 Management 账号查找该角色:

1
2
aws iam get-role --role-name AWSControlTowerExecution
# → NoSuchEntity,Management 账号中不存在

但在 Log-Archive 账号中,可以直接通过 IAM User 跨账号 Assume:

1
2
3
4
aws sts assume-role \
  --role-arn "arn:aws:iam::575267657812:role/AWSControlTowerExecution" \
  --role-session-name test-ct-bypass
# ✅ 成功!

这意味着 Control Tower 自动为该角色配置了跨账号信任——Management 账号的 IAM User 可以直接 Assume 成员账号的 AWSControlTowerExecution,无需额外配置。

信任链如下:

1
2
IAM User (terraform-bootstrap-admin)
  → Log-Archive 的 AWSControlTowerExecution(绕过 CT Guardrail)

Terraform 配置方案

添加 Provider

providers.tf 中增加一个使用 CT 角色的 provider:

1
2
3
4
5
6
7
8
provider "aws" {
  alias   = "log_archive_ct"
  region  = var.aws_region
  profile = var.aws_profile
  assume_role {
    role_arn = "arn:aws:iam::575267657812:role/AWSControlTowerExecution"
  }
}

模块声明

模块需声明接受第二个 provider alias:

1
2
3
4
5
6
7
8
9
# modules/logging/terraform.tf
terraform {
  required_providers {
    aws = {
      source                = "hashicorp/aws"
      configuration_aliases = [aws.log_archive_ct]
    }
  }
}

模块调用

主模块传入 alias:

1
2
3
4
5
6
7
8
module "cross_cloud_logging" {
  source = "../../modules/logging"

  providers = {
    aws                = aws.log_archive
    aws.log_archive_ct = aws.log_archive_ct
  }
}

资源指定

在被拦截的资源上显式指定 CT provider:

1
2
3
4
resource "aws_s3_bucket_lifecycle_configuration" "this" {
  provider = aws.log_archive_ct
  # ...
}

适用场景

场景使用 OrganizationAccountAccessRole使用 AWSControlTowerExecution
创建 S3 桶✅ 可以
配置版本控制✅ 可以
配置 Object Lock✅ 可以
配置 SSE 加密❌ 被 CT Guardrail 拦截✅ 可以
配置生命周期❌ 被 CT Guardrail 拦截✅ 可以
配置桶策略❌ 被 CT Guardrail 拦截✅ 可以

注意事项

  1. 只在必要时使用——推荐在被 Guardrail 拦截的具体资源上指定 CT provider,常规资源仍用 OrganizationAccountAccessRole,维持最小权限原则。

  2. 角色位置——AWSControlTowerExecution 不在 Management 账号,只在 Control Tower 创建的成员账号(Log-Archive、Audit 等)中。对不同的成员账号使用,只需修改 role_arn 中的账号 ID。

  3. 复用性——如 Audit 账号也面临相同的 Guardrail 限制,同样的方案只需修改账号 ID 即可复用。

  4. 不要滥用绕过——Guardrail 本质是安全基线,绕过前应评估是否有更优方案。在实际项目中最终选择的方案是关闭过宽的 Mandatory Guardrail,用自定义 SCP 替代AWSControlTowerExecution 作为备用方案存档。


总结

AWSControlTowerExecution 可以被 IAM User 直接从 Management 账号跨账号 Assume,这是控制台不直接透露、实测才确认的事实。当 IaC 工具与 Control Tower 的 Mandatory Guardrail 冲突时,这是一个已验证的后备方案。

这个案例也反映了一个更普遍的问题:AWS Control Tower 的设计默认假设管理员通过 CT Console 操作,未充分考虑 IaC 场景。当安全基线 SCP 以 Resource = "*" 无差别拦截时,留给运维人员的通道只有 AWSControlTowerExecution 这一个狭窄入口,跨云架构师需要提前了解这个限制。

本文由作者按照 CC BY 4.0 进行授权

多云安全架构对比 — AWS / 阿里云 / 腾讯云 / 华为云

AWS 安全策略清单 — 从 SCP 到 Config Rule 的完整落地指南