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. Commands, fields, and decisions mirror what sockguard prints; release provenance and timestamps are explicit placeholders. 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, unsafe controls, archive writes, and tar imports. Native Podman builds add fail-closed primary/additional-context, host-mount, and resource-usage host-file handling on top of the shared classic-build policy. Bounded inspectors reject oversized bodies with 413 and enforce a 30-second read deadline through logging and metrics.
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, cross-owner access is denied, and method-aware path handling keeps resources named after API actions inside the boundary.
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. The trusted principal and selected profile also isolate mediated BuildKit session state.
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. Method and unknown-route labels are finite, so caller-controlled values cannot grow the registry without bound. Trace/log correlation works without an OTLP exporter.
YAML Configuration
ControlWe use declarative YAML rules with glob patterns, first-match-wins evaluation, and 17 bundled workload presets (drydock, drydock with self-update, drydock with compose, Traefik, Portainer, Watchtower, Homepage, Homarr, Diun, Autoheal, read-only, CIS Docker Benchmark, GitHub Actions self-hosted runner, GitLab Runner, Portwing, Portwing with exec, Portwing with compose) plus the default config.
Tecnativa Compatible
OperationsMigrating from Tecnativa? We match its full env surface in unsigned mode: section vars, ALLOW_RESTARTS, SOCKET_PATH, and LOG_LEVEL. Signed policies reject rule-generating compatibility variables so environment state cannot change verified rules.
Minimal Attack Surface
SecurityWe ship a Wolfi-based image, cosign-signed with SBOM and build provenance.
Signed Policy Bundles
SecurityPin keyed or keyless sigstore trust in a separate bootstrap file selected with --policy-bundle-trust-config. Candidate and trust YAML stop at 16 MiB, bundles at 4 MiB, and cooperative cancellation stops signer fallback. Startup and every hot reload verify before applying policy.
Container Image Trust
SecurityVerify cosign signatures on a container's image before `POST /containers/create` reaches the daemon. Keyed and keyless trust is configurable per client profile, with redirect-safe registry response limits, direct-manifest signature references, no alternate payload URLs, bounded signature fan-out, and aggregate payload budgets. Legal media-type parameters remain compatible. `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. Keyword-named resources stay covered, and combined label plus name/image checks use one bounded inspect. Sensitive metadata is redacted by default, while raw archive/export reads require 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. Signed trust stays pinned in the separate bootstrap file; only the candidate's signature path rotates on reload. `POST /admin/validate` dry-runs a candidate, and `GET /admin/policy/version` returns its generation, SHA-256, and verified signer.
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.
│Extend the shipped BuildKit gRPC mediation foundation: v2.0.0's bounded-depth LLB walk denies any ExecOp, and any op that fails to decode, in frontend-less Solves when allow_run_instructions is false — a blanket refusal; the remaining work is real per-instruction mediation of raw-LLB and third-party frontends so RUN-equivalent ops can be inspected and selectively allowed instead of refused wholesale
│Signed policies pin keyed or keyless trust in a separate bootstrap file selected out of band, with bounded YAML and bundle inputs; candidate YAML can no longer select, replace, or disable its own trust
│Native Podman builds gain fail-closed primary/additional-context controls, host-write acknowledgments, Dockerfile inspection, and owner-label stamping
│+4 moreshow less
│BuildKit state is isolated by trusted caller principal, selected profile, and session ID, with atomic admission, bounded persistent IDs, expiring upload grants, and bounded upgrade handshakes
│Unix socket ownership, response filtering, keyword-named resource authorization, metric cardinality, and inspected-body deadlines are hardened across the composed proxy
│Helm supports separate candidate, trust, and signature objects with signed listener settings and render-time trust-boundary validation
│Release archives and checksums use sigstore bundles; images are signed and verified by digest, with verified publication metadata and persisted provenance records
│Swarm services can no longer reach the host network through TaskTemplate.Networks, closing a silent bypass of allow_host_network: false
│Three build-time RUN-instruction gate bypasses are closed: duplicate-Dockerfile context tars, upload-session contexts, and frontend-less raw-LLB Solves — the latter now denied outright (any ExecOp, or any op that fails to decode) when allow_run_instructions is false
│+2 moreshow less
│Cross-owner image-attestations access and two stale identity caches are closed, and image-trust verification rejects cross-repository signature transplants
│Grype now scans the published multi-arch image per platform, on release and weekly, instead of only from-source CI builds
│The star-history chart is a committed first-party SVG pair regenerated at each release cut, replacing the dead Warpchart embed, whose domain is out of the site CSP
│A daily Main Is Released monitor asserts main's HEAD points at a release tag, so default-branch scanners always describe the shipped version
│Fixes the v1.7.2 publish failure: cosign v3 defaults to the new sigstore bundle format and signing-config flow, which made it ignore the signs: blocks' output flags and demand an unset --bundle path
│Both signs: blocks now pass --new-bundle-format=false and --use-signing-config=false, restoring the legacy .sig/.pem output the docs and QA-6 verification rely on
│+1 moreshow less
│Every cosign-installer step pins cosign-release v3.1.3 so CI runs the exact binary the flags were verified against
│The publish run failed while signing checksums.txt against cosign v3's new bundle-format defaults, so this tag shipped no artifacts, release, or images; v1.7.3 carries its content
│Per-platform release archives are now cosign-signed and carry SLSA build provenance, closing the gap where only the source tarball was signed and attested
│+5 moreshow less
│The tri-tool conformance matrix's store-sync poll reads drydock's versioned /api/v1 endpoints and stops swallowing its own errors
│current-standard and current-edge conformance rows now resolve sockguard to the newest published stable release instead of a stale floor pin
│The lockfile-dedupe pre-push hook no longer misreports a stale node_modules as lockfile drift
│release-cut.yml's CI gate no longer times out before CI: Verify can finish
│Biome now lints and formats the 38 tracked .mjs files it had never been checking
│Four PR-gate gaps closed: Dependency Review, Gitleaks secret scanning, and Actionlint workflow linting all now run and block on every PR; Grype image scanning moves into the required Docker Build job
│Release binaries ship a CycloneDX SBOM per archive; the SLSA claim is qualified to Build L2 with a protected v* tag ruleset
│+4 moreshow less
│Weekly ZAP baseline passive DAST scan covers the static getsockguard.com marketing site and docs
│Cookieless PostHog analytics replaces Vercel Analytics across the marketing site and docs — no cookies, no consent banner
│Branch protection required contexts grow from 9 to 17; all CI job names drop decorative emoji since check names are matched by exact string
│CI runners pin to ubuntu-24.04 and harden-runner begins moving off blanket audit-mode egress
│BuildKit gRPC mediation: terminate the hijacked session/gRPC tunnel, parse moby.buildkit.v1.Control frames, and apply policy to solve requests, secret/SSH forwarding, and file-sync sessions instead of the current all-or-nothing acknowledgment
│request_body.network.endpoint_config.* per-field gates (allow_static_addressing, allow_link_local_ips, allow_mac_pinning, allow_gw_priority, allow_aliases) for endpoint settings on both network connect and container-create, so benign endpoint settings no longer require also allowing static-IP/MAC address pinning; allow_endpoint_config: true remains the broad whole-object override
│Podman support: preserve Docker-compatible API behavior and add explicit, default-deny policy coverage for the native /libpod namespace, including pod lifecycle operations
│Multiple independently configured main listeners (Unix and/or TCP), with listener-scoped TLS and client-profile boundaries
│+4 moreshow less
│Declarative safe admission mutations, starting with mandatory label injection and image-reference remapping; every mutated body is re-inspected before forwarding
│Published-image conformance matrix for Sockguard + Portwing + Drydock across standard and edge transports; remote update claims stay gated on implemented watcher/trigger contracts
│Docker Engine API 1.55 and current Compose/BuildKit conformance, replacing assumptions around the now-deprecated /session and /grpc endpoints with a verified fail-closed transport model
│Close the remaining resource-policy parity gaps: revalidate hard resource caps on container update and add swarm-service CPU-limit requirements
│Image-type Mounts entries on containers/create and services/create/update now enforce image_trust before forwarding, closing an unverified-image-filesystem bypass
│redact_network_topology now also strips the Engine API 1.53 network-inspect Status field
│+5 moreshow less
│Homebrew distribution through CodesWhat/homebrew-tap for native macOS and Linux installs of stable releases
│New docker-compose.edge-exec.yml tri-tool variant wires Portwing Edge Mode to drydock over Ed25519 key-based auth for interactive exec
│Portwing presets set deny_verbosity: verbose so sockguard's detailed denial reasons reach Portwing and drydock
│Nightly integration CI no longer breaks on Docker Hub busybox re-tags — the pre-pull step reads the pinned digest straight from the test helper constant
│Website overview matrix now exposes the multiple-listener comparison alongside the Podman gap
│Fresh named-volume Unix-socket deployments start under UID/GID 65532 without manual ownership repair
│Portwing examples use the published GHCR image and a portable OpenSSL token command
│+3 moreshow less
│Tri-tool local demo binds dashboards to loopback and explicitly configures drydock authentication behavior
│Published-image compatibility audit documents the remote update-trigger boundary across Portwing Standard and Edge modes
│Competitor review refreshes comparison facts and commits the evidence-backed v1.6 roadmap
│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
│+9 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
│allow_host_cgroupns (default false) extends the host-mode denials to HostConfig.CgroupnsMode — the one host namespace the allow_host_* family didn't previously gate
│require_cpu_limit_hard — opt-in hard CPU-time cap (NanoCpus/CpuQuota), independent of require_cpu_limit
│Exec Env policy — name allow/denylisting plus exact allowed_env_values pinning for security-sensitive entries
│New presets: portwing-with-compose.yaml, drydock-with-compose.yaml, plus a tri-tool compose example
│Helm chart: pod-level podSecurityContext
│Security-pass fixes merged forward: fresh embedded-resource ownership checks, registry-push exfiltration gate, browser headers, non-root/seccomp Helm defaults, structured-log sanitization, fail-closed plugin inspection, and patched dependency graphs
│Promoted to stable after rc.1/rc.2 field exposure and clean rc.3 CI, security, artifact, signature, and published-image validation
│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
│+6 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
│v1.4.1–v1.4.4 security patches: reachable TLS and dependency advisories cleared, case-varied JSON and log-injection paths closed, embedded owner dependencies freshly authorized, plugin inspection made fail-closed, registry-push exfiltration gated, and Vercel/Helm defaults hardened
│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 also inspects request bodies across container, exec, image, Docker and Podman build, volume, network, service, swarm, and plugin writes. Per-client profiles, signed policies, image trust, rollout modes, bounded Prometheus metrics, and hot reload add controls Tecnativa does not provide. The compatibility env surface remains a drop-in path for unsigned mode; convert generated rules to YAML before enabling signed-policy trust.
Does Sockguard inspect request bodies?
Yes. We inspect container create and exec, image build and load, Docker's classic build, native Podman's /libpod/build, volume, network, secret, config, service, swarm, node, and plugin writes. Native Podman build checks primary and additional remote contexts, host networking, every repeated query control, Dockerfile RUN policy, and resource-usage host-file output. Host/local/multipart and host-file controls require the global blind-write acknowledgment. Oversized bounded bodies return 413, and a 30-second read deadline stops slow clients from pinning an inspector even when logging and metrics are enabled.
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 treat the candidate YAML as untrusted until a cosign bundle verifies it. Keyed or keyless trust, Rekor posture, and a cooperative verification deadline live in a separate bootstrap file; candidate and trust YAML stop at 16 MiB, bundles at 4 MiB, and non-regular inputs are refused. Verification runs at startup and every reload. Container image trust separately resolves workload images to digests, rejects alternate payload URLs, accepts legal manifest media-type parameters, and applies redirect-safe response, signature fan-out, and aggregate payload limits before enforcing signer 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?
Point DOCKER_HOST at Sockguard and keep the current Tecnativa section and ALLOW_* variables for the initial unsigned migration. Then translate the generated allow surface into YAML, add body policy and per-client profiles, and use warn mode to measure tighter rules. Signed-policy mode is the final step: it rejects rule-generating compatibility variables so unsigned environment state cannot change a verified policy.

