Skip to content

Latest commit

 

History

814 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
sockguard

sockguard

Control what gets through. A default-deny Docker socket proxy built in Go.

Release Multi-arch Image size License Apache-2.0
CI Integration Nightly fuzz Weekly Grype OpenSSF Scorecard OpenSSF Best Practices Maintainability Coverage Mutation score
Release downloads GHCR Docker Hub pulls Quay.io Stars Issues Discussions


Note

v2.0.0 is the latest stable release. Signed-policy trust now lives in a separate bootstrap file, outside the signed candidate it authenticates. Existing signed-policy deployments must follow the v1.7.5 to v2.0.0 migration guide before upgrading. The release also completes native Podman build inspection, isolates mediated BuildKit state by trusted caller and session, hardens proxy deadlines and socket cleanup, and ships the matching Helm, website, documentation, and sigstore-only verification surfaces. See CHANGELOG.md for the complete release notes.

Contents


Quick Start

Drop sockguard in front of any Docker API consumer. The proxy filters requests, your app stays unchanged.

# docker-compose.yml
services:
  sockguard:
    image: codeswhat/sockguard:latest
    restart: unless-stopped
    read_only: true
    cap_drop:
      - ALL
    security_opt:
      - no-new-privileges:true
    group_add:
      - "${DOCKER_SOCK_GID:?set to the GID of /var/run/docker.sock}"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    environment:
      - SOCKGUARD_LISTEN_ADDRESS=:2375
      - SOCKGUARD_LISTEN_INSECURE_ALLOW_PLAIN_TCP=true
      - SOCKGUARD_LISTEN_INSECURE_ALLOW_UNAUTHENTICATED_CLIENTS=true
      - SOCKGUARD_INSECURE_ALLOW_READ_EXFILTRATION=true
      - CONTAINERS=1
      - IMAGES=1
      - EVENTS=1

  # Your app talks to tcp://sockguard:2375 over the compose network
  # instead of mounting /var/run/docker.sock.
  drydock:
    image: codeswhat/drydock:latest
    depends_on:
      - sockguard
    environment:
      - DD_WATCHER_LOCAL_SOCKET=tcp://sockguard:2375

Before docker compose up, set DOCKER_SOCK_GID to the socket's numeric group owner (export DOCKER_SOCK_GID=$(stat -c '%g' /var/run/docker.sock) on Linux; use stat -f '%g' on macOS).

By default sockguard listens on loopback TCP 127.0.0.1:2375, not on all interfaces. Non-loopback TCP now requires mutual TLS via listen.tls by default.

The compose example above opts into legacy plaintext TCP so migration from tecnativa/docker-socket-proxy and linuxserver/socket-proxy still works on a private Docker network. A non-loopback plaintext listener requires two deliberate acknowledgments — SOCKGUARD_LISTEN_INSECURE_ALLOW_PLAIN_TCP=true (unencrypted transport) and SOCKGUARD_LISTEN_INSECURE_ALLOW_UNAUTHENTICATED_CLIENTS=true (any host that can reach the port can impersonate a client) — so a single fat-fingered flag cannot expose it. It also opts into SOCKGUARD_INSECURE_ALLOW_READ_EXFILTRATION=true because broad CONTAINERS=1 / IMAGES=1 compatibility includes raw archive/export and log/attach streaming endpoints. Do not publish that plaintext listener to the host or Internet, and remove the read-exfil opt-in once you migrate to tighter YAML list/inspect rules.

If you run sockguard directly on a host, keep SOCKGUARD_LISTEN_ADDRESS=127.0.0.1:2375, configure listen.tls for remote TCP, or switch to SOCKGUARD_LISTEN_SOCKET to avoid a network listener entirely.

Container runtime hardening

Sockguard runs as UID 65532 (Chainguard nonroot) inside the container. On stock Linux Docker hosts where /var/run/docker.sock is 0660 root:docker, add the container to the socket's numeric group ID with group_add or run Sockguard as a user/group that can open the socket. For this class of tool, the meaningful hardening levers are the proxy policy, a read-only root filesystem, dropped capabilities, no-new-privileges, and the host runtime's seccomp/AppArmor/SELinux confinement.

The examples in this README already opt into the container-level controls sockguard actually benefits from:

  • read_only: true
  • cap_drop: [ALL]
  • security_opt: ["no-new-privileges:true"]

On Linux, one common pattern is:

group_add:
  - "${DOCKER_SOCK_GID:?set this to the numeric group owner of /var/run/docker.sock}"

Keep Docker's default seccomp profile or replace it with a stricter custom profile via security_opt. On AppArmor or SELinux hosts, keep the runtime's default confinement enabled or replace it with a stricter host policy. If the host runs rootless dockerd, a compromised Docker API client inherits the daemon's reduced authority instead of full host root.

mTLS TCP mode (recommended for remote TCP)
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
      - ./certs:/certs:ro
    environment:
      - SOCKGUARD_LISTEN_ADDRESS=:2376
      - SOCKGUARD_LISTEN_TLS_CERT_FILE=/certs/server-cert.pem
      - SOCKGUARD_LISTEN_TLS_KEY_FILE=/certs/server-key.pem
      - SOCKGUARD_LISTEN_TLS_CLIENT_CA_FILE=/certs/client-ca.pem
      - SOCKGUARD_INSECURE_ALLOW_READ_EXFILTRATION=true
      - CONTAINERS=1

Non-loopback TCP without listen.tls fails startup unless you explicitly set SOCKGUARD_LISTEN_INSECURE_ALLOW_PLAIN_TCP=true. Sockguard's server-side TLS minimum for listen.tls is TLS 1.3, so remote clients must support TLS 1.3. If one client CA issues multiple workloads, narrow the trusted set further in YAML with listen.tls.common_names, dns_names, ip_addresses, uri_sans, and/or public_key_sha256_pins so any CA-issued client cert is not automatically accepted.

Unix socket mode (filesystem-bounded access)

If you prefer to expose sockguard as a unix socket (no network surface at all), opt in by setting SOCKGUARD_LISTEN_SOCKET and sharing the socket via a named volume:

services:
  sockguard:
    image: codeswhat/sockguard:latest
    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

  drydock:
    image: codeswhat/drydock:latest
    depends_on:
      - sockguard
    volumes:
      - sockguard-socket:/var/run/sockguard:ro
    environment:
      - DD_WATCHER_LOCAL_SOCKET=/var/run/sockguard/sockguard.sock

volumes:
  sockguard-socket:

Sockguard hardens its own unix socket to 0600 owner-only permissions. listen.socket_mode remains in the config surface as a guardrail and must stay 0600; broader modes are rejected at startup instead of being applied.

The named-volume quick start creates that socket and its parent directory as UID/GID 65532. A non-root consumer must therefore run as UID 65532 (as the Portwing examples do); a root consumer can also connect. If an application must keep another UID, run Sockguard with that same UID against a pre-owned bind-mounted directory, or use an authenticated TCP listener instead. Do not broaden the socket mode.


Recent Updates

Latest release highlights
  • v2.0.0 shipped on 2026-08-28 after the published 2.0.0-rc.4 candidate passed the four-hour soak and full Sockguard + Portwing + drydock conformance matrix. Signed-policy trust is now selected from a separate bootstrap file instead of by the signed candidate itself, and unsigned compatibility variables can no longer mutate a verified deployment. Native Podman builds receive complete body inspection and owner stamping; BuildKit admission state is isolated by trusted caller, profile, and session; routing, visibility, request deadlines, Unix-socket cleanup, and metric cardinality are hardened; and release blobs now use sigstore bundles only. Existing signed-policy deployments must complete the v2 migration before upgrading.
  • v1.7.5 shipped on 2026-08-23 — promotes 1.7.5-rc.1 to stable. The candidate's security fixes: the Swarm host-network deny now covers TaskTemplate.Networks (#332); duplicate-Dockerfile build-context tars, frontend-less raw-LLB Solves, and upload-session contexts can no longer bypass allow_run_instructions (#333); cross-owner image-attestations access and two stale identity caches are closed (#334); image-trust verification no longer leaks a trust-root refresh goroutine per hot-reload and rejects cross-repository signature transplants (#335); and Grype scans the published multi-arch image per platform on release and weekly (#318). The rc-to-GA delta is release and CI infrastructure: a tracked-file conflict-marker guard (#336), a release-cut guard refusing prerelease tags dispatched on main (#337), and harden-runner egress allowlists covering the Go module proxy's storage.googleapis.com redirect (#338).
  • v1.7.4 shipped on 2026-08-21 — the star-history chart is now a committed first-party SVG pair (light + dark) regenerated at each release cut, replacing the dead Warpchart embed, whose domain leaves the site CSP; a contract test keeps both retired chart hosts from coming back (#303). A daily Main Is Released monitor now asserts main's HEAD points at a release tag, so Scorecard, CodeQL, and Grype runs against the default branch always describe the shipped version.
  • v1.7.3 shipped on 2026-08-21 — the v1.7.2 tag's publish run died signing checksums.txt because the cosign that sigstore/cosign-installer now installs defaults to the new sigstore bundle format and ignores the signs: blocks' output flags; both blocks now pass --new-bundle-format=false/--use-signing-config=false and every cosign-installer step pins v3.1.3 (#271). v1.7.2 remains a tag with no published artifacts; v1.7.3 ships its content.
  • v1.7.2 shipped on 2026-08-21 — 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 (#271); the tri-tool conformance matrix's store-sync poll now reads drydock's versioned /api/v1 endpoints and stops swallowing its own errors, and current-standard/current-edge rows resolve sockguard to the newest published stable release instead of a stale floor pin (#289); the lockfile-dedupe pre-push hook no longer reports a stale node_modules as lockfile drift (#295); release-cut.yml's CI gate no longer times out before CI: Verify can finish (#288); and Biome now lints and formats the 38 tracked .mjs files it had never been checking, which surfaced and fixed a misleading-character-class bug in the release-version classifier (#297).
  • v1.7.1 shipped on 2026-08-19 — closes four PR-gate gaps from the house security-gating audit: Dependency Review and a new Gitleaks job now run and block on every PR, a new Actionlint job lints workflow files, and Grype image scanning moves into the required Docker Build job (#271); release binaries now ship a CycloneDX SBOM per archive and the SLSA claim is qualified to Build L2 (#279); a weekly ZAP baseline DAST scan covers the static getsockguard.com site and docs (#280); cookieless PostHog analytics replaces Vercel Analytics (#253); branch protection grows from 9 to 17 required contexts and all CI job names drop decorative emoji (#285); and CI runners pin to ubuntu-24.04 with harden-runner beginning its move off blanket audit-mode egress (#282).
  • v1.7.0 shipped on 2026-08-11 — full BuildKit gRPC mediation (#185): the hijacked POST /session/POST /grpc tunnel is now terminated as h2c on both sides, with per-message policy on Control/Solve/Control/Status, credential session mediation (Auth/Secrets/SSH), and file-sync/upload stream mediation, replacing the opaque-tunnel acknowledgment (insecure_accept_opaque_buildkit_tunnels, now deprecated) with real enforcement. Also narrows request_body.network.allow_endpoint_config into independent per-field gates — static addressing, link-local IPs, MAC pinning, and gateway priority — on both network connect and container-create (#186).
  • v1.6.0 shipped on 2026-08-08 — promotes 1.6.0-rc.1 to stable after the pre-GA tri-tool conformance gate ran green on all three published-image matrix rows; the rc.1→GA delta adds the libpod exec-start hijack-path fix (#194), a bounded exec-inspect decode (#188), real-daemon conformance tests for every portwing/drydock preset (#196), and a pinned multi-Engine CI matrix (#187). The v1.6 line delivers the full roadmap sequence: multiple independently scoped main listeners (#149), fail-closed declarative admission mutations (#151), effective-state resource-limit guarantees for container updates and Swarm services (#152), Engine API 1.55 validation plus the fail-closed Compose/BuildKit transport acknowledgment (#153), first-class Podman support across the Docker-compatible and native /libpod API surfaces with a dedicated guide and podman-readonly.yaml preset (#148), and the weekly Sockguard + Portwing + drydock conformance matrix (#150).
  • v1.5.2 shipped on 2026-08-04 — closes an image_trust bypass where image-type Mounts entries on container/service create skipped signature verification, extends redact_network_topology to the Engine API 1.53 network-inspect Status field, adds Homebrew tap distribution, ships the tri-tool Edge Mode + exec Compose variant, and fixes nightly integration digest drift.
  • v1.5.1 shipped on 2026-07-28 — fixes fresh named-volume Unix-socket startup for the non-root image, corrects Portwing's published registry reference and token command, loopback-binds the authenticated tri-tool demo, and publishes the tested three-tool compatibility boundary plus the competitor-driven v1.6 roadmap.
  • v1.5.0 shipped on 2026-07-28 — promotes rc.3 to stable after the v1.5 feature surface had been exercised since rc.1 on July 11 and rc.2 on July 20, followed by clean CI, security, artifact, signature, and published-image validation on rc.3 plus a final full-delta review. Safer finite-request timeouts, namespace-sharing and host-cgroupns controls, a hard CPU-cap option, exact exec-environment value pinning, endpoint-config parity, Compose presets, Helm security defaults, fresh embedded-resource ownership checks, registry-push exfiltration gating, structured-log sanitization, fail-closed plugin inspection, and patched dependency graphs are now GA.
  • v1.5.0-rc.3 shipped on 2026-07-28 — forwarded the complete v1.4.4 security patch into the v1.5 line: untrusted structured-log fields escape record delimiters, malformed plugin configuration is denied before forwarding, and the affected Go and Node dependencies move to patched releases. It retained rc.2's endpoint-config symmetry and rc.1's safer defaults and namespace hardening, passed the full release gates, and became the final candidate promoted to v1.5.0.
  • v1.4.4 shipped on 2026-07-28 — security patch. Every attacker-controlled value crossing into structured logs now escapes CR/LF record delimiters; malformed plugin config.json is denied before forwarding instead of bypassing inspection; Next.js, PostCSS, sharp, js-yaml, gRPC, x/net, x/text, x/crypto, and klauspost/compress move to patched releases. Main-branch protection now requires two approvals and a code-owner review with no bypass actors. No public configuration or API change.
  • v1.5.0-rc.2 shipped on 2026-07-20 — the second v1.5 candidate, carrying the full five-finding security pass forward from v1.4.3 alongside the endpoint-config symmetry fix landed after rc.1. It freshly authorizes every embedded workload dependency, removes mutable-name ownership caching, gates registry pushes as exfiltration, hardens Vercel and Helm defaults, and keeps the v1.5 namespace-sharing protections in the same authorization path. Its field time contributed to the completed v1.5 prerelease validation.
  • v1.4.3 shipped on 2026-07-20 — security and correctness patch. Owner isolation now authorizes the images, named volumes, networks, secrets, and configs embedded inside container/service create/update payloads, denies foreign or unresolved dependencies, and freshly inspects mutable Docker names/tags on every authorization decision instead of reusing a ten-second positive cache entry. The exfiltration acknowledgement now covers image/plugin registry pushes; the Vercel-hosted site and copied docs export gain an enforced CSP plus browser hardening headers; Helm defaults pin UID/GID 65532, runAsNonRoot, and RuntimeDefault seccomp. Also fixes the v1.4 runtime wiring for explicitly acknowledged unpinned exec without weakening the privilege, root-user, or configured command rails.
  • v1.4.2 shipped on 2026-07-11 — security patch. Backports the case-varied-JSON-key filter-bypass fix from the v1.5 line: the daemon decodes body keys case-insensitively and honors the last duplicate after re-encoding, so a shadow lowercase "image"/"labels"/"hostconfig" could survive struct-decode inspection and then win at the daemon on every path that mutates and re-marshals a body (owner-label spoofing, image-trust digest pinning, and whole-body reorder of any container-create/service rule). Create/update bodies carrying duplicate case-variant keys are now rejected fail-closed (400/403) before re-marshaling, a lone lowercase variant is collapsed to canonical so it stays inspected, and image-trust no longer forwards the original tag when digest pinning fails after a successful verify. No config or API change.
  • v1.4.1 shipped on 2026-07-10 — security patch. Go toolchain 1.26.41.26.5 to clear a reachable crypto/tls ECH advisory (GO-2026-5856) in the remote-upstream TLS and connection-hijack paths; the v1.4.0 images carried it, v1.4.1 rebuilds them clean (govulncheck reports zero reachable vulnerabilities). No proxy behavior, config, or API change.
  • v1.4.0 shipped on 2026-07-10 — remote upstreams, confinement-mode parity, and supply-chain consolidation. Sockguard can now dial remote Docker daemons over TCP+mTLS with active/passive failover (upstream.endpoints[], per-endpoint TLS and connect-level health probes, instant demotion on failure). SecurityOpt SELinux/system-paths rails (deny_selinux_disable, deny_selinux_label_override, deny_unconfined_system_paths) and swarm seccomp/AppArmor confinement modes (deny_unconfined_seccomp, deny_custom_seccomp_profiles, deny_unconfined_apparmor) complete ContainerSpec privilege parity with container create. The rate-limit token bucket is now allocation-free (0 allocs/op; burst validated at ≤ 65535). Three new Portwing/drydock presets (12 → 15). CVE scanning consolidated on Grype + govulncheck (Snyk dropped) to kill module-graph false positives, a multi-axis security/performance/supply-chain audit hardening pass (no critical/high findings), and an enforced 96% production-coverage floor with live Qlty Cloud reporting.
  • v1.3.0 shipped on 2026-06-11 — swarm posture parity and admin-surface hardening. Swarm service create/update now enforces the container-create identity/privilege rails (require_non_root_user, require_no_new_privileges, require_readonly_rootfs, require_drop_all_capabilities on ContainerSpec), closing the bypass where a service could request a workload shape /containers/create would deny. A zero-padded-UID root bypass ("00", "0000:5") is sealed across container create and exec. A wide-open dedicated admin listener is now a validation error; admin paths are normalized before matching; non-upgrade hijack responses strip hop-by-hop headers. Operational fixes: the signature_path hot-reload wedge, three silently-ignored SOCKGUARD_* env vars, oversized bodies returning 403 instead of 413, and release images now carrying real commit/built metadata. Multi-arch images cross-compile natively (no more emulated toolchain faults).
  • v1.2.0 shipped on 2026-06-02 — operational resilience for a wedged daemon. An opt-in readiness probe (health.readiness.*, default /ready) issues a real GET /containers/json against the Docker API and returns 503 when the daemon accepts connections but no longer answers — the gap the raw-dial /health watchdog misses. An opt-in upstream.request_timeout bounds finite proxied requests with a total deadline, converting a hung body or heavy read into a fast 504 (reason_code=upstream_request_timeout) while exempting streaming and long-lived endpoints. New metrics sockguard_upstream_api_up + sockguard_upstream_readiness_checks_total mirror the watchdog. The bundled drydock preset now allowlists the stock runc runtime so drydock recreation stops getting 403'd out of the box. Dependency hygiene: the Go toolchain moves to 1.26.4 (clearing two reachable stdlib advisories, GO-2026-5037 / GO-2026-5039), plus the go-minor / npm-minor / actions-minor groups; govulncheck reports zero vulnerabilities.
  • v1.1.0 shipped on 2026-06-01 — image-trust verification wired end to end: registry digest resolution, cosign signature discovery (classic tag + OCI 1.1 referrers), digest-pinned forwarding, keyed (PEM) and keyless (Fulcio + Rekor) both enforced, swarm-service create/update now subject to the same image-trust policy as container create. A 21-finding security audit landed alongside: closed request-inspection bypasses (plugin multipart, BuildKit # syntax=, gzip bombs, swarm-service capability/sysctl/image-trust escapes), read-side sub-resource visibility gating, new allowed_runtimes allowlist, hardened config/admin paths (signed-bundle TOCTOU, PID-only peer rejection, admin-listener CIDR backstop), response redaction extended to HostConfig.Mounts[].Source and service PreviousSpec. CodeQL actions analysis and supply-chain dependency hygiene (govulncheck reports zero vulnerabilities) round out the release.
  • v1.0.0 shipped on 2026-05-20 with the public proxy contract locked: YAML schema, CLI flags, env vars, admin endpoints, and Prometheus metric names are now under the v1.x compatibility promise.
  • 17 bundled presets cover drydock, 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, drydock with self-update, and drydock with compose.
  • Expanded QA hardening added proxy-vs-daemon differential tests, real-dockerd preset conformance, fuzz corpora for routing and visibility, weekly soak testing, and TLS edge-case coverage.
  • Supply-chain verification covers release images across GHCR, Docker Hub, and Quay.io using the same cosign commands documented for operators.

See CHANGELOG.md for the full itemized history.


Why Sockguard

The Docker socket is root access to your host. Every container with socket access can escape containment, mount the host filesystem, and pivot to other containers. Yet tools like Traefik, Portainer, and drydock need socket access to function.

Most existing socket proxies stop at method/path or regex filtering. Tecnativa gates broad Docker API sections; LinuxServer adds explicit Podman/libpod families; wollomatic adds regex allowlists, caller admission, bind-source restrictions, JSON logs, and a watchdog; 11notes ships a fixed allow-most-reads proxy over Unix and TCP; and CetusGuard pairs default-deny regex rules with mTLS, native libpod routes, and multiple listeners. Sockguard goes further on body-aware policy enforcement, per-client profiles, ownership isolation, and read-side visibility/redaction, and as of v1.6.0 also covers native /libpod routes and multiple independently scoped listeners.


Features

Feature Description
Default-Deny Posture Everything blocked unless explicitly allowed. No match means deny.
Granular Control Allow start/stop while blocking create/exec. Per-operation POST controls with glob matching.
YAML Configuration Declarative rules, glob path patterns, first-match-wins evaluation, and canonical path matching that strips API versions, collapses dot segments, and decodes escaped separators before policy evaluation. 17 bundled workload presets (including CIS Docker Benchmark, self-hosted GitHub Actions runners, GitLab Runner, and Portwing) plus the default config.
Structured Access Logging JSON access logs with method, raw path, normalized path, decision, matched rule, latency, canonical request ID, W3C traceparent correlation fields, and client info. Untrusted string fields escape CR/LF record delimiters as visible \r/\n sequences before reaching slog, preserving forensic content without allowing forged records even through custom handlers. Use normalized_path for SIEM correlation and policy analysis; raw path is preserved for forensic replay. Canonical request IDs are generated from a buffered pool so request logging does not block on a fresh entropy read per request.
mTLS for Remote TCP Non-loopback TCP listeners require mutual TLS by default. Plaintext TCP is explicit legacy mode only.
Client ACL Primitives Optional source-CIDR admission checks, client-container label ACLs, listener certificate selectors (CN/DNS/IP/URI SAN/SPKI), profile certificate selectors (CN/DNS/IP/URI/SPIFFE/SPKI), and unix peer credentials let one proxy differentiate callers before the global rule set runs. When mTLS is enabled, certificate selectors follow the verified client leaf certificate rather than an unverified peer slice entry. The same trusted principal and selected profile scope mediated BuildKit state across its /grpc and /session connections; persistent IDs are bounded and abandoned upload grants expire.
Safe Inspect Strategy Visibility checks reuse a bounded, short-lived singleflight cache, while authorization-critical ownership checks always inspect current Docker state so a deleted/recreated name or retagged image cannot inherit a stale allow decision.
Request Body Inspection POST /containers/create, /containers/*/update, /containers/*/exec, /exec/*/start, PUT /containers/*/archive, /images/create, /images/load, /build, /libpod/build, /volumes/create, /networks/create, /networks/*/connect, /networks/*/disconnect, /secrets/create, /configs/create, /services/create, /services/*/update, /swarm/init, /swarm/join, /swarm/update, /swarm/unlock, /nodes/*/update, /plugins/pull, /plugins/*/upgrade, /plugins/*/set, and /plugins/create are inspected before the daemon sees the request. Native Podman builds share request_body.build with Docker's classic builder, including every repeated or legacy additional-context definition; host/local/multipart and resource-usage host-file controls require the global blind-write acknowledgment. Sockguard blocks privileged or host-bound workloads, non-allowlisted mounts/devices/commands/remotes, unsafe network/service/swarm/node controls, image archive imports outside registry policy, and unsafe container filesystem archives. POST /plugins/create is inspected whether the tar upload arrives as a raw body or multipart/form-data. Oversized bodies on bounded JSON/tar inspectors are rejected with 413 Payload Too Large, and inspected-body reads have a 30-second deadline through both logging and metrics wrappers. These inspectors intentionally decode the policy-relevant subset of Docker's schema and still defer full-schema validation to the daemon itself.
Image Trust Container and Swarm-service images can require keyed or keyless cosign signatures before deployment. Registry-controlled discovery is bounded across metadata responses, referrers, signature images, layers, aggregate payload bytes, annotations, and verification candidates; signature references must be direct image manifests, not recursively resolved indexes, and payload layers with alternate URLs are rejected before blob resolution. Legal media-type parameters on direct manifests are accepted. A limit breach aborts discovery; enforce denies the request, while warn logs the failure and forwards it. Verified references are digest-pinned before forwarding.
Owner Label Isolation A proxy instance can stamp label-capable creates plus build-produced images with an owner label, auto-filter labeled list/prune/events calls, and deny cross-owner access across containers, images, networks, volumes, services, tasks, secrets, configs, nodes, and swarm state, including images, named volumes, networks, secrets, and configs referenced inside container/service payloads. Resource names that happen to equal Docker collection actions such as create or prune are still checked according to the request method and exact path.
Visibility-Controlled Reads Redacts env, mount, network, config, plugin, and swarm-sensitive metadata by default, can hide labeled list/inspect plus selected service/task log reads behind per-client visibility rules, and keeps raw archive/export and stream-style reads behind explicit opt-in. Combined label and name/image visibility checks share one bounded inspect, and keyword-named resources remain covered.
Body-Blind Write Guardrail Any remaining write control Sockguard cannot safely constrain stays behind explicit insecure_allow_body_blind_writes opt-in instead of being silently exposed. Today that guardrail chiefly covers arbitrary exec without request_body.exec.allowed_commands, POST /swarm/join without request_body.swarm.allowed_join_remote_addrs, plugin setting writes without explicit allowed assignment prefixes, Podman build host/local/multipart or resource-usage file controls, and the documented uninspected libpod writes. For exec and Podman build, the flag lifts only the uninspectable gate; all other configured checks remain active.
Tecnativa Compatible Drop-in replacement for the current Tecnativa env surface, including section vars, ALLOW_RESTARTS, SOCKET_PATH, and LOG_LEVEL. Rule-generating compatibility variables are intentionally rejected when signed-policy mode is active; convert those rules to signed YAML before enabling the trust gate.
Rollout Modes Per-profile mode: enforce|warn|audit lets operators stage a tighter policy without breaking callers. warn/audit pass-through with decision=would_deny on the audit record and a mode label on the deny/throttle counters, so dashboards compare blocked vs. would-have-been-blocked volume side by side.
Hot-Reload + Policy Versioning reload.enabled: true watches the config file via fsnotify (Linux inotify / macOS kqueue) and accepts SIGHUP. The new policy goes through signature verification when enabled, the full validator, and rule compilation before an atomic swap. Immutable listener, upstream, log, health, metrics, and admin fields refuse reload. Signed-policy bootstrap trust stays pinned outside the candidate, while its signature_path can rotate. A monotonic generation counter is exposed at GET /admin/policy/version and via sockguard_policy_version.
Admin API Opt-in POST /admin/validate accepts a candidate YAML body and returns the same verdict the offline sockguard validate command would — perfect for a CI gate before promoting a config. GET /admin/policy/version reports {version, loaded_at, rules, profiles, source, config_sha256, bundle_signer?}. Both endpoints can ride the main listener or move to a dedicated admin.listen.* (socket or TCP, mTLS-aware) firewalled from Docker-API consumers.
Signed Policy Bundles sockguard serve --policy-bundle-trust-config <path> pins keyed or keyless trust, Rekor posture, and a cooperative verification deadline in a bootstrap file separate from the signed YAML. Candidate and trust YAML are capped at 16 MiB, and signature bundles at 4 MiB, before parsing; FIFOs, devices, directories, and other non-regular inputs are rejected without blocking. Keyless trust loads initially and refreshes about every 24 hours through a process-wide memoized, read-only-filesystem-compatible TUF client with bounded downloads; startup fails closed if the initial load fails, while a failed background refresh is logged and retains the last valid root. Keyed-only policy-bundle trust loading makes no network call. Verification checks cancellation before beginning and after each synchronous local Sigstore verification attempt and stops signer fallback, but sigstore-go cannot preempt an individual crypto call. The signed candidate carries policy_bundle.signature_path; candidate trust fields cannot disable or redefine the gate. Verification runs before the operational logger or any rule compilation and again on every hot reload. Unsigned, oversized, or tampered bundles abort startup and reject reloads with reject_signature, while rule-generating compatibility variables reject with reject_compat. The verified certificate SAN or keyed fingerprint and YAML digest are stamped on the policy-version snapshot.
Minimal Attack Surface Wolfi-based image. Cosign-signed with SBOM and build provenance.
Streaming-Safe Preserves Docker streaming endpoints (logs, attach, events) without breaking timeouts, while reaping idle TCP keep-alive connections after 120s. Attach and exec-start handshakes have a 30-second client/upstream deadline that is cleared after a valid 101 upgrade.
Health, Watchdog + Readiness /health endpoint with cached upstream reachability probes, an opt-in active Docker socket watchdog that logs state transitions, and an opt-in /ready probe that issues a real GET /containers/json against the Docker API — returning 503 when the daemon accepts connections but has stopped answering, the wedged-daemon case a raw socket dial misses.
Upstream Request Timeout upstream.request_timeout (default 60s) bounds finite proxied requests with a total deadline, turning a hung response body or heavy read into a fast 504 (reason_code=upstream_request_timeout). Streaming and long-lived endpoints (events, follow logs/stats, pull/build/push/load, export, archive/docker cp, attach, container wait) are exempt. Set "off" to disable.
Prometheus Metrics Opt-in /metrics endpoint with bounded-cardinality request counters, deny counters, latency histograms, active request gauge, upstream watchdog + readiness state/check metrics, plus sockguard_build_info and sockguard_start_time_seconds gauges for version panels and uptime alerts. Unknown HTTP methods collapse to OTHER and unknown route families to unknown.
Trace/Log Correlation Preserves valid W3C traceparent context or generates local context, forwards a proxy-local span ID, and records trace fields in access, audit, and upstream error logs without an OTLP exporter.
Battle-Tested 96%+ statement coverage (enforced by a CI coverage gate), race-detector clean, monthly Gremlins mutation testing, and fuzz testing on filter, config, proxy, and hijack paths.

Supported Profiles

Bundled presets (18)

drydock · drydock with self-update · drydock with compose · Portwing · Portwing with exec · Portwing with compose · Traefik · Portainer · Watchtower · Homepage · Homarr · Diun · Autoheal · read-only · CIS Docker Benchmark · GitHub Actions self-hosted runner · GitLab Runner · multiple listeners

Ready-to-run compose examples

drydock · Portwing · Portwing + drydock (tri-tool) · Traefik · Portainer · Watchtower · GitHub Actions self-hosted runner · GitLab Runner · CIS Docker Benchmark gate

Each example pairs a downstream Docker API consumer with a sockguard.yaml overlay and a short README covering audience, exposed API surface, and security tradeoffs.

Policy surfaces

Rules can cover method/path filters, body-aware write inspection, declarative admission mutation (fail-closed label injection and image remapping), read-side redaction and visibility, per-client profile selection, rate limits, concurrency caps, owner-label isolation, rollout modes, hot reload, signed policy bundles, and admin validation.


Feature Comparison

How does Sockguard compare to other Docker socket proxies?

How we stack up against other Docker socket proxies:

Feature Tecnativa LinuxServer wollomatic 11notes CetusGuard Sockguard
Method + path filtering ✅ (regex) Fixed read-only ✅ (regex)
Granular container write ops Partial (ALLOW_*) Via regex ❌ (read-only) Via regex
Request body inspection Partial (bind-mount source restrictions) ✅ (container create/update/exec/archive, image pull/load, Docker + Podman build, volume, network create/connect/disconnect, secret, config, service, swarm init/join/update/unlock, node update, plugin)
Per-client admission / policy selection Partial (IP/hostname + per-container labels) ✅ (CIDR + labels + cert selectors incl. SPKI + unix peer profiles)
Read-side visibility / redaction Partial (blocks 7 risky GETs) ✅ (visibility + protected JSON redaction)
Remote TCP mTLS (listener) ✅ (TLS 1.3)
Remote daemon upstream (TLS) ✅ (failover)
Podman native /libpod API Via manual regex ✅ (default-deny, incl. pod lifecycle)
Multiple main listeners ✅ (Unix + TCP) ✅ (Unix and/or TCP, listener-scoped TLS + profiles)
Structured access logs ✅ (JSON option) ✅ (request + trace correlation)
Dedicated audit log schema ✅ (JSON schema + reason codes)
Rate limits / concurrency caps ✅ (per-profile token-bucket + global priority gate)
Rollout modes (audit/warn/enforce) ✅ (per-profile shadow + would_deny audit)
Hot-reload + policy versioning ✅ (fsnotify + SIGHUP, /admin/policy/version)
Signed policy bundles ✅ (sigstore keyed + keyless)
YAML config
Tecnativa env compat N/A

11notes/docker-socket-proxy takes a deliberately narrow stance: it allows most Docker API reads, blocks seven sensitive GET surfaces, and refuses all writes. Sockguard instead starts from a configurable default deny, offers finer-grained redaction/visibility, and can authorize inspected writes. hectorm/cetusguard is the closest in spirit: default-deny regex rules plus frontend/backend mTLS, native libpod families, and multiple frontend addresses. Sockguard is stronger on request-body inspection, per-client profiles, ownership, read filtering, metrics, hot reload, and health-checked upstream failover; v1.6.0 closed the libpod and multi-listener gaps that were CetusGuard's remaining advantages. The full evidence and resulting priorities are in the roadmap.


Configuration

Environment Variables (Tecnativa-compatible)

CONTAINERS=1    # Allow /containers/** (GET/HEAD when POST=0)
IMAGES=0        # Deny /images/**
SERVICES=1      # Allow /services/** (GET/HEAD when POST=0)
EVENTS=1        # Allow /events (default)
POST=0          # Read-only mode

# Granular container writes still work even when POST=0
ALLOW_START=1
ALLOW_STOP=1
ALLOW_RESTARTS=1

# Compat aliases
SOCKET_PATH=/var/run/docker.sock
LOG_LEVEL=warning

Compat env vars only generate rules when no explicit rules: are configured. If you provide rules: in YAML, those rules win even when they happen to match the built-in defaults exactly. Broad compat reads (CONTAINERS=1, IMAGES=1, POST=0) that pull in raw archive/export and log/attach streaming also need SOCKGUARD_INSECURE_ALLOW_READ_EXFILTRATION=true; see the configuration reference for the full env-var surface. Signed-policy mode does not permit rule generation after signature verification, so any section, POST, GRPC/SESSION, or ALLOW_* compatibility variable causes startup to fail. Translate those grants into the signed YAML first.

YAML Config (recommended)

listen:
  address: 127.0.0.1:2375   # loopback TCP; use listen.socket or listen.tls for anything else

rules:
  - match: { method: GET, path: "/_ping" }
    action: allow
  - match: { method: GET, path: "/containers/json" }
    action: allow
  - match: { method: GET, path: "/containers/*/json" }
    action: allow
  - match: { method: POST, path: "/containers/*/start" }
    action: allow
  - match: { method: "*", path: "/**" }   # default-deny backstop
    action: deny

Trailing /** matches both the base path and any deeper path. For example, /containers/** matches /containers and /containers/abc/json.

Multiple independently scoped listeners are also supported — replace listen: with a listeners: list (unix socket and/or mTLS TCP, any combination), each entry naming which clients.profiles it admits via allowed_profiles:

listeners:
  - name: ci
    socket: /var/run/sockguard-ci.sock
    socket_mode: "0600"
    allowed_profiles: [ci]
  - name: ops
    address: 0.0.0.0:8443
    tls: { cert_file: ..., key_file: ..., client_ca_file: ... }
    allowed_profiles: [ops]

A client resolved to a profile outside the listener it connected on's allowed_profiles is denied, even if the base policy would otherwise allow it — see configs/multi-listener.yaml for a complete working example and the configuration reference for the full schema and reload semantics.

Sockguard inspects the body of allowed write requests — containers/create, containers/*/update, exec, build, images/create, services/create, swarm/init, and the rest of the body-bearing write paths — and blocks privileged or host-bound workloads, non-allowlisted mounts, devices, and registries, and unsafe swarm/network controls. Response bodies are redacted (env, mount paths, topology, secrets) by default. None of that needs configuration to switch on.

Beyond these essentials, every knob is documented in full on the docs site rather than duplicated here:

  • Configuration reference — full YAML schema, request-body inspection, mTLS client selectors, per-client ACLs and profiles, rate limiting and concurrency caps, owner-label isolation, rollout modes, hot-reload, signed policy bundles, insecure_* opt-ins, response redaction, and config precedence (CLI flags > env vars > config file > defaults).
  • Admin API — the POST /admin/validate CI gate and GET /admin/policy/version.
  • Observability — Prometheus metrics, access/audit log fields, and trace/log correlation.
  • Security model — the defense-in-depth layers and known limitations.

Bundled presets and ready-to-run compose stacks are summarized in Supported Profiles.


CLI

Install the latest stable native binary on macOS or Linux through the CodesWhat Homebrew tap:

brew install --cask codeswhat/tap/sockguard
sockguard version

The cask installs only the sockguard command; it does not create a service, grant Docker socket access, or generate a policy. The macOS binary is not yet Apple notarized, so the cask removes quarantine from only its staged binary after Homebrew verifies the archive checksum; see the getting-started guide for the trust boundary. That checksum only proves the downloaded archive is intact, not who published it, so it's not a substitute for Apple Developer ID signing/notarization; if your policy requires notarization or you don't want the quarantine bypass, use the container image or verify the GitHub Releases binary with cosign instead. Container deployment remains the recommended production path.

sockguard serve                                     # Start proxy (default)
sockguard validate -c sockguard.yaml                # Validate + print compiled rule table
sockguard match -c sockguard.yaml -X GET --path /v1.45/containers/json
                                                    # Dry-run a single request through the rules
sockguard version                                   # Print version

sockguard match is the offline rule-evaluation probe — point it at a config and a <method, path> and it prints which rule fires, what the normalized path looks like, and the reason (if any), so you can sanity-check a ruleset before any traffic hits the proxy. Output is text by default or JSON via -o json.


Migration

Migrating from Tecnativa or LinuxServer socket proxies

Replace the image — your current Tecnativa env surface maps over directly, with two explicit security acknowledgements for the non-loopback plaintext TCP listener plus a third for broad archive/export or log/attach streaming parity:

 services:
   socket-proxy:
-    image: tecnativa/docker-socket-proxy
+    image: codeswhat/sockguard
     volumes:
       - /var/run/docker.sock:/var/run/docker.sock:ro
     environment:
       - SOCKGUARD_LISTEN_ADDRESS=:2375
       - SOCKGUARD_LISTEN_INSECURE_ALLOW_PLAIN_TCP=true
+      - SOCKGUARD_LISTEN_INSECURE_ALLOW_UNAUTHENTICATED_CLIENTS=true
       - SOCKGUARD_INSECURE_ALLOW_READ_EXFILTRATION=true
       - CONTAINERS=1
       - SERVICES=1
       - POST=0

LinuxServer's socket-proxy env surface is already Tecnativa-compatible for the broad section toggles Sockguard consumes. For tighter policies, migrate from broad env vars to YAML rules plus body-inspection settings. Finish that conversion before enabling signed-policy mode, which rejects rule-generating compatibility variables so unsigned environment state cannot change a verified policy.


Roadmap

Version themes & highlights

v2.0.0 shipped on 2026-08-28 as the signed-policy trust-boundary release. v2.1 is the next planned line, focused on selective mediation of raw-LLB and third-party BuildKit frontends. Work beyond the next planned line remains driven by demonstrated risk and operator demand. See CHANGELOG.md for release history and the roadmap docs for compatibility evidence and scope boundaries.

Shipped in v2.0.0

Track Delivered
Signed-policy integrity Bootstrap trust is selected out of band, cannot share authority with the signed candidate or rotatable signature object, stays pinned across reload, and rejects unsigned compatibility-rule mutation. Existing deployments must follow the migration guide.
Native Podman builds /libpod/build constrains primary and additional contexts, host-facing controls, host networking, Dockerfile RUN, and owner labeling on direct and versioned paths.
BuildKit isolation Caller identity comes from verified certificates or Unix peer credentials where available, session state is correlated by principal, profile, and session, Solve admission is atomic, and upgrade handshakes are bounded.
Proxy hardening Socket cleanup is inode-owned, method-aware routing protects keyword-named resources, generated response errors clear stale representation headers, inspected bodies retain deadlines, and request metric labels have finite cardinality.
Release surfaces README, docs, website, Helm metadata, verification assets, and migration guidance agree on v2.0.0; release blobs use sigstore bundles only and published metadata is read back before the release succeeds.

Shipped in v1.7.0

Tracked in the v1.7.0 GitHub milestone.

Track Issue Delivered
BuildKit gRPC mediation #185 Full mediation of the hijacked POST /session/POST /grpc tunnel across six phases — h2c termination and stream routing, per-message Control/Solve/Control/Status policy with ref ownership, credential session mediation (Auth/Secrets/SSH), and file-sync/upload stream mediation — replacing the opaque-tunnel acknowledgment with real enforcement. insecure_accept_opaque_buildkit_tunnels is now deprecated.
Granular endpoint-config gates #186 request_body.network.endpoint_config.* splits the broad allow_endpoint_config override into independent per-field gates for static addressing, link-local IPs, MAC pinning, and gateway priority.

Shipped in v1.6.0

Tracked in the v1.6.0 GitHub milestone. Delivered in three waves — Wave 1 landed in parallel, Wave 2 was sequential because both items touch the route classifier, and Wave 3 gated GA.

Wave 1 — parallel

Track Issue Required outcome
Multiple listeners #149 Run Unix and TCP listeners together, or multiple instances of either, with listener-scoped TLS and profile boundaries.
Safe admission mutation #151 Operator-configurable mandatory-label injection and image-reference remapping; canonicalize and re-inspect every mutation before forwarding, fail closed.
Resource parity #152 Revalidate required memory/CPU/CPU-hard/PIDs limits against effective state during container update (request_body.container_update.require_*, opt-in, gated by allow_resource_updates), and add Swarm-service CPU-limit requirements covering create/update and both rollback paths (request_body.service.require_cpu_limit/require_cpu_limit_hard).

Wave 2 — sequential

Track Issue Required outcome
Engine/build compatibility #153 Validate Docker Engine API 1.55 and current Compose/BuildKit transport without opening the API v1.53-deprecated /session and /grpc endpoints by assumption. Lands first; Podman routing builds on the updated classifier.
Podman/libpod #148 Preserve Docker-compatible Podman behavior and add explicit default-deny, body-aware coverage for native /libpod routes and pod lifecycle operations.

Wave 3 — gates GA

Track Issue Required outcome
Three-tool conformance #150 Publish the tested Sockguard + Portwing + drydock conformance matrix across Standard/Edge version combinations; remote-update claims remain blocked until watcher/trigger contracts work in both peer repositories.

Shipped in v1.5.0

Track Surface
Safer defaults upstream.request_timeout defaults to 60s (was unlimited), so a wedged daemon that hangs a response body is caught out of the box; long-lived endpoints remain exempt, and "off" restores unlimited behavior. ownership.allow_cross_owner_namespace_sharing defaults to false, denying cross-owner container:<ref> joins when ownership is enabled.
Namespace hardening restrict_namespace_sharing plus allowed_namespace_sharing_containers gates container:<ref> joins across Network/PID/IPC/User namespaces; deny_namespace_path_mode blocks raw ns:<path> network namespace attachment; allow_host_cgroupns is now required for host cgroup-namespace mode.
CPU hard limit New opt-in request_body.container_create.require_cpu_limit_hard requires a genuine CPU-time cap (HostConfig.NanoCpus or CpuQuota); CpuShares alone or a lone CpuPeriod does not satisfy it.
Exec environment policy New opt-in request_body.exec.allowed_env_vars/denied_env_vars restrict exec-create environment entries by name, while allowed_env_values can pin selected entries to exact NAME=VALUE strings; denials never log or echo values.
Endpoint-config parity Create-time NetworkingConfig.EndpointsConfig now enforces the same static-IP, MAC, links, and driver-option policy as POST /networks/*/connect; Compose aliases remain allowed by default.
Integrations Adds portwing-with-compose.yaml, drydock-with-compose.yaml, and the tri-tool Compose example, taking the bundled preset set from 15 to 17.
Helm security The DaemonSet pins runAsNonRoot, UID/GID 65532, and seccompProfile.type: RuntimeDefault at pod level while allowing the host socket GID to be merged through podSecurityContext.supplementalGroups.
Configuration internals Viper-default registration is generated by reflection off Defaults() rather than a hand-maintained list; exhaustive tests now prove every mapstructure leaf and SOCKGUARD_* override is registered.
Security release train Carries fresh embedded-resource ownership checks, registry-push exfiltration gating, browser and Helm hardening, structured-log sanitization, fail-closed plugin inspection, and patched dependency graphs through rc.3 into stable.
Validation Full Go/TypeScript CI, CodeQL, Grype, govulncheck, gosec, real-dockerd integration, fuzzing, artifact verification, signatures, provenance, and published-image scans passed before GA promotion.

Shipped in v1.4.4

Track Surface
Scanner remediation Sanitizes attacker-controlled structured-log fields to close the CodeQL log-injection findings; upgrades the affected Go and Node dependency graphs to patched releases for the Grype and OpenSSF Scorecard findings; retains only the documented x/crypto/openpgp exception where no fixed version exists and the vulnerable package is absent from the shipped binary
Fail-closed inspection Malformed or schema-incompatible plugin config.json is denied before forwarding, so an archive Sockguard cannot inspect cannot bypass plugin bind, device, capability, environment, or namespace policy
Repository protection main requires two approvals, a code-owner review, stale-review dismissal, last-push approval, resolved conversations, strict required checks, and no bypass actors
Compatibility No YAML schema, CLI flag, environment variable, admin endpoint, metric, or other public API change

Shipped in v1.4.0

Track Surface
Remote upstreams & failover upstream.endpoints[] — ordered failover set of Docker daemons (unix:// or tcp://host:port), per-endpoint mTLS (tls.ca_file/cert_file/key_file/server_name), per-endpoint insecure opt-ins; active connect-level health probes on configurable failover.health_interval/health_timeout; request failure demotes the active endpoint for immediate failover; DOCKER_HOST/DOCKER_TLS_VERIFY/DOCKER_CERT_PATH auto-detected when no endpoints are set
SecurityOpt policy rails deny_selinux_disable, deny_selinux_label_override, deny_unconfined_system_paths for containers/create; deny_unconfined_seccomp, deny_custom_seccomp_profiles, deny_unconfined_apparmor for services/create/update; swarm ContainerSpec.Privileges confinement parity with container create
RC hardening pass A multi-axis internal audit of the v1.4 RC (security, performance, tests, supply chain) found no critical or high issues and drove plugin-inspection, SPKI comparison, upstream warning, hot-path allocation, DAST, integration, fuzz, and documentation hardening

Shipped in v1.3.0

Track Surface
Swarm posture parity Service create/update enforces the container-create identity/privilege rails on ContainerSpec: require_non_root_user (numeric-UID parsing, zero-padded forms rejected), require_no_new_privileges, require_readonly_rootfs, require_drop_all_capabilities — all opt-in, closing the bypass where a service could request a workload shape /containers/create would deny
Security fixes Zero-padded-UID root bypass sealed (container create require_non_root_user + exec allow_root_user parse the UID instead of comparing to "0"); wide-open dedicated admin listener (non-loopback plaintext, no CIDR backstop) is a validation error unless explicitly acknowledged; admin endpoint paths path.Clean-normalized before matching; non-upgrade hijack fallbacks strip hop-by-hop headers; container-label ACL exclusivity warning at startup; filters query params capped at 64 KiB
Operational fixes signature_path hot-reload no longer wedges subsequent reloads; three silently-ignored SOCKGUARD_* env vars registered; oversized bodies return 413 (was 403) on node-update and build; coalesced inspect-cache waiters honor their own context; release images carry real commit/built metadata
Build & internals Multi-arch images cross-compile natively (--platform=$BUILDPLATFORM), fixing emulated-toolchain faults; shared internal/dockerfilters decoder with deterministic legacy-format ordering; upstream inspect responses drained for keep-alive reuse

Shipped in v1.2.0

Track Surface
Operational resilience Opt-in readiness probe (health.readiness.*, default /ready) that issues a real GET /containers/json against the Docker API and returns 503 on a daemon that connects but no longer answers; opt-in upstream.request_timeout total per-request deadline that converts a hung body / heavy read into a 504 (reason_code=upstream_request_timeout) while exempting streaming and long-lived endpoints; new sockguard_upstream_api_up gauge + sockguard_upstream_readiness_checks_total{result} counter
Preset fix drydock preset allowlists the stock runc runtime so drydock's recreate-from-inspect updates stop getting 403'd at POST /containers/create out of the box
Dependencies Go toolchain 1.26.31.26.4 (builder image + go.mod directive), clearing reachable stdlib advisories GO-2026-5037 / GO-2026-5039; go-minor group (go-containerregistry, sigstore, protobuf-specs + closure); npm-minor group (12, website/docs/tooling); actions-minor group (4, SHA-pinned); govulncheck reports zero vulnerabilities

Shipped in v1.1.0

Track Surface
Image trust (end-to-end) Registry manifest digest resolution via internal/imagefetch; cosign signature discovery (classic sha256-<digest>.sig tag + OCI 1.1 referrers); Sigstore bundle reconstruction and digest-binding before verify; keyed (PEM public key) and keyless (Fulcio + Rekor, TUF-fetched trust root) both enforced; require_rekor_inclusion defaults to true for keyless; verified images digest-pinned (registry/repo@sha256:…) before forwarding to close the verify→pull TOCTOU; image-trust policy now also applied to swarm service create/update (ContainerSpec)
Security audit (21 findings) Plugin multipart-boundary inspection bypass closed; read-side visibility gates container/image sub-resources (logs/stats/top/changes/export/archive/attach, image history/get); new allowed_runtimes allowlist for HostConfig.Runtime; empty/whitespace exec User treated as root; capability-enforcement fixes; BuildKit # syntax= directive denial + gzip-bomb decompression cap; swarm services enforce capability allowlist, allow_sysctls gate, and image-trust; keyless SAN patterns anchored; docker load gzipped archive false-deny fixed; image /get export owner-filtered; inspect cache no longer memoizes not-found verdicts; response redaction extended to HostConfig.Mounts[].Source and service PreviousSpec; signed-bundle verify-then-load TOCTOU closed + env vars cannot override signed policy; PID-only unix-peer profile assignment rejected; dedicated admin TCP listener enforces clients.allowed_cidrs
CI / supply chain CodeQL actions language enabled for workflow static analysis; 20 OSSF Scorecard / Go vuln-DB advisory dependency bumps (x/crypto, x/net, x/sys + closure); govulncheck reports zero vulnerabilities

Shipped in v1.0.0

Track Surface
Foundation Default-deny proxy, glob path rules, Tecnativa env compatibility, structured access + audit logging, health endpoint, hardened Wolfi image, multi-arch
Transport Unix socket and mTLS-protected TCP listener, TLS 1.3 minimum, loopback by default, SPKI pins, plaintext non-loopback rejected without explicit opt-in
Body inspection Every Docker write surface with a meaningful body shape — containers/create, exec, build, services, swarm, configs/secrets, volumes, plugins, networks, image load, container update, archive write, node update
Container enforcement Privileged / host namespaces / CapAdd / device passthrough denied by default; no-new-privileges, non-root, readonly rootfs, drop-all-capabilities, memory / CPU / PIDs limits, seccomp + AppArmor allowlists; cosign image-trust policy schema and rule compiler (end-to-end enforcement wired in v1.1.0)
Per-client policy Source-IP, mTLS (CN/DNS/IP/URI/SPIFFE/SPKI), unix SO_PEERCRED, container-label resolution; named profiles with rollout modes (enforce / warn / audit)
Read-side visibility Response filtering across containers/services/tasks/configs/secrets/nodes/plugins/swarm/info/system-df with generic protected-JSON mediation
Abuse controls Per-client token-bucket rate limits, burst budgets, concurrency caps, endpoint-cost weighting, system-wide priority-aware fairness gate
Observability Prometheus /metrics, dedicated audit schema, trusted request IDs, deny-reason enums, W3C trace/log correlation, active upstream socket watchdog, lock-free hot path
Dynamic policy POST /admin/validate CI gate, fsnotify + SIGHUP hot reload with immutable-field gate, monotonic policy versioning, optional dedicated admin listener, cosign-signed policy bundles

Later directions

These themes remain unscheduled until their scope and security boundary are concrete.

Tier Theme
Security hardening (v1.x) Continued mutation-test hardening of the rule-evaluation core and config validators
Supply chain (v1.x) egress-policy: block with curated allow-lists on high-privilege release jobs
Policy refinement (v1.x) Named rule path aliases and further response-policy refinement
Internals (v1.x) Code-review backlog: collapse the config → filter-options → policy translation layers behind a single source of truth; profiling-gated JSON redaction fast path
Compliance (v1.x) CIS Docker Benchmark control mapping, audit-ready policy templates
Extensibility (v1.x+) Optional plugin extension points (WASM or Go plugins), OPA/Rego policy integration

Documentation

Resource Link
Website getsockguard.com
Docs getsockguard.com/docs
Getting Started Getting Started
Configuration Configuration
Presets Presets
Migration Migration
Roadmap Roadmap
CIS Docker Benchmark CIS Docker Benchmark
Admin API Admin API
Observability Observability
Security Model Security Model
Image Verification Image Verification
Changelog CHANGELOG.md
Contributing CONTRIBUTING.md
Code of Conduct CODE_OF_CONDUCT.md
Governance GOVERNANCE.md
Security Assurance SECURITY-ASSURANCE.md
Security Policy SECURITY.md
Issues GitHub Issues
Discussions GitHub Discussions

Star History


Built With

Go 1.26 Sigstore Wolfi Docker GoReleaser
Next.js Fumadocs Tailwind CSS Turborepo Biome

Anthropic OpenAI

SemVer Conventional Commits Keep a Changelog

Community & Support

GitHub Issues for bugs and feature requests, GitHub Discussions for design questions and Q&A, and the CodesWhat Discord for real-time chat.

Start with CONTRIBUTING.md before opening a pull request, and use SECURITY.md for private vulnerability disclosure.

For local fuzz triage, run scripts/local-fuzz.sh --suite ci --fuzztime 2m. Use --suite ultra for every fuzzer, --timeout to set the Go watchdog explicitly, and --docker --platform linux/amd64 when you want closer GitHub Actions parity.

Every release image is cosign-signed via GitHub Actions OIDC. Before running a sockguard image in production, verify it with the canonical invocation in the image verification guide.

Part of the CodesWhat ecosystem

ToolRole
drydockContainer update monitoring — web UI and notification engine
portwingRemote Docker agent — secure socket-level access from Drydock or standalone
sockguardDocker socket proxy — default-deny allowlist filter protecting the socket

These three tools are designed to layer: sockguard filters the socket, portwing exposes it remotely, and drydock monitors and acts on container state.

See portwing's COMPATIBILITY.md for the full compatibility matrix across all three tools.

Apache-2.0 License

CodesWhat

Sponsor

Back to top

About

Docker socket proxy. Filter API requests by method and path with default-deny posture, structured audit logging, and Tecnativa drop-in compatibility.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

8 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages