edilsonathayde/iac-multi-account-security
GitHub: edilsonathayde/iac-multi-account-security
一个基于模块化 Terraform 与自动化扫描的 IaC 安全方案,用于解决多账户基础设施的一致性、密钥安全与审计追踪问题。
Stars: 0 | Forks: 0
# IaC 多账户安全:大规模模块化 Terraform
## 问题
一个不断增长的工程组织拥有多个团队,每个团队都在独立配置自己的 AWS 基础设施。结果是可以预见的:没有一致性、没有可见性,并且安全债务不断累积。
具体表现如下:
- **缺乏共享标准。** 每个团队使用不同的模式来配置 VPC、IAM 角色和密钥管理。对任何一个团队基础设施的安全审查都需要从头理解其自定义方法。
- **明文存储密钥。** API 密钥和数据库凭证以 Kubernetes Secret 的形式存储——仅进行 Base64 编码,未加密。任何拥有 `kubectl get secret` 权限的人都可以读取生产凭证。
- **缺乏安全扫描。** 基础设施代码像应用代码一样由人工审查:人工阅读 Terraform 代码。错误配置(过于宽松的 IAM 策略、公开 S3 存储桶、缺少加密)在生产环境中才会被发现,而不是在拉取请求中。
- **缺乏审计追踪。** 基础设施变更发生在 Git 之外——直接点击控制台、从笔记本电脑执行 `terraform apply`,没有记录谁修改了什么以及为什么修改。
- **规模扩张需要复制。** 添加一个新团队意味着复制现有团队的 Terraform 配置,并希望他们正确更新变量。模块漂移是常态。
团队需要一个共享的基础设施基座:一致的模式、自动化的安全 enforcement,以及一个让审计追踪不可避免的 GitOps 工作流。
## 决策
构建一个可版本化的模块化 Terraform 库,在 CI/CD 流水线中集成自动化安全扫描,并强制将 GitOps 作为生产基础设施变更的唯一路径。
**为什么使用模块库而不是模板?**
模板会被复制并产生漂移,而模块是被引用和版本化的。当对模块应用安全修复时,所有使用者只需提升版本约束即可采用,无需重写配置。
**为什么使用自动化扫描而不是人工审查?**
人工审查可以发现明显问题,而自动化扫描器能发现系统性的问题——那些在孤立环境中看起来正常但违反合规策略的错误配置。Checkov、tfsec 和 TFLint 各自捕获不同类别的问题。在 CI 中同时运行这三者只需几秒钟。
**为什么使用 Sealed Secrets 来管理 Kubernetes 凭证?**
Kubernetes Secrets 并不安全——它们只是 Base64 编码,并非加密。Sealed Secrets 使用集群特定密钥对密钥值进行加密。加密后的 Secret 可以安全地提交到 Git——只有持有私钥的集群才能解密。
| 组件 | 角色 |
|------|------|
| **Terraform 模块** | 可重用、可版本化的基础设施模式 |
| **Checkov** | 策略即代码扫描——CIS 基准、合规规则 |
| **tfsec** | Terraform 专用安全分析 |
| **TFLint** | 代码检查与最佳实践强制 |
| **AWS GuardDuty** | 运行时威胁检测——在所有账户中启用 |
| **AWS Security Hub** | 跨账户的集中式发现结果 |
| **Sealed Secrets** | 加密的 Kubernetes Secret,可安全提交到 Git |
| **GitLab CI/CD** | 流水线:计划 → 扫描 → 审查 → 应用 |
## 架构
```
┌──────────────────────────────────────────────────────────────────┐
│ Module Library (Git) │
│ │
│ modules/
│ ├── vpc/ # VPC, subnets, routing, flow logs │
│ ├── iam/ # Roles, policies, least-privilege patterns │
│ ├── eks/ # EKS cluster, node groups, IRSA │
│ ├── rds/ # RDS with encryption, backups, security groups│
│ ├── s3/ # Buckets with versioning, encryption, policy │
│ ├── kafka/ # MSK cluster, topic configuration │
│ └── observability/ # CloudWatch, GuardDuty, Security Hub │
│ │
│ Each module: semantic versioning (v1.2.3) │
│ Each module: README with inputs, outputs, examples │
│ Each module: built-in security defaults (encryption on, public │
│ access off, flow logs enabled) │
└────────────────────────┬─────────────────────────────────────────┘
│
│ (module references with version pins)
▼
┌──────────────────────────────────────────────────────────────────┐
│ Squad Repositories │
│ │
│ squad-a/infra/ │
│ ├── main.tf # module "vpc" { source = "...//vpc?ref=v2.1.0" }│
│ ├── variables.tf │
│ └── environments/ # dev / staging / prod │
└────────────────────────┬─────────────────────────────────────────┘
│
│ (pull request triggers CI)
▼
┌──────────────────────────────────────────────────────────────────┐
│ CI/CD Pipeline (GitLab) │
│ │
│ Stage 1: Plan │
│ └── terraform init + terraform plan │
│ Plan output posted as PR comment │
│ │
│ Stage 2: Security Scan │
│ ├── Checkov → CIS AWS benchmarks + custom policies │
│ ├── tfsec → Terraform-specific security rules │
│ └── TFLint → syntax, naming conventions, deprecated syntax │
│ Findings posted as PR comments. HIGH severity = pipeline fail│
│ │
│ Stage 3: Human Review │
│ └── Required approval from infra team (not the squad itself) │
│ │
│ Stage 4: Apply │
│ └── terraform apply (only after merge to main) │
│ State stored in S3 + DynamoDB locking │
└────────────────────────┬─────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ AWS (Multi-Account) │
│ │
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌─────────────┐ │
│ │ Dev │ │ Staging │ │ Prod │ │ Security │ │
│ │ Account │ │ Account │ │ Account │ │ Account │ │
│ │ │ │ │ │ │ │ │ │
│ │ GuardDuty │ │ GuardDuty │ │ GuardDuty │ │ Security Hub│ │
│ │ enabled │ │ enabled │ │ enabled │ │ aggregates │ │
│ └───────────┘ └───────────┘ └───────────┘ │ all findings│ │
│ └─────────────┘ │
└──────────────────────────────────────────────────────────────────┘
```
## 内置在每个模块中的安全默认设置
模块编码了安全决策,因此团队无需反复思考:
**S3 模块默认设置:**
- 禁止所有公共访问(启用)
- 服务器端加密(AES-256 或 KMS)
- 启用版本控制
- 启用访问日志
- 生命周期规则用于成本管理
**IAM 模块默认设置:**
- 最小权限原则——模块暴露特定权限集合,而非 `*`
- 管理策略中不包含通配资源
- 对敏感操作强制使用 MFA 的条件键
**VPC 模块默认设置:**
- 启用并导出到 S3 的流量日志
- 无默认安全组规则
- 配置保守的网络 ACL
**EKS 模块默认设置:**
- 私有 API 端点
- IRSA(用于服务账户的 IAM 角色)——节点级凭证
- 对静态密钥使用信封加密
## 密钥管理:Sealed Secrets
在使用 Sealed Secrets 之前,工作流程如下:
1. 创建 Kubernetes Secret 清单
2. 清单包含 Base64 编码的值
3. 清单无法提交到 Git(凭证为明文)
4. 密钥手动应用——无审计追踪
使用 Sealed Secrets 后:
1. 创建包含真实值的 Kubernetes Secret 清单
2. 运行 `kubeseal`——使用集群公钥加密值
3. 将加密后的清单提交到 Git——密文可安全存储
4. ArgoCD 应用该加密清单——控制器使用私钥解密
5. 完整的 GitOps、完整的审计追踪、仓库中零明文凭证
## 结果
| 指标 | 之前 | 之后 |
|------|------|------|
| 每个基础设施 PR 的安全发现 | 在生产中发现 | 在 CI 中发现(平均每个 PR 阻止 2.3 个发现) |
| 仓库中的明文密钥 | 多次发现 | 零(Sealed Secrets 强制执行) |
| 基础设施审计追踪 | 不完整(控制台 + 本地应用) | 完整(每个变更都在 Git 中) |
| 配置标准组件所需时间 | 小时(每个团队独立定制) | 分钟(模块引用) |
| 团队间的模块版本漂移 | 持续发生 | 受控(显式版本约束、升级 PR) |
| GuardDuty 覆盖范围 | 部分(部分账户) | 100% 账户覆盖 |
## 改进方向
- **使用 OPA/Conftest 处理自定义策略。** Checkov 和 tfsec 很好地覆盖了通用安全规则。组织特定策略(命名约定、强制标签、批准的 AMI 列表)更适合用 OPA Rego 表达,并通过 Conftest 运行。我们后来才加入此功能,真希望一开始就采用。
- **从一开始就进行漂移检测。** 即使使用 GitOps,手动控制台变更仍会发生。AWS Config 搭配漂移检测规则可在变更累积前捕获带外变更。中途加入此功能需要回溯性修复。
- **模块发布流程需要变更日志。** 当模块更新时,使用方需要了解变更内容和原因。遵循“保持变更日志”规范的语义化版本管理能让升级变得可预测。
## 作者
**Edilson Athayde Junior** · 数据平台架构师
[LinkedIn](https://linkedin.com/in/edilson-athayde-junior) · [Medium](https://medium.com/@edilsonathaydejunior)
标签:AWS, Base64, DPI, EC2, ECS, GitOps, IaC, IAM策略, Kubernetes Secrets, Terraform, 代码审查, 公开S3桶, 共享标准, 加密, 基础设施一致性, 多账户, 安全债务, 安全可观测性, 安全扫描, 审计追踪, 开源框架, 持续交付, 持续集成, 时序注入, 明文密钥, 权限管理, 模块化, 模块复用, 模块漂移, 模型越狱, 漏洞扫描器, 版本控制, 生产环境安全, 结构化查询, 自动化安全