4. 基础设施即代码安全工程

基础设施即代码(IaC)把云资源的创建从控制台点击变成了可版本化、可评审、可复现的代码。Terraform 的 HCL、CloudFormation 的 YAML/JSON 模板、Pulumi 的多语言程序,本质上都是“期望状态的声明”。这带来一个关键安全机会:在资源真正被创建之前,就能对配置做策略检查。IaC 安全工程的核心,不是等云上出现一个公开的 S3 桶再去告警,而是在 terraform plan 或模板校验阶段就阻断它。

要理解 IaC 安全,先要理解 IaC 的执行链路。以 Terraform 为例,工程师编写 .tf 文件,执行 terraform init 拉取 provider,执行 terraform plan 生成执行计划,最后 terraform apply 调用云 API 创建资源。策略检查可以嵌入在多个位置:编辑时的 IDE 插件、提交时的 pre-commit 钩子、CI 中的静态扫描、plan 后的策略评估,以及 apply 前的准入控制。越靠前,修复成本越低;越靠后,越接近“最后一道闸门”。

IaC 执行链路中的策略检查嵌入点 越靠左修复成本越低,越靠右越接近阻断闸门 编写 IaC .tf / YAML / 程序 init / 模板校验 拉取 provider plan / 变更预览 生成执行计划 apply / 创建资源 调用云 API 云上资源 运行态 IDE 实时提示 编辑即反馈 成本最低 pre-commit 提交前拦截 本地快速 CI 静态扫描 Checkov / tfsec 全量规则 plan 策略评估 OPA / Sentinel 看真实变更 准入控制 apply 前阻断 最后闸门 违规 → 阻断并回退修改 策略检查关注的三类对象 资源属性:加密、公开访问、日志、版本控制、删除保护 关系与拓扑:安全组与子网、IAM 信任策略、跨账户引用 变更语义:plan 中的 create / update / destroy 例外与豁免:带过期时间的策略豁免,避免永久放行

图中把“执行链路”和“策略检查层”分开画,是为了强调一个工程事实:策略检查不是单一工具,而是一组分布在不同阶段的控制点。IDE 插件和 pre-commit 负责快速反馈,CI 静态扫描负责全量规则覆盖,plan 后的策略评估负责看真实变更,准入控制负责最后阻断。四层配合,才能既快又稳。

策略即代码:把合规要求写成可执行规则

IaC 安全的关键转变,是把“安全要求”从文档变成“策略即代码”。以 OPA(Open Policy Agent)的 Rego 为例,一条禁止 S3 桶公开访问的策略可以写成:

package terraform.s3

deny[msg] {
  resource := input.resource_changes[_]
  resource.type == "aws_s3_bucket_acl"
  resource.change.after.acl == "public-read"
  msg := sprintf("S3 桶 %s 不允许 public-read ACL", [resource.address])
}

deny[msg] {
  resource := input.resource_changes[_]
  resource.type == "aws_s3_bucket"
  not resource.change.after.server_side_encryption_configuration
  msg := sprintf("S3 桶 %s 未配置服务端加密", [resource.address])
}

这段策略的输入是 terraform plan 转换成的 JSON(通过 terraform show -json plan.out 得到),检查的是 resource_changes 中每个资源的 after 状态。注意它检查的是“变更后”的期望状态,而不是当前代码文本,因此能覆盖变量、模块和条件表达式展开后的真实结果。这正是 plan 阶段策略评估比纯文本扫描更强的地方。

Checkov 和 tfsec 则采用内置规则库加自定义规则的路线。以 Checkov 为例,一条自定义 YAML 策略可以检查 RDS 实例是否开启删除保护:

metadata:
  name: "RDS 必须开启删除保护"
  id: "CKV_CUSTOM_001"
definition:
  cond_type: attribute
  resource:
    - aws_db_instance
  attribute: deletion_protection
  operator: equals
  value: true

把这类规则放进 CI,配合 checkov -d . --framework terraform --external-checks-dir ./policies,就能在合并请求阶段给出通过或失败。失败时输出会指出具体文件、行号和规则 ID,工程师据此修改。

从“能扫”到“能阻断”:分级与豁免

实际落地时,最大的阻力往往不是工具不会用,而是“一上来全阻断”导致业务无法推进。合理的做法是按严重级别分级:高危规则(公开存储、无加密、宽泛 IAM)设为硬阻断,中危规则设为告警并记录,低危规则仅提示。同时必须提供带过期时间的豁免机制,例如在代码中标注:

# checkov:skip=CKV_AWS_20: 该桶用于静态网站托管,已通过 CloudFront 限制访问,豁免至 2025-12-31
resource "aws_s3_bucket" "site" {
  bucket = "example-static-site"
}

豁免必须包含理由和到期时间,并纳入定期审计。没有到期时间的豁免,等同于永久关闭了这条规则。

一个可执行的检查闭环

把上述内容串成一个最小闭环:工程师提交 Terraform 代码 → CI 执行 terraform init 和 terraform plan -out=plan.out → terraform show -json plan.out > plan.json → OPA 用 opa eval -d policies/ -i plan.json "data.terraform.deny" 评估 → 若 deny 非空则 CI 失败并输出违规信息 → 工程师修改后重新提交。这个闭环的价值在于:违规配置在进入云环境之前就被拦住,而不是在运行态被扫描出来。

IaC 安全不是替代运行时安全,而是把防线前移。运行态扫描发现的是“已经发生的问题”,IaC 策略检查阻止的是“即将发生的问题”。两者结合,才构成完整的云原生应用保护链路。

最后要强调,IaC 策略检查的规则不是越多越好,而是要与组织的实际风险对齐。建议从 10 到 20 条高危规则起步,覆盖存储公开、加密缺失、网络暴露、权限过宽、日志关闭这几类高频问题,稳定运行后再逐步扩展。规则本身也要版本化管理,和 IaC 代码一起评审、一起发布。