在私有化部署场景中,极狐GitLab Runner 是 CI/CD 流水线真正执行构建、测试与部署任务的节点。它运行的是来自项目仓库的脚本,本质上提供的是远程代码执行能力。如果 Runner 主机暴露在共享网络中、使用高权限执行器,或者复用同一套环境处理多个项目,安全风险会被迅速放大。
本文从执行器选择、容器隔离、网络分段、镜像策略、主机加固、Git 清理六个维度,梳理一套可直接落地的 Runner 自托管安全加固方案。所有配置项均来自极狐GitLab 官方文档,适用于 JihuLab.com 与私有化部署两种场景。
01 为什么 Runner 安全容易被忽视
很多团队在部署极狐GitLab 时,会把主要精力放在服务端:HTTPS、备份、高可用、权限模型。但对 Runner 的关注往往停留在"能跑起来就行"。
这种疏忽的代价很高。任何拥有项目开发者角色的用户,都可以通过.gitlab-ci.yml在 Runner 上执行任意命令。如果 Runner 使用 Shell 执行器、以 root 身份运行,并且长期服务于多个项目,那么:
- 一个嵌入恶意代码的任务可以读取同一 Runner 上其他项目的代码;
- 任务可以窃取 CI/CD 变量,包括
CI_JOB_TOKEN; - 任务可以安装持久化后门,影响后续所有在该 Runner 上执行的构建。
因此,Runner 的安全加固不是可选项,而是私有化部署 CI/CD 的必选项。
02 执行器选择:隔离级别决定安全基线
极狐GitLab Runner 支持多种执行器,安全隔离级别差异很大。官方文档给出了明确的安全排序:
| 执行器 | 隔离方式 | 安全风险 | 适用场景 |
|---|---|---|---|
| Shell | 无隔离 | 高 | 仅受信任的单一项目 |
| SSH | 远程主机执行 | 高(易受中间人攻击) | 不推荐长期使用 |
| Docker | 容器隔离 | 中(非特权模式下可控) | 大多数场景 |
| Kubernetes | Pod 隔离 | 中 | 云原生大规模构建 |
| Parallels / VirtualBox | 完整虚拟机隔离 | 低 | 对隔离要求最高的场景 |
Shell 执行器的风险最高,因为任务直接以 Runner 进程用户的权限在主机上运行。除非是在完全受信、单一用途的 Runner 上,否则不应使用 Shell 执行器。
如果必须使用容器化能力,优先选择 Docker 或 Kubernetes 执行器,并确保容器运行在非特权模式。特权容器会获得主机 root 的所有能力,包括挂载文件系统、运行嵌套容器,一旦发生容器逃逸,主机完全失守。
[[runners]] executor = "docker" [runners.docker] privileged = false services_privileged = false03 Docker 执行器加固:最小权限原则
在非特权模式下,Docker 执行器已经提供了基础隔离。但要让它更安全,还需要做几件事。
以非 root 用户运行
默认情况下,容器内进程可能以 root 运行。即使容器被限制,root 进程仍比非 root 进程拥有更大的攻击面。建议在config.toml中为 Runner 指定一个非特权用户:
[runners.docker] user = "gitlab-runner"同时在镜像构建阶段创建该用户,避免使用 root 作为默认运行身份。
收缩 Linux Capability
容器默认携带的 Linux Capability 可能超过 CI/CD 任务实际需要。通过cap_drop删除不必要的权限,是减少攻击面的有效手段。
[runners.docker] cap_drop = ["ALL"] cap_add = ["NET_BIND_SERVICE"]上述配置先移除所有 capability,再按需添加。具体需要保留哪些 capability,取决于你的构建任务是否需要监听低端口、加载内核模块等特殊能力。
固定镜像并始终拉取
镜像拉取策略是 Runner 安全中最容易踩坑的地方之一。if-not-present策略会在本地没有镜像时拉取,本地存在时复用。这个策略在共享 Runner 上会造成信息泄露:用户 A 用私有凭据拉取的镜像可能残留在 Runner 主机,用户 B 随后启动的构建可以复用该镜像,即使 B 本身没有拉取权限。
官方建议:对于被多项目、多用户共享的 Runner,使用always拉取策略;如果希望严格限制镜像来源,则使用never并结合预下载白名单。
[runners.docker] pull_policy = ["always"] allowed_images = ["registry.example.com/ci/*:*", "docker.io/library/alpine:*"]启用用户命名空间
用户命名空间可以把容器内的 root 映射到主机上的非特权用户。即使容器发生逃逸,攻击者在主机上也只是普通用户权限。配置方式是在config.toml中启用:
[runners.docker] userns_mode = "host"同时需要在 Docker 守护进程中开启userns-remap功能。这是一项系统级配置,不能仅在 Runner 层面完成。
04 网络分段:把构建节点放进独立的安全域
Runner 运行的是用户控制的脚本,网络行为不可预测。把 Runner 和其他内部服务放在同一网络段,意味着一旦任务被篡改,攻击者可能横向移动。
官方文档建议为 Runner 规划独立的网络段,至少包含以下控制点:
- Runner 虚拟机部署在独立的子网或 VPC 中;
- 禁止 Runner 之间的自由流量互访;
- 限制 Runner 访问云厂商的元数据端点;
- 关闭 Runner 面向互联网的 SSH 访问,统一使用堡垒机或串口管理。
所有 Runner 都需要出站到极狐GitLab 实例或 JihuLab.com。大多数构建任务还需要出站到互联网拉取依赖。其余访问应尽可能通过安全组、防火墙或零信任策略收紧。
05 主机加固:静态 Runner 的最后一道防线
如果你的 Runner 运行在静态主机上,而不是每次构建后销毁的短暂实例,那么主机本身的安全基线决定了 worst-case 影响范围。
启用构建目录清理
长期运行的 Runner 会在主机上留下构建目录。.git目录、缓存、产物都可能被后续任务读取。官方提供功能标志FF_ENABLE_JOB_CLEANUP,开启后 Runner 会在每次构建结束后清理构建目录。
[runners] environment = ["FF_ENABLE_JOB_CLEANUP=1"]清理 Git 配置
从 Runner 17.10 开始,clean_git_config默认启用。它会在每次构建开始和结束时清理 Git 锁文件、post-checkout hooks 以及.git/config和.git/hooks目录,防止恶意 Git 配置在任务之间残留。
[runners] clean_git_config = true保护主机上的敏感文件
静态主机上不要留存可以被 CI/CD 任务读取的 SSH 私钥、云凭证或.docker/config.json。如果 Runner 需要访问私有镜像仓库,应通过 CI/CD 变量DOCKER_AUTH_CONFIG注入,而不是依赖主机本地配置。并且该变量建议使用file 类型变量,避免在日志中泄露。
限制并发与日志
concurrent = 8 output_limit = 4096 [[runners]] limit = 4 [runners.docker] memory = "2g" cpus = "2"通过concurrent、limit、memory、cpus限制资源使用,既能防止资源耗尽型攻击,也能避免单个恶意任务拖垮整个 Runner。
关闭调试跟踪
CI_DEBUG_TRACE=true会输出所有环境变量和命令执行细节,可能泄露敏感变量。在 Runner 配置中关闭调试跟踪,可以防止用户通过 CI 变量强行开启。
[runners] debug_trace_disabled = true06 SSH 与 Git 安全:两个容易忽略的点
SSH 执行器
如果你还在使用 SSH 执行器,需要注意官方文档中的明确警告:SSH 执行器缺少StrictHostKeyChecking选项,容易受到中间人攻击。在短期无法替换执行器的情况下,至少应确保目标主机密钥被预先分发到 Runner 主机的known_hosts,并尽量通过 VPN 或专线连接。
Git 策略
GIT_STRATEGY: fetch可以复用本地仓库副本,提高构建速度。但在共享 Runner 上,这意味着一个项目的.git目录会被其他项目复用,可能引入子模块残留或 reflog 中的敏感信息。只有在信任所有访问共享环境的用户时,才应启用fetch策略。
07 特权容器:如果必须用它,请隔离到短命虚拟机
有些构建任务确实需要--privileged标志,例如在 Docker 中运行 Docker(DinD)。这种情况下,加固思路不是禁用特权,而是把特权任务关进最短的牢笼:
- 仅让专用 Runner 运行特权任务;
- 该 Runner 只处理受保护分支;
- Runner 主机使用短暂虚拟机,每个实例只运行一个或少量构建后就被销毁。
如果使用 Docker Machine 执行器,可以配置MaxBuilds = 1,确保每个自动扩展虚拟机只处理一个构建后就被回收。
[runners.machine] MaxBuilds = 108 升级与注册:一些基础但关键的操作
保持 Runner 版本与极狐GitLab 实例版本接近,可以及时获得安全修复。使用官方仓库安装时,建议固定到具体版本,而不是默认安装最新版,避免 CI/CD 环境被意外升级破坏。
# Debian/Ubuntu 示例 apt-cache madison gitlab-runner sudo apt install gitlab-runner=17.7.1-1 gitlab-runner-helper-images=17.7.1-1注册 Runner 时,不要把注册令牌硬编码在脚本或仓库中。注册完成后, Runner 使用与极狐GitLab 服务器通信的令牌来识别身份。不要克隆一个已注册的 Runner 到另一台主机,否则两个 Runner 共享同一令牌,会互相"抢任务",也成为潜在攻击向量。
写在最后
Runner 安全不是单一配置可以解决的问题,而是一组叠加的防护层:从执行器选择开始,到容器隔离、网络分段、主机加固、Git 清理,再到特权场景的最小化暴露。每一层都不能替代其他层。
对于企业私有化部署,建议把 Runner 视为与极狐GitLab 服务端同等重要的安全对象。一个配置不当的 Runner,可能成为从 CI/CD 环境横向进入内部网络的跳板。
极狐GitLab 官方文档提供了完整的安全指南和高级配置项。在部署或审查 Runner 时,建议对照本文清单逐条核对,把"能跑起来"变成"能安全地跑起来"。