私有化部署 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 或删除 SETUID 和 SETGID 能力。
在非特权模式下,可以通过 cap_add/cap_drop 设置配置更细粒度的权限。
Docker 中的特权容器拥有宿主虚拟机的所有 root 能力。 有关更多信息,请查看官方 Docker 文档 运行时权限和 Linux 能力 同样,在宿主 PID 命名空间中运行容器会破坏容器隔离,这是不安全的。
不建议在特权模式下运行容器,也不建议使用 --pid=host 标志。
当启用特权模式时,运行 CI/CD 作业的用户可以获得 Runner 宿主系统的完全 root 访问权限,可以挂载和卸载卷,并运行嵌套容器。
通过启用特权模式,您实际上禁用了容器的所有安全机制,并使您的宿主面临权限提升的风险,这可能导致容器逃逸。
如果您使用 Docker Machine 执行器,我们还强烈建议使用 MaxBuilds = 1 设置,这可以确保单个自动扩缩的虚拟机(可能因特权模式引入的安全弱点而受损)仅用于处理一个且仅一个作业。
使用带有 if-not-present 拉取策略的私有 Docker 镜像
当使用 高级配置:使用私有容器镜像仓库 中描述的私有 Docker 镜像支持时,您应该使用 always 作为 pull_policy 的值。特别是,如果您使用 Docker 或 Kubernetes 执行器托管公共实例 Runner,则应使用 always 拉取策略。
让我们考虑一个拉取策略设置为 if-not-present 的示例:
- 用户 A 在 registry.example.com/image/name 有一个私有镜像。
- 用户 A 在实例 Runner 上启动构建:构建接收镜像仓库凭据,并在镜像仓库授权后拉取镜像。
- 该镜像存储在实例 Runner 的主机上。
- 用户 B 无权访问 registry.example.com/image/name 的私有镜像。
- 用户 B 在与用户 A 相同的实例 Runner 上启动使用此镜像的构建:Runner 会找到该镜像的本地版本并使用它,即使由于缺少凭据而无法拉取该镜像。
因此,如果您托管的 Runner 可以被不同的用户和不同的项目(混合了私有和公共访问级别)使用,则绝不应使用 if-not-present 作为拉取策略值,而应使用:
- never - 如果您希望限制用户仅使用您预先下载的镜像。
- always - 如果您希望允许用户从任何镜像仓库下载任何镜像。
if-not-present 拉取策略应仅用于由受信任的构建和用户使用的特定 Runner。
请阅读 拉取策略文档 了解更多信息。
使用 Docker 凭据助手
作业的 DOCKER_AUTH_CONFIG 变量引用的 Docker 凭据助手作为二进制文件在 Runner 管理器主机上执行,而不是在作业环境中执行。能够执行任意 docker-credential-* 二进制文件的作业可以使用该主机的身份拉取镜像。云 SDK 和 Docker Desktop 会作为副作用安装凭据助手,因此即使您从未配置过,也可能存在助手。
默认情况下,极狐GitLab Runner 不会执行作业提供的 DOCKER_AUTH_CONFIG 所引用的任何凭据助手。要使用凭据助手,请列出您信任的助手及其可服务的镜像仓库,可通过 allowed_docker_credential_helpers 设置 进行配置。
此设置不限制 Runner 主机自身的 ~/.docker/config.json 和 ~/.dockercfg 文件。它们提供的每个凭据都可供 Runner 执行的每个作业使用。在共享 Runner 上,不要以 Runner 用户身份运行 docker login。不要将 Docker 配置文件保存在 Runner 用户的主目录中。
使用 SSH 执行器
SSH 执行器容易受到 MITM 攻击(中间人攻击),因为缺少 StrictHostKeyChecking 选项。这将在未来的某个版本中修复。
使用 Parallels 执行器
Parallels 执行器是最安全的选择,因为它使用完整的系统虚拟化,并且虚拟机配置为在隔离的虚拟化模式下运行。它会阻止对所有外围设备和共享文件夹的访问。
克隆 Runner
Runner 使用令牌向极狐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 虚拟机配置在它们自己的网络分段中
- 阻止从互联网对 Runner 虚拟机进行 SSH 访问
- 限制 Runner 虚拟机之间的流量
- 过滤对云提供商元数据端点的访问
所有 Runner 都需要出站网络连接才能连接到 JihuLab.com 或您的极狐GitLab 实例。 大多数作业还需要出站网络连接才能连接到 互联网 - 用于依赖拉取等。
保护 Runner 主机
如果您为 Runner 使用静态主机(无论是裸机还是虚拟机),您应该为宿主操作系统实施安全最佳实践。
在 CI 作业上下文中执行的恶意代码可能会危及主机,因此安全协议有助于减轻影响。其他需要记住的要点包括保护或删除主机系统上的 SSH 密钥等文件,这些文件可能使攻击者能够访问环境中的其他端点。
每次构建后清理 .git 文件夹
如果您为 Runner 使用静态主机,您可以通过启用 FF_ENABLE_JOB_CLEANUP 功能标志 来实施额外的安全层。
当您启用 FF_ENABLE_JOB_CLEANUP 时,您的 Runner 在主机上使用的构建目录会在每次构建后被清理。