「最佳实践与安全加固」

讲解 Terraform 安全加固:sensitive 标记、密钥管理与加密、IAM 权限最小化、策略即代码、供应链安全与审计合规,并给出常见安全坑与纵深防御清单。

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 / KMSbackend 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 策略工具对比

工具语言集成点
SentinelHCL 类似语法Terraform Cloud
OPARego任意 plan 输出
ConftestRegoOPA + tfplan
CheckovPython 规则静态扫描

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 体系才真正值得被生产环境托付。

延伸阅读

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「terraform」更多文章

  1. 「漂移检测与收敛」
  2. 「资源重构与迁移」
  3. 「数据源与远程数据读取」