Presets
Ready-made sockguard configs for drydock, drydock with self-update, drydock with compose, drydock with build, drydock with mediated build, Portwing, Portwing with exec, Portwing with compose, Portwing with build, Portwing with mediated build, Traefik, Portainer, Watchtower, Homepage, Homarr, Diun, Autoheal, GitHub Actions and GitLab runners, the CIS Docker Benchmark, read-only dashboards, and Podman read-only monitoring.
Sockguard ships with ready-made config presets for common Docker consumers. All presets are bundled in the container image at /etc/sockguard/.
Drydock (drydock.yaml)
Optimized for the drydock container update manager.
Use for: drydock, or any container updater that needs lifecycle control + image pull.
# Allows: container read, start, stop, restart, remove, create
# Allows: image read, pull
# Allows: network read, volume read, distribution
# Denies: exec, build, swarm, secrets, plugins
# container-create bodies are inspected by default — add host bind-mount sources to
# request_body.container_create.allowed_bind_mounts if your updates require them.
# image pulls are inspected too; this preset explicitly allows arbitrary registries
# while still denying image imports via fromSrc
# container updates are inspected; restart-policy/resource/device/capability changes
# stay denied unless you explicitly allow them under request_body.container_update
# the stock "runc" runtime is allowlisted (request_body.container_create.allowed_runtimes)
# because drydock recreates containers from their inspect spec, which usually carries an
# explicit HostConfig.Runtime — without it every update would 403 at /containers/createDrydock with Self-Update (drydock-with-selfupdate.yaml)
Extends the drydock preset with the exec paths required for drydock's self-update
finalize flow. The finalize step runs a short-lived docker exec inside the new
container to POST a completion callback to drydock's internal API.
Use for: drydock deployments where the sockguard proxy is in the self-update path and you need the finalize callback to complete cleanly.
# Extends drydock.yaml with:
# Allows: POST /containers/{id}/exec, POST /exec/{id}/start, GET /exec/{id}/json
# Exec body inspection: allow_privileged=false, allow_root_user=true
# (drydock's exec create sets no User field, and sockguard treats an empty
# User as root, so allow_root_user must be true or the finalize exec is
# denied before allowed_commands is ever checked)
# allowed_commands pins exec to the single finalize entrypoint argv, so the
# root-user allowance is safe: only that exact command can run
# allowed_env_values pins the finalize callback to Drydock's loopback URL at
# port 3000; update the exact value if you configure a different server portWithout this preset (using drydock.yaml), the self-update still completes — the old container is renamed, the new one starts — but the finalize callback exec is denied and drydock logs the failure. Use this preset to get a clean finalize.
Drydock with Compose (drydock-with-compose.yaml)
Extends the drydock preset with the network and volume lifecycle a
docker compose stack needs — the same gap portwing-with-compose.yaml
closes for Portwing.
Use for: drydock deployments that recreate a docker-compose.yml stack
through this proxy, not just a single standalone container.
# Extends drydock.yaml with:
# Allows: POST /networks/create, DELETE /networks/*, POST /networks/*/disconnect
# POST /volumes/create, DELETE /volumes/*
# (POST /networks/*/connect is already allowed by drydock.yaml — untouched)
# Body inspection for the new endpoints reuses drydock.yaml's existing
# network/volume create guardrails (deny custom drivers/driver opts by default)
# Still denied: /build, /session, /grpc. `docker compose build` / `up --build`
# with default settings never calls POST /build at all — it drives dockerd's
# embedded BuildKit over the hijacked, unversioned /session and /grpc streams
# instead, both denied here. Use drydock-with-mediated-build.yaml if you want
# `--build` support with BuildKit's default (no env var) transport, fully
# mediated (issue #185) rather than opened wholesale. Use drydock-with-build.yaml
# instead if you specifically want the CLASSIC builder (POST /build only,
# requires DOCKER_BUILDKIT=0 in the client). See the Compose/BuildKit
# Transport section of the security model docs for the full breakdown.
# Self-update finalize exec is still denied, same caveat as drydock.yaml;
# combine with drydock-with-selfupdate.yaml by hand if you need bothDrydock with Build (drydock-with-build.yaml)
Extends drydock-with-compose.yaml with classic builder only support for
docker compose build / up --build, for deployments that need Sockguard to
inspect build requests rather than blanket-deny them.
Use for: drydock-managed compose stacks that build images locally instead
of only pulling pre-built ones — with the caveat that the client must run with
DOCKER_BUILDKIT=0 set (see below).
# Extends drydock-with-compose.yaml with:
# Allows: POST /build — classic builder only, request_body.build left at its
# secure defaults (allow_remote_context/allow_host_network/
# allow_run_instructions all false)
# Still denies: /session, /grpc, and any moby.buildkit.v1.Control method path
# — this preset does not open either endpoint, no insecure_accept_opaque_
# buildkit_tunnels acknowledgment (now deprecated) and no request_body.buildkit
# mediation policy. THIS FAILURE IS INTENTIONAL: modern Buildx defaults to
# BuildKit, so a default `docker compose build` against this preset still
# fails with a /session or /grpc denial. Set DOCKER_BUILDKIT=0 in the client
# environment to fall back to the classic builder this preset actually
# supports, or use drydock-with-mediated-build.yaml for the fully-mediated
# BuildKit alternative.Drydock with Mediated Build (drydock-with-mediated-build.yaml)
Extends drydock-with-compose.yaml with mediated BuildKit support —
docker compose build / up --build run with BuildKit enabled (the Docker
CLI default, no DOCKER_BUILDKIT=0 needed). Sockguard terminates the
POST /session/POST /grpc tunnel as h2c and inspects every gRPC message
crossing it (issue #185) instead of admitting the tunnel wholesale.
Use for: drydock-managed compose stacks that build images locally with
BuildKit's default transport, where you want the same per-message inspection
this preset's classic-builder sibling gets for /build.
# Extends drydock-with-compose.yaml with:
# Allows: POST /session, POST /grpc — mediated only; request_body.buildkit
# admits Control.Solve (an "image"-typed exporter, no push registry
# allowlisted), Control.Info/ListWorkers/Status (worker metadata and
# ref-owned build progress), and Session.FileSync (REQUIRED — this is
# how the Dockerfile and context reach buildkitd; still subject to
# the same RUN-instruction hold-and-inspect scan classic /build gets,
# since allow_run_instructions stays false)
# request_body.build's three flags stay at their secure defaults
# (allow_remote_context/allow_host_network/allow_run_instructions all false),
# reused verbatim for Solve's own entitlement/frontend checks
# Left denied by default (widen only if you need it): session.auth (base-image
# pulls needing registry auth — exact realm/scope match, no generic default
# covers arbitrary Dockerfiles), session.secrets/ssh (RUN --mount=type=secret/
# ssh), session.file_send/upload (local/tar export, remote/stdin context)
# Still denies: classic POST /build (use drydock-with-build.yaml instead) and
# the literal /moby.buildkit.v1.Control/<Method> probe path (no h2c upgrade
# for any mediator to terminate)
# insecure_accept_opaque_buildkit_tunnels is NOT set — it is deprecated and
# mutually exclusive with request_body.buildkitPortwing (portwing.yaml)
Optimized for the Portwing Docker agent.
Use for: Portwing in the tri-tool topology (sockguard → Portwing → drydock), or any remote Docker agent that needs lifecycle control, image pull, and event streaming.
# Allows: container read (list, inspect, stats, top, changes, logs)
# Allows: container lifecycle (start, stop, restart, kill, rename, update, wait, create, remove)
# Allows: image read, pull, remove
# Allows: network read, volume read, distribution, Swarm service reads
# Denies: exec, build, swarm writes, secrets, plugins, raw archive/export/attach streams
# insecure_allow_read_exfiltration=true — required because GET /containers/*/logs is allowed
# (Portwing's GetContainerLogs()); container logs can carry secrets. Drop the logs rule and this
# flag to harden. /containers/*/archive, /export, /attach stay denied (bulk-exfil paths).
# Response redactions disabled (redact_mount_paths=false, redact_container_env=false,
# redact_network_topology=false) — required for drydock passthrough topology so
# inspect data forwarded to drydock is not corrupted by "<redacted>" placeholders
# Standalone operators not paired with drydock can re-enable all three redactionsPortwing with Exec (portwing-with-exec.yaml)
Extends the Portwing preset with exec support for interactive terminal access through the Portwing agent, e.g. terminal-over-websocket or drydock-driven exec in edge mode.
Use for: Portwing deployments where exec sessions are needed (interactive terminals, drydock edge mode exec calls).
# Extends portwing.yaml with:
# Allows: POST /containers/{id}/exec, POST /exec/{id}/start,
# POST /exec/{id}/resize, GET /exec/{id}/json
# Exec body inspection: allow_privileged=false, allow_root_user=true
# (most container workloads run as root; set false + use allowed_commands for
# non-root or command-pinned deployments)
# insecure_allow_body_blind_writes=true — interactive exec can't be pinned to a fixed argv,
# so the body-blind-write guard is bypassed; the allow_privileged/allow_root_user layer still
# applies. insecure_allow_read_exfiltration=true is inherited from portwing.yaml for the
# GET /containers/*/logs rule (see the Portwing section above).Portwing with Compose (portwing-with-compose.yaml)
Extends the Portwing preset with the network and volume lifecycle a
docker compose stack needs, so Portwing can deploy compose stacks through
this proxy, not just standalone containers.
Use for: Portwing deployments that create/remove compose-managed
networks and named volumes (docker compose up/down), in addition to
plain container orchestration.
# Extends portwing.yaml with:
# Allows: POST /networks/create, POST /networks/*/connect,
# POST /networks/*/disconnect, DELETE /networks/*
# POST /volumes/create, DELETE /volumes/*
# Body inspection for the new endpoints reuses portwing.yaml's existing
# network/volume create guardrails (deny custom drivers/driver opts by default)
# Still denied: /build, /session, /grpc. `docker compose build` / `up --build`
# with default settings never calls POST /build at all — it drives dockerd's
# embedded BuildKit over the hijacked, unversioned /session and /grpc streams
# instead, both denied here. Use portwing-with-mediated-build.yaml if you want
# `--build` support with BuildKit's default (no env var) transport, fully
# mediated (issue #185) rather than opened wholesale. Use portwing-with-build.yaml
# instead if you specifically want the CLASSIC builder (POST /build only,
# requires DOCKER_BUILDKIT=0 in the client). See the Compose/BuildKit
# Transport section of the security model docs for the full breakdown.
# insecure_allow_read_exfiltration=true is inherited from portwing.yaml for the
# GET /containers/*/logs rule (see the Portwing section above)
# Exec is denied, same as portwing.yaml — combine with portwing-with-exec.yaml's
# request_body.exec block by hand if you need both compose deploys and execPortwing with Build (portwing-with-build.yaml)
Extends portwing-with-compose.yaml with classic builder only support
for docker compose build / up --build.
Use for: Portwing-managed compose stacks that build images locally
instead of only pulling pre-built ones — with the caveat that the client must
run with DOCKER_BUILDKIT=0 set (see below).
# Extends portwing-with-compose.yaml with:
# Allows: POST /build — classic builder only, request_body.build left at its
# secure defaults (allow_remote_context/allow_host_network/
# allow_run_instructions all false)
# Still denies: /session, /grpc, and any moby.buildkit.v1.Control method path
# — this preset does not open either endpoint, no insecure_accept_opaque_
# buildkit_tunnels acknowledgment (now deprecated) and no request_body.buildkit
# mediation policy. THIS FAILURE IS INTENTIONAL: modern Buildx defaults to
# BuildKit, so a default `docker compose build` against this preset still
# fails with a /session or /grpc denial. Set DOCKER_BUILDKIT=0 in the client
# environment to fall back to the classic builder this preset actually
# supports, or use portwing-with-mediated-build.yaml for the fully-mediated
# BuildKit alternative.
# insecure_allow_read_exfiltration=true is inherited from portwing.yaml for the
# GET /containers/*/logs rule (see the Portwing section above)Portwing with Mediated Build (portwing-with-mediated-build.yaml)
Extends portwing-with-compose.yaml with mediated BuildKit support —
docker compose build / up --build run with BuildKit enabled (the Docker
CLI default, no DOCKER_BUILDKIT=0 needed). Sockguard terminates the
POST /session/POST /grpc tunnel as h2c and inspects every gRPC message
crossing it (issue #185) instead of admitting the tunnel wholesale.
Use for: Portwing-managed compose stacks that build images locally with
BuildKit's default transport, where you want the same per-message inspection
this preset's classic-builder sibling gets for /build.
# Extends portwing-with-compose.yaml with:
# Allows: POST /session, POST /grpc — mediated only; request_body.buildkit
# admits Control.Solve (an "image"-typed exporter, no push registry
# allowlisted), Control.Info/ListWorkers/Status (worker metadata and
# ref-owned build progress), and Session.FileSync (REQUIRED — this is
# how the Dockerfile and context reach buildkitd; still subject to
# the same RUN-instruction hold-and-inspect scan classic /build gets,
# since allow_run_instructions stays false)
# request_body.build's three flags stay at their secure defaults
# (allow_remote_context/allow_host_network/allow_run_instructions all false),
# reused verbatim for Solve's own entitlement/frontend checks
# Left denied by default (widen only if you need it): session.auth (base-image
# pulls needing registry auth — exact realm/scope match, no generic default
# covers arbitrary Dockerfiles), session.secrets/ssh (RUN --mount=type=secret/
# ssh), session.file_send/upload (local/tar export, remote/stdin context)
# Still denies: classic POST /build (use portwing-with-build.yaml instead) and
# the literal /moby.buildkit.v1.Control/<Method> probe path (no h2c upgrade
# for any mediator to terminate)
# insecure_allow_read_exfiltration=true is inherited from portwing.yaml for the
# GET /containers/*/logs rule (see the Portwing section above)
# insecure_accept_opaque_buildkit_tunnels is NOT set — it is deprecated and
# mutually exclusive with request_body.buildkitTraefik (traefik.yaml)
Minimal read-only access for Traefik reverse proxy.
Use for: Traefik, nginx-proxy, or any reverse proxy with Docker provider.
# Allows: GET on containers, networks, services, tasks, events, ping, version
# Denies: everything elsePortainer (portainer.yaml)
Full Docker API access for Portainer management UI.
Use for: Portainer with full management capabilities.
# Allows: containers, images, networks, volumes, exec, build, swarm, plugins (full R/W)
# WARNING: This grants near-full Docker socket access
# Container, image, build, network, swarm, node, and plugin writes are inspected, but
# Portainer's arbitrary exec/plugin-setting behavior may still require the unsafe opt-inWatchtower (watchtower.yaml)
Optimized for the Watchtower container auto-updater.
Use for: Watchtower with lifecycle hooks and network-aware updates.
# Allows: container lifecycle (start, stop, kill, restart, rename, remove, create)
# Allows: image pull/remove, exec (lifecycle hooks), network connect/disconnect
# Denies: build, swarm, secrets, plugins
# Container-create, image-pull, and network attach requests are inspected; exec lifecycle
# hooks can be inspected too, but this preset keeps insecure_allow_body_blind_writes=true for arbitrary hooksHomepage (homepage.yaml)
Read-only access for the Homepage dashboard.
Use for: Homepage, or any dashboard that only reads container state and stats.
# Allows: GET on containers (list, inspect, stats), images, services, tasks, events
# Denies: all write operationsHomarr (homarr.yaml)
Optimized for Homarr dashboard with container management.
Use for: Homarr with start/stop/restart controls enabled.
# Allows: container read (list, inspect, stats), start, stop, restart
# Allows: image read
# Denies: exec, create, network/volume writesDiun (diun.yaml)
Minimal read-only access for Diun (Docker Image Update Notifier).
Use for: Diun, or any tool that only needs to discover running containers and inspect images.
# Allows: GET on containers (list, inspect), images (inspect)
# Denies: everything else — Diun checks registries directlyAutoheal (autoheal.yaml)
Minimal access for docker-autoheal.
Use for: Autoheal, or any health-check-based restart tool.
# Allows: GET /containers/json (list unhealthy), POST /containers/*/restart
# Denies: everything else — the most restrictive write presetGitHub Actions Self-Hosted Runner (github-actions-runner.yaml)
For the actions/runner process on a self-hosted runner that needs Docker
socket access to spawn jobs.*.container + jobs.*.services containers.
Use for: self-hosted GitHub Actions runners that handle untrusted workflows (i.e. anything from a fork PR or a low-trust org member).
# Allows: container lifecycle, exec, attach, log streaming (GET /containers/*/logs),
# image pull, per-job networks, named volumes for cache
# Denies: privileged containers, privileged exec, host namespace sharing, bind mounts,
# capability additions, build, swarm, secrets, plugins,
# raw tarball export (/containers/*/export, /images/*/get)
# insecure_allow_body_blind_writes=true — exec argv is not command-pinned (workflow steps are
# arbitrary); only privileged exec is denied. insecure_allow_read_exfiltration=true — the runner
# streams job output via the logs and attach APIs. Gate the proxy socket to the runner process
# via clients.unix_peer_profiles or clients.allowed_cidrs to bound both.The preset enforces what a workflow can do once admitted. Pair it
with clients.allowed_cidrs (TCP listener) or clients.unix_peer_profiles
(unix listener) to authenticate which process is connecting — sockguard
on its own does not know which runner identity is on the other end.
GitLab Runner (Docker Executor) (gitlab-runner.yaml)
For gitlab-runner with executor = "docker" in config.toml. Spawns
build + service + helper containers per CI job and uses docker exec
to inject job steps.
Use for: GitLab Runner deployments handling untrusted CI jobs.
# Allows: container lifecycle, exec, attach, log streaming (GET /containers/*/logs),
# image pull, per-job networks, named volumes
# Denies: privileged containers (even if config.toml asks for them), privileged exec,
# host namespace sharing, bind mounts, build, swarm, secrets,
# plugins, raw tarball export (/containers/*/export, /images/*/get)
# insecure_allow_body_blind_writes=true — exec argv is not command-pinned; the runner injects
# arbitrary job-step commands. insecure_allow_read_exfiltration=true — job trace output streams
# via the logs and attach APIs. Gate the proxy socket to the runner via clients.unix_peer_profiles
# or clients.allowed_cidrs to bound both.Notable: the preset deliberately rejects privileged containers even
if the runner's config.toml has privileged = true. DinD-style jobs
that require a privileged build container will fail through the proxy
until the operator either removes the flag or moves the affected
project to a different runner. This is the secure default; widening it
is an audit-trail-bearing edit.
CIS Docker Benchmark (cis-docker-benchmark.yaml)
Turns sockguard into an admission gate for the inspectable subset of the CIS Docker Benchmark v1.6.0 Section 5 (Container Runtime) controls.
Use for: any deployment that has to evidence CIS posture — every
non-compliant docker run is rejected at the API boundary with 403
before dockerd executes it.
# Enforces CIS 5.3, 5.4, 5.5, 5.9, 5.10, 5.11, 5.12, 5.15, 5.16, 5.17,
# 5.21, 5.22, 5.23, 5.25, 5.28, 5.30, 5.31 (structural)
# Allows: container/image/network/volume reads, container lifecycle
# Denies: exec, build, swarm, secrets, plugins, raw archive/log/attachSee the dedicated CIS Docker Benchmark guide for the full control-by-control mapping, the negative-test recipe, and notes on companion tools for the host/daemon/image controls sockguard cannot inspect.
Read-Only (readonly.yaml)
Allows GET access to all Docker API endpoints. Denies all write operations.
Use for: generic dashboards, monitoring tools, read-only Portainer.
# Allows: GET on containers, images, networks, volumes, system, events, info, ping, version
# Denies: all POST, PUT, DELETEThis preset intentionally includes raw archive/export and log/attach stream reads, so it sets insecure_allow_read_exfiltration: true. Tighten it if your client only needs list/inspect endpoints.
Podman Read-Only (podman-readonly.yaml)
Read-only monitoring posture covering both of Podman's API surfaces — the
Docker-compat /vN.NN/... paths and the native /libpod/... paths — in one
file. See the Podman guide for the full routing model.
Use for: dashboards and monitoring tools watching a Podman host.
# Allows: GET list/inspect/stats/top/changes on containers; list/inspect
# (+ history on the Docker-compat side) on images; list/inspect
# only on networks, volumes, secrets — plus pod list/inspect/stats
# (libpod only) and health/version/info/events, on both surfaces
# Denies: all writes on both surfaces, including libpod-only writes
# (pod create, play/kube); archive/export/logs/attach/get/push reads
# on both surfacesUnlike readonly.yaml, this preset does not set
insecure_allow_read_exfiltration: true — every allowed read is a narrow
list/inspect/metadata endpoint, and it validates clean without the
acknowledgment. Point upstream.socket at either a rootful
(/run/podman/podman.sock) or rootless ($XDG_RUNTIME_DIR/podman/podman.sock)
Podman socket; the rule set itself doesn't change.
Using a Preset
Mount the preset as your config file:
services:
sockguard:
image: codeswhat/sockguard:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- sockguard-socket:/var/run/sockguard
command: ["serve", "-c", "/etc/sockguard/drydock.yaml"]All preset configs are bundled in the image at /etc/sockguard/.
Presets that keep broad container/image read compatibility also carry explicit insecure_allow_read_exfiltration: true acknowledgements so they continue to validate honestly under the read-side export guardrail.
Remote Upstreams & Failover
Connect sockguard to a remote Docker daemon over TCP+mTLS, or configure two endpoints for active/passive HA failover with automatic health probing.
Podman
Running sockguard in front of a Podman socket — the Docker-compat and native libpod API surfaces, which request_body.* keys govern which endpoint, the uninspected play/kube blind-write surface, ownership/visibility coverage, and current limitations.