nik129linux/aws-secure-landing-zone
GitHub: nik129linux/aws-secure-landing-zone
一个用 Terraform 构建并经过真实 AWS 环境验证的 AWS 单账户安全基线项目,涵盖网络、存储、加密、审计、IAM 等安全控制模块,并附带 Python 安全工具与详尽文档。
Stars: 0 | Forks: 0
# aws-secure-landing-zone
一个安全的 AWS 账户基线,从头开始使用 Terraform 构建,并使用我编写的 Python 工具进行了审计。每个 control 都被部署到了真实的 AWS 账户中,针对 API 进行了在线验证,并记录了我在此过程中遇到的所有 bug。
**实话实说的工作范围:** 这是一个 **单账户** 安全基线。AWS 意义上完整的“landing zone”意味着 Organizations、多账户、集中式日志记录和 SCP —— 该扩展已列入计划,但尚未构建。这里的内容都是已经部署并验证过的。
属于 142 天的 Cloud Security Engineering 路线图的一部分。目前处于第 24 天。
## 实际包含的内容
| 路径 | 具体内容 |
|---|---|
| `terraform/01-backend` | S3 上的 Remote state,带有原生 state locking (`use_lockfile`),无 DynamoDB |
| `terraform/02-network` | 跨越 2 个 AZ 的 VPC,public/private subnets,IGW,显式 route tables,最小化的 security groups。**经过深思熟虑的 NO-NAT 决策**,已记录在案 |
| `terraform/03-endpoints` | S3 Gateway Endpoint + 显式的私有 route tables —— private subnets 无需 egress 即可访问 S3 |
| `terraform/04-ec2-ssm` | EC2,**没有 SSH 也没有 key pair**。仅限 SSM Session Manager,强制执行 IMDSv2 |
| `terraform/05-s3-baseline` | Block Public Access(全部四个选项),`BucketOwnerEnforced`,SSE-S3,versioning,拒绝非 TLS 请求的 bucket policy,并将 access logging 记录到单独的 bucket |
| `terraform/06-kms` | Customer-managed key + alias,带有 **显式** key policy(不是隐式默认值),在 apply 之前检查了锁定风险 |
| `terraform/07-cloudtrail` | 带有日志文件验证的 Multi-region trail;log bucket policy 包含针对 confused-deputy 的 `aws:SourceArn` condition |
| `terraform/08-cloudwatch-logs` | CloudWatch Logs sink + service role,其 **trust policy** 包含 confused-deputy condition |
| `terraform/09-cloudwatch-alarms` | 针对 root 使用和未启用 MFA 的控制台登录的 Metric filters,连接到带有已确认邮件订阅的 SNS |
| `terraform/10-boto3` | 第一个 boto3 脚本 —— 分页的 S3 清单,`python/security-utils` 的雏形 |
| `iam/basics` | Least-privilege role + policy,permission boundary,IAM Access Analyzer 发现结果 |
| `python/security-utils` | 两个安全工具,见下文 |
| `docs/` | ADRs,架构图,Zero Trust 清单,以及 AI-security 审查 |
尚不存在的目录(`setup/`、`detections/`、`playbooks/`、`ir/`)未在此处列出。它们将随着构建它们的路线图日期的到来而出现。
## Python 安全工具
两者均基于 stdlib 和 boto3,经过测试,并针对该账户的真实数据运行。
**`s3_inventory.py`** —— 通过 paginator 枚举每个 bucket,并报告 Block Public Access 和加密态势。在每个 bucket 上容忍 `AccessDenied` 错误,而不是在遇到第一个错误时就崩溃。
**`cloudtrail_normalize.py`** —— 读取 gzipped 的 CloudTrail 日志,并将每个 event 展平为 7 个字段的类 OCSF schema(`time · actor · source_ip · event_name · resource · outcome · error_code`),导出 JSONL 和 CSV。具有 `--dry-run`,跳过并统计不可用的记录,并按名称捕获读取错误,这样一个坏文件就不会导致运行中止。
针对 **308 个真实日志文件,1203 个 events,跳过 0 个** 运行。
```
cd python/security-utils
python -m venv .venv && ./.venv/bin/pip install -r requirements.txt
./.venv/bin/python s3_inventory.py
./.venv/bin/python cloudtrail_normalize.py --dry-run --input
./.venv/bin/python -m pytest -q # 10 tests
./.venv/bin/ruff check . && ./.venv/bin/ruff format --check .
```
注意:测试会导入 `botocore`,因此请从 venv 中运行它们,而不是系统 Python。
## 来自我自己的账户的发现
这些是由上述工具在针对该账户的真实状态和审计追踪运行后得出的结果 —— 而非来自教程:
1. **账户级别的 S3 Public Access Block 未配置。** `GetAccountPublicAccessBlock` 在 trail 中反复返回 `NoSuchPublicAccessBlockConfiguration`。对于所有四个 bucket,bucket 级别的 BPA *确实* 处于开启状态 —— 这是一个不同的 control:bucket 级别保护你已配置的 bucket,账户级别是涵盖尚不存在 bucket 的后盾。一个工具读取实时 API 状态,另一个读取日志历史;单靠任何一个都无法发现此问题。
2. **审计日志 bucket 没有 Object Lock 和 lifecycle rule** —— 证据存储本身可被删除,且日志在累积中没有任何过期机制。
3. **65% 的日志量是 CloudTrail 在读取它自己的 bucket**(1203 个 events 中的 789 个),相比之下只有 53 个人工 events。这为 SIEM 工作提供了一个真实的信噪比输入,从我自己的数据中挖掘出来。
## 部署
每个 stack 都是独立的,并且持有 **各自独立的 state key**。这种隔离正是目的所在:在一个 stack 中的失误不会破坏另一个。
```
cd terraform/02-network
terraform init -backend-config=backend.hcl
terraform fmt && terraform validate
terraform plan # read it before applying, every time
terraform apply
```
`terraform/01-backend` 是个例外 —— 它创建了 state bucket,因此它在迁移之前使用 local state 进行引导。
一些 stack 会通过 `terraform_remote_state` 读取另一个 stack 的 outputs,因此顺序很重要:
`01` → `02` → `03`/`04`/`05` → `06` → `07`/`08` → `09`。
## 销毁
```
terraform destroy # from each stack directory, reverse order
```
首先销毁 `09` → `08` → `07`:较晚的 stack 依赖于较早的 stack,并且 CloudTrail 的 log bucket 启用了 versioning,因此在删除 bucket 之前必须清除 object versions。
## 成本触发器
验证总计:**~$1.80/月。**
| 项目 | 成本 | 说明 |
|---|---|---|
| KMS customer-managed key | ~$1/月 | 唯一保证的经常性费用。删除 CMK 使其归零。 |
| NAT Gateway | **$0** | 刻意不构建 —— 而是使用 S3 Gateway Endpoint。这是 AWS 实验环境(~$32/月)中最大的单一成本陷阱。 |
| EC2 | 停止时 $0 | 使用后停止;否则属于免费套餐 |
| S3, CloudTrail, CloudWatch, SNS | 几分钱 | 一个 trail 上的管理事件是免费的;日志量很小 |
在离开会话之前:EC2 已停止,无 NAT gateway,无未关联的 Elastic IP。
## 证据与诚实
每日的证据文件(构建了什么,在线验证了什么,发现并修复的每一个 bug)位于 `_private/` 中,并且 **被刻意进行了 gitignored** —— 它们包含真实的账户 ID、ARNs、bucket 内容和日志摘录。发布它们将会破坏此 repo 所展示的 control。如有需要,可以在打码后提供。
这个 repo 并不掩饰两件事:
- **这些 bug。** 在构建过程中出现了 40 多个真实的 bug,包括一个 `backend.hcl` 的复制粘贴错误,如果当时被 apply,将会摧毁整个 CloudTrail stack,幸好在运行 `init` 之前被捕获。
- **哪些未经验证。** 当一个 control 已部署但尚未通过在线测试证明时,证据会如实说明,而不是仅仅因为 `terraform apply` 正常退出(zero)就宣称成功。
提交前会运行 `gitleaks`;fixtures 使用了保留的账户 ID `123456789012`。
## AI 安全
`docs/ai-security/owasp-llm-top10.md` 审查了我构建的一个 WhatsApp AI agent(n8n,26 个节点,LLM 加上 Google Sheets)是否符合 OWASP LLM Top 10 —— 将发现结果追踪到了具体的工作流节点,包括一个通过攻击者控制的 WhatsApp 个人资料名称进行 prompt-injection 的向量。在该 agent 处理任何实时流量之前编写。
## 技术栈
AWS · Terraform · Python (boto3, pytest, ruff) · CloudTrail · CloudWatch · KMS · IAM Access Analyzer · SSM
## 目标职位
IAM Analyst · SOC Analyst L1 (Cloud SOC) · Junior Cloud Security Engineer
标签:AWS, DevSecOps, DPI, ECS, Python, Terraform, 上游代理, 安全基线, 安全规则引擎, 教学环境, 无后门, 逆向工具