Security Model
Sockguard's defense-in-depth model — transport admission, client admission, method/path filtering, request-body inspection, ownership isolation, visibility-controlled reads, and audit logging.
Why Socket Proxying Matters
The Docker socket (/var/run/docker.sock) is equivalent to root access on the host. Any container with unrestricted socket access can:
- Create a privileged container that mounts the host filesystem
- Execute arbitrary commands via
docker exec - Access host PID, network, and IPC namespaces
- Pull and run malicious images
- Manipulate Swarm clusters
A socket proxy sits between consumers and the raw socket, filtering requests to limit what each consumer can do.
Defense in Depth
Sockguard implements multiple layers of filtering:
Layer 1: Transport Admission
Non-loopback TCP listeners require mutual TLS 1.3 by default via listen.tls. Plaintext remote TCP is rejected unless you set listen.insecure_allow_plain_tcp: true for legacy compatibility on a private network. Unix socket listeners bypass this layer because they are filesystem-bounded.
Layer 2: Client Admission
clients.allowed_cidrs gates incoming TCP callers by source CIDR before any rule evaluation runs. When clients.container_labels.enabled is true, Sockguard resolves the calling container by source IP and enforces per-client com.sockguard.allow.<method> label allowlists in addition to the global rule set.
Named client profiles sit on top of that admission layer. Sockguard can now select a per-client ruleset and request-body policy by source IP or verified mTLS client certificate common name, with a configurable default profile for unmatched callers. That turns one proxy from "one ruleset in front of Docker" into a shared control plane for multiple consumers without collapsing back to broad allowlists.
Layer 3: Method Filtering
Block entire HTTP methods. Most consumers only need GET (read-only mode).
Layer 4: Path Filtering
Allow or deny specific Docker API endpoint paths using glob patterns. Docker API version prefixes (/v1.45/) and . / .. segments are normalized before matching so adversarial path shapes cannot slip past a literal allowlist.
Layer 5: Request Body Inspection
POST /containers/create bodies are parsed on every request and denied when they contain dangerous configuration:
HostConfig.Privileged: trueHostConfig.NetworkMode: host- Any bind mount whose source is outside
request_body.container_create.allowed_bind_mounts
POST /containers/*/exec and POST /exec/*/start can also be inspected now. When request_body.exec.allowed_commands is configured, Sockguard denies argv vectors outside that allowlist, denies privileged exec unless explicitly allowed, denies root-user exec unless explicitly allowed, and re-inspects POST /exec/*/start against Docker's stored exec metadata before the command runs.
POST /images/create is inspected by default. Sockguard blocks fromSrc imports unless explicitly allowed and constrains pulls to Docker Hub official images unless the operator opts into allow_all_registries or an explicit registry allowlist.
POST /build is inspected by default. Sockguard blocks remote build contexts, networkmode=host, and Dockerfiles containing RUN instructions unless those behaviors are explicitly allowed.
The remaining blind-write guardrail now covers the gaps Sockguard still cannot constrain safely, chiefly arbitrary exec without an allowlist and Swarm service management. Validation refuses to start with those rules allowed unless you explicitly set insecure_allow_body_blind_writes: true, to keep the enforcement boundary honest.
Future body-inspection additions on the roadmap: escalation capabilities like SYS_ADMIN/NET_ADMIN, device mappings, host namespace sharing beyond NetworkMode, and security profile overrides.
Layer 6: Owner Label Isolation
When ownership.owner is set, Sockguard stamps created containers, networks, volumes, and build-produced images with an owner label, injects label= filters into list/prune/events responses, and inspects target resources on individual requests to deny cross-owner access. This turns one shared Docker socket into N isolated identity views.
Layer 7: Visibility-Controlled Reads
Sockguard now filters successful Docker read responses and narrows read visibility itself:
- Inject label visibility selectors into
GET /containers/json,GET /images/json,GET /networks,GET /volumes, andGET /events - Return
404for hidden targets on inspect-style reads such asGET /containers/*/json,GET /images/*/json,GET /networks/*,GET /volumes/*, andGET /exec/*/json - Redact
Config.EnvonGET /containers/*/json - Redact
HostConfig.Bindshost paths plusMounts[*].Sourceon container list/inspect responses - Redact volume
MountpointonGET /volumesandGET /volumes/* - Redact container and network address topology on container/network list and inspect responses
These controls are on by default where they are pure redaction because runtime env vars routinely carry credentials and Docker read APIs expose raw host mount paths plus internal network layout.
Future response-filtering additions still tracked for the next tier:
- Hide containers by name/label beyond ownership
- Name/image pattern visibility controls beyond label selectors
Layer 8: Audit Logging
Every request is logged with method, path, decision, matched rule index, selected client profile when present, latency, request ID, and client metadata. Upstream reverse-proxy errors are correlated with the outer access-log entry by shared request ID.
Dangerous Docker API Endpoints
| Risk Level | Endpoints |
|---|---|
| Critical | POST /containers/create, POST /containers/{id}/exec, POST /exec/{id}/start |
| High | POST /images/create (pull), POST /build, POST /swarm/*, POST /plugins/install |
| Medium | POST /volumes/create, POST /networks/create, DELETE /containers/{id} |
| Low | GET /containers/json, GET /events, GET /version, GET /_ping |
Image Security
Sockguard's container image is built on Wolfi (Chainguard):
- Near-zero CVEs in the base image
- Built-in SBOM and SLSA provenance
- Cosign-signed for verification — see the image verification guide for the canonical
cosign verifyinvocation - No shell, no package manager in production image
Runtime Hardening
Sockguard runs as root inside the container by default so it can open
/var/run/docker.sock on stock Docker hosts without user or
group_add overrides. For a Docker socket proxy, the real security
frontier is what the daemon will accept through the proxy, not whether
the proxy process reports UID 0 or UID 65534 after it already has access
to the upstream socket.
The runtime controls that matter are:
- Correct policy rules and request-body inspection
read_only: truecap_drop: [ALL]security_opt: ["no-new-privileges:true"]- Docker's default seccomp profile or a stricter custom profile
- AppArmor/SELinux confinement on the host
- Rootless dockerd on the host when available
The getting-started examples use the container-level controls above by default so the drop-in path stays simple without hiding the real hardening story.
Migration
Drop-in migration paths from Tecnativa, LinuxServer, and wollomatic socket proxies — same env vars, same intent, stronger inspection underneath.
Image Verification
Verify sockguard images and release tarballs with cosign — keyless GitHub Actions OIDC signatures, SBOM, and SLSA build provenance.