Control what
gets through
We put a default-deny proxy in front of your Docker socket. Every request is filtered by method, path, and body before it reaches the daemon — then we layer on signed policy bundles, per-profile rollout modes, rate limits, hot-reload, and Prometheus metrics.
In the real CLI
See it work
A looping recreation of the real CLI — inspect a config, dry-run a single request through the rules, and watch the proxy stream access logs.
A hand-rendered recreation of the real CLI — every frame mirrors what sockguard actually prints. Use the controls above to pause, restart, or change speed.
Batteries included
What we enforce
We ship a lean Go binary with a stdlib request hot path and zero daemon dependencies. Default-deny, ready to drop in front of your socket.
Default-Deny Posture
SecurityEverything blocked unless explicitly allowed. Request paths are percent-decoded and canonicalized before matching, so `%2e%2e` and encoded-separator tricks cannot slip past an allowlist.
Request Body Inspection
SecurityWe parse every container, image, build, volume, network, secret, config, service, swarm, node, and plugin write to block privileged or host-namespace workloads, non-allowlisted mounts/devices, device requests, device cgroup rules, commands, remotes, unsafe network/swarm/node controls, archive writes, and tar imports. We inspect multipart plugin uploads too, and reject oversized bounded bodies with 413 before the inspector runs.
mTLS for Remote TCP
SecurityWe require mutual TLS 1.3 on non-loopback TCP listeners by default. Plaintext remote TCP is an explicit legacy opt-in only.
Owner Label Isolation
ControlStamp label-capable creates, node/swarm claim updates, and build images with an owner label. Labeled list, prune, and event reads are auto-filtered, and cross-owner access is denied across workload and control-plane resources.
Client ACL Primitives
ControlGate callers by source CIDR, bridge-network container labels, mTLS certificate selectors (CN, DNS/IP/URI SAN, SHA-256 SPKI pin), and unix peer credentials before the global policy runs.
Granular Control
ControlYou can allow start/stop while blocking create/exec. We give you per-operation POST controls with glob matching.
Structured Access Logging
OperationsWe emit JSON access logs with method, raw and normalized paths, decision, matched rule index, latency, canonical request_id, preserved client request IDs, and W3C trace correlation fields.
Operator Observability
OperationsOur opt-in Prometheus metrics expose request totals, deny counts, latency buckets, active requests, watchdog state, plus build_info and start_time gauges for version panels and uptime alerts. We feed /health from the active Docker socket watchdog and log state transitions; trace/log correlation works without an OTLP exporter.
YAML Configuration
ControlWe use declarative YAML rules with glob patterns, first-match-wins evaluation, and 15 bundled workload presets (drydock, Traefik, Portainer, Watchtower, Homepage, Homarr, Diun, Autoheal, read-only, CIS Docker Benchmark, GitHub Actions self-hosted runner, GitLab Runner, Portwing, Portwing with exec, Drydock with self-update) plus the default config.
Tecnativa Compatible
OperationsMigrating from Tecnativa? We match its full env surface: section vars, ALLOW_RESTARTS, SOCKET_PATH, and LOG_LEVEL. Swap us in without touching your config.
Minimal Attack Surface
SecurityWe ship a Wolfi-based image, cosign-signed with SBOM and build provenance.
Signed Policy Bundles
SecurityTreat the on-disk YAML config as untrusted until a cosign / sigstore bundle confirms it. Supports keyed (PEM ECDSA/RSA/ed25519) and keyless (Fulcio + Rekor) verification. Bundle is checked at startup and on every hot reload — a bad signature rejects the reload and leaves the running policy untouched.
Container Image Trust
SecurityVerify cosign signatures on a container's image before `POST /containers/create` reaches the daemon. Keyed (PEM ECDSA/RSA/ed25519) and keyless (Fulcio issuer + SAN, optional Rekor inclusion) verification, configurable per client profile. `enforce` denies unsigned or wrong-signer images; `warn` logs the failure and forwards the request.
Visibility-Controlled Reads
SecurityWe use label selectors to hide labeled list, inspect, and selected service/task log reads for non-matching resources. Env/mount/network/config/plugin/swarm-sensitive metadata is redacted by default, and raw archive/export reads stay behind explicit opt-in.
Named Client Profiles
ControlRoute callers to named profiles with their own rules and request-body policy by source CIDR, mTLS client certificate selectors including SPKI pins, or unix peer credentials, with a configurable default fallback.
Rate Limits & Concurrency Caps
ControlWe return `429 Too Many Requests` with `Retry-After` when per-profile token-bucket rate limits (`limits.rate`) or in-flight concurrency caps (`limits.concurrency`) are exhausted. A system-wide priority fairness gate (`clients.global_concurrency`) prevents low-priority callers from starving high-priority profiles. Anonymous callers bucket under `_anonymous` so they can't bypass limits by skipping identification.
Per-Profile Rollout Modes
ControlStage policy changes without blocking traffic. Set a profile to `warn` or `audit` to serve requests while logging `decision=would_deny` audit records — then compare blocked vs. would-have-been-blocked in your dashboards before flipping to `enforce`. Pre-auth gates (CIDR allowlist, identity failures) always stay in enforce regardless of profile mode.
Hot-Reload + Admin API
OperationsWe watch for config changes with fsnotify and support SIGHUP reload with immutable-field gating: listener, upstream socket, and trust-material fields require a restart. `POST /admin/validate` dry-runs a candidate config without touching the running policy. `GET /admin/policy/version` returns the generation counter, config SHA-256, and verified bundle signer. Optionally bind the admin API to a dedicated listener so admin traffic never traverses the Docker-API filter chain.
Remote Upstreams & Failover
OperationsDial a remote Docker daemon over TCP with mutual TLS instead of the local socket. Configure an ordered set of redundant endpoints for the same daemon or swarm node with active health checks and automatic failover.
Get running
Get started in minutes
Add sockguard to your compose file and point your app at its scoped socket.
$ docker run -d \
--name sockguard \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
-v /var/run/sockguard:/var/run/sockguard \
-e SOCKGUARD_LISTEN_SOCKET=/var/run/sockguard/sockguard.sock \
codeswhat/sockguardMounts the Docker socket directly — fine for a local try, not for production.
On the horizon
Roadmap
Where we've been and where we're headed.
│upstream.request_timeout now defaults to 60s (was unlimited); 'off' restores unlimited
│ownership.allow_cross_owner_namespace_sharing defaults to false — cross-owner container:<id> namespace joins denied by default when ownership.owner is set
│+6 moreshow less
│Namespace-sharing gate: restrict_namespace_sharing + allowed_namespace_sharing_containers gate container:<id> joins across NetworkMode/PidMode/IpcMode/UsernsMode; deny_namespace_path_mode blocks raw ns:<path> on NetworkMode only
│require_cpu_limit_hard — opt-in hard CPU-time cap (NanoCpus/CpuQuota), independent of require_cpu_limit
│Exec Env allow/denylisting — request_body.exec.allowed_env_vars/denied_env_vars, denylist wins
│New presets: portwing-with-compose.yaml, drydock-with-compose.yaml, plus a tri-tool compose example
│Helm chart: pod-level podSecurityContext
│Config Viper-default registration generated by reflection off Defaults()
│Remote Docker daemon over TCP with mutual TLS and active/passive failover
│Ordered endpoint failover — connect failure instantly promotes next endpoint without retry
│+5 moreshow less
│Portwing presets (portwing.yaml, portwing-with-exec.yaml) and drydock-with-selfupdate.yaml
│Drydock preset conformance audit — runc runtime, multi-network connect, self-update finalize exec all fixed
│Swarm service seccomp/AppArmor confinement rails (deny_unconfined_seccomp, deny_unconfined_apparmor)
│Rate-limit token bucket: allocation-free hot path, 0 allocs/0 B per op at ~36 ns
│Enforced CI coverage floor (96%) with Qlty Cloud reporting
│Swarm service create/update enforces container-create identity/privilege rails (non-root, no-new-privileges, readonly rootfs, drop ALL)
│Zero-padded UID bypass sealed — '00', '000', '0:0' all parsed numerically as root
│+5 moreshow less
│Wide-open dedicated admin listener rejected at config validation, not just warned
│Admin endpoint paths normalized before matching — trailing-slash and dot-segment variants closed
│signature_path hot-reload wedge fixed — verification reads from candidate config
│Non-upgrade hijack responses strip hop-by-hop headers
│Multi-arch images cross-compile natively (no QEMU emulation in CI)
│Opt-in upstream readiness probe (GET /containers/json health check — returns 503 on a wedged daemon)
│upstream.request_timeout — 504 Gateway Timeout on hung proxied finite requests
│+3 moreshow less
│Upstream response-header timeout hardened across all side-channel transports
│drydock preset: allowlisted runc runtime to fix update recreate rollbacks
│Go toolchain bumped to 1.26.4 — clears two reachable stdlib advisories (GO-2026-5037, GO-2026-5039)
│21 HIGH + MEDIUM security findings fixed across a full multi-axis audit
│Image-trust wired end-to-end — cosign signatures verified against resolved manifest digest, TOCTOU sealed
│+5 moreshow less
│Plugin install, service create/update, and docker load bypass vectors closed
│Ownership isolation: bounded LRU caches, negative-cache closed, image /get gated
│Response redaction gaps closed (Mounts source, PreviousSpec, MaskedPaths/ReadonlyPaths)
│Constant-time SPKI pin comparison; gzip-bomb guards on build and plugin paths
│QA harness: proxy-vs-daemon differential, mTLS edge-case suite, fuzz targets, goroutine-leak soak
│Default-deny proxy: method + path filtering with percent-decoded path canonicalization
│Request body inspection (container, exec, image, build, volume, network, secret, config, service, swarm, node, plugin)
│+7 moreshow less
│Mutual TLS 1.3 enforced on all non-loopback TCP listeners
│Signed policy bundles (cosign keyed + keyless, Rekor inclusion proof)
│Container image trust (cosign verify before /containers/create, enforce / warn modes)
│Owner label isolation for workload and control-plane resources
│Client ACL primitives: CIDR, mTLS cert selectors, unix peer credentials
│Named client profiles with per-profile rollout modes (enforce / warn / audit)
│12 bundled policy presets: drydock, Traefik, Portainer, Watchtower, CIS Docker Benchmark, GitHub Actions runner, GitLab Runner, and more
Why Sockguard
How we compare
A quick look at what we support that Tecnativa, CetusGuard, and wollomatic don't.
| Feature | Sockguard | Tecnativa | CetusGuard | wollomatic |
|---|---|---|---|---|
| Method + path filtering | Yes | Yes | Partial | Partial |
| Request body inspection | Yes | No | No | Partial |
| Per-client policies | Yes | No | No | Partial |
| Remote TCP mTLS | Yes | No | Yes | No |
| Signed policy bundles | Yes | No | No | No |
| Prometheus metrics | Yes | No | No | No |
Ecosystem
Part of the CodesWhat stack
Sockguard is one piece of a small, focused toolkit — each tool does one job, and they compose.

Default-deny Docker socket proxy

Container update monitoring

Secure remote Docker agent
FAQ
Frequently asked questions
Common questions about how Sockguard works and how we compare.
What is Sockguard and how does the default-deny model work?
Sockguard is a Docker socket proxy we built in Go. Every Docker API request is blocked unless an explicit rule in your YAML config allows it — method, path, and request body are all evaluated before the request reaches the daemon. A client that connects without a matching allow rule gets a 403; there is no fallback to 'pass everything'. This posture means a compromised container or CI job can only do what you deliberately permitted.
How is Sockguard different from Tecnativa's docker-socket-proxy?
Tecnativa filters by URL path using environment variables. Sockguard adds request body inspection — we parse every container, exec, image, build, volume, network, service, swarm, and plugin write to block privileged workloads, non-allowlisted mounts and devices, unsafe sysctls, and more. We also support per-client policy profiles (route different callers to different rule sets by CIDR, mTLS certificate, or unix peer), signed policy bundles (cosign), container image trust verification, per-profile rollout modes (enforce / warn / audit), Prometheus metrics, and hot-reload with an admin API. Tecnativa has none of these. We also ship a Tecnativa-compatible env surface so you can swap us in without touching your existing config.
Does Sockguard inspect request bodies?
Yes — request body inspection is one of our core differentiators. We parse the JSON body on every write endpoint: container create and exec, image build and load, volume create, network create, secret and config create, service create and update, swarm init and join, node update, and plugin install. We check for privileged mode, host namespace sharing, non-allowlisted bind mounts and devices, capability additions, unsafe sysctls, non-allowlisted runtimes, and more. Oversized bodies return 413 before the inspector runs. We also inspect multipart plugin uploads and gzip-bomb guard all archive paths.
Can Sockguard listen over TCP, and is remote access secure?
Yes. Sockguard can listen on a TCP port in addition to (or instead of) a unix socket. For any non-loopback TCP listener we require mutual TLS 1.3 by default — plaintext remote TCP needs two explicit insecure acknowledgement flags before we accept it. Client identity on TCP is established via mTLS certificate selectors (CN, DNS/IP/URI SAN, SHA-256 SPKI pin). Sockguard can also dial a remote Docker daemon over TCP with mTLS and automatic endpoint failover (added in v1.4.0).
What are signed policy bundles and container image trust?
Signed policy bundles let you treat the on-disk YAML config as untrusted until a cosign / sigstore bundle confirms it. We support keyed (PEM ECDSA/RSA/ed25519) and keyless (Fulcio + Rekor) verification. The bundle is checked at startup and on every hot reload — a bad signature rejects the reload and leaves the running policy untouched. Container image trust goes further: before forwarding a POST /containers/create to the daemon, we resolve the image to its registry manifest digest, discover cosign signatures, and verify them against your configured signer identity. In enforce mode a container create is denied if the image is unsigned or signed by the wrong identity.
Is Sockguard production-ready and what license does it use?
Sockguard is Apache-2.0 licensed and has been in production use since v1.0.0. The proxy binary ships as a minimal Wolfi-based container image, cosign-signed with an SBOM and build provenance attached. We enforce a 96%+ Go statement-coverage floor in CI, run a proxy-vs-daemon differential fuzz harness on every PR, and have a published security policy at security@getsockguard.com. The v1.1.0 release incorporated fixes for 21 HIGH and MEDIUM findings from a full multi-axis security audit.
How do I migrate from Tecnativa's docker-socket-proxy?
We match Tecnativa's full environment-variable surface — CONTAINERS, EVENTS, SERVICES, NETWORKS, VOLUMES, TASKS, NODES, CONFIGS, SECRETS, ALLOW_RESTARTS, SOCKET_PATH, LOG_LEVEL, and the full section-variable set. Point DOCKER_HOST at the Sockguard socket instead of Tecnativa's and your existing env config continues to work. Sockguard's built-in Tecnativa env-var compatibility layer covers the same allow surface. Once migrated you can layer on body inspection, per-client profiles, and signed policies incrementally without breaking running workloads — use a profile in warn mode to measure what would have been denied before flipping to enforce.

