ARTICLE DETAIL

资讯详情

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

为 Checkov 贡献新的 GitLab 配置策略:从 API 数据采集到规则落地的完整实战指南

为 Checkov 贡献新的 GitLab 配置策略:从 API 数据采集到规则落地的完整实战指南 为 Checkov 贡献新的 GitLab 配置策略从 API 数据采集到规则落地的完整实战指南【免费下载链接】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/checkovCheckov 的gitlab_configuration框架能够扫描 GitLab 项目与组的安全配置如合并请求审批规则并输出合规检查报告。本文以强制合并请求至少需要 2 个审批人这一策略即仓库中的CKV_GITLAB_1为例完整演示如何为 Checkov 新增一条 GitLab 配置策略从在数据访问层DAL新增 API 调用、定义响应 JSON Schema、编写检查类到补充单元测试的四个标准步骤。读完本文你将掌握 GitLab 配置类策略的完整贡献流程并能基于仓库源码理解其底层运行机制。一、GitLab 配置策略的整体框架在动手写代码之前先理解 Checkov 是如何处理 GitLab 配置的。与扫描 Terraform、Kubernetes 等 IaC 文件不同GitLab 配置策略扫描的对象是从 GitLab API 拉取下来的运行时配置数据而非仓库内的静态文件。从源码结构看整个框架由四个核心模块组成均位于 checkov/gitlab 目录下模块文件职责数据访问层checkov/gitlab/dal.py通过 GitLab REST API 拉取项目审批配置、组配置并落盘为 JSONSchema 校验checkov/gitlab/schemas用 JSON Schema 校验 API 响应结构防止脏数据进入检查逻辑检查实现checkov/gitlab/checks继承BaseGitlabCheck的具体策略类执行入口checkov/gitlab/runner.py串起拉取数据 → 落盘 → 读取 → 扫描 → 报告全流程其中Runner继承自 JSON 文档扫描器checkov/json_doc/runner.pycheck_type为GITLAB_CONFIGURATION其import_registry()返回 checkov/gitlab/registry.py 中定义的Registry(CheckType.GITLAB_CONFIGURATION)用于登记本框架内置的所有检查。值得注意的运行细节Runner.run()在扫描时显式传入filesNone这意味着 GitLab 策略只扫描由 DAL 落盘的配置目录gitlab_conf_dir_path不会接受用户手动指定的文件。这一点在tests/gitlab/test_runner.py的test_runner_files_ignore用例中得到了验证——即使传入一个内容可扫描的 JSON 文件扫描结果也为空。二、第一步在 DAL 中新增 GitLab API 调用新增一条策略的第一步是确认所需的数据是否已经被 Checkov 从 GitLab 拉取。以合并请求审批规则为例我们需要 GitLab 的GET /projects/{id}/approvals接口返回的项目审批设置。先查看 checkov/gitlab/dal.py 是否已有相关方法如果没有就在Gitlab(BaseVCSDAL)类中新增如下方法class Gitlab(BaseVCSDAL): ... def get_project_approvals(self): if self.project_id: project_approvals self._request( endpointfprojects/{self.project_id}/approvals) return project_approvals return None def persist_project_approvals(self): project_approvals self.get_project_approvals() if project_approvals: BaseVCSDAL.persist(pathself.gitlab_project_approvals_file_path, confproject_approvals) def persist_all_confs(self): if strtobool(os.getenv(CKV_GITLAB_CONFIG_FETCH_DATA, True)): self.persist_project_approvals() self.persist_groups()仓库中 checkov/gitlab/dal.py#L44-L69 的实现与上述示例完全一致可以直接对照阅读。2.1 理解_request的底层行为get_project_approvals内部调用的_request定义在基类 checkov/common/vcs/base_vcs_dal.py#L82-L100理解它的行为对排查问题至关重要请求地址拼接规则为{api_url}/{endpoint}其中api_url来自discover()中读取的CI_SERVER_URL环境变量默认https://gitlab.com即https://gitlab.com/api/v4/使用urllib3发起GET请求认证头为Authorization: Bearer {token}token 来自CI_JOB_TOKEN环境变量若 token 为空_request直接返回None不会发起网络请求只有当响应状态码在allowed_status_codes默认[200]内且响应体不含errors字段时才将 JSON 解析结果返回任何异常都会被捕获并仅记录 debug 日志返回None。这解释了为什么测试中要显式关闭数据拉取见下文第四步。2.2 环境变量与落盘路径discover()和setup_conf_dir()checkov/gitlab/dal.py#L14-L39共同决定了数据从哪来、写到哪去涉及的环境变量如下环境变量默认值含义CI_SERVER_URLhttps://gitlab.comGitLab 服务器地址用于拼接 REST 与 GraphQL APICI_JOB_TOKEN空字符串访问 GitLab API 的 Bearer TokenCI_MERGE_REQUEST_PROJECT_PATH空字符串当前合并请求对应的项目路径CI_COMMIT_REF_NAME空字符串当前分支/引用名CI_PROJECT_NAMESPACE空字符串项目所属组命名空间CI_PROJECT_ID空字符串项目 ID决定查询哪个项目的审批配置CKV_GITLAB_CONF_DIR_NAMEgitlab_conf配置落盘目录名相对当前工作目录CKV_GITLAB_CONFIG_FETCH_DATATrue是否拉取 GitLab 数据测试时置为False落盘路径通过setup_conf_dir()计算得出例如项目审批数据写入{cwd}/gitlab_conf/project_approvals.json组数据写入{cwd}/gitlab_conf/groups.json。持久化由基类的静态方法BaseVCSDAL.persistcheckov/common/vcs/base_vcs_dal.py#L131-L136完成它会自动创建目录并以indent4的格式写出 JSON。三、第二步定义 JSON Schema 校验 API 响应拉取到的 API 响应是未经校验的原始数据直接进入策略逻辑存在结构不符的风险。因此第二步是为响应数据定义 JSON Schema。在 checkov/gitlab/schemas 目录下新增project_approvals.pyfrom checkov.common.vcs.vcs_schema import VCSSchema class ProjectApprovalsSchema(VCSSchema): def __init__(self): schema { $schema: http://json-schema.org/draft-04/schema#, type: object, properties: { approvals_before_merge: { type: integer }, reset_approvals_on_push: { type: boolean }, disable_overriding_approvers_per_merge_request: { type: boolean }, merge_requests_author_approval: { type: boolean }, merge_requests_disable_committers_approval: { type: boolean }, require_password_to_approve: { type: boolean } }, required: [ approvals_before_merge, reset_approvals_on_push, disable_overriding_approvers_per_merge_request, merge_requests_author_approval, merge_requests_disable_committers_approval, require_password_to_approve ] } super().__init__(schemaschema) schema ProjectApprovalsSchema()仓库中 checkov/gitlab/schemas/project_approvals.py 即为上述代码的落地版本。关键设计点Schema 基于 JSON Schemadraft-04VCSSchema基类封装了validate()方法检查逻辑在扫描时调用properties定义了审批配置的六个字段其中approvals_before_merge是本次策略判断的核心字段required列出的六个字段必须全部存在否则校验失败——这保证了策略只对结构完整的 API 响应做判定避免因缺字段而产生误报。同目录下的 checkov/gitlab/schemas/groups.py 是组配置groups.json的 Schema展示了更复杂的场景当接口返回数组时type为array且通过items定义元素结构可空字段使用oneOf组合boolean与null类型。如果你要新增组级策略可以仿照该文件编写。四、第三步编写 GitLab Check 检查类数据与校验器就绪后第三步是编写策略本体。在checkov/gitlab/checks目录下新增检查文件原文档示例命名为enforce_branch_protection_on_admins.py仓库实际实现为merge_requests_approvals.pyclass MergeRequestRequiresApproval(BaseGitlabCheck): def __init__(self): name Merge requests should require at least 2 approvals id CKV_GITLAB_1 categories [CheckCategories.SUPPLY_CHAIN] super().__init__( namename, idid, categoriescategories, supported_entities[*], block_typeBlockType.DOCUMENT ) def scan_entity_conf(self, conf): if project_aprovals_schema.validate(conf): if conf.get(approvals_before_merge, 0) 2: return CheckResult.FAILED, conf return CheckResult.PASSED, conf check MergeRequestRequiresApproval()对照仓库中 checkov/gitlab/checks/merge_requests_approvals.py 的实际实现有几个细节需要留意id遵循CKV_GITLAB_N的全局唯一编号约定当前为CKV_GITLAB_1categories为SUPPLY_CHAIN来源于 checkov/common/models/enums.py 中的CheckCategories枚举supported_entities[*]表示该检查作用于整个 JSON 文档GitLab 配置以整份响应为单位扫描block_typeBlockType.DOCUMENT与之对应scan_entity_conf先调用project_aprovals_schema.validate(conf)做结构校验再比较approvals_before_merge是否小于 2conf.get(approvals_before_merge, 0)的默认值 0 保证字段缺失时按不合格处理。4.1BaseGitlabCheck与注册机制BaseGitlabCheck定义在 checkov/gitlab/base_gitlab_configuration_check.pyclass BaseGitlabCheck(BaseCheck): def __init__(self, name, id, categories, supported_entities, block_type, pathNone, guidelineNone): super().__init__( namename, idid, categoriescategories, supported_entitiessupported_entities, block_typeblock_type, guidelineguideline, ) self.path path registry.register(self)它继承通用的BaseCheck并在构造时自动执行registry.register(self)。因此你不需要手动注册检查——只要模块被导入检查实例文件末尾的check MergeRequestRequiresApproval()就会进入gitlab_configuration注册表。这也意味着文件内必须保留模块级实例化的check变量导入机制才能发现这条策略。五、第四步添加测试用例策略合并前必须通过测试验证。参照 tests/gitlab/test_runner.py 中已有的用例模式为新增检查补充测试。5.1 准备测试数据测试资源位于 tests/gitlab/resources/gitlab_conf分为fail与pass两个目录fail/merge_request_approval_conf.json应触发CKV_GITLAB_1失败{ approvals_before_merge: 1, reset_approvals_on_push: true, disable_overriding_approvers_per_merge_request: false, merge_requests_author_approval: true, merge_requests_disable_committers_approval: false, require_password_to_approve: true }pass/merge_request_approval_conf.json应通过检查{ approvals_before_merge: 3, reset_approvals_on_push: true, disable_overriding_approvers_per_merge_request: false, merge_requests_author_approval: true, merge_requests_disable_committers_approval: false, require_password_to_approve: true }两份数据仅approvals_before_merge不同1 vs 3恰好覆盖阈值 2两侧的判定路径。5.2 编写测试用例在 tests/gitlab/test_runner.py 中新增测试核心模式如下mock.patch.dict(os.environ, {CKV_GITLAB_CONFIG_FETCH_DATA: False, PYCHARM_HOSTED: 1}, clearTrue) def test_runner_object_failing_check(self): current_dir os.path.dirname(os.path.realpath(__file__)) valid_dir_path os.path.join(current_dir, resources, gitlab_conf, fail) runner Runner() runner.gitlab.gitlab_conf_dir_path valid_dir_path checks [CKV_GITLAB_1] report runner.run( root_foldervalid_dir_path, runner_filterRunnerFilter(checkschecks) ) self.assertEqual(len(report.failed_checks), 1) self.assertEqual(report.parsing_errors, []) self.assertEqual(len(report.passed_checks), 0) self.assertEqual(report.skipped_checks, [])配套的通过用例test_runner_object_passing_check结构与上例一致断言为failed_checks为 0、passed_checks为 1。tests/gitlab/test_runner.py中还包含test_runner_honors_enforcement_rules验证强制执行规则可关闭检查和test_runner_files_ignore验证 Runner 忽略显式传入的文件两个用例可作为理解框架行为的补充参考。测试中的两个关键技巧关闭真实网络请求通过mock.patch.dict(os.environ, {...}, clearTrue)将CKV_GITLAB_CONFIG_FETCH_DATA置为False使persist_all_confs()跳过 API 拉取重定向配置目录手动将runner.gitlab.gitlab_conf_dir_path指向测试资源目录让 Runner 直接扫描预置的 JSON 数据。六、串联全流程Runner 如何执行你的新策略新增的策略要真正生效还依赖 Runner 的调度。看 checkov/gitlab/runner.py 的关键逻辑class Runner(JsonRunner): check_type CheckType.GITLAB_CONFIGURATION def run(self, root_folderNone, external_checks_dirNone, filesNone, runner_filterNone, collect_skip_commentsTrue): ... self.prepare_data() # 内部调用 self.gitlab.persist_all_confs()拉取并落盘配置 report super().run( root_folderself.gitlab.gitlab_conf_dir_path, # 只扫描 gitlab_conf 目录 filesNone, # 忽略文件级扫描 ... ) JsonRunner._change_files_path_to_relative(report) return report一次完整的 GitLab 配置扫描链路为prepare_data()→Gitlab.persist_all_confs()checkov/gitlab/dal.py#L66-L69依据CKV_GITLAB_CONFIG_FETCH_DATA决定是否拉取项目审批与组配置并写入gitlab_conf目录JSON Runner 遍历gitlab_conf_dir_path下的 JSON 文件读取project_approvals.json与groups.json注册表中的CKV_GITLAB_1对每个文档执行scan_entity_conf先用 Schema 校验结构再判定approvals_before_merge 2结果汇总为Report并通过_change_files_path_to_relative将绝对路径改写为相对路径便于在 CLI 输出中展示。也就是说一条新策略只要完成了API 拉取 Schema 校验 Check 类 测试四步就会被 Runner 自动纳入gitlab_configuration框架的扫描与报告流程无需修改 Runner 本身。七、总结与贡献检查清单至此一条 GitLab 配置策略的完整贡献闭环已经走通DAL 负责从 GitLab API 采集数据Schema 负责数据质量把关Check 类负责业务判定测试用例负责回归保障。合并后新策略即随gitlab_configuration框架在 CI 中自动生效Checkov 会在扫描报告中对不合规的项目配置给出FAILED结果。动手贡献前对照以下清单逐项确认checkov/gitlab/dal.py 中已有拉取所需数据的 API 方法且persist_all_confs()调用了对应的persist_*方法checkov/gitlab/schemas 中已定义并导出响应数据的 JSON Schema 实例模块级schema变量checkov/gitlab/checks 中新增了继承BaseGitlabCheck的检查类id全局唯一文件末尾实例化check变量tests/gitlab/test_runner.py 中补充了 fail/pass 两方向的用例且测试资源放入tests/gitlab/resources/gitlab_conf对应目录运行pytest tests/gitlab/全部通过。按照此流程你可以将任意 GitLab 项目或组级安全配置分支保护、组两因素认证、可见性设置等逐步扩展为 Checkov 的内置策略持续充实gitlab_configuration框架的覆盖面。【免费下载链接】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),仅供参考
返回列表