Is your feature request related to a problem? Please describe.
When a CI job uses services: [docker:dind] (Docker-in-Docker), it creates a Docker daemon inside the CI container. On shared runners running in privileged mode, this enables:
- Container escape: Access to the host's Docker daemon and kernel namespaces
- Lateral movement: Listing/inspecting containers from other projects running on the same runner
- Volume access: Reading volumes mounted by other CI jobs (potentially containing secrets)
- Network reconnaissance: Probing the runner's internal network
This is a well-known attack vector documented in GitLab CI security best practices.
Describe the solution you'd like
Add a new control pipelineMustNotUseDockerInDocker that:
- Detects
docker:dind (and variants like docker:*-dind, docker:latest) in services: blocks
- Optionally detects insecure configurations:
DOCKER_TLS_CERTDIR: "" or DOCKER_HOST: tcp://docker:2375 (unencrypted)
- Reports each affected job with a warning
Configuration in .plumber.yaml
controls:
pipelineMustNotUseDockerInDocker:
enabled: true
# Suggest alternatives in the output
suggestAlternatives: true # e.g., kaniko, buildah
# Also flag insecure Docker daemon configuration
detectInsecureDaemon: true # DOCKER_TLS_CERTDIR="" or tcp://docker:2375
Implementation Hints
These are just ideas. Feel free to change the implementation.
- Data source: The
PipelineImage collector already parses images from CI jobs, including services:. The PipelineOrigin collector provides the merged CI config with per-job variables.
- New control file: Create
control/controlGitlabPipelineDockerInDocker.go.
- Logic:
- Iterate over all jobs from the merged CI config
- For each job, check
services: for images matching docker:dind or docker:*-dind
- Optionally check job variables for
DOCKER_TLS_CERTDIR: "" or DOCKER_HOST pointing to tcp://...:2375 (non-TLS)
- Compliance: 0% if any job uses
docker:dind, 100% otherwise.
- Output: Include the job name, the dind image reference, and (if
suggestAlternatives is enabled) a recommendation to use kaniko or buildah instead.
Files Touched
control/controlGitlabPipelineDockerInDocker.go (new control)
control/types.go (add result field to AnalysisResult)
control/task.go (wire the new control in RunAnalysis())
configuration/plumberconfig.go (add config struct and getter)
.plumber.yaml (add default config section)
cmd/analyze.go (add output formatting)
Why It's Valuable
Docker-in-Docker is one of the most common and dangerous patterns in CI/CD pipelines, especially on shared runners. While it's sometimes necessary, teams should be aware of the risk and consider alternatives like kaniko or buildah. This control raises visibility on the attack surface without necessarily blocking the build — compliance teams can decide whether to enforce or just warn.
Is your feature request related to a problem? Please describe.
When a CI job uses
services: [docker:dind](Docker-in-Docker), it creates a Docker daemon inside the CI container. On shared runners running in privileged mode, this enables:This is a well-known attack vector documented in GitLab CI security best practices.
Describe the solution you'd like
Add a new control
pipelineMustNotUseDockerInDockerthat:docker:dind(and variants likedocker:*-dind,docker:latest) inservices:blocksDOCKER_TLS_CERTDIR: ""orDOCKER_HOST: tcp://docker:2375(unencrypted)Configuration in
.plumber.yamlImplementation Hints
These are just ideas. Feel free to change the implementation.
PipelineImagecollector already parses images from CI jobs, includingservices:. ThePipelineOrigincollector provides the merged CI config with per-job variables.control/controlGitlabPipelineDockerInDocker.go.services:for images matchingdocker:dindordocker:*-dindDOCKER_TLS_CERTDIR: ""orDOCKER_HOSTpointing totcp://...:2375(non-TLS)docker:dind, 100% otherwise.suggestAlternativesis enabled) a recommendation to use kaniko or buildah instead.Files Touched
control/controlGitlabPipelineDockerInDocker.go(new control)control/types.go(add result field toAnalysisResult)control/task.go(wire the new control inRunAnalysis())configuration/plumberconfig.go(add config struct and getter).plumber.yaml(add default config section)cmd/analyze.go(add output formatting)Why It's Valuable
Docker-in-Docker is one of the most common and dangerous patterns in CI/CD pipelines, especially on shared runners. While it's sometimes necessary, teams should be aware of the risk and consider alternatives like kaniko or buildah. This control raises visibility on the attack surface without necessarily blocking the build — compliance teams can decide whether to enforce or just warn.