16 KiB
Docker Socket Proxy - Secure Docker Socket Access
Table of Contents
- Overview
- What is Docker Socket Proxy?
- Why Use Docker Socket Proxy?
- How It Works
- Configuration in AI-Homelab
- Official Resources
- Educational Resources
- Docker Configuration
- Access Control
- Advanced Topics
- Troubleshooting
Overview
Category: Infrastructure Security
Docker Image: tecnativa/docker-socket-proxy
Default Stack: infrastructure.yml
Web UI: None (proxy service)
Port: 2375 (internal only)
Purpose: Secure access layer for Docker socket
What is Docker Socket Proxy?
Docker Socket Proxy is a security-focused proxy that sits between Docker management tools and the Docker socket. It provides fine-grained access control to Docker API endpoints, allowing you to grant specific permissions rather than full Docker socket access.
Key Features
- Granular Permissions: Control which Docker API endpoints are accessible
- Security Layer: Prevents full root access to host
- Read/Write Control: Separate read-only and write permissions
- API Filtering: Whitelist specific Docker API calls
- No Authentication: Relies on network isolation
- Lightweight: Minimal resource usage
- HAProxy Based: Stable, proven technology
- Zero Config: Works out of the box with sensible defaults
Why Use Docker Socket Proxy?
The Problem
Direct Docker socket access (/var/run/docker.sock) grants root-equivalent access to the host:
# DANGEROUS: Full access to Docker = root on host
traefik:
volumes:
- /var/run/docker.sock:/var/run/docker.sock
Risks:
- Container can access all other containers
- Can mount host filesystem
- Can escape container isolation
- Can compromise entire system
The Solution
Docker Socket Proxy provides controlled access:
# SAFER: Limited access via proxy
traefik:
environment:
- DOCKER_HOST=tcp://docker-proxy:2375
# No direct socket mount!
docker-proxy:
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
- CONTAINERS=1 # Allow container list
- SERVICES=1 # Allow service list
- TASKS=0 # Deny task access
Benefits:
- Principle of Least Privilege: Only grant necessary permissions
- Reduced Attack Surface: Limit what compromised container can do
- Audit Trail: Centralized access point
- Network Isolation: Proxy can be on separate network
- Read-Only Socket: Proxy uses read-only mount
How It Works
Management Tool (Traefik/Portainer/Dockge)
↓
TCP Connection to docker-proxy:2375
↓
Docker Socket Proxy (HAProxy)
├─ Check permissions
├─ Filter allowed endpoints
└─ Forward or block request
↓
Docker Socket (/var/run/docker.sock)
↓
Docker Engine
Request Flow
- Tool makes API request: "List containers"
- Connects to proxy: tcp://docker-proxy:2375
- Proxy checks permissions: Is CONTAINERS=1?
- If allowed: Forward to Docker socket
- If denied: Return 403 Forbidden
- Response returned: To requesting tool
Permission Model
Environment variables control access:
CONTAINERS=1→ Allow container operationsIMAGES=1→ Allow image operationsNETWORKS=1→ Allow network operationsVOLUMES=1→ Allow volume operationsSERVICES=1→ Allow swarm service operationsTASKS=1→ Allow swarm task operationsSECRETS=1→ Allow secret operationsPOST=0→ Deny all write operations (read-only)
Configuration in AI-Homelab
Directory Structure
# No persistent storage needed
# All configuration via environment variables
Environment Variables
# Core Docker API endpoints
CONTAINERS=1 # Container list, inspect, logs, stats
SERVICES=1 # Service management (for Swarm)
TASKS=1 # Task management (for Swarm)
NETWORKS=1 # Network operations
VOLUMES=1 # Volume operations
IMAGES=1 # Image list, pull, push
INFO=1 # Docker info, version
EVENTS=1 # Docker events stream
PING=1 # Health check
# Write operations (set to 0 for read-only)
POST=1 # Create operations
BUILD=0 # Image build (usually not needed)
COMMIT=0 # Container commit
CONFIGS=0 # Config management
DISTRIBUTION=0 # Registry operations
EXEC=0 # Execute in container (dangerous)
SECRETS=0 # Secret management (Swarm)
SESSION=0 # Not commonly used
SWARM=0 # Swarm management
# Security
LOG_LEVEL=info # Logging verbosity
Official Resources
- GitHub: https://github.com/Tecnativa/docker-socket-proxy
- Docker Hub: https://hub.docker.com/r/tecnativa/docker-socket-proxy
- Documentation: https://github.com/Tecnativa/docker-socket-proxy/blob/master/README.md
Educational Resources
Videos
- Docker Socket Security (TechnoTim)
- Why You Should Use Docker Socket Proxy
- Container Security Best Practices
Articles & Guides
Concepts to Learn
- Unix Socket: Inter-process communication file
- Docker API: RESTful API for Docker operations
- TCP Socket: Network socket for remote access
- HAProxy: Load balancer and proxy
- Least Privilege: Minimal permissions principle
- Attack Surface: Potential vulnerability points
- Container Escape: Breaking out of container isolation
Docker Configuration
Complete Service Definition
docker-proxy:
image: tecnativa/docker-socket-proxy:latest
container_name: docker-proxy
restart: unless-stopped
privileged: true # Required for socket access
networks:
- docker-proxy-network # Isolated network
ports:
- "2375:2375" # Only expose internally
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro # Read-only!
environment:
# Core permissions (what Traefik/Portainer need)
- CONTAINERS=1
- SERVICES=1
- NETWORKS=1
- IMAGES=1
- INFO=1
- EVENTS=1
- PING=1
# Write operations (enable as needed)
- POST=1
# Deny dangerous operations
- BUILD=0
- COMMIT=0
- EXEC=0
- SECRETS=0
# Logging
- LOG_LEVEL=info
networks:
docker-proxy-network:
internal: true # No external access
Connecting Services to Proxy
Traefik Configuration:
traefik:
networks:
- traefik-network
- docker-proxy-network
environment:
- DOCKER_HOST=tcp://docker-proxy:2375
# NO volumes for Docker socket!
# volumes:
# - /var/run/docker.sock:/var/run/docker.sock # REMOVE THIS
Portainer Configuration:
portainer:
networks:
- traefik-network
- docker-proxy-network
environment:
- AGENT_HOST=docker-proxy
# NO volumes for Docker socket!
Dockge Configuration:
dockge:
networks:
- traefik-network
- docker-proxy-network
environment:
- DOCKER_HOST=tcp://docker-proxy:2375
# NO volumes for Docker socket!
Access Control
Traefik Minimal Permissions
Traefik only needs to read container labels:
docker-proxy:
environment:
- CONTAINERS=1 # Read container info
- SERVICES=1 # Read services
- NETWORKS=1 # Read networks
- INFO=1 # Docker info
- EVENTS=1 # Watch for changes
- POST=0 # No write operations
Portainer Full Permissions
Portainer needs more access for management:
docker-proxy:
environment:
- CONTAINERS=1
- SERVICES=1
- TASKS=1
- NETWORKS=1
- VOLUMES=1
- IMAGES=1
- INFO=1
- EVENTS=1
- POST=1 # Create/update
- PING=1
Watchtower Minimal Permissions
Watchtower needs to pull images and recreate containers:
docker-proxy:
environment:
- CONTAINERS=1
- IMAGES=1
- INFO=1
- POST=1 # Create operations
Read-Only Mode
For monitoring tools (Glances, Dozzle):
docker-proxy:
environment:
- CONTAINERS=1
- SERVICES=1
- TASKS=1
- NETWORKS=1
- VOLUMES=1
- IMAGES=1
- INFO=1
- EVENTS=1
- POST=0 # No writes
- BUILD=0
- COMMIT=0
- EXEC=0
Advanced Topics
Multiple Proxy Instances
Run separate proxies for different permission levels:
docker-proxy-read (for monitoring tools):
docker-proxy-read:
image: tecnativa/docker-socket-proxy
environment:
- CONTAINERS=1
- IMAGES=1
- INFO=1
- POST=0 # Read-only
networks:
- monitoring-network
docker-proxy-write (for management tools):
docker-proxy-write:
image: tecnativa/docker-socket-proxy
environment:
- CONTAINERS=1
- IMAGES=1
- NETWORKS=1
- VOLUMES=1
- POST=1 # Read-write
networks:
- management-network
Custom HAProxy Configuration
For advanced filtering, mount custom config:
docker-proxy:
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
Custom haproxy.cfg:
global
log stdout format raw local0
defaults
log global
mode http
timeout connect 5s
timeout client 30s
timeout server 30s
frontend docker
bind :2375
default_backend docker_backend
backend docker_backend
server docker unix@/var/run/docker.sock
# Custom ACLs
acl containers_req path_beg /containers
acl images_req path_beg /images
# Only allow specific endpoints
http-request deny unless containers_req or images_req
Logging and Monitoring
docker-proxy:
environment:
- LOG_LEVEL=debug # More verbose logging
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
Monitor access:
# View proxy logs
docker logs -f docker-proxy
# See what endpoints are being accessed
docker logs docker-proxy | grep -E "GET|POST|PUT|DELETE"
Network Isolation
networks:
docker-proxy-network:
driver: bridge
internal: true # No internet access
ipam:
config:
- subnet: 172.25.0.0/16
Only allow specific services:
services:
traefik:
networks:
docker-proxy-network:
ipv4_address: 172.25.0.2
portainer:
networks:
docker-proxy-network:
ipv4_address: 172.25.0.3
Troubleshooting
Services Can't Connect to Docker
# Check if proxy is running
docker ps | grep docker-proxy
# Test proxy connectivity
docker exec traefik wget -qO- http://docker-proxy:2375/version
# Check networks
docker network inspect docker-proxy-network
# Verify service is on proxy network
docker inspect traefik | grep -A10 Networks
Permission Denied Errors
# Check proxy logs
docker logs docker-proxy
# Example error: "POST /containers/create 403"
# Solution: Add POST=1 to docker-proxy environment
# Check which endpoint is being blocked
docker logs docker-proxy | grep 403
# Enable required permission
# If /images/create is blocked, add IMAGES=1
Traefik Not Discovering Services
# Ensure these are enabled:
docker-proxy:
environment:
- CONTAINERS=1
- SERVICES=1
- EVENTS=1 # Critical for auto-discovery
# Check if Traefik is using proxy
docker exec traefik env | grep DOCKER_HOST
# Test manually
docker exec traefik wget -qO- http://docker-proxy:2375/containers/json
Portainer Shows "Cannot connect to Docker"
# Portainer needs more permissions
docker-proxy:
environment:
- CONTAINERS=1
- SERVICES=1
- TASKS=1
- NETWORKS=1
- VOLUMES=1
- IMAGES=1
- POST=1
# In Portainer settings:
# Environment URL: tcp://docker-proxy:2375
# Not: unix:///var/run/docker.sock
Watchtower Not Updating Containers
# Watchtower needs write access
docker-proxy:
environment:
- CONTAINERS=1
- IMAGES=1
- POST=1 # Required for creating containers
# Check Watchtower logs
docker logs watchtower
High Memory/CPU Usage
# Check proxy stats
docker stats docker-proxy
# Should be minimal (~10MB RAM, <1% CPU)
# If high, check for connection leaks
# Restart proxy
docker restart docker-proxy
# Check for excessive requests
docker logs docker-proxy | wc -l
Security Best Practices
- Read-Only Socket: Always mount socket as
:ro - Minimal Permissions: Only enable what's needed
- Network Isolation: Use internal network
- No Public Exposure: Never expose port 2375 to internet
- Separate Proxies: Different proxies for different trust levels
- Monitor Access: Review logs regularly
- Disable Exec: Never enable EXEC unless absolutely necessary
- Regular Updates: Keep proxy image updated
- Principle of Least Privilege: Start with nothing, add as needed
- Testing: Test permissions in development first
Migration Guide
Converting from Direct Socket Access
Before (insecure):
traefik:
volumes:
- /var/run/docker.sock:/var/run/docker.sock
After (secure):
- Add docker-proxy:
docker-proxy:
image: tecnativa/docker-socket-proxy
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
- CONTAINERS=1
- SERVICES=1
- NETWORKS=1
- EVENTS=1
networks:
- docker-proxy-network
- Update service:
traefik:
environment:
- DOCKER_HOST=tcp://docker-proxy:2375
networks:
- traefik-network
- docker-proxy-network
# Remove socket volume!
- Create network:
networks:
docker-proxy-network:
internal: true
- Test:
docker compose up -d
docker logs traefik # Check for errors
Performance Impact
Overhead:
- Latency: ~1-2ms per request
- Memory: ~10-20MB
- CPU: <1%
Minimal impact because:
- Docker API calls are infrequent
- Proxy is extremely lightweight
- HAProxy is optimized for performance
Benchmark:
# Direct socket
time docker ps
# ~0.05s
# Via proxy
time docker -H tcp://docker-proxy:2375 ps
# ~0.06s
# Negligible difference for management operations
Summary
Docker Socket Proxy is a critical security component that:
- Provides granular access control to Docker API
- Prevents root-equivalent access from containers
- Uses principle of least privilege
- Adds minimal overhead
- Simple to configure and maintain
Essential for:
- Production environments
- Multi-user setups
- Security-conscious homelabs
- Compliance requirements
- Defense in depth strategy
Implementation Priority:
- Deploy docker-proxy with minimal permissions
- Update Traefik to use proxy (most critical)
- Update Portainer to use proxy
- Update other management tools
- Remove all direct socket mounts
- Test thoroughly
- Monitor logs
Remember:
- Direct socket access = root on host
- Always use read-only socket mount in proxy
- Start with restrictive permissions
- Add permissions only as needed
- Use separate proxies for different trust levels
- Never expose proxy to internet
- Monitor access logs
- Essential security layer for homelab