Skip to content
v2.0.0 · Open Source · Apache-2.0

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.

19features
5alternatives compared
96%+coverage
Apache-2.0license

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.

sockguard — zsh
# One command, and every Docker API call runs through your rules.
$

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.

sockguard capabilities19 modules
01

Default-Deny Posture

Security

Everything 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.

02

Request Body Inspection

Security

We 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.

03

mTLS for Remote TCP

Security

We require mutual TLS 1.3 on non-loopback TCP listeners by default. Plaintext remote TCP is an explicit legacy opt-in only.

04

Owner Label Isolation

Control

Stamp 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.

05

Client ACL Primitives

Control

Gate 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.

06

Granular Control

Control

You can allow start/stop while blocking create/exec. We give you per-operation POST controls with glob matching.

07

Structured Access Logging

Operations

We 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.

08

Operator Observability

Operations

Our 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.

09

YAML Configuration

Control

We 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.

10

Tecnativa Compatible

Operations

Migrating 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.

11

Minimal Attack Surface

Security

We ship a Wolfi-based image, cosign-signed with SBOM and build provenance.

12

Signed Policy Bundles

Security

Pin 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.

13

Container Image Trust

Security

Verify 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.

14

Visibility-Controlled Reads

Security

We 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.

15

Named Client Profiles

Control

Route 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.

16

Rate Limits & Concurrency Caps

Control

We 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.

17

Per-Profile Rollout Modes

Control

Stage 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.

18

Hot-Reload + Admin API

Operations

We 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.

19

Remote Upstreams & Failover

Operations

Dial 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.

Terminal
$ 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/sockguard

Mounts 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.

ReleasedCurrent (HEAD)Planned
roadmap
v2.1.0🧩BuildKit RUN-Instruction Coverage

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

HEAD -> v2.0.0🛡️Pinned Policy Trust & Whole-App Hardening

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 more

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

v1.7.5🔒Security Patch: Filter Bypass Closures

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 more

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

v1.7.4📈First-Party Star Chart & Release Monitor

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

v1.7.3🔏Cosign v3 Signing Fix

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 more

Every cosign-installer step pins cosign-release v3.1.3 so CI runs the exact binary the flags were verified against

v1.7.2🔧Release Signing & Conformance Fixes

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 more

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

v1.7.1🔒CI/CD Security Gating

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 more

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

v1.7.0🔬BuildKit Mediation & Policy Narrowing

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

v1.6.0🧭Runtime Compatibility & Policy Mediation

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 more

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

v1.5.2🔧Security, Integration & Distribution Fixes

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 more

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

v1.5.1🔧Integration & Packaging Fixes

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 more

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

v1.5.0🔧Safer Defaults & Namespace Hardening

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 more

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()

v1.4.0🌐Remote Upstreams & Fleet Presets

Remote Docker daemon over TCP with mutual TLS and active/passive failover

Ordered endpoint failover — connect failure instantly promotes next endpoint without retry

+6 more

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

v1.3.0🛡️Posture Hardening

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 more

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)

v1.2.0📊Resilience & Observability

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 more

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)

v1.1.0🔒Security Hardening

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 more

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

v1.0.0Foundation

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 more

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

(initial commit)

Community

Star History

Growing every week — join us on GitHub.

Star history chart

Why Sockguard

How we compare

A quick look at what we support that Tecnativa, CetusGuard, and wollomatic don't.

FeatureSockguardTecnativaCetusGuardwollomatic
Method + path filteringYesYesPartialPartial
Request body inspectionYesNoNoPartial
Per-client policiesYesNoNoPartial
Remote TCP mTLSYesNoYesNo
Signed policy bundlesYesNoNoNo
Prometheus metricsYesNoNoNo
Yes Partial No

Ecosystem

Part of the CodesWhat stack

Sockguard is one piece of a small, focused toolkit — each tool does one job, and they compose.

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.