Checkov Terraform Plan 扫描实战:在 apply 之前对计划文件执行 IaC 安全策略评估
2026/9/16 14:05:52 网站建设 项目流程

Checkov Terraform Plan 扫描实战:在 apply 之前对计划文件执行 IaC 安全策略评估

【免费下载链接】checkovPrevent cloud misconfigurations and find vulnerabilities during build-time in infrastructure as code, container images and open source packages with Checkov by Bridgecrew.项目地址: https://gitcode.com/GitHub_Trending/ch/checkov

Checkov 不仅可以直接扫描.tf源文件,还可以对terraform plan生成的 JSON 计划文件执行策略评估,从而在资源真正落地之前发现云资源配置错误与安全隐患。本文以仓库文档 Terraform Plan Scanning 为核心骨架,结合checkov/terraform/下的 plan 解析器与 runner 源码、单元测试,完整讲解 plan 扫描的命令流程、输出解读、生命周期检查忽略规则、基于__change_actions__/TF_PLAN_RESOURCE_CHANGE_KEYS的变更检测,以及--repo-root-for-plan-enrichment--deep-analysis的增强模式。读完本文,你将掌握一套可直接落地的"plan 即安全门禁"工作流,并理解 Checkov 内部如何把 JSON 形态的 plan 文件还原成可评估的资源配置。

为什么要扫描 Terraform Plan

Checkov 对 Terraform 的支持分为两条路径(见 plan_runner.py):

  • 静态源文件扫描:对.tf文件中声明的资源直接做策略评估;
  • Plan 文件扫描:对terraform plan导出为 JSON 的tfplan.json做评估,框架类型为terraform_planCheckType.TERRAFORM_PLAN)。

Plan 评估的价值在于它带有静态扫描所没有的依赖关系与求值上下文:plan 文件里已经完成了变量插值、模块展开、count/for_each 实例化以及data数据源解析,因此可以得出比裸.tf扫描更完整的结果。例如,某个属性在源码里是变量引用,静态扫描无从判断最终值,而 plan 文件里已经给出了具体值。

需要特别强调的是安全边界:plan 文件可能包含动态注入的实参(如密钥),原文档明确建议将 plan 扫描放在安全的 CI/CD 流水线环境中执行。这一设计在 runner 源码中也有呼应——plan_runner.py 定义了RESOURCE_ATTRIBUTES_TO_OMIT,对aws_db_instance.passwordaws_ssm_parameter.valueazurerm_storage_account.primary_access_keygoogle_kms_secret_ciphertext.plaintext等敏感字段进行输出脱敏,且run_block中通过omit_secret_value_from_checks从代码片段里抹掉密钥值再写入报告。

完整扫描流程:从 plan 到 checkov

第一步:生成 plan JSON 文件

原文档给出的标准流程是使用jq格式化输出(jq需预先安装,非硬性要求,仅让结果更美观):

terraform init terraform plan --out tfplan.binary terraform show -json tfplan.binary | jq > tfplan.json checkov -f tfplan.json

实际执行中(对应 Shell 环境)命令序列为:

terraform init terraform plan -out tfplan.binary terraform show -json tfplan.binary | jq > tfplan.json checkov -f tfplan.json

关键点说明:

  • terraform plan -out先把计划固化到二进制文件,保证terraform show读取的是同一份计划;
  • terraform show -json输出机器可读的 JSON 计划格式,其中包含planned_values(计划后的资源值)、resource_changes(每个资源的变更信息)、configuration(配置源码块)等区块;
  • checkov -f tfplan.json直接按文件扫描,也可以改用-d指定目录扫描(runner 支持root_folder模式)。

第二步:识别与解析机制(源码视角)

Checkov 只把满足特定条件的 JSON 文件当作 plan 来解析:

  1. 扩展名必须是.json:plan runner 的file_extensions = ['.json'](plan_runner.py);
  2. 内容必须带terraform_version字段:plan_utils.py 的create_definitions在目录扫描模式下会加载每个.json文件并检查content.get('terraform_version'),命中才加入待扫描列表;
  3. 必须同时包含terraform_versionplanned_values:tf_plan 上下文解析器 在解析后校验这两个键,缺失即判定不是合法 plan 并跳过。

真正把 JSON plan 转换为 Checkov 内部"资源配置字典"的是 plan_parser.py 的parse_tf_plan(第 507 行起):它把 plan 拆成providerresourcedata三类块,并通过_hclify把 plan 中"单值/列表/嵌套字典"的形态规整为 Checkov 策略引擎熟悉的"列表包裹"形态;同时递归处理child_modules_find_child_modules),因此嵌套模块生成的资源也能被正确扫描(对应测试 test_runner_child_modules / test_runner_nested_child_modules)。

第三步:输出示例

对一份含aws_s3_bucket的 plan 执行扫描,输出大致如下(沿用原文档示例):

checkov -f tf.json Check: CKV_AWS_21: "Ensure all data stored in the S3 bucket have versioning enabled" FAILED for resource: aws_s3_bucket.customer File: /tf/tf1.json:224-268 Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/s3-policies/s3-16-enable-versioning 225 | "values": { 226 | "acceleration_status": "", 227 | "acl": "private", 228 | "arn": "arn:aws:s3:::mybucket",

每条记录都给出检查 ID、检查名称、FAILED/PASSED 状态、资源地址(如aws_s3_bucket.customer)、plan 文件中的行号范围(File: /tf/tf1.json:224-268)以及对应的策略说明。这份输出同时会体现在 JSON、SARIF、JUnit 等报告格式中,便于接入 CI 门禁。仓库自带的 plan 扫描单元测试(test_plan_runner.py)使用tests/terraform/runner/resources/plan/tfplan.json等样例验证了failed/passed数量与资源地址的正确性,可作为你自建样例的参照。

自动忽略不适用于 Plan 的检查:lifecycle 类检查

由于 Terraform 的检查既用于普通模板也用于 plan 文件,部分检查在 plan 场景下不适用。原文档指出:它们评估的是lifecycle块,而lifecycle只在 CLI 上下文中相关,并不会存储进 plan 文件

因此以下 4 个检查在 plan 扫描中会被静默忽略:

  • CKV_AWS_217
  • CKV_AWS_233
  • CKV_AWS_237
  • CKV_GCP_82

源码印证:这些 ID 被硬编码在 plan_runner.py 的TF_LIFECYCLE_CHECK_IDS集合中,run_block扫描出结果后会执行if check.id in TF_LIFECYCLE_CHECK_IDS: continue直接跳过。以CKV_AWS_233("Ensure Create before destroy for ACM certificates")为例,其检查实现 ACMCertCreateBeforeDestroy.py 的get_inspected_key返回"lifecycle/[0]/create_before_destroy",依赖lifecycle块,因此在 plan 中必然无法评估。对应的单元测试test_runner_ignore_lifecycle_checks(test_plan_runner.py)直接断言对含 lifecycle 的 plan 报告failed_checks为空。

如果你自定义的检查同样依赖lifecycle语义,建议参考该集合,在 plan 场景下显式跳过,避免误报。

检测资源将被删除:__change_actions__

Plan 文件的核心价值之一就是回答"这次变更会做什么"。原文档说明:要判断某个资源是否会被删除或变更,可以通过属性名__change_actions__访问其 change actions 值(Terraform 的 change 表示法包含 create / read / update / delete / no-op 等动作)。

Python 检查示例

def scan_resource_conf(self, conf: dict[str, Any]) -> CheckResult: actions = conf.get("__change_actions__") if isinstance(actions, list) and "delete" in actions: return CheckResult.FAILED return CheckResult.PASSED

YAML 检查示例

cond_type: attribute resource_types: - aws_secretsmanager_secret attribute: __change_actions__ operator: not_contains value: delete

源码印证:__change_actions__正是 plan_parser.py 中常量TF_PLAN_RESOURCE_CHANGE_ACTIONS的值,由_prepare_resource_block(第 214 行)从 plan 的resource_changes[address]["change"]["actions"]写入每个资源的配置字典。仓库测试 test_runner_extra_check 使用resources/plan_with_deleted_resources样例验证了"删除资源检测":自定义检查CUSTOM_DELETE_1命中aws_secretsmanager_secret.default并在报告中带上自定义 details,证明该属性在真实 plan 数据上可稳定读取。

检测字段是否变更:TF_PLAN_RESOURCE_CHANGE_KEYS

除了资源本身的增删,有时还需要判断某个具体字段在这次变更中是否被修改。原文档说明:可以通过TF_PLAN_RESOURCE_CHANGE_KEYS(变更键列表)访问发生了变更的字段。

from checkov.terraform.plan_parser import TF_PLAN_RESOURCE_CHANGE_ACTIONS, TF_PLAN_RESOURCE_CHANGE_KEYS def scan_resource_conf(self, conf: dict[str, Any]) -> CheckResult: actions = conf.get(TF_PLAN_RESOURCE_CHANGE_ACTIONS) if isinstance(actions, list) and "update" in actions: if "protocol" in conf.get(TF_PLAN_RESOURCE_CHANGE_KEYS): return CheckResult.FAILED return CheckResult.PASSED

源码印证(plan_parser.py):

  • 常量定义在第 20-21 行:TF_PLAN_RESOURCE_CHANGE_ACTIONS = "__change_actions__"TF_PLAN_RESOURCE_CHANGE_KEYS = "__change_keys__"
  • _get_resource_changes(第 461 行起)遍历resource_changes,对每个资源逐字段对比beforeafter的值,凡是value != change_after.get(field)的字段都会追加进__change_keys__列表;
  • _prepare_resource_block(第 214-215 行)随后把这两个属性注入资源配置,供 Python/YAML 检查直接读取。

对应的集成测试 test_plan_change_keys 用resources/plan_change_keys/tfplan.json验证:aws_security_group_rule.foo因字段未变更而通过、aws_security_group_rule.bar因变更而失败,正好演示了"仅当某字段发生变更时才告警"这一精细的变更门禁场景。

结合 Terraform 源文件做 Plan 增强扫描

Plan 扫描可以结合 Terraform 源文件一起工作,以改进输出、支持 skip 注释并扩大覆盖范围。原文档也提醒:这会增加扫描耗时

Enrichment(富化模式)

使用--repo-root-for-plan-enrichment标志后,输出中的代码块与资源 ID 将来自Terraform 源文件,且 Terraform 文件中的 skip 注释会在 plan 扫描中被尊重。

checkov -f tfplan.json --repo-root-for-plan-enrichment /pathToTF/

Deep Analysis(深度分析)

--deep-analysis--repo-root-for-plan-enrichment组合使用,会把plan 文件扫描的图Terraform 源文件扫描的图合并。这样 Checkov 就能在 plan 文件信息不完整的地方补出图连接——例如locals在 plan 文件中并不携带其连接定义,而深度分析可以恢复这类连接,从而让依赖型(graph-based)检查得出更准确的结果。

checkov -f tfplan.json --repo-root-for-plan-enrichment /pathToTF/ --deep-analysis

源码层面的实现印证(plan_runner.py):

  • 属性self.deep_analysisself.repo_root_for_plan_enrichment分别取自 runner filter(第 113-115 行);
  • _should_run_deep_analysis(第 307-309 行)要求三个条件同时成立:deep_analysis开启、提供了 plan 富化根目录、且 plan 局部图已构建;
  • _create_terraform_graph(第 183-191 行)以render_variables=True从源目录构建 Terraform 图;
  • deep_analysis_plan_graph_manager.py 中的DeepAnalysisGraphManager负责合并:enrich_tf_graph_attributes按资源地址(__address__)把 plan 顶点属性并入 Terraform 图顶点、缺失的地址直接补挂顶点与边,filter_report再把报告裁剪到 plan 中真实出现的资源地址上,避免把 plan 之外的资源误报进结果。

测试 test_plan_and_tf_combine_graph 直观地展示了差异:对同一份resources/plan_and_tf_combine_graph数据,关闭深度分析时CKV2_AWS_6有 2 项失败,开启后全部通过——这正是"locals 连接被补全后误报消除"的效果。值得注意该测试还启用了环境变量CHECKOV_EXPERIMENTAL_CROSS_VARIABLE_EDGES=True,说明跨变量边属于实验性能力。

进阶机制:after_unknown 与 provider 解析

在源码级了解 plan 解析时,还有两个与扫描准确率直接相关的机制值得关注:

  • after_unknown 求值(plan_parser.py):plan 中部分属性在 apply 之前值未知(after_unknown标记)。开启环境变量EVAL_TF_PLAN_AFTER_UNKNOWN后,Checkov 会用常量TRUE_AFTER_UNKNOWN占位未知值——这样"检查属性是否存在"的策略可以通过,而"检查具体取值"的策略会失败,从而避免误判;复杂类型(list/dict)中已知与未知混合的情况也有专门的递归处理(_handle_complex_after_unknown)。注意该行为默认关闭,需要显式设置环境变量。
  • Provider 块解析_get_providers,第 410-458 行):plan 文件顶层的provider_config会被还原为带alias、region 等表达式的 provider 字典,因此像CKV_AWS_41(provider 凭据检查)这类检查也能在 plan 上运行,测试 test_plan_with_providers 已验证。

落地建议与边界说明

  1. 在 CI/CD 中运行:plan 文件含动态注入的敏感参数,务必在受控的流水线环境执行;Checkov 本身也会对报告中的已知敏感字段做脱敏。
  2. 生成与扫描使用同一份计划:务必terraform plan -out后立即terraform show -json,避免对过期或漂移的计划做门禁判断。
  3. 结合源文件增强:追求更准的代码位置、skip 注释支持时用--repo-root-for-plan-enrichment;需要图连接补全(locals、跨资源引用)时叠加--deep-analysis,并接受扫描耗时上升。
  4. 自定义变更门禁:用__change_actions__拦截删除高危资源、用TF_PLAN_RESOURCE_CHANGE_KEYS拦截敏感字段变更,是 plan 扫描独有的能力,静态扫描无法实现。

参考资源

  • 原文档:Terraform Plan Scanning
  • 核心实现:plan_parser.py(plan 解析与__change_actions__/__change_keys__注入)、plan_runner.py(runner、lifecycle 忽略集合、敏感字段脱敏)、plan_utils.py(plan 文件识别与定义构建)、deep_analysis_plan_graph_manager.py(深度分析图合并)
  • 上下文解析:tf_plan 解析器
  • 单元测试:test_plan_runner.py(覆盖生命周期忽略、变更键检测、删除资源检测、模块嵌套、深度分析等场景)
  • 相关示例文档:Terraform.md、Handling Variables.md

【免费下载链接】checkovPrevent cloud misconfigurations and find vulnerabilities during build-time in infrastructure as code, container images and open source packages with Checkov by Bridgecrew.项目地址: https://gitcode.com/GitHub_Trending/ch/checkov

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询