GitLab CI/CD 与 GitHub Actions 的密钥管理对比

  • infisical
  • 发布于 2026-02-19 10:25
  • 阅读 107

文章对比了 GitHub Actions 与 GitLab CI/CD 在密钥管理上的差异,核心围绕 secrets 的定义、作用域、注入时机、审计与轮换展开。GitHub 以仓库为信任边界,适合轻量和分散团队,但在大规模场景下容易因多仓库复制而产生密钥漂移;GitLab 采用组/项目层级继承,便于集中治理,却更容易因变量覆盖和继承链复杂而误配。文章进一步提出通过统一命名、分阶段轮换、优先使用 OIDC 临时凭证,以及引入中心化密钥代理来降低静态密钥风险。

Blog image

密钥很少会因为加密被攻破而失效。它们之所以出问题,往往是因为团队失去了对其存放位置的掌控。在 CI/CD pipeline 中,这种失控表现为 secret drift,也就是数据库密码或 API key 在轮换或重构过程中,悄然在不同环境之间发生偏移。

GitHub 和 GitLab 都默认保护 secrets,但它们体现了两种不同的控制理念。GitHub 将每个 repository 视为独立的 trust boundary,偏向隔离与简洁。GitLab 则基于层级化模型构建,让 secrets 可以跨 group 和 project 级联传播,以实现中心化治理。

这种权衡对于任何大规模管理基础设施的人来说都很熟悉。更简单的系统更容易理解。更结构化的系统提供了更广泛的控制,但也带来了更多发生细微错误的空间。

真正的差异体现在细节中。Secrets 如何定义。可以在哪里设定 scope。如何注入到 jobs 中。审计是什么样的。以及在 rotation 期间会发生什么。这些方面细微的架构选择,会以很容易被忽视的方式影响日常安全结果,直到某些地方出问题。

在并排比较这些机制之前,先理解为什么 CI/CD 中的 secrets 与传统应用环境中的 secrets 表现不同会很有帮助,以及为什么这种差异让问题比最初看起来更难。

为什么 Secrets Management 在 CI/CD 中会不同

CI/CD 中的 secrets 面临着与应用运行时中的 secrets 不同的一组约束。一个正在运行的服务可能会从持久化 vault 或自动续期的云 IAM role 中获取凭据。而 CI job 则会在一次性 hosted runner 上启动,执行构建,然后在几分钟内消失。在这段时间里,secrets 必须是短生命周期的、scope 精确受限的,并且在 job 完成后立即清除。

每个平台解决这一问题的方式都反映了它们的历史。GitHub 和 GitLab 最初都是 version-control 工具,而不是构建编排器,它们各自以不同方向扩展模型来支持 continuous integration 和 delivery。

  • GitHub built Actions 围绕 repository 这一 trust 单位来构建。每个 workflow、permission 和 secret 都属于某个特定 repo 或 environment。
  • GitLab embedded CI/CD 直接将其嵌入核心平台。它将 pipelines 视为更大层级命名空间的一部分,variables 从 instance 流向 group,再到 project。

这些理念上的差异塑造了每个平台在后续如何处理 security。理解这些根源有助于说明为什么每个工具会这样运作,以及为什么 CI/CD 中的 secrets management 更关乎架构,而不是加密本身。

那么,下一个问题就是一个实际问题:secret 到底是如何在各自的 pipeline 中流动的?

Secrets 如何在 GitHub 和 GitLab Pipelines 中流动

假设我们有一个 deployment job 需要访问数据库密码。在 GitHub Actions 和 GitLab CI 中,这个 secret 都会通过一个短生命周期 runner 传递,并由它 just in time 获取。区别在于每个平台如何定义、scope 和交付它。

在 GitHub Actions 中

GitHub 中的 secrets 分为三个层级定义:repository、organization 和 environment。对于一个 deployment workflow,这个数据库密码可能作为 production 下的 environment secret 存在,并由 required reviewers 进行保护。

当该 workflow 运行时,GitHub 会先将 job 排队并解析其 secrets。Organization 和 repository secrets 会在 workflow run 创建时解析。Environment secrets 则在 job 启动时解析,并且是在满足所有 required protection rules(例如 reviewer approvals)之后。YAML 中,workflow 会直接引用这些 secrets:

env:
  DB_PASSWORD: ${{ secrets.DB_PASSWORD }}

Runner 会把该值注入到 job 的 environment 中,而一旦 runner 停止,environment(以及其中的 secrets)就会消失。

这种设计让 secrets 的 scope 非常清晰,也很容易理解,但它也使管理变得碎片化。每个 repository 或 environment 都维护自己的副本,这意味着同一个凭据可能在组织内存在几十份。团队的一种变通方式是使用 bots 或 scripts,通过 API 在各个 repository 之间更新 secrets。

这种对小团队有保护作用的简洁性,在规模化之后可能会变成维护负担。

在 GitLab CI/CD 中

在 GitLab 中,同一个 deployment job 可能依赖一个名为 DB_PASSWORD 的 group-level variable。如果该值满足 GitLab 的 masking 要求,例如长度和字符限制,它就可以被 masked,并且 scope 到 production environment。该 variable 会被该 group 中的每个 project 自动继承,除非被显式覆盖。

当 pipeline 运行时,runner 会收集在 instance、group 和 project 级别定义的 variables(应用 inheritance rules),然后将它们注入到 job 的 shell environment 中。这个过程看起来很熟悉:

deploy_prod:
  environment: production
  script:
    - echo $DB_PASSWORD | psql ...

每一次 variable 的创建或变更都会记录在 GitLab 的 audit events 中。大多数团队会把日志流式传输到外部 SIEM。

这种层级化模型允许一个凭据在无需手动复制的情况下支持多个 project。代价则是更高的认知负担。开发者必须理解哪个 variable source 优先,以及 inheritance 如何与 environment scopes 交互。一个错误的 override 很容易把错误的 secret 推到错误的 environment 中,这不是因为 bug,而是因为 inheritance 的解析方式本就如此。

两个平台都以类似方式保护并注入 secrets。但 rotation 的机制会带来非常不同的运营现实。差异也正是在这里开始显现。

两种模型在运营上的分歧点

要理解 GitHub Actions 和 GitLab CI/CD 之间的差异,最好聚焦于每个系统在简洁性和控制之间的权衡。当团队试图在多个环境之间轮换、审计或调试凭据时,这些权衡会体现得最为明显。

Rotation

Rotation 听起来很简单。毕竟,我们只是把旧 secret 换成新 secret。但在 CI/CD pipeline 中,时机会把它变成一个协调问题。

GitHub

在 GitHub 中,secrets 不会 自动 rotate。没有原生 scheduler,也没有自动 hook 来触发更新。大多数团队会使用 GitHub Actions 或 API scripts,将新 secrets 推送到各个 repository。

由于 secrets 的 scope 是按 repository 或 environment 划分的,每一份副本都必须单独更新。而且时机很重要:repository 和 organization secrets 会在 workflow run 被排队时读取,而 environment secrets 只会在 job 开始时获取。这一时间差会导致微妙的不一致。即便 rotation 已经完成,一个已排队的 job 仍然可能拿到旧版本。

GitLab

在 GitLab 中,rotation 更像 configuration management。定义在 group-level 的 variables 可以通过 API 集中更新,而这些变更会自动向下级联。

这种 inheritance model 简化了大范围发布,但也引入了另一种脆弱性。如果某个 project 在本地覆盖了一个 variable,那么来自父 group 的新值就永远不会到达。在大型层级结构中,陈旧的 overrides 可能长期潜伏而无人察觉,直到 deployment 失败,或某个 environment 使用了过时的 key。

GitLab 的 hierarchy 让 rotation 更快,但也要求团队定期审计 overrides,以确保 inheritance chain 仍然符合预期。两个平台都没有原生的 secret versioning。如果一次 rotation 引入了错误的值,恢复就必须手动进行,因为没有内置 rollback 机制。

审计性

可见性对于团队响应 incident 至关重要。GitHub 和 GitLab 都会记录围绕 secrets 的管理活动,但都不会记录这些 secrets 在运行中的 job 里如何被使用。这个区别很重要。它决定了审计时你能证明什么,以及你只能推断什么。

GitHub

GitHub 的 audit trail 会记录 configuration events(即 secret 被创建、更新或删除时),并跟踪谁批准了 environment deployments。虽然 GitHub 的管理审计轨迹不会记录 workflow 执行期间单个 secret 的“读取”事件,但当 runners 请求短生命周期凭据时,基于 OIDC 的 credential exchange 可以通过 cloud provider 的日志系统进行审计。一旦 secret 被注入到 job 中,它的使用就不会留下任何痕迹,除了 job logs 本身。

对于 incident 调查来说,这意味着可见性止步于 configuration。你可以确认 secret 发生了变化,但无法确认 workflow 是否实际使用了它,或者某个 external action 是否误把它打印出来。一些团队会用 security scanning tools 来补充 audit logs,以检测 logs、artifacts 或提交代码中的意外 secret 暴露。

GitLab

GitLab 遵循同样的原则,但把它扩展到了更多层级。每一次 variable 的创建、更新或删除都会作为 audit event 被记录在 project、group 或 instance 级别。这些日志可以直接查询,也可以流式传输到外部 SIEM systems 以便长期保留。

虽然 GitLab 同样不会记录 pipeline 执行期间的读取访问,但它与 protected environments 和 approval workflows 的集成提供了间接可见性,显示谁可以访问某个 variable,即使不是谁实际访问了它。

典型故障模式

只有当事情出问题时,运营差异才会真正变得重要。GitHub 和 GitLab 各自的失败方式都映射了它们的优势:GitHub 通过 fragmentation 出问题,GitLab 通过 inheritance complexity 出问题。两者都在设计上是安全的,但如果没有任何可见警告,也可能悄悄 drift out of sync。

GitHub

image2

在 GitHub 中,挑战在于 fragmentation。repository 太多时,同一个 secret 的副本也会太多。一个大型组织可能会在几十个 repo 中存放相同的 DB_PASSWORD。当它轮换该凭据时,有些 repository 不可避免地会落后,部署失败可能会在 pipeline 中层层放大。

即使 rotation 成功,时机仍会带来另一个边缘情况。因为 GitHub 在 workflow run 被排队时解析 repository 和 organization secrets,所以任何已经在队列中的 job 都会继续使用旧的 secret 值,即使 secret 在 job 开始执行之前已经被轮换。旧值会在没有任何警告的情况下被使用,job 运行时使用了过期凭据这一事实也不会有任何提示。这个模型的简洁性降低了跨项目风险,但在规模化时,它需要 orchestration 来保持每个 secret 副本一致。

GitLab

image3

在 GitLab 中,失败模式则相反。为 staging 定义的层级变量可能会在 environment scopes 配置错误时意外级联到 production。一个 project-level override 可能会遮蔽父 variable,使一个 environment 被更新,而另一个仍停留在旧 key 上。

Merge request pipelines 还带来另一种细微风险。如果 MR pipeline 被配置为在父 project 而不是 fork 中运行,它就会继承父项目的所有 variables。GitHub Actions 面临类似风险的是 pull request workflows,如果没有适当限制,来自 base repository 的 secrets 可能会暴露给外部贡献者触发的 workflows。所有这些问题都可以追溯到 inheritance。Inheritance 正是 GitLab 如此强大的原因,但也让它更容易出现过度暴露。

Environment Management:GitHub Actions vs. GitLab CI/CD

类别 GitHub Actions GitLab CI/CD
Scope Model Repository, Organization, Environment Instance → Group → Project → Environment
Rotation 手动或外部 scripts API-native with inheritance
Audit Logs 仅更新(无读取可见性) 跨 scope 的完整 audit API
Risk Profile 通过重复复制导致 fragmentation 通过 inheritance 导致过度暴露
Best Fit 轻量级或去中心化团队 中心化、合规驱动的组织

没有哪个平台天生更安全。它们只是针对不同的 failure mode 进行了优化。这也是为什么成熟的团队更关注如何在两个平台上保持一致性,而不是单纯关注使用哪个平台。下一节将探讨如何设计一个无论 pipeline 运行在哪里都能保持一致的 secrets strategy。

设计跨平台一致的 Secrets Strategy

保持 secrets 对齐的关键原则,是建立一致的运营节奏。大多数 secret 泄露并不是源于加密薄弱,而是源于命名不一致、值过时或所有权不清晰。一个好的 strategy 会聚焦于可控变更,使其能够在系统之间以可预测的方式演进。

标准化命名和 Scoping

防止 drift 的第一道防线是清晰。团队应采用一致的凭据命名方案(PROD_DB_PASSWORDSTAGING_API_KEY 等),并在各个平台上使用相同的 environment labels。当名称和 scope 对齐时,rotation scripts、dashboards 和 brokers 就可以统一处理 secrets,而不是逐个工具分别处理。

在 GitHub 上,这意味着按 environment 而不是按 repository 来分组 secrets。在 GitLab 上,这意味着在 group 或 project 级别使用 environment-scoped variables,而不是把 credentials 分散到各个 job 中。

自动化 Rotations

时机不佳的更新会让旧值比预期更长时间保持有效。最佳模式是分阶段 rotation。

  1. 先轮换非关键环境,例如 staging 或 test
  2. 确认 builds
  3. 将变更推进到 production
  4. 在验证通过后删除或归档已弃用的 secrets

优先使用 Ephemeral Credentials

在 CI/CD 的语境下,static credentials 永远是负担。GitHub 和 GitLab 都提供 OIDC federation,它用短生命周期、可验证的 tokens 替代已存储的云 key。这消除了在 CI/CD 设置中存储 cloud access keys 的需要。

GitHub 的 OIDC 实现会发放与 repositoryrepository_ownerjob_workflow_ref 等 claims 绑定的 identity tokens。GitLab 的等效机制会生成 scope 到 project 和 job context 的短生命周期 JWTs,并在 pipeline 执行期间将其交换为 cloud credentials。在这两种情况下,runner 都会在 job 开始时直接将这些 tokens 交换为 cloud credentials。

集成一个统一的 Broker

在小规模场景下,GitHub 和 GitLab 的原生 secrets management 通常已经足够。但随着 pipelines 和 teams 增多,协调反而成为更难的问题。一个中央 broker(例如 Infisical 或 AWS Secrets Manager)提供了一个单一的 truth source,供 pipelines 动态查询。

与其把 static secrets 嵌入平台设置中,不如让 jobs 在运行时从 broker 请求凭据,并通过 OIDC 或短生命周期 tokens 进行认证。Broker 负责集中处理 rotation、记录访问并强制执行 policy,而 CI 平台则保持为轻量级消费者。

这并不是对原生机制的替代。相反,它是在这些机制之上做抽象,确保所有 secrets 都通过一个受治理的通道流动,无论 pipeline 运行在哪里。

Secrets Management 的发展方向

在大规模场景下,CI/CD 中的 secrets management 与其说是哪个平台更安全,不如说是团队在多系统之间维持一致性的能力有多强。GitHub 和 GitLab 走的是不同路径,但两者都面临同一个挑战:credentials 会随着时间发生 drift。

这一领域的未来正朝着 ephemeral credentials 和 centralized injection 方向发展。短生命周期的 OIDC tokens 已经在大多数云工作负载中消除了对 static keys 的需求,而 external brokers 可以统一这些 credentials 在各个服务中的发放、rotation 和审计。随着 DevOps 方案日趋成熟,人们也越来越期待 secrets management 成为一个完全自动化的层,而不是一项手动配置任务。

Infisical 将这一模型进一步延伸。通过直接集成 GitHub 和 GitLab,Infisical 为 secret delivery 和 rotation 提供了一个单一且受治理的层。

  • 原文链接: infisical.com/blog/gitla...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
infisical
infisical
江湖只有他的大名,没有他的介绍。