glsec

Rules / GL054

GL054 Docker-in-Docker service implies a privileged runner

Severity: warn
OWASP: CICD-SEC-7 – Insecure System Configuration
CWE: CWE-250 – Execution with Unnecessary Privileges

Risk

Using docker:dind as a service requires the GitLab Runner to be configured with privileged = true in its config.toml. A privileged runner grants the job full root access to the host kernel — container isolation is effectively removed. Any compromised dependency, injected script, or malicious image running in that job can escape the container and access the runner host, other jobs' secrets, or the broader network.

privileged: true cannot be set in .gitlab-ci.yml itself, but the presence of a docker:dind service is a reliable indicator that the runner must be privileged. This makes DinD usage the detectable proxy for a privileged-runner requirement.

Trigger example

build-image:
  services:
    - docker:26.0-dind
  variables:
    DOCKER_HOST: tcp://docker:2375
    DOCKER_TLS_CERTDIR: ""
  script:
    - docker build -t myapp .
    - docker push myapp

The rule also flags a DOCKER_HOST: tcp://docker:2375 (or :2376) variable on its own, since it points at a DinD daemon even when the service is declared elsewhere.

Safe alternatives

Kaniko (runs as non-root, no Docker daemon):

build-image:
  image:
    name: gcr.io/kaniko-project/executor:v1.21.0-debug
    entrypoint: [""]
  script:
    - /kaniko/executor --context $CI_PROJECT_DIR --destination $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG

Buildah (rootless builds):

build-image:
  image: quay.io/buildah/stable:v1.35.0
  script:
    - buildah bud -t myapp .
    - buildah push myapp

GitLab-managed Docker socket binding (uses the runner host's Docker socket — reduces isolation but avoids full privileged mode):

build-image:
  image: docker:26.0
  variables:
    DOCKER_HOST: unix:///var/run/docker.sock
  script:
    - docker build -t myapp .

Notes

Documentation for glsec 1.23.0. Source of this page · Rule implementation