BlogCI/CD

Hardening self-hosted GitLab runners

Isolate jobs, scope CI tokens, and pin executors so one poisoned pipeline can't pivot into your whole GitLab runner fleet or leak another team's secrets.

Aug 13, 2024·4 min readAdvanced·By the SecOpsLog team · command-tested

A self-hosted GitLab runner executes whatever a pipeline script contains — including malicious code from a fork merge request or a compromised dependency install step. If runners are privileged, share a Docker socket, reuse dirty workspaces, or hold org-wide deploy tokens, one bad job owns the host and every other project's CI variables. Hardening is isolation and least privilege: treat each job as untrusted code on a machine that also holds secrets.

You will disable privileged mode, never mount /var/run/docker.sock, use Kaniko or Buildah for image builds, scope CI_JOB_TOKEN to the minimum project access, and assign dedicated runners per team instead of one shared fleet. Protected variables, protected branches, and fork MR rules complete the picture in Secure CI/CD with GitLab.

From shared runner risk to isolated executor

Every checkbox in config.toml is a decision about whether a poisoned pipeline becomes a host takeover.

1Audit runnersprivileged? docker.sock?2Disable privilegedexecutor defaults3Rootless buildKaniko / Buildah4Fresh workspaceclean git checkout5Per-group runnerno org-wide fleet6Protect secretsprotected branches only7Monitor + patchrunner auto-update

Do not run privileged

Privileged Docker executors exist mainly for Docker-in-Docker image builds — and they are equivalent to giving every job root on the runner host. Use Kaniko or Buildah inside an unprivileged container to push images instead. If you must use Docker, use a dedicated isolated builder pool with no access to production secrets — not the same runners that deploy prod.

config.toml
[[runners]]
name = "acme-unprivileged"
executor = "docker"
[runners.docker]
privileged = false
disable_cache = false
volumes = ["/cache"]
# NEVER: "/var/run/docker.sock:/var/run/docker.sock"

Scope the tokens and variables

The job token is short-lived but powerful within GitLab's API — restrict which projects it can reach via CI/CD job token scope settings. Mark deploy credentials as protected so only protected branches and tags receive them. Fork MR pipelines from external contributors should not run deploy jobs at all; use rules: and manual approval for contributions that need CI but not secrets.

bash — signs of an exposed runnerlive
grep privileged /etc/gitlab-runner/config.toml
privileged = true
grep docker.sock /etc/gitlab-runner/config.toml
/var/run/docker.sock:/var/run/docker.sock
either finding = treat runner as compromised, rotate all CI secrets

Separate runners by trust zone

Tag runners so production deploy jobs land only on hardened hosts in a locked network segment; build and test jobs run on a general pool with fewer secrets. Auto-scaled runners should ephemeralize disks so one job cannot leave artifacts for the next. Keep runner manager and worker patches current — stale runner versions are a known attack surface.

Shared-runner risk
Dangerous defaults
privileged = true
Docker socket mounted
Shared across all projects
Protected secrets on fork MRs
Hardened pattern
Unprivileged Kaniko builds
Per-team runner tags
CI_JOB_TOKEN scoped narrow
Deploy only on protected refs
A privileged runner is a host takeover
privileged = true plus a mounted Docker socket means any job can escape to the runner host, read `/etc/gitlab-runner/config.toml`, and exfiltrate every CI variable cached on that machine. Treat misconfigured runners like a root breach — rotate secrets, audit job history, rebuild the host.

Where this goes next

Runner isolation lets you enforce SAST, secret scanning, and signed-image gates without fearing that a fork MR steals production kubeconfig. Wire scanning jobs on shared runners and deploy jobs on locked runners with OIDC to your cluster. Secure CI/CD with GitLab covers runners, protected pipelines, environments, and supply-chain controls as one system.

Go deeper in a courseSecure CI/CD with GitLabRunner isolation, scoped tokens, protected pipelines, and scanning gates.View course

Related posts