ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Checkov ImageReferencer 实现指南:为 IaC 框架接入容器镜像漏洞扫描

Checkov ImageReferencer 实现指南:为 IaC 框架接入容器镜像漏洞扫描 Checkov ImageReferencer 实现指南为 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容器镜像广泛出现在 CI 工作流文件、Terraform、Serverless、Kubernetes 清单等各种 IaC 与配置文件中这些文件本身可能存在错误配置同时其引用的镜像也可能携带已知漏洞。Checkov 通过ImageReferencer抽象机制让任意 Runner 能够从自身解析的文件中提取镜像引用并交由sca_image扫描器对这些镜像执行漏洞扫描将结果统一合并进最终报告。本文以docs/6.Contribution/Implementing ImageReferencer.md为骨架结合当前仓库源码完整讲解该机制的设计动机、核心类、自动注册流程、扫描调用链、参考实现与实战验证方法帮助你为自己的 IaC 框架 Runner 实现 ImageReferencer。一、设计动机为什么需要 ImageReferencer容器镜像被广泛引用在各类文件中CI 工作流GitHub Actions、Argo Workflows、CircleCI、Terraform 资源如aws_ecs_task_definition、aws_batch_job_definition、Serverless 函数、Kubernetes 清单等。这些文件可能同时存在两类问题文件本身配置不当misconfiguration由对应框架的常规检查捕获引用的容器镜像带有已知漏洞vulnerability需要借助镜像漏洞库进行识别。当以 API token 方式使用 Checkov 时Checkov 获得执行镜像扫描并利用 Prisma Cloud Compute 漏洞数据库的能力。此时只要某个Runner派生自ImageReferencer该 Runner 解析的 IaC 文件中被引用的镜像就会被自动纳入漏洞扫描范围。这一设计的核心价值在于在不改变原有 IaC 检查流程的前提下以统一、可插拔的方式为每个框架补充镜像级安全能力。二、核心抽象checkov/common/images/image_referencer.py所有 ImageReferencer 实现都以 image_referencer.py 中的抽象类为基础。该文件同时定义了配套的数据结构与工具函数。2.1Image数据类Image描述一个从文件中提取出的镜像引用字段如下见 image_referencer.py#L41-L68字段含义示例file_path引用该镜像的源文件路径integration_tests/example_workflow_file/.github/workflows/vulnerable_container.yamlname镜像名称node:14.16start_line/end_line镜像声明在源文件中的起止行号8/16related_resource_id可选的关联资源 ID便于把漏洞结果关联回具体 IaC 资源null该类实现了基于(file_path, name, start_line, end_line)的__hash__与__eq__因此同一文件中重复引用的镜像会被去重。2.2ImageReferencer抽象基类ImageReferencer 声明了两个必须实现的抽象方法class ImageReferencer: abstractmethod def is_workflow_file(self, file_path: str) - bool: 判断该文件是否是可能包含镜像引用的文件例如 CI 工作流文件 abstractmethod def get_images(self, file_path: str) - Iterable[Image]: 从文件中提取全部容器镜像引用返回 Image 对象列表此外还提供静态工具方法inspect(image_name)对镜像执行docker inspect若本地不存在则先docker pull返回镜像的短 ID形如sha256:6a353e22ce失败时返回空字符串见 image_referencer.py#L91-L109。2.3 公共镜像名校验is_valid_public_image_name 用于过滤不适合云端扫描的镜像名以localhost开头的镜像本地私有构建返回False包含[、{、(、、$等字符多为模板变量或插值表达式返回False冒号数量大于 1通常是含端口号的私有仓库地址返回False。只有通过该校验的镜像名才会被提交查询从而避免把无法解析的插值或内网镜像误发给扫描服务。2.4ImageReferencerMixin基于图的 IaC 模板扫描对于基于图graph的 IaC 框架Terraform、CloudFormation、Bicep、Kubernetes 等ImageReferencerMixin 提供了另一套入口check_container_image_references()其工作流程为通过should_run_scan(runner_filter.checks)判断--check是否指定了 CVE 检查 ID若没有则跳过整轮扫描image_referencer.py#L137-L139调用子类必须实现的抽象方法extract_images(graph_connector, definitions, definitions_raw)提取镜像对镜像名执行is_valid_public_image_name过滤后用asyncio.gather并发向镜像扫描缓存查询结果_fetch_image_results_async并发获取各镜像的许可证状态_fetch_licenses_per_image逐个镜像把扫描结果通过_add_image_records→_add_vulnerability_records写入Report(CheckType.SCA_IMAGE)。值得注意的是扫描结果在生成image_cached_results时还会通过fix_related_resource_ids去掉临时目录前缀image_referencer.py#L31-L38保证报告中relatedResourceId指向仓库内真实相对路径。三、自动注册RunnerRegistry 如何发现派生类容器镜像会被自动扫描吗会。只要--framework sca_image未被排除在执行范围之外、且提供了 API token自动扫描就会发生。其奥秘在于 runner_registry.py 中的注册流程# checkov/common/runners/runner_registry.py#L97 self.image_referencing_runners self._get_image_referencing_runners() ... # checkov/common/runners/runner_registry.py#L105-L107 for runner in runners: if isinstance(runner, image_runner): runner.image_referencers self.image_referencing_runners_get_image_referencing_runnersrunner_registry.py#L782-L788遍历构造 RunnerRegistry 时传入的所有 runner凡是issubclass(runner.__class__, ImageReferencer)的都会被收集进一个set并在初始化阶段注入到sca_imageRunner 的image_referencers属性上def _get_image_referencing_runners(self) - set[ImageReferencer]: image_referencing_runners: set[ImageReferencer] set() for runner in self.runners: if issubclass(runner.__class__, ImageReferencer): image_referencing_runners.add(cast(ImageReferencer, runner)) return image_referencing_runners这意味着你只需要让你的 Runner 类继承ImageReferencer并实现两个抽象方法无需改动sca_image与注册中心的任何代码该框架的镜像引用即会被自动接管。这就是文档中所说的 registration process for any Derived class of ImageReferencers that occurs inRunnerRegistryinit。四、扫描调用链sca_image Runner 如何消费镜像引用sca_imageRunnersca_image/runner.py持有self.image_referencers集合在run()中遍历目录或文件列表时对每个文件调用iterate_image_files()sca_image/runner.py#L193-L222其核心逻辑为for image_referencer in self.image_referencers: if image_referencer.is_workflow_file(abs_fname): images image_referencer.get_images(file_pathabs_fname) for image in images: image_cached_result image_scanner.get_scan_results_from_cache(fimage:{image.name}) image_cached_report self.get_image_cached_results( dockerfile_pathabs_fname, imageimage, image_cached_resultimage_cached_result, root_folderroot_folder) if image_cached_report: report.image_cached_results.append(image_cached_report) if strtobool(os.getenv(CHECKOV_CREATE_SCA_IMAGE_REPORTS_FOR_IR, True)): image_report self.get_image_report( dockerfile_pathabs_fname, imageimage, runner_filterrunner_filter, image_cached_resultimage_cached_result) merge_reports(report, image_report)流程可概括为四步文件匹配依次询问每个 ImageReferencer 的is_workflow_file确认该文件属于它的管辖范围镜像提取调用get_images拿到该文件引用的全部Image缓存查询以image:镜像名为键查询本地扫描缓存image_scanner.get_scan_results_from_cache报告合并命中缓存时先由docker_image_scanning_integration.create_report生成带relatedResourceId与error_lines即镜像声明的起止行的缓存报告追加到report.image_cached_results随后再通过get_image_report生成漏洞明细报告并merge_reports合并进主报告。若缓存未命中get_image_report会通过image_scanner.setup_scan与execute_scan真正拉起 twistcli 执行images scan见 sca_image/runner.py#L82-L99 中的命令组装。同时需要注意RunnerFilter.should_run_scan的约束--check未携带 CVE 检查 ID 时整轮扫描会被跳过且未提供--bc-api-key时 SCA 包扫描不会执行sca_image/runner.py#L56-L62。五、参考实现一GitHub Actions Runner文档明确要求参考checkov/github_actions/runner.py。该框架的 Runner 继承自 YAML 基础 Runnergithub_actions/runner.py其文件匹配逻辑位于 github_actions/utils.py#L54-L59def is_workflow_file(file_path: str | Path) - bool: abspath os.path.abspath(file_path) return get_workflow_dir() in abspath and abspath.endswith((yml, yaml))即文件路径位于工作流目录如.github之下、且以yml或yaml结尾即为可扫描的 GitHub Actions 工作流文件。Runner 自身的included_paths()返回[.github]限定扫描范围github_actions/runner.py#L72-L73。六、参考实现二Argo Workflows Runner最小完整范例如果你需要从零实现argo_workflows/runner.py 提供了最直观的范式——Runner 同时继承YamlRunner与ImageReferencerargo_workflows/runner.py#L21class Runner(YamlRunner, ImageReferencer): check_type CheckType.ARGO_WORKFLOWSis_workflow_file(file_path)通过读取文件内容判断是否为 Argo Workflows 清单需包含argoproj.io标记且为 yaml/yml见 argo_workflows/runner.py#L44-L64get_images(file_path)解析工作流文件后遍历spec.templates从每个 template 的container或script节点取出image字段构造Image对象并放入set去重argo_workflows/runner.py#L66-L134extract_image(file_path, container)读取__startline__/__endline__记录镜像声明的行列位置供报告精确定位argo_workflows/runner.py#L136-L148。# 一个可被 Argo Workflows ImageReferencer 提取镜像的示例片段 apiVersion: argoproj.io/v1alpha1 kind: Workflow spec: entrypoint: main templates: - name: whalesay container: image: argoproj/argosay:v2 - name: retry-backoff container: image: python:alpine3.6七、基于图的框架Provider / Manager 组合模式对于 Terraform、CloudFormation、Bicep、Kubernetes 等基于依赖图扫描的框架镜像提取被拆分为Manager与Provider两层Provider抽象类 GraphImageReferencerProvider以及工作流形态的 WorkflowImageReferencerProvider负责从图或工作流配置中按资源类型提取镜像Manager如 TerraformImageReferencerManager组合多个云厂商 ProviderAWS、Azure、GCP并聚合结果class TerraformImageReferencerManager(GraphImageReferencerManager): def extract_images_from_resources(self) - list[Image]: images [] images.extend(AwsTerraformProvider(graph_connectorself.graph_connector).extract_images_from_resources()) images.extend(AzureTerraformProvider(graph_connectorself.graph_connector).extract_images_from_resources()) images.extend(GcpTerraformProvider(graph_connectorself.graph_connector).extract_images_from_resources()) return imagesProvider 通过supported_resource_types声明感兴趣的资源类型再调用基类的extract_nodes支持 RUSTWORKX / NETWORKX 两种图后端可用环境变量CHECKOV_GRAPH_FRAMEWORK切换见 image_referencer_provider.py#L33-L37筛选出对应资源节点进行提取。从当前仓库源码结构看已接入该机制的框架及其实现位置包括框架实现位置Terraformcheckov/terraform/image_referencer/AWS / Azure / GCP ProviderCloudFormationcheckov/cloudformation/image_referencer/Bicepcheckov/bicep/image_referencer/Kubernetescheckov/kubernetes/image_referencer/Helmcheckov/helm/image_referencer/Kustomizecheckov/kustomize/image_referencer/Dockerfilecheckov/dockerfile/image_referencer/GitHub Actions / Argo Workflows 等直接在 Runner 中继承ImageReferencer八、实战验证示例 CLI 命令与输出解读以文档给出的命令为例对一个包含 GitHub Actions 工作流文件的目录执行镜像漏洞扫描integration_tests/example_workflow_file/下的示例文件结构可参考 integration_tests/example_workflow_file/checkov -d /checkov/integration_tests/example_workflow_file/.github/workflows/ --framework sca_image --bc-api-key SOME_TOKEN其中-d 目录指定扫描目录--framework sca_image把执行范围限定为 SCA 镜像扫描框架即文档所述只要不把sca_image从执行范围中排除即可触发自动扫描--bc-api-key SOME_TOKEN提供 Bridgecrew / Prisma Cloud API token这是启用漏洞数据库查询与镜像拉取扫描的前提。命令输出分为两部分。第一部分是 GitHub Actions 框架自身的常规检查结果注意示例输出中展示的是文档原作者本机路径实际运行会显示你本地的文件路径_ _ ___| |__ ___ ___| | _______ __ / __| _ \ / _ \/ __| |/ / _ \ \ / / | (__| | | | __/ (__| (_) \ V / \___|_| |_|\___|\___|_|\_\___/ \_/ By Prisma Cloud | version: x.x.x github_actions scan results: Passed checks: 7, Failed checks: 1, Skipped checks: 0 Check: CKV_GHA_1: Ensure ACTIONS_ALLOW_UNSECURE_COMMANDS isnt true on environment variables on a job PASSED for resource: .../.github/workflows/vulnerable_container.yaml.jobs.my_job.CKV_GHA_1 File: .../.github/workflows/vulnerable_container.yaml:8-17 Check: CKV_GHA_2: Ensure run commands are not vulnerable to shell injection PASSED for resource: .../.github/workflows/vulnerable_container.yaml.jobs.my_job.CKV_GHA_2 File: .../.github/workflows/vulnerable_container.yaml:8-17 Check: CKV_GHA_2: Ensure run commands are not vulnerable to shell injection FAILED for resource: .../.github/workflows/vulnerable_container.yaml.jobs.unsecure-job.CKV_GHA_2 File: .../.github/workflows/vulnerable_container.yaml:28-36 28 | runs-on: ubuntu-latest 29 | run: | 30 | title${{ github.event.issue.title }} 31 | if [[ ! $title ~ ^.*:\ .*$ ]]; then 32 | echo Bad issue title 33 | exit 1 34 | fi第二部分是sca_image的镜像漏洞扫描结果。由于 GitHub Actions Runner 实现了 ImageReferencer工作流中引用的容器镜像如vulnerable_container.yaml中 job 使用的镜像会被自动提取并扫描输出每个镜像的 CVE 统计与逐包漏洞明细原文档输出中该部分出现两次此处合并展示一次sca_image scan results: Passed checks: 0, Failed checks: 989, Skipped checks: 0 //.../.github/workflows/vulnerable_container.yaml (sha256:6a353e22ce) ┌────────────────────┬────────────────────┬────────────────────┬────────────────────┬────────────────────┬────────────────────┐ │ Total CVEs: 344 │ critical: 8 │ high: 19 │ medium: 24 │ low: 293 │ skipped: 0 │ ├────────────────────┴────────────────────┴────────────────────┴────────────────────┴────────────────────┴────────────────────┤ ├────────────────────┬────────────────────┬────────────────────┬────────────────────┬────────────────────┬────────────────────┤ │ Package │ CVE ID │ Severity │ Current version │ Fixed version │ Compliant version │ ├────────────────────┼────────────────────┼────────────────────┼────────────────────┼────────────────────┼────────────────────┤ │ libwebp │ CVE-2018-25014 │ critical │ 0.5.2-1 │ 0.5.2.post1deb9u1 │ 0.5.2.post1deb9u1 │ │ │ CVE-2018-25011 │ critical │ │ 0.5.2.post1deb9u1 │ │ │ │ CVE-2020-36332 │ low │ │ N/A │ │ ├────────────────────┼────────────────────┼────────────────────┼────────────────────┼────────────────────┼────────────────────┤ │ elfutils │ CVE-2018-16402 │ critical │ 0.168-1 │ 0.168.post1deb9u1 │ 0.168.post1deb9u1 │ │ │ CVE-2018-18520 │ medium │ │ 0.168.post1deb9u1 │ │ │ │ CVE-2019-7150 │ low │ │ N/A │ │ └────────────────────┴────────────────────┴────────────────────┴────────────────────┴────────────────────┴────────────────────┘可以看到镜像vulnerable_container.yaml的扫描image idsha256:6a353e22ce共发现 344 个 CVE8 critical、19 high、24 medium、293 low漏洞报告精确到Package、CVE ID、Severity、Current version、Fixed version与Compliant version可直接指导镜像升级与修复。九、适用场景与注意事项好的候选框架使用容器的 Serverless 函数、以及任何可能引用镜像的 IaC 清单Terraform、CloudFormation、Kubernetes、Helm、Kustomize、Bicep、Dockerfile、CI 工作流等都是 ImageReferencer 的理想实现对象。执行前提必须提供--bc-api-key--framework sca_image不能被排除--check若使用则需包含 CVE 检查 ID否则should_run_scan会跳过整轮扫描。性能开销启用镜像引用扫描后镜像会被拉取并扫描因此扫描结果会额外占用时间。命中本地缓存键为image:镜像名时可显著加速环境变量CHECKOV_CREATE_SCA_IMAGE_REPORTS_FOR_IR可控制是否为 ImageReferencer 生成独立漏洞报告默认True见 sca_image/runner.py#L218。镜像名过滤私有仓库、本地镜像localhost前缀与含模板插值字符的镜像名会被is_valid_public_image_name排除不会发送到云端扫描。平台订阅sca_image属于 SCA 订阅模块licensing_integration.should_run_image_referencer()会依据open_source_only与订阅状态决定是否启用该能力见 licensing_integration.py#L65-L66。十、测试与验证仓库为 ImageReferencer 提供了较完整的测试覆盖是验证新实现是否正确的最直接参考Terraform 的 Provider 与 Runner 级测试tests/terraform/image_referencer/test_aws.py、test_runner_aws_resources.py、test_runner_azure_resources.py、test_runner_gcp_resources.py、test_manager.pyBicep 的 Manager 与 Runner 级测试tests/bicep/image_referencer/test_manager.py、test_runner_azure_resources.py测试中通过unittest.mock.patch替换checkov.common.images.image_referencer.image_scanner.get_scan_results_from_cache_async与get_license_statuses_async无需真实拉取镜像即可断言提取结果与报告内容见 tests/bicep/image_referencer/test_runner_azure_resources.py。总结实现一个 ImageReferencer 只需三步让你的 Runner 继承ImageReferencer、实现is_workflow_file与get_images两个抽象方法、让RunnerRegistry在初始化时收集到该 Runner——之后sca_image会自动接管该框架文件中所有镜像引用的漏洞扫描。若框架基于依赖图扫描则可复用GraphImageReferencerManager/ Provider 组合模式按资源类型声明式提取镜像。这套可插拔设计让 Checkov 能够在一次扫描中同时覆盖配置错误与镜像漏洞两类风险且新增框架支持无需侵入扫描核心。【免费下载链接】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),仅供参考
返回列表