Docker Socket Proxy - CI/CD Security
433
Secure Docker proxy for CI/CD, DevOps and multi-tenant environments with advanced and granular filtering system.
A professional Docker socket proxy with advanced regex filtering, specifically designed to secure CI/CD pipelines (GitHub Actions, GitLab CI, Jenkins, CircleCI, etc.) and cloud-native environments. Inspired by Tecnativa/docker-socket-proxyโ , implemented in high-performance Go with Gin and Resty.
๐ณ Docker Hub: hypolas/dockershieldโ ๐ฆ GitHub: hypolas/dockershieldโ
Ideal for securing your CI/CD pipelines by exposing only the necessary Docker functionalities:
This Docker proxy offers advanced security for your Docker environments through:
Ideal for cloud-native security, Kubernetes, Docker Swarm, PaaS, and CI/CD pipelines (GitHub Actions, GitLab CI, Jenkins, CircleCI, Azure DevOps).
# Block mounting sensitive directories
export DKRPRX__VOLUMES__DENIED_PATHS="^/etc/.*,^/root/.*,^/home/.*"
# Allow only images from a private registry
export DKRPRX__CONTAINERS__ALLOWED_IMAGES="^registry.company.com/.*"
# Block :latest tag
export DKRPRX__IMAGES__DENIED_TAGS="^latest$"
# Block privileged containers
export DKRPRX__CONTAINERS__DENY_PRIVILEGED="true"
# Require specific labels
export DKRPRX__CONTAINERS__REQUIRE_LABELS="env=production,team=backend"
THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
The developer disclaims all liability concerning:
You use this proxy at your own risk. It is your responsibility to:
Pull the pre-built image from Docker Hub:
# Pull latest version
docker pull hypolas/dockershield:latest
# Or specific version
docker pull hypolas/dockershield:1.0.0
# Run it
docker run -d \
--name dockershield \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
-p 2375:2375 \
-e CONTAINERS=1 \
-e IMAGES=1 \
hypolas/dockershield:latest
Multi-platform support:
linux/amd64 (x86_64)linux/arm64 (ARM 64-bit - Raspberry Pi 4, Apple M1/M2, AWS Graviton)linux/arm/v7 (ARM 32-bit - Raspberry Pi 2/3)Download ready-to-use binaries from GitHub Releases:
# Linux amd64
wget https://github.com/hypolas/dockershield/releases/latest/download/dockershield-linux-amd64
chmod +x dockershield-linux-amd64
./dockershield-linux-amd64
# macOS Apple Silicon
wget https://github.com/hypolas/dockershield/releases/latest/download/dockershield-darwin-arm64
chmod +x dockershield-darwin-arm64
./dockershield-darwin-arm64
# Windows
# Download from: https://github.com/hypolas/dockershield/releases/latest
Available platforms:
Verify checksums:
wget https://github.com/hypolas/dockershield/releases/latest/download/checksums.txt
sha256sum -c checksums.txt --ignore-missing
# Clone repository
git clone https://github.com/hypolas/dockershield.git
cd dockershield
# Build binary
go mod download
go build -o dockershield ./cmd/dockershield
# Or build Docker image
docker build -t hypolas/dockershield .
Configuration is done via environment variables:
| Variable | Description | Default |
|---|---|---|
LISTEN_SOCKET | ๐ Recommended: Unix socket to listen on (format: unix:///path or /path). Takes priority over LISTEN_ADDR. More secure than TCP. | - |
LISTEN_ADDR | TCP listen address (less secure, use LISTEN_SOCKET if possible) | :2375 |
DOCKER_SOCKET | Path to Docker socket (formats: unix:///path, /path, or tcp://host:port) | unix:///var/run/docker.sock |
LOG_LEVEL | Log level (debug, info, warn, error) | info |
API_VERSION | Docker API version (auto-detected if not set) | Auto-detection |
SOCKET_PERMS | Permissions for Unix socket created by proxy (octal format) | 0666 |
๐ Security: Prefer
LISTEN_SOCKET(Unix socket) overLISTEN_ADDR(TCP). Unix sockets offer better permission control via the filesystem and avoid network exposure.
Allowed by default (value: 1):
EVENTS - Docker eventsPING - HealthcheckVERSION - Docker versionDenied by default (value: 0), must be explicitly enabled:
AUTH - AuthenticationBUILD - Image buildingCOMMIT - Container commitCONFIGS - Swarm configsCONTAINERS - Container managementDISTRIBUTION - Image distributionEXEC - Command executionIMAGES - Image managementINFO - System informationNETWORKS - Network managementNODES - Swarm nodesPLUGINS - Docker pluginsSECRETS - Swarm secretsSERVICES - Swarm servicesSESSION - SessionsSWARM - Swarm modeSYSTEM - Docker systemTASKS - Swarm tasksVOLUMES - Volume managementGET, HEAD: Always allowed (read-only)POST: Default 0 (set POST=1 to enable)DELETE: Default 0 (set DELETE=1 to enable)PUT, PATCH: Default 0 (set PUT=1 to enable)export CONTAINERS=1
export IMAGES=1
./dockershield
export CONTAINERS=1
export IMAGES=1
export POST=1
export DELETE=1
./dockershield
# Listen on Unix socket instead of TCP
export LISTEN_SOCKET=unix:///tmp/dockershield.sock
export CONTAINERS=1
export IMAGES=1
./dockershield
# Test with curl
curl --unix-socket /tmp/dockershield.sock http://localhost/v1.41/containers/json
๐ Security:
LISTEN_SOCKETalways takes precedence overLISTEN_ADDR. Unix sockets avoid network exposure and offer better permission control.โ ๏ธ Docker: If you use
LISTEN_SOCKET=unix:///tmp/dockershield.sock, you must mount the corresponding directory in volumes:-v /tmp:/tmp. The path in theunix:///pathformat must match the mounted volume.
Proxy configuration sample:
services:
dockershield:
build: .
image: hypolas/dockershield:latest
container_name: dockershield
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- /tmp:/tmp # โ ๏ธ IMPORTANT: must match the path in LISTEN_SOCKET
environment:
# Basic configuration (unix:// format recommended)
- LISTEN_SOCKET=unix:///tmp/dockershield.sock
- DOCKER_SOCKET=unix:///var/run/docker.sock
โ ๏ธ Important: The mounted volume (
/tmp:/tmp) must match the path defined inLISTEN_SOCKET. If you useLISTEN_SOCKET=unix:///tmp/dockershield.sock, you must mount/tmp:/tmp.
graph LR
subgraph "Agent Container"
agent[CI/CD agent]
end
subgraph "Proxy Container"
proxy[dockershield]
proxySock[[unix:///tmp/dockershield.sock]]
end
subgraph "Docker Host"
dockerSock[[/var/run/docker.sock]]
engine[(Docker Engine)]
end
start((Start))
start --> agent[CI/CD agent]
agent -->|"(1) Docker Proxy API calls"| proxySock
proxy <-- "(2) binds" --> proxySock
proxy -->|"(3 - OK ) validates & forwards"| dockerSock
proxy -->|"(3 - KO ) validatio failed"| proxySock
dockerSock -->|"(4) native API"| engine
engine -->|"(5) responses"| proxy
proxySock -->|"(6) responses"| agent
# .github/workflows/docker.yml
name: Docker Build
on: [push]
services:
dockershield:
image: hypolas/dockershield:latest
env:
CONTAINERS: 1
IMAGES: 1
BUILD: 1
POST: 1
# Force private registry
DKRPRX__CONTAINERS__ALLOWED_IMAGES: "^registry.company.com/.*"
# Block :latest
DKRPRX__IMAGES__DENIED_TAGS: "^latest$"
LISTEN_SOCKET: unix:///tmp/dockershield.sock
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- /tmp:/tmp # โ ๏ธ Required for LISTEN_SOCKET unix:///tmp/dockershield.sock
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build Docker image
env:
DOCKER_HOST: /tmp/dockershield.sock
run: docker build -t registry.company.com/app:${{ github.sha }} .
# .gitlab-ci.yml
variables:
DOCKER_HOST: unix:///tmp/dockershield.sock
- name: hypolas/dockershield:latest
alias: dockershield
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- /tmp:/tmp # โ ๏ธ Required for LISTEN_SOCKET
variables:
CONTAINERS: "1"
IMAGES: "1"
BUILD: "1"
POST: "1"
DKRPRX__CONTAINERS__ALLOWED_IMAGES: "^registry.company.com/.*"
LISTEN_SOCKET: unix:///tmp/dockershield.sock
build:
script:
- docker build -t registry.company.com/app:$CI_COMMIT_SHA .
- docker push registry.company.com/app:$CI_COMMIT_SHA
pipeline {
agent any
environment {
DOCKER_HOST = 'tcp://dockershield:2375'
}
stages {
stage('Build') {
steps {
sh 'docker build -t registry.company.com/app:${BUILD_NUMBER} .'
}
}
}
}
services:
dockershield:
build: .
ports:
- "2375:2375"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
- CONTAINERS=1
- IMAGES=1
- NETWORKS=1
- VOLUMES=1
- POST=0
- DELETE=0
- LOG_LEVEL=info
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o dockershield ./cmd/dockershield
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/dockershield .
EXPOSE 2375
CMD ["./dockershield"]
.
โโโ cmd/
โ โโโ dockershield/ # Application entry point
โ โโโ main.go
โโโ config/ # Configuration and env var loading
โ โโโ config.go
โโโ internal/
โ โโโ middleware/ # Gin middlewares
โ โ โโโ acl.go # Access control
โ โ โโโ logging.go # Structured logging
โ โโโ proxy/ # Proxy handler
โ โโโ handler.go
โโโ pkg/
โโโ rules/ # Access rules engine
โโโ matcher.go
The proxy's strength: an extremely granular and powerful filtering system.
Unlike basic proxies, this proxy allows fine-grained control of every aspect of Docker operations via regex patterns:
# Allow only specific named volumes
export DKRPRX__VOLUMES__ALLOWED_NAMES="^data-.*,^app-.*,^logs-.*"
# Block mounting sensitive system directories
export DKRPRX__VOLUMES__DENIED_PATHS="^/etc/.*,^/root/.*,^/sys/.*,^/proc/.*,^/var/run/.*"
# Allow only bind mounts in /data
export DKRPRX__VOLUMES__ALLOWED_PATHS="^/data/.*"
# Restrict to local drivers
export DKRPRX__VOLUMES__ALLOWED_DRIVERS="local"
# Allow only images from private registry with semantic versioning
export DKRPRX__CONTAINERS__ALLOWED_IMAGES="^registry.company.com/.*:v[0-9]+\.[0-9]+\.[0-9]+$"
# Block any image with :latest or :dev tag
export DKRPRX__CONTAINERS__DENIED_IMAGES=".*:(latest|dev|test)$"
# Require container names prefixed by environment
export DKRPRX__CONTAINERS__ALLOWED_NAMES="^(prod|staging|dev)-.*"
# Require mandatory labels
export DKRPRX__CONTAINERS__REQUIRE_LABELS="env=production,team=backend,cost-center=IT-001"
# Block privileged containers and host network
export DKRPRX__CONTAINERS__DENY_PRIVILEGED="true"
export DKRPRX__CONTAINERS__DENY_HOST_NETWORK="true"
# Allow only approved registries
export DKRPRX__IMAGES__ALLOWED_REPOS="^(docker\.io/library|registry\.company\.com)/.*"
# Block insecure registries
export DKRPRX__IMAGES__DENIED_REPOS=".*\.(cn|ru|suspicious)/"
# Allow only versioned tags (semver)
export DKRPRX__IMAGES__ALLOWED_TAGS="^v[0-9]+\.[0-9]+\.[0-9]+$"
# Block development tags
export DKRPRX__IMAGES__DENIED_TAGS="^(latest|dev|test|alpha|beta|rc).*"
# Allow only application networks
export DKRPRX__NETWORKS__ALLOWED_NAMES="^app-.*"
# Block host network (security)
export DKRPRX__NETWORKS__DENIED_NAMES="^host$"
# Restrict to bridge and overlay drivers
export DKRPRX__NETWORKS__ALLOWED_DRIVERS="bridge,overlay"
For complex configurations, use JSON:
{
"volumes": {
"allowed_names": ["^data-.*", "^app-.*"],
"denied_paths": ["^/etc/.*", "^/root/.*", "^/sys/.*", "^/proc/.*"],
"allowed_paths": ["^/data/.*", "^/mnt/volumes/.*"]
},
"containers": {
"allowed_images": ["^registry.company.com/.*:v[0-9]+\\.[0-9]+\\.[0-9]+$"],
"denied_images": [".*:(latest|dev)$"],
"require_labels": {
"env": "production",
"approved": "true"
},
"deny_privileged": true,
"deny_host_network": true
},
"networks": {
"allowed_names": ["^app-.*"],
"allowed_drivers": ["bridge", "overlay"]
},
"images": {
"allowed_repos": ["^registry.company.com/.*"],
"denied_tags": ["^latest$"]
}
}
export FILTERS_CONFIG=./filters.json
Note: Environment variables take priority over JSON.
TENANT_ID="client-123"
export DKRPRX__VOLUMES__ALLOWED_NAMES="^${TENANT_ID}-.*"
export DKRPRX__CONTAINERS__ALLOWED_NAMES="^${TENANT_ID}-.*"
export DKRPRX__CONTAINERS__REQUIRE_LABELS="tenant=${TENANT_ID}"
export DKRPRX__NETWORKS__ALLOWED_NAMES="^${TENANT_ID}-.*"
# Only versioned images from private registry
export DKRPRX__CONTAINERS__ALLOWED_IMAGES="^registry.prod.company.com/.*:v[0-9]+\.[0-9]+\.[0-9]+$"
# Only mounts in /data/prod
export DKRPRX__VOLUMES__ALLOWED_PATHS="^/data/prod/.*"
# Mandatory labels
export DKRPRX__CONTAINERS__REQUIRE_LABELS="env=production,approved=true,security-scan=passed"
# Enhanced security
export DKRPRX__CONTAINERS__DENY_PRIVILEGED="true"
export DKRPRX__CONTAINERS__DENY_HOST_NETWORK="true"
# Allow build but not latest
export DKRPRX__IMAGES__DENIED_TAGS="^latest$"
# Allow only CI registry
export DKRPRX__IMAGES__ALLOWED_REPOS="^registry.ci.company.com/.*"
# Block sensitive volumes
export DKRPRX__VOLUMES__DENIED_PATHS="^/(etc|root|home|sys|proc)/.*"
The filtering system allows as precise control as needed for your environment!
The proxy applies several protections by default to prevent privilege escalation:
The following paths are blocked by default:
/var/run/docker.sock/run/docker.sockThe dockershield container itself is protected against any manipulation:
If the proxy uses a dedicated network, it is also protected.
# Proxy container name (default: dockershield)
export PROXY_CONTAINER_NAME="dockershield"
# Proxy network name (optional)
export PROXY_NETWORK_NAME="dockershield-network"
To disable all protections:
export DKRPRX__DISABLE_DEFAULTS="true"
To explicitly allow Docker socket:
export DKRPRX__VOLUMES__ALLOWED_PATHS="^/var/run/docker\\.sock$"
:ro)To test the proxy:
# Start proxy with containers in read-only
export CONTAINERS=1
./dockershield
# In another terminal
curl http://localhost:2375/v1.41/containers/json
# Test a denied endpoint
curl http://localhost:2375/v1.41/images/json # 403 Forbidden
# Start proxy on Unix socket
export LISTEN_SOCKET=/tmp/dockershield.sock
export CONTAINERS=1
./dockershield
# In another terminal
curl --unix-socket /tmp/dockershield.sock http://localhost/v1.41/containers/json
# Or with Docker CLI
export DOCKER_HOST=unix:///tmp/dockershield.sock
docker ps
This proxy is specifically designed to secure CI/CD pipelines. See CICD_EXAMPLES.mdโ for detailed examples:
Typical use cases:
:latest, :dev, :test tagsSee SECURITY.mdโ for:
Dual License: GPL-3.0 (Free) OR Commercial
Contact [email protected]โ for commercial licensing.
Developed in high-performance Go (Golang) with Gin framework and Resty HTTP client, this proxy offers a robust API wrapper around the Docker Engine API and Docker SDK. Native support for Unix sockets and TCP sockets for flexible integration.
Implements zero-trust, least privilege, and defense in depth principles to prevent:
Native compatibility with:
Support for:
Developed for DevOps, SRE and Security teams seeking granular control over Docker in multi-tenant, cloud-native and CI/CD environments.
Content type
Image
Digest
sha256:42a6d65bdโฆ
Size
18.6 MB
Last updated
9 months ago
docker pull hypolas/dockershield