681 lines
16 KiB
Markdown
681 lines
16 KiB
Markdown
# Docker Socket Proxy - Secure Docker Socket Access
|
|
|
|
## Table of Contents
|
|
- [Overview](#overview)
|
|
- [What is Docker Socket Proxy?](#what-is-docker-socket-proxy)
|
|
- [Why Use Docker Socket Proxy?](#why-use-docker-socket-proxy)
|
|
- [How It Works](#how-it-works)
|
|
- [Configuration in AI-Homelab](#configuration-in-ai-homelab)
|
|
- [Official Resources](#official-resources)
|
|
- [Educational Resources](#educational-resources)
|
|
- [Docker Configuration](#docker-configuration)
|
|
- [Access Control](#access-control)
|
|
- [Advanced Topics](#advanced-topics)
|
|
- [Troubleshooting](#troubleshooting)
|
|
|
|
## Overview
|
|
|
|
**Category:** Infrastructure Security
|
|
**Docker Image:** [tecnativa/docker-socket-proxy](https://hub.docker.com/r/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:
|
|
|
|
```yaml
|
|
# 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:
|
|
|
|
```yaml
|
|
# 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:**
|
|
1. **Principle of Least Privilege:** Only grant necessary permissions
|
|
2. **Reduced Attack Surface:** Limit what compromised container can do
|
|
3. **Audit Trail:** Centralized access point
|
|
4. **Network Isolation:** Proxy can be on separate network
|
|
5. **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
|
|
|
|
1. **Tool makes API request:** "List containers"
|
|
2. **Connects to proxy:** tcp://docker-proxy:2375
|
|
3. **Proxy checks permissions:** Is CONTAINERS=1?
|
|
4. **If allowed:** Forward to Docker socket
|
|
5. **If denied:** Return 403 Forbidden
|
|
6. **Response returned:** To requesting tool
|
|
|
|
### Permission Model
|
|
|
|
**Environment variables control access:**
|
|
- `CONTAINERS=1` → Allow container operations
|
|
- `IMAGES=1` → Allow image operations
|
|
- `NETWORKS=1` → Allow network operations
|
|
- `VOLUMES=1` → Allow volume operations
|
|
- `SERVICES=1` → Allow swarm service operations
|
|
- `TASKS=1` → Allow swarm task operations
|
|
- `SECRETS=1` → Allow secret operations
|
|
- `POST=0` → Deny all write operations (read-only)
|
|
|
|
## Configuration in AI-Homelab
|
|
|
|
### Directory Structure
|
|
|
|
```
|
|
# No persistent storage needed
|
|
# All configuration via environment variables
|
|
```
|
|
|
|
### Environment Variables
|
|
|
|
```bash
|
|
# 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)](https://www.youtube.com/watch?v=0aOqx8mQZFk)
|
|
- [Why You Should Use Docker Socket Proxy](https://www.youtube.com/results?search_query=docker+socket+proxy+security)
|
|
- [Container Security Best Practices](https://www.youtube.com/watch?v=9weaE6QEm8A)
|
|
|
|
### Articles & Guides
|
|
- [Docker Socket Proxy Documentation](https://github.com/Tecnativa/docker-socket-proxy)
|
|
- [Docker Socket Security Risks](https://docs.docker.com/engine/security/)
|
|
- [Principle of Least Privilege](https://en.wikipedia.org/wiki/Principle_of_least_privilege)
|
|
|
|
### 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
|
|
|
|
```yaml
|
|
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:**
|
|
```yaml
|
|
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:**
|
|
```yaml
|
|
portainer:
|
|
networks:
|
|
- traefik-network
|
|
- docker-proxy-network
|
|
environment:
|
|
- AGENT_HOST=docker-proxy
|
|
# NO volumes for Docker socket!
|
|
```
|
|
|
|
**Dockge Configuration:**
|
|
```yaml
|
|
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:
|
|
|
|
```yaml
|
|
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:
|
|
|
|
```yaml
|
|
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:
|
|
|
|
```yaml
|
|
docker-proxy:
|
|
environment:
|
|
- CONTAINERS=1
|
|
- IMAGES=1
|
|
- INFO=1
|
|
- POST=1 # Create operations
|
|
```
|
|
|
|
### Read-Only Mode
|
|
|
|
For monitoring tools (Glances, Dozzle):
|
|
|
|
```yaml
|
|
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):**
|
|
```yaml
|
|
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):**
|
|
```yaml
|
|
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:
|
|
|
|
```yaml
|
|
docker-proxy:
|
|
volumes:
|
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
- ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
|
|
```
|
|
|
|
**Custom haproxy.cfg:**
|
|
```haproxy
|
|
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
|
|
|
|
```yaml
|
|
docker-proxy:
|
|
environment:
|
|
- LOG_LEVEL=debug # More verbose logging
|
|
logging:
|
|
driver: json-file
|
|
options:
|
|
max-size: "10m"
|
|
max-file: "3"
|
|
```
|
|
|
|
**Monitor access:**
|
|
```bash
|
|
# 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
|
|
|
|
```yaml
|
|
networks:
|
|
docker-proxy-network:
|
|
driver: bridge
|
|
internal: true # No internet access
|
|
ipam:
|
|
config:
|
|
- subnet: 172.25.0.0/16
|
|
```
|
|
|
|
**Only allow specific services:**
|
|
```yaml
|
|
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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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"
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
1. **Read-Only Socket:** Always mount socket as `:ro`
|
|
2. **Minimal Permissions:** Only enable what's needed
|
|
3. **Network Isolation:** Use internal network
|
|
4. **No Public Exposure:** Never expose port 2375 to internet
|
|
5. **Separate Proxies:** Different proxies for different trust levels
|
|
6. **Monitor Access:** Review logs regularly
|
|
7. **Disable Exec:** Never enable EXEC unless absolutely necessary
|
|
8. **Regular Updates:** Keep proxy image updated
|
|
9. **Principle of Least Privilege:** Start with nothing, add as needed
|
|
10. **Testing:** Test permissions in development first
|
|
|
|
## Migration Guide
|
|
|
|
### Converting from Direct Socket Access
|
|
|
|
**Before (insecure):**
|
|
```yaml
|
|
traefik:
|
|
volumes:
|
|
- /var/run/docker.sock:/var/run/docker.sock
|
|
```
|
|
|
|
**After (secure):**
|
|
|
|
1. **Add docker-proxy:**
|
|
```yaml
|
|
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
|
|
```
|
|
|
|
2. **Update service:**
|
|
```yaml
|
|
traefik:
|
|
environment:
|
|
- DOCKER_HOST=tcp://docker-proxy:2375
|
|
networks:
|
|
- traefik-network
|
|
- docker-proxy-network
|
|
# Remove socket volume!
|
|
```
|
|
|
|
3. **Create network:**
|
|
```yaml
|
|
networks:
|
|
docker-proxy-network:
|
|
internal: true
|
|
```
|
|
|
|
4. **Test:**
|
|
```bash
|
|
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:**
|
|
```bash
|
|
# 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:**
|
|
1. Deploy docker-proxy with minimal permissions
|
|
2. Update Traefik to use proxy (most critical)
|
|
3. Update Portainer to use proxy
|
|
4. Update other management tools
|
|
5. Remove all direct socket mounts
|
|
6. Test thoroughly
|
|
7. 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
|