1. 敏感数据与 sensitive
一句话总结: sensitive 只是「显示层打码」,不改变存储与传输;真正要防泄密必须配合密钥管理、加密 backend 与不落盘原则,三层缺一不可。
output { sensitive = true } 能让 apply 后的终端不打印明文,但 state 里仍然以明文存储,plan/日志中也可能通过其他路径泄露。把它当作「第一道防线」,而不是全部。
variable "db_password" {
type = string
sensitive = true # 禁止在 plan 中显示默认值
}
resource "aws_db_instance" "db" {
password = var.db_password
}
output "db_password" {
value = aws_db_instance.db.password
sensitive = true
}
1.1 sensitive 的边界
| 场景 | 是否受保护 | 说明 |
|---|---|---|
| apply 终端回显 | 是 | 输出打码 |
| state 文件 | 否 | 明文存储 |
terraform output 直接读 | 是 | 打码 |
| 日志 / debug | 否 | TF_LOG 可能打出 |
| plan 产物二进制 | 否 | 可反解析 |
# 查看 state 中的敏感字段(会明文)
terraform state show aws_db_instance.db
# 或直接查 backend 桶中的文件
aws s3 cp s3://my-infra-tfstate/prod/db/terraform.tfstate - | jq .outputs
一句话:sensitive 保护「人的眼睛」,不保护「文件的存储」,真正的机密必须交给密钥管理系统。
2. 密钥管理与加密
一句话总结: 密钥用 Vault 或云平台托管服务(KMS/SSM Secrets Manager)保存,Terraform 运行时从环境注入;backend 存储与传输默认加密,敏感字段尽量不进 state。
2.1 密钥来源与注入
# 从环境变量读取(CI Secret / 本地 shell 注入)
variable "db_password" {
type = string
}
provider "aws" {
region = var.region
}
# 从 Vault 获取后注入环境变量
export DB_PASSWORD=$(vault kv get -field=password secret/team/db)
terraform apply -var="db_password=$DB_PASSWORD"
2.2 加密矩阵
| 层次 | 加密方式 | 说明 |
|---|---|---|
| state 传输 | TLS | 默认 |
| state 存储 | S3 SSE-S3 / KMS | backend encrypt = true |
| 密钥本身 | Vault / KMS / SSM | 托管轮换 |
| 提交仓库 | git-secrets / gitleaks | 拦截误提交 |
terraform {
backend "s3" {
bucket = "my-infra-tfstate"
key = "prod/db/terraform.tfstate"
region = "ap-northeast-1"
encrypt = true
# kms_key_id = "arn:aws:kms:...:key/xxx" # 指定 KMS 客户密钥
}
}
# 提交前扫描密钥
gitleaks detect --source ./
一句话:云平台托管密钥服务自带轮换与审计,比「把密钥写进变量文件再加密提交」更省心也更安全。
3. 权限最小化
一句话总结: 谁运行 Terraform,就给谁「刚好够用」的权限:state 桶最小读写、资源权限按动作裁剪、流水线角色不赋予删除权限。
3.1 权限分层
| 角色 | 权限 | 说明 |
|---|---|---|
| 开发者 | plan + 读 state | 不触发 apply |
| 流水线 | plan + 有限 apply | 按环境裁剪 |
| 管理员 | 全部 | 带 MFA/批准 |
| state 桶 | 只读/只写对应 key | 最小化 |
3.2 最小权限 IAM 示例
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:DescribeInstances",
"ec2:RunInstances",
"ec2:TerminateInstances"
],
"Resource": "*",
"Condition": {
"StringEquals": { "aws:RequestedRegion": "ap-northeast-1" }
}
},
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::my-infra-tfstate/*"
}
]
}
| 裁剪点 | 做法 |
|---|---|
| 区域限制 | Condition 限定 region |
| 资源限定 | Resource 指向具体桶/资源 ARN |
| 动作裁剪 | 只给 plan/apply 需要的动作 |
| 删除保护 | 生产环境禁止 delete 类动作 |
# 资源级的删除保护
resource "aws_s3_bucket" "critical" {
bucket = "critical-data"
force_destroy = false
}
resource "aws_instance" "web" {
disable_api_termination = true # 防止误删
}
一句话:权限最小化的检验标准是「用最小角色跑通整个流水线」,多一个动作都意味着扩大爆炸半径。
4. 策略即代码
一句话总结: Policy as Code 用 Sentinel/OPA/Conftest 在 plan 阶段强制合规:标签必须、网段必须私有、禁止公开暴露,不合规直接阻止 apply。
4.1 策略工具对比
| 工具 | 语言 | 集成点 |
|---|---|---|
| Sentinel | HCL 类似语法 | Terraform Cloud |
| OPA | Rego | 任意 plan 输出 |
| Conftest | Rego | OPA + tfplan |
| Checkov | Python 规则 | 静态扫描 |
4.2 Conftest 示例
# policy/bucket.rego
package main
deny[msg] {
input.resource_changes[_].change.actions[_] == "create"
resource := input.resource_changes[_]
resource.type == "aws_s3_bucket"
resource.change.after.force_destroy == true
msg := "禁止对 S3 桶启用 force_destroy"
}
deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_security_group"
rule := resource.change.after.ingress[_]
rule.cidr_blocks[_] == "0.0.0.0/0"
rule.from_port == 22
msg := "禁止对 22 端口开放 0.0.0.0/0"
}
# 把 plan 转成 JSON 后交给 Conftest
terraform show -json tfplan.binary | conftest test -
4.3 内置到流水线
- name: Policy check
run: |
terraform show -json tfplan.binary | conftest test - --policy policy/
| 策略类别 | 示例规则 |
|---|---|
| 标签合规 | 所有资源必须有 owner 标签 |
| 网络合规 | 禁止公开 SSH 端口 |
| 存储合规 | 禁止 force_destroy、必须加密 |
| 成本合规 | 禁止超大实例类型 |
一句话:把策略当成「测试一样必跑」的环节,放在 plan 之后、apply 之前,是安全兜底的自动化手段。
5. 供应链安全
一句话总结: Terraform 的供应链风险来自 Provider/Module 来源不可信与 lockfile 缺失,锁定版本 + 校验哈希 + 私有 Registry 是三道防线。
5.1 风险面
| 环节 | 风险 | 缓解 |
|---|---|---|
| Provider | 恶意/仿冒包 | 官方 Registry + 校验 |
| Module | 来源不明 | 私有 Registry / 锁定版本 |
| CLI 工具 | 插件投毒 | 校验和 |
| Registry 账号 | 被篡改 | 开启签名 |
5.2 lockfile 与校验
# 生成并提交 lockfile
terraform init
git add .terraform.lock.hcl
# 校验 Provider 完整性
terraform providers lock -platform=linux_amd64
# .terraform.lock.hcl 示例
provider "registry.terraform.io/hashicorp/aws" {
version = "5.40.0"
hashes = [
"h1:xxx...",
]
}
| 防线 | 说明 |
|---|---|
| required_providers 锁定 | 版本范围 |
| lockfile 哈希 | 防篡改 |
| 私有 Registry | 内部模块审计 |
| 依赖扫描 | Dependabot/Renovate 跟踪 Provider 版本 |
一句话:把
.terraform.lock.hcl提交进仓库,并开启依赖更新机器人,供应链风险就变成了「可审查的版本变更」。
6. 审计与合规
一句话总结: 让每次 plan/apply 都留下「谁、何时、改了哪、为什么」的审计记录,配合 AWS CloudTrail 等平台审计与定期合规扫描,才能满足监管要求。
6.1 审计留痕
# 1. 记录每次 apply 的操作者与产物
echo "$GITHUB_ACTOR $GITHUB_SHA $(date)" > apply-log.txt
# 2. 把 plan 产物归档到审计桶
aws s3 cp tfplan.binary s3://audit-bucket/$(date +%Y%m%d)/tfplan.binary
# 3. 记录变更摘要
terraform show -json tfplan.binary | jq '.resource_changes[] | {address, actions: .change.actions}' > audit.json
6.2 合规矩阵
| 合规点 | 手段 |
|---|---|
| 变更审计 | 流水线日志 + plan 产物归档 |
| 云平台审计 | CloudTrail / Activity Log |
| 配置漂移 | 定期 terraform plan 巡检 |
| 标签合规 | 策略即代码 |
| 密钥轮换 | 托管服务 + 到期提醒 |
# 定期巡检:每周跑一次 plan 并对比
terraform plan -detailed-exitcode
# exit code 2 → 存在漂移,触发告警
一句话:审计不是「事后翻日志」,而是「每次变更都自动留痕」,让任何一次误操作都能定位到人、时间和具体动作。
7. 常见安全坑
一句话总结: 安全事故大多不是工具缺陷,而是「敏感信息落盘」「权限过大」「供应链失控」「策略缺失」这四类习惯性失误。
| 坑 | 后果 | 对策 |
|---|---|---|
| 密钥写进 tfvars 入库 | 凭据泄露 | gitleaks + Secret 注入 |
| 以为 sensitive 加密了 | 误以为安全 | 配合 KMS/backend 加密 |
| 流水线角色权限过大 | 误删全账号 | 最小权限 + 删除保护 |
| 不提交 lockfile | 供应链漂移 | 提交 lockfile |
| 无策略检查 | 合规漂移 | 策略即代码 |
| 生产无门禁 | 误删误改 | 人工批准 + delete 告警 |
# 一键自查:扫描仓库中的疑似密钥
gitleaks detect --source ./
# 检查 state 桶是否开启加密
aws s3api get-bucket-encryption --bucket my-infra-tfstate
一句话:把「安全自查」变成流水线里的一个必过步骤(gitleaks + Checkov + Conftest),人类失误就会被自动化拦住。
8. 总结
Terraform 安全加固不是单一工具,而是「标记、存储、权限、策略、供应链、审计」六层纵深:
| 环节 | 要点 |
|---|---|
| sensitive | 打码显示,不改变存储 |
| 密钥管理 | Vault / KMS / SSM 托管轮换 |
| 加密 | backend encrypt + 托管密钥 |
| 权限 | 最小化,区域/资源/动作裁剪 |
| 策略即代码 | Sentinel / OPA / Conftest 强约束 |
| 供应链 | 锁版本 + lockfile + 私有 Registry |
| 审计 | 每次变更留痕 + 平台审计 + 巡检 |
一句话收尾:把安全当成流水线的默认环节,而不是事后补救,Terraform 这套 IaC 体系才真正值得被生产环境托付。
延伸阅读
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。