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:
- ✅ GitHub Actions, GitLab CI, Jenkins: Limit allowed Docker actions
- ✅ Secure Docker-in-Docker (DinD): Control build, push, run
- ✅ Mandatory private registry: Force usage of your internal registries
- ✅ Ban :latest tag: Enforce semantic versioning
- ✅ Complete audit: Structured logs of all operations
- Kubernetes, Docker Swarm, Nomad: Isolation between namespaces/tenants
- PaaS & Container-as-a-Service: Granular control per client
- Shared environments: Strict security and isolation
- Zero-trust architecture: Least privilege principle applied
- Compliance & Audit: Complete traceability of operations
- Multi-layer security: Protection against privilege escalation
This Docker proxy offers advanced security for your Docker environments through:
- Policy enforcement and RBAC for Docker API
- Admission control for containers with regex filters
- Zero-trust architecture applied to Docker socket
- Multi-tenant isolation with granular namespace control
- Container escape prevention via volume and privilege restrictions
- Complete audit trail of all Docker operations
Ideal for cloud-native security, Kubernetes, Docker Swarm, PaaS, and CI/CD pipelines (GitHub Actions, GitLab CI, Jenkins, CircleCI, Azure DevOps).
- ACL per endpoint: Enable only necessary Docker endpoints
- Advanced filters with regex: Precise control over volumes, containers, images, networks
- 🆕 Filter priority: Advanced filters (
DKRPRX__*) can override ACL restrictions - Content filtering: Inspect and validate requests before forwarding
- Flexible configuration: JSON or environment variables (priority)
- Docker socket protection: Blocked by default to prevent privilege escalation
- Self-protection: Proxy protects itself against any manipulation
- Read-only mode: Disable POST/DELETE/PUT by default
- Auto-detection: Docker API version auto-detected
# 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"Advanced filters now have priority over basic ACL rules! This allows fine-grained control:
# Example: Allow ONLY private registry pulls, even with IMAGES=0
export IMAGES=0 # Disable all image operations by default
export DKRPRX__IMAGES__ALLOWED_REPOS="^registry\.company\.com/.*"
# Result:
# ❌ docker pull nginx → Blocked (public registry)
# ✅ docker pull registry.company.com/nginx → Allowed (matches filter)See docs/ADVANCED_FILTERS.md for complete guide.
THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
The developer disclaims all liability concerning:
- Direct or indirect damages caused by using this software
- Security flaws or vulnerabilities
- Data loss or service interruption
- Any malicious or inappropriate use
You use this proxy at your own risk. It is your responsibility to:
- Properly configure security filters
- Test the configuration in a development environment
- Regularly audit access and logs
- NEVER expose the proxy on a public network
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:latestMulti-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/latestAvailable platforms:
- Linux: amd64, arm64, armv7
- macOS: amd64 (Intel), arm64 (Apple Silicon)
- Windows: amd64
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 version
Denied 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 management
GET,HEAD: Always allowed (read-only)POST: Default0(setPOST=1to enable)DELETE: Default0(setDELETE=1to enable)PUT,PATCH: Default0(setPUT=1to enable)
export CONTAINERS=1
export IMAGES=1
./dockershieldexport 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 useLISTEN_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_SHApipeline {
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=infoFROM 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.jsonNote: 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)/.*"- ADVANCED_FILTERS.md - Complete guide to advanced filters with detailed examples
- ENV_FILTERS.md - Configuration via environment variables
- SECURITY.md - Security guide and blocked attack vectors
- Examples - Ready-to-use snippets for Docker Compose, CLI, and the standalone binary
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.sock
The dockershield container itself is protected against any manipulation:
- ❌ Cannot stop/restart the proxy container
- ❌ Cannot modify the proxy container
- ❌ Cannot delete the proxy container
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$"- NEVER expose this proxy on a public network
- NEVER mount the Docker socket in an unsecured container
- Enable only necessary endpoints
- Use read-only mode when possible
- Mount Docker socket as read-only when possible (
:ro) - Use advanced filters for granular control
Comprehensive test suite with 19 tests across 5 scenarios:
cd tests/integration
chmod +x run-tests.sh
# Run all scenarios (~2 minutes)
./run-tests.sh all
# Or specific scenarios
./run-tests.sh readonly # Read-only mode tests
./run-tests.sh volumes # Volume filter tests with regex
./run-tests.sh images # Image filter tests with regex
./run-tests.sh security # Default security testsWhat's tested:
- ✅ ACL rules (CONTAINERS, IMAGES, POST, DELETE, etc.)
- ✅ Advanced filters with regex patterns
- ✅ Volume path filtering
- ✅ Image/container name filtering
- ✅ Default security (socket blocking, name protection)
See tests/integration/README.md for details.
# 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 psThis proxy is specifically designed to secure CI/CD pipelines. See CICD_EXAMPLES.md for detailed examples:
- GitHub Actions - Secure Docker in workflows
- GitLab CI - Granular access control with services
- Jenkins - Secure pipeline with Docker proxy
- Azure DevOps - Integration with ACR
- CircleCI - Secure build with remote Docker
Typical use cases:
- Force use of private registry only
- Block
:latest,:dev,:testtags - Block privileged containers in CI
- Complete audit of Docker operations
- Multi-tenant isolation in shared runners
See SECURITY.md for:
- Implemented default protections
- Blocked attack vectors
- Production security checklist
- Deployment best practices
Dual License: GPL-3.0 (Free) OR Commercial
- 🆓 FREE for open-source projects (GPL-3.0)
- 💼 Commercial license for proprietary/closed-source use
- 📋 See LICENSE for complete terms
- 💰 Pricing: Startup €500/year, SME €2k/year, Enterprise €10k/year
Contact nicolas.hypolite@gmail.com 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:
- Privilege escalation prevention: Block privileged containers and host network
- Container escape prevention: Strict restrictions on volumes and bind mounts
- Socket injection prevention: Automatic Docker socket protection
- Regex-based filtering: Advanced patterns for images, volumes, networks
- Network policy and granular volume restriction
- Image policy and customizable tag policy
- Label enforcement for organizational compliance
- Registry whitelist: Force use of approved registries
Native compatibility with:
- GitHub Actions, GitLab CI/CD, Jenkins, CircleCI, Azure DevOps
- Travis CI, Drone CI, Bamboo, TeamCity
- Improved Docker-in-Docker (DinD) security
- Kubernetes admission controller via webhook
Support for:
- Kubernetes (pods, deployments, namespaces)
- Docker Swarm (services, stacks, secrets)
- HashiCorp Nomad (jobs, tasks)
- Rancher, Portainer, OpenShift
- Any platform using Docker API
Developed for DevOps, SRE and Security teams seeking granular control over Docker in multi-tenant, cloud-native and CI/CD environments.