「策略与合规门禁」

在 Terraform 工作流中落地策略与合规门禁:Sentinel/OPA 策略编写、terrascan 静态扫描、checkov 补充,以及 CI 门禁与审批流设计。

1. 为什么需要策略门禁

一句话总结: 策略门禁把「代码评审才发现的规范问题」提前到「资源创建之前拦截」,是 IaC 安全治理从被动审计走向主动防线的关键一步。

terraform plan 只回答「会变成什么」,不回答「是否允许」。合规门禁在 plan 与 apply 之间加一道闸:违反策略就拒绝合并或拒绝执行。常见违规如「存储桶公开可读」「未启用加密」「权限过宽」,人工评审成本高且易漏。

# 门禁在流水线中的位置:plan → 策略校验 → 人工/自动批准 → apply
terraform plan -out=plan.tfplan
terraform show -json plan.tfplan > plan.json
# 策略引擎对 plan.json 做判定

策略引擎分两类:在 Terraform Cloud 内跑的 Sentinel,与 独立运行的 OPA/Rego、terrascan。前者绑定平台,后者可嵌入任意 CI。

一句话:门禁不是「额外的检查」,而是把安全基线固化成可执行代码,让每个 plan 都过一遍规则。

2. Sentinel 策略编写

一句话总结: Sentinel 是 HashiCorp 的策略即代码语言,面向 tfplan 的 mock 数据做判定,策略文件以 allowed/denied 结果驱动门禁。

Sentinel 直接消费 Terraform Cloud 的 plan 数据。策略文件由规则(rule)组成,规则返回布尔值,最终 main = rule 决定通过与否。

# sentinel.hcl 注册策略入口
policy "require_encryption" {
  source            = "./require-encryption.sentinel"
  enforcement_level = "hard-mandatory"
}
# require-encryption.sentinel
import "tfplan"

config = tfplan.module_paths

main = rule {
  all tfplan.resources.aws_s3_bucket as _, bucket {
    all bucket.change.after as attr, value {
      attr not in ["server_side_encryption_configuration"] or
        value != null
    }
  }
}

2.1 Sentinel 判定逻辑

Sentinel 的 all ... as ... { ... } 遍历资源集合,配合 enforcement_level 决定硬拦截还是仅告警。

enforcement_level行为
advisory只记录不阻断
soft-mandatory记录并提示,人工可放行
hard-mandatory直接阻断 apply
policy "no_public_sg" {
  source            = "./no-public-sg.sentinel"
  enforcement_level = "soft-mandatory"
}

一句话:Sentinel 强在「与 tfplan 原生集成」,弱在「只能在 Terraform Cloud 里跑」,本地/自托管 CI 更适合 OPA。

3. OPA / Rego 策略编写

一句话总结: OPA 是通用策略引擎,Rego 是其查询语言,把 tfplan JSON 喂给 OPA,用 deny 规则表达「什么情况不允许」。

OPA 与引擎解耦:任何 CI 都能调用 opa eval。把 terraform show -json 的输出作为 input,策略文件用 Rego 编写。

# policy/main.rego
package terraform

import rego.v1

deny[msg] {
  resource := input.resource_changes[_]
  resource.type == "aws_s3_bucket"
  resource.change.after.bucket == "*"
  msg := sprintf("存储桶 %s 名称过宽", [resource.address])
}

deny[msg] {
  resource := input.resource_changes[_]
  resource.type == "aws_security_group"
  resource.change.after.ingress[_].cidr_blocks[_] == "0.0.0.0/0"
  msg := sprintf("安全组 %s 对公网开放", [resource.address])
}

3.1 用 opa eval 校验

opa eval --data policy/main.rego --input plan.json \
  "data.terraform.deny"

# 期望输出空数组表示通过
opa test policy/

Rego 的关键是「规则返回条件集合」:deny 规则一旦有成员就说明存在违规,返回结果直接供 CI 判定。

一句话:OPA 把策略从「某平台的私有格式」变成「通用可移植的 Rego」,一套策略可同时服务 Terraform、K8s、服务网格。

4. Terrascan 静态扫描

一句话总结: Terrascan 直接扫 IaC 源码而非 plan,内置数百条安全基线,适合「编写期」的快速反馈,可接入 pre-commit 与 CI。

Terrascan 不依赖云账号,读取 HCL 源码就出报告,缺陷是拿不到插值后的真实值,适合做静态基线。

terrascan scan -i terraform -d ./

# 指定规则与输出格式
terrascan scan -i terraform -d ./modules \
  -o json > terrascan-report.json

# 只看违规列表
terrascan scan -i terraform -d ./ | \
  jq '.results.violations[] | {resource: .resource_name, rule: .rule_name}'

4.1 接入 pre-commit

在提交前拦下明显违规,把反馈前置到最便宜的阶段。

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/tenable/terrascan
    rev: v1.18.3
    hooks:
      - id: terrascan
        args: ["scan", "-i", "terraform"]

一句话:Terrascan 是「源码级体检」,Sentinel/OPA 是「计划级裁决」,两者互补而非替代。

5. Checkov 与 Conftest 补充

一句话总结: Checkov 用 Python 与 Graph 框架扫描云资源与 Dockerfile,Conftest 用 OPA 校验配置,多工具叠加提高覆盖但不增加心智负担。

Checkov 覆盖面广(含 AWS/Azure/GCP/K8s/Dockerfile),自带数百条内置检查,也支持自定义 Python 检查。

# custom-check.py:禁止 MySQL 使用过旧版本
from checkov.terraform.checks.resource.base_resource_check import BaseResourceCheck

class MysqlVersion(BaseResourceCheck):
    def __init__(self):
        super().__init__(
            name="MySQL 版本不得低于 8.0",
            id="CKV_CUSTOM_001",
            supported_resources=("aws_db_instance",),
        )

    def scan_resource_conf(self, conf):
        version = conf.get("engine_version", [None])[0]
        return not (version and version.startswith("5."))

check = MysqlVersion()
checkov -f main.tf --custom-check custom-check.py
checkov -d ./modules --quiet --compact

Conftest 复用 OPA 引擎,用 Rego 校验任意配置格式,适合「Terraform + K8s YAML + Dockerfile」混合仓库的统一门禁。

# policy/terraform.rego 拒绝未加密的磁盘
package main

import rego.v1

deny[msg] {
  resource := input.resource_changes[_]
  resource.type == "azurerm_linux_virtual_machine"
  os_disk := resource.change.after.os_disk
  not os_disk.managed_disk_type
  msg := sprintf("VM %s 未指定托管磁盘类型", [resource.address])
}
conftest test main.tf -p policy/terraform.rego
# 通过时输出 PASS,违规时列出 deny 消息

一句话:工具不在多而在组合——Checkov 管广度、OPA 管定制、Terrascan 管源码反馈,流水线按「源码→计划」两级串联。

6. CI 门禁集成

一句话总结: CI 门禁把策略引擎串进流水线,plan 产物 → 策略校验 → 失败即阻断,GitHub Actions 上可用 OPA/Checkov 容器与审批保护。

一个典型 GitHub Actions 门禁:拉取代码后先跑静态扫描,plan 生成 JSON 后交给 OPA 判定,最后用 environment 审批保护 apply。

# .github/workflows/policy-gate.yml
name: policy-gate

on:
  pull_request:
    paths: ["**.tf"]

jobs:
  policy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3
      - name: Terraform Plan
        run: |
          terraform init
          terraform plan -out=plan.tfplan
          terraform show -json plan.tfplan > plan.json
      - name: OPA Policy Check
        uses: open-policy-agent/opa@v1
        with:
          args: eval --data policy/main.rego --input plan.json "data.terraform.deny"
# 本地等价流程
terraform init
terraform plan -out=plan.tfplan
terraform show -json plan.tfplan > plan.json
opa eval --data policy/ --input plan.json "data.terraform.deny"

6.1 环境级审批

apply 阶段用 GitHub environment 的 required reviewers 做人工放行,形成「策略自动拦截 + 关键变更人工审批」的双保险。

一句话:CI 门禁的价值在「自动且可复现」——同样的 plan 在任何机器上跑出同样的判定,杜绝「本地过、线上挂」。

7. 审批流与审计

一句话总结: 审批流解决「谁来放行」,审计解决「出了事查得清」,两者都要把策略版本与 plan 快照留存下来。

合规门禁只是起点,完整治理还要有:谁在什么策略版本下放行、plan 当时的完整内容、以及 apply 后的实际资源快照。

# 留存审计证据
jq -c '.resource_changes[].address' plan.json > changed-resources.txt
sha256sum plan.tfplan > plan.sha256
git tag "policy-approval-$(date +%Y%m%d-%H%M)"
环节留存物用途
策略版本策略仓库 tag回溯当时规则
计划快照plan.tfplan + sha256复核实际变更
审批记录PR 评论 / 审批人责任可追溯
应用记录apply 日志事后对账

一句话:审批与审计是「门禁的闭环」——没有审计的门禁只是形式,没有门禁的审计只是事后追责。

8. 总结

策略与合规门禁把「安全规范」从文档变成可执行代码:

工具定位接入点
SentinelTerraform Cloud 原生策略tfplan 数据
OPA/Rego通用策略引擎plan.json 任意 CI
Terrascan源码静态扫描pre-commit/CI
Checkov广度基线扫描源码/镜像
Conftest配置格式校验混合仓库
审批流人工放行关键变更apply 前
审计留存证据可追溯全程

一句话收尾:策略门禁不是「多买一个工具」,而是「把安全基线放进流水线的固定闸门」。从 Terrascan 的源码反馈,到 OPA 对 plan 的硬裁决,再到关键变更的人工审批,三级防线让「合规」成为每次发布默认发生的动作,而不是上线前的突击检查。

延伸阅读

继续阅读

探索更多技术文章

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

全部文章 返回首页

「terraform」更多文章

  1. Helm Provider 与应用发布:值注入与回滚
  2. 模块注册表与分发:版本、文档与测试
  3. DNS 与证书编排:托管区域与自动验证