Introduction
Sockguard is a Docker socket proxy that inspects the bodies it proxies, not just the URLs.
A Docker socket proxy that actually inspects what it proxies.
Why Sockguard?
The Docker socket (/var/run/docker.sock) is effectively root access to your host.
Any container with socket access can create privileged containers, mount the host
filesystem, and escape containment entirely.
Most existing proxies stop at method/path or regex filtering. Sockguard goes further:
- Request body inspection — Docker write bodies across container create/update/exec/archive, image pull/load/build, volume/network/secret/config/service/swarm/node, and plugin endpoints are inspected before Docker sees the request
- Owner label isolation — stamp label-capable creates and build images with an owner label, auto-filter labeled list/prune, deny cross-owner access across workload and control-plane resources
- Per-client policies — gate callers by source CIDR, certificate selectors including SPKI pins, unix peer credentials, or calling container labels, then apply named profiles
- mTLS for remote TCP — non-loopback TCP requires mutual TLS 1.3 by default, plaintext TCP is an explicit legacy opt-in
- Structured access logging — JSON logs with method, raw path, normalized policy path, decision, matched rule, latency, canonical request ID, preserved client request ID when present, and W3C trace correlation fields
- Operator observability — opt-in
/metricsscrape endpoint, active upstream watchdog signals for Docker socket reachability, and trace/log correlation without an OTLP exporter - Abuse controls — per-profile token-bucket rate limits, concurrency caps, per-endpoint cost weighting, and a system-wide priority-aware fairness gate keep noisy callers from starving the rest
- Dynamic policy delivery — per-profile
enforce|warn|auditrollout modes for safe staged rollouts, fsnotify + SIGHUP hot-reload with an immutable-field gate, monotonic policy versioning atGET /admin/policy/versionand onsockguard_policy_version, an optional dedicated admin listener, and cosign-signed policy bundles verified at startup and every reload - Default-deny — everything blocked unless explicitly allowed
- Tecnativa compatible — drop-in replacement for the current Tecnativa environment-variable surface
Quick Start
services:
sockguard:
image: codeswhat/sockguard:latest
restart: unless-stopped
read_only: true
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- sockguard-socket:/var/run/sockguard
environment:
- SOCKGUARD_LISTEN_SOCKET=/var/run/sockguard/sockguard.sock
- SOCKGUARD_INSECURE_ALLOW_READ_EXFILTRATION=true
- CONTAINERS=1
- IMAGES=1
- EVENTS=1
your-app:
depends_on:
- sockguard
volumes:
- sockguard-socket:/var/run/sockguard:ro
environment:
- DOCKER_HOST=unix:///var/run/sockguard/sockguard.sock
volumes:
sockguard-socket:The published image runs as UID 65532 (Chainguard
nonroot) inside the container. On stock Docker hosts where/var/run/docker.sockis owned by thedockergroup you may need agroup_addoverride with the socket's numeric group ID or a matchinguser:override. The meaningful hardening levers are the proxy policy,read_only, dropped capabilities,no-new-privileges, and your runtime's seccomp/AppArmor/SELinux defaults.
SOCKGUARD_INSECURE_ALLOW_READ_EXFILTRATION=trueis only there to preserve broadCONTAINERS=1/IMAGES=1compatibility, including raw archive/export and log/attach streaming endpoints. Remove it once you switch to tighter YAML list/inspect rules.
If you choose TCP instead of a unix socket, Sockguard only allows loopback TCP by default. Non-loopback TCP requires mutual TLS unless you explicitly opt into legacy plaintext mode with both
SOCKGUARD_LISTEN_INSECURE_ALLOW_PLAIN_TCP=trueandSOCKGUARD_LISTEN_INSECURE_ALLOW_UNAUTHENTICATED_CLIENTS=true.