Getting Started

Install sockguard with Docker Compose, Docker Run, Homebrew, or a release binary, and point your apps at the proxy socket.

Installation

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
      - ./sockguard.yaml:/etc/sockguard/sockguard.yaml:ro
      - sockguard-socket:/var/run/sockguard
    environment:
      - SOCKGUARD_LISTEN_SOCKET=/var/run/sockguard/sockguard.sock

volumes:
  sockguard-socket:

Docker Run

docker run -d \
  --name sockguard \
  --read-only \
  --cap-drop=ALL \
  --security-opt no-new-privileges:true \
  --group-add <docker-socket-gid> \
  -v /var/run/docker.sock:/var/run/docker.sock:ro \
  -v ./sockguard.yaml:/etc/sockguard/sockguard.yaml:ro \
  -v sockguard-socket:/var/run/sockguard \
  -e SOCKGUARD_LISTEN_SOCKET=/var/run/sockguard/sockguard.sock \
  codeswhat/sockguard:latest

Sockguard runs as UID 65532 (Chainguard nonroot) inside the container. Set DOCKER_SOCK_GID before starting Compose (export DOCKER_SOCK_GID=$(stat -c '%g' /var/run/docker.sock) on Linux; use stat -f '%g' on macOS), or replace <docker-socket-gid> in the docker run command with that numeric value. The hardening knobs that matter in practice are --read-only, --cap-drop=ALL, --security-opt no-new-privileges:true, and your runtime's seccomp/AppArmor/SELinux defaults.

The filtered socket and its named-volume directory are owner-only and owned by UID/GID 65532. A non-root consumer of that socket must also use UID 65532; root consumers can connect as well. If an application must keep another UID, run Sockguard with that matching UID against a pre-owned bind-mounted directory, or use an authenticated TCP listener. The socket mode is deliberately fixed at 0600 and should not be broadened.

Homebrew

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

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

Stable releases update the cask automatically. Release candidates and other prereleases remain available from GitHub Releases without moving the stable Homebrew channel.

The cask installs only the sockguard command. It does not create a service, grant Docker socket access, or generate a policy file. Run it under your own service manager after configuring the upstream Docker socket and an explicit policy; Docker Compose remains the recommended production deployment.

The macOS binary is not yet signed and notarized with an Apple Developer ID. After Homebrew verifies the release archive's SHA-256 checksum, the cask removes the quarantine attribute from its staged sockguard binary so Gatekeeper does not block it. This bypass applies only to that installed file. That checksum proves the archive wasn't corrupted or tampered with in transit, it does not prove who published it, so it is not a substitute for Apple Developer ID signing/notarization. If your policy requires Apple notarization, or you don't want the quarantine bypass at all, use the container image or verify the GitHub Releases binary with cosign instead.

Upgrade or remove the cask with:

brew upgrade --cask sockguard
brew uninstall --cask sockguard

Binary

Download from GitHub Releases:

sockguard serve -c /etc/sockguard/sockguard.yaml

This guide uses a unix socket because it is the simplest secure deployment. If you expose Sockguard on non-loopback TCP, configure listen.tls for mutual TLS. Plaintext remote TCP requires the two-flag legacy opt-in — both SOCKGUARD_LISTEN_INSECURE_ALLOW_PLAIN_TCP=true and SOCKGUARD_LISTEN_INSECURE_ALLOW_UNAUTHENTICATED_CLIENTS=true.

Connecting Your Apps

Point your Docker consumers at the proxy socket instead of docker.sock:

services:
  traefik:
    volumes:
      - sockguard-socket:/var/run/sockguard:ro
    environment:
      - DOCKER_HOST=unix:///var/run/sockguard/sockguard.sock

  drydock:
    volumes:
      - sockguard-socket:/var/run/sockguard:ro
    environment:
      - DD_WATCHER_LOCAL_SOCKET=/var/run/sockguard/sockguard.sock

CLI Commands

# Start the proxy (default command)
sockguard serve

# Start with a signed policy and separate pinned trust
sockguard serve --config /etc/sockguard/sockguard.yaml --policy-bundle-trust-config /etc/sockguard/policy-bundle-trust.yaml

# Validate configuration and print the compiled rule table
sockguard validate -c /etc/sockguard/sockguard.yaml

# Offline dry-run: evaluate a single request against the rules,
# show which rule fires and why, without starting the proxy.
sockguard match -c /etc/sockguard/sockguard.yaml \
  -X GET --path /v1.45/containers/json

# Print version
sockguard version

sockguard match reads the same config, applies the same path normalization as the running proxy, and reports the decision in text (default) or JSON (-o json). Use it to sanity-check a ruleset before any traffic hits the proxy.

Signed-policy mode requires the candidate and bootstrap trust config to be different files, including after symlink or hardlink resolution. The trust file uses policy_bundle.enabled, signing identities, Rekor posture, and the verification timeout. The signed candidate carries policy_bundle.signature_path. Rule-generating Tecnativa variables cannot be combined with this mode; migrate those grants to YAML before signing. See Signed Policy Bundles for the complete layout.

Next Steps

  • Configuration — YAML config and environment variables
  • Presets — Ready-made configs for drydock, Traefik, and more
  • Migration — Migrate from Tecnativa or LinuxServer