自托管 runner 的安全性

  • Tier: 基础版, 专业版, 旗舰版
  • Offering: JihuLab.com, 私有化部署

极狐GitLab CI/CD 流水线是一个用于简单或复杂 DevOps 自动化任务的工作流自动化引擎。因为这些流水线启用了远程代码执行服务,所以你应该实施以下过程以降低安全风险:

  • 配置整个技术栈安全性的系统方法。
  • 对平台的配置和使用进行持续严格的审查。

如果你计划在私有化部署的 runner 上运行极狐GitLab CI/CD 任务,那么你的计算基础设施和网络存在安全风险。

runner 执行在 CI/CD 任务中定义的代码。任何对项目仓库拥有开发者角色的用户都可能无意或故意地危害托管 runner 的环境的安全。

如果你的私有化部署 runner 是非短暂性的,并用于多个项目,这种风险会更加严重。

  • 来自嵌入恶意代码的仓库的任务可能危害由非短暂性 runner 服务的其他仓库的安全。
  • 根据执行程序的不同,任务可以在托管 runner 的虚拟机上安装恶意代码。
  • 暴露给在受损环境中运行的任务的密钥变量可能会被盗取,包括但不限于 CI_JOB_TOKEN。
  • 拥有开发者角色的用户可以访问与项目相关的子模块,即使他们没有访问子模块上游项目的权限。

不同执行程序的安全风险#

根据你使用的执行程序,你可能会面临不同的安全风险。

使用 Shell 执行程序#

使用 shell 执行程序运行构建时,你的 runner 主机和网络存在高安全风险。 任务是以极狐GitLab Runner 的用户权限运行的,可以窃取在此服务器上运行的其他项目的代码。仅在运行受信任的构建时使用它。

使用 Docker 执行程序#

在非特权模式下运行时,Docker 可以被认为是安全的。 为了使这种配置更加安全,在 Docker 容器中以非 root 用户身份运行任务,并禁用 sudo 或删除 SETUIDSETGID 功能。

在非特权模式下可以通过 cap_add/cap_drop 设置配置更细化的权限。

在 Docker 中,特权容器拥有主机虚拟机的所有 root 功能。有关更多信息,请查看官方 Docker 文档关于运行时特权和 Linux 能力。

不建议在特权模式下运行容器。

启用特权模式时,运行 CI/CD 任务的用户可能获得对 runner 主机系统的完全 root 访问权限,权限包括挂载和分离卷以及运行嵌套容器。

通过启用特权模式,你实际上禁用了所有容器的安全机制,并暴露你的主机给特权升级,从而导致容器逃逸。

如果你使用 Docker Machine 执行程序,我们还强烈建议使用 MaxBuilds = 1 设置,确保一个单一的自动扩展虚拟机(可能因为特权模式引入的安全漏洞而受损)用于处理唯一一个任务。

使用 if-not-present 拉取策略的私有 Docker 镜像#

在使用高级配置:使用私有容器注册表中描述的私有 Docker 镜像支持时,你应该使用 always 作为 pull_policy 值。特别是如果你托管一个公共实例 runner,并使用 Docker 或 Kubernetes 执行程序时,你应该使用 always 拉取策略。

让我们考虑一个例子,其中拉取策略设置为 if-not-present

  1. 用户 A 在 registry.example.com/image/name 拥有一个私有镜像。
  2. 用户 A 在实例 runner 上启动构建:构建接收到注册表凭据并在注册表中授权后拉取镜像。
  3. 镜像存储在实例 runner 的主机上。
  4. 用户 B 无法访问 registry.example.com/image/name 的私有镜像。
  5. 用户 B 在与用户 A 相同的实例 runner 上启动使用此镜像的构建:runner 找到镜像的本地版本并使用它即使因为缺少凭据而无法拉取镜像

因此,如果你托管的 runner 可以被不同用户和不同项目使用(具有混合的私有和公共访问级别),你绝不应该使用 if-not-present 作为拉取策略值,而是使用:

  • never - 如果你想限制用户仅使用你预先下载的镜像。
  • always - 如果你想给用户提供从任何注册表下载任何镜像的可能性。

if-not-present 拉取策略应该用于特定的 runner,并由受信任的构建和用户使用。

阅读拉取策略文档以获取更多信息。

安装了 Docker 的系统#

这适用于 0.5.0 以下的安装或已升级到较新版本的安装。

在安装了 Docker 的 Linux 系统上安装极狐GitLab Runner 软件包时,gitlab-runner 创建一个有权访问 Docker 守护进程的用户。这使得使用 shell 执行程序运行的任务能够以完全权限访问 docker,并可能允许对服务器的 root 访问。

使用 SSH 执行程序#

SSH 执行程序容易受到 MITM 攻击(中间人攻击),因为缺少 StrictHostKeyChecking 选项。这将在未来的版本中修复。

使用 Parallels 执行程序#

Parallels 执行程序是最安全的选项,因为它使用完整的系统虚拟化,并且虚拟机被配置为在隔离模式下运行。它阻止了对所有外设和共享文件夹的访问。

克隆一个 runner#

Runners 使用令牌来识别极狐GitLab 服务器。如果你克隆一个 runner,那么克隆的 runner 可能会接收用于该令牌的相同任务。这是一个可能的攻击向量,用于“窃取” runner 任务。

在共享环境中使用 GIT_STRATEGY: fetch 的安全风险#

当你将 GIT_STRATEGY 设置为 fetch 时,runner 尝试重用 Git 仓库的本地工作副本。

使用本地副本可以提高 CI/CD 任务的性能。然而,任何有权访问该可重用副本的用户都可以添加在其他用户的流水线中执行的代码。

Git 将子模块(嵌入在另一个仓库中的仓库)的内容存储在父仓库的 Git reflog 中。因此,在项目的子模块被初次克隆后,后续任务可以通过在脚本中运行 git submodule update 来访问子模块的内容。这适用于即使子模块已被删除且启动任务的用户没有访问子模块项目的权限。

仅当你信任所有有权访问共享环境的用户时,才使用 GIT_STRATEGY: fetch

安全加固选项#

减少使用特权容器的安全风险#

如果你必须运行需要使用 Docker 的 --privileged 标志的 CI/CD 任务,可以采取以下步骤来降低安全风险:

  • 仅在隔离和短暂的虚拟机上运行启用了 --privileged 标志的 Docker 容器。
  • 配置专用的 runner 来执行需要使用 Docker 的 --privileged 标志的任务。然后将这些 runner 配置为仅在受保护的分支上执行任务。

网络分段#

极狐GitLab Runner 旨在运行用户控制的脚本。为了减少如果任务是恶意的攻击面,你可以考虑在它们自己的网络段中运行它们。这将提供与其他基础设施和服务的网络隔离。

所有需求都是独特的,但对于云环境,这可能包括:

  • 在自己的网络段中配置 runner 虚拟机
  • 阻止来自互联网的 SSH 访问到 runner 虚拟机
  • 限制 runner 虚拟机之间的流量
  • 过滤对云提供商元数据端点的访问

所有 runner 都需要出站网络连接到 JihuLab.com 或你的极狐GitLab 实例。 大多数任务还需要出站网络连接到互联网 - 以获取依赖项等。

保护 runner 主机#

如果你为 runner 使用静态主机,无论是裸机还是虚拟机,你都应该为主机操作系统实施安全最佳实践。

在 CI 任务上下文中执行的恶意代码可能会危害主机,所以安全协议可以帮助减轻影响。需要记住的其他要点包括保护或删除主机系统上的文件(如 SSH 密钥),这些文件可能会使攻击者能够访问环境中的其他端点。

在每次构建后清理 .git 文件夹#

如果你为你的 runner 使用静态主机,可以通过启用 FF_ENABLE_JOB_CLEANUP 功能标志来实施额外的安全层。

当你启用 FF_ENABLE_JOB_CLEANUP 时,runner 在主机上使用的构建目录会在每次构建后清理。