通过访问控制加固开源项目 CI/CD 安全

概述

在云原生时代,CI/CD 系统的可靠性与安全性直接决定了软件供应链的完整性。一次针对构建环境的攻击,其破坏力可能波及数百万个运行中的容器,例如文中提到的 SolarWinds 事件。对于 SRE 而言,保障 CI/CD 流水线的安全是维持系统可靠性的关键前提,它涉及控制“谁”可以触发构建,以及构建过程能够“执行什么”代码。本文将深入剖析 Cilium 项目用于加固其开源 CI/CD 流水线的一套访问控制模式,这些实践具有广泛的可借鉴性。

核心概念

在 CI/CD 安全领域,几个核心概念构成了访问控制的基础:

  • 供应链攻击:攻击者通过渗透软件依赖、构建工具或 CI/CD 流程,将恶意代码注入到最终软件产品中。CI/CD 流程本身是高价值目标,因为它拥有构建、签名和发布代码的特权权限。
  • 信任边界:在流水线中明确区分“可信”与“不可信”代码的界限至关重要。通常,来自代码仓库基础分支或主分支的代码被视为可信,而来自拉取请求(PR)或外部贡献者的代码则被视为不可信。
  • 最小权限原则:每个构建任务、工作流步骤或机器人账户只应拥有完成其任务所必需的最小权限集。例如,CI 构建凭证只应能推送到开发镜像仓库,而非生产发布仓库。
  • 透明化审查门禁:通过技术手段(如 CODEOWNERS 文件)强制要求特定的高风险目录变更必须由指定的安全团队或维护者进行审查,确保变更在合并前经过充分的安全评估。

实践

Cilium 项目围绕“控制谁运行什么”这一核心问题,实施了多层次的访问控制措施。

1. 通过专用机器人严格控制工作流触发

为防止未授权用户或外部贡献者触发昂贵或敏感的 CI 工作流,Cilium 开发了内部机器人 Ariane。该机器人监听 PR 评论中的特定命令(如 /test),并执行以下校验:

  • 团队验证:只有 organization-members 团队的成员才能成功触发工作流。
  • 允许列表机制:所有可触发的工作流及其依赖关系都在配置文件(.github/ariane-config.yaml)中显式定义。任何未被列入允许列表的工作流命令都会被忽略。
  • 自动化依赖管理:配置支持定义工作流之间的依赖关系(如 depends-on),确保复杂的测试流水线按正确顺序执行。

这种模式将触发权限从 GitHub 原生的、较宽泛的 pull_request 事件,收窄到由经过身份验证的维护者通过特定命令触发,实现了更精细的控制。

2. 在 pull_request_target 工作流中隔离可信与不可信代码

pull_request_target 工作流拥有对仓库的读写权限和对 secrets 的访问权限,如果处理不当,是极其危险的安全陷阱。Cilium 在必须使用该上下文构建镜像时,采用了“两阶段检出”模式来建立信任边界:

# .github/workflows/build-images-ci.yaml 示例片段
- name: Checkout base or default branch (trusted)
  uses: actions/checkout@<SHA>
  with:
    ref: ${{ github.base_ref || github.event.repository.default_branch }}
    persist-credentials: false
- name: Checkout pull request branch (NOT TRUSTED)
  uses: actions/checkout@<SHA>
  with:
    persist-credentials: false
    ref: ${{ steps.tag.outputs.sha }}

安全架构要点:

  • 首先检出可信代码:从基础分支检出代码,用于加载所有可信的复合动作(Composite Actions)、脚本和签名逻辑。
  • 隔离使用不可信代码:PR 分支的代码仅作为 docker build 的上下文使用,其 绝不会 被作为脚本直接执行(run: 步骤中的所有脚本均为内联编写或来自可信来源)。
  • 构建环境隔离:Docker BuildKit 在隔离环境中执行不可信的 Dockerfile,无法访问 GitHub Actions 的环境变量、仓库 Secrets 或运行器的凭证。
  • 数据流净化:来自不可信 PR 的数据(如运行时镜像引用)在传入可信动作前会进行严格校验(例如验证镜像来自 quay.io/cilium/ 命名空间),防止注入攻击。

3. 使用 CODEOWNERS 强制安全审查

为了确保 CI/CD 配置的变更必须经过安全专家的审查,Cilium 在仓库根目录配置了 CODEOWNERS 文件:

/.github/                          @cilium/github-sec @cilium/ci-structure
/.github/ariane-config.yaml        @cilium/github-sec @cilium/ci-structure
/.github/workflows/                @cilium/github-sec @cilium/ci-structure
/.github/workflows/auto-approve.yaml  @cilium/cilium-maintainers

这意味着,任何对 .github/ 目录下文件的修改(包括工作流定义和触发配置),都必须获得 @cilium/github-sec 和 @cilium/ci-structure 这两个团队成员的批准。这为 CI/CD 基础设施代码建立了一个不可绕过的审查关卡。

SRE 要点

Cilium 的实践为 SRE 团队提供了宝贵的启示:

  • 与可靠性、可观测性的关联:一个安全的 CI/CD 流程是生产环境可靠性的基石。防止恶意代码进入发布版本,等同于预防了一次潜在的、影响面极广的生产事故。同时,清晰的访问控制和审计跟踪(谁能触发哪个工作流)增强了流水线的可观测性,便于事件溯源。
  • 最佳实践与常见反模式:
    • 最佳实践:对所有第三方 Actions 和容器镜像进行 SHA 摘要固定,防止被篡改;使用机器人账户并限制其权限;实施强制性的代码审查。
    • 常见反模式:滥用 pull_request_target 而不采取隔离措施;使用过于宽泛的 GITHUB_TOKEN 权限;忽视 CI 凭证与生产凭证的隔离。
  • 相关指标(SLI/SLO/SLA):虽然不直接度量,但这些安全控制措施为定义“供应链完整性”相关的 SLI 提供了基础,例如:
    • SLI:受控工作流中,未授权触发尝试次数;依赖项中使用 SHA 固定比例;需要安全团队审查的 CI 配置变更平均审核时间。
    • SLO:100% 的核心发布工作流必须通过经批准的触发器启动;关键构建工作流中第三方依赖的 SHA 固定率 ≥ 99.9%。

尽管 Cilium 已建立了坚固的防线,但他们也坦诚地指出了仍在改进的领域,例如尚未实现 SLSA 溯源证明、缺少 PR 时依赖审查以及部分内部引用需要优化。这体现了安全加固是一个持续迭代的过程。其他团队可以以此为蓝图,逐步构建自己的 CI/CD 安全控制体系。