Configuration
Configure sockguard via YAML or environment variables — listeners, TLS, request-body inspection, client profiles, ownership, and audit logging.
Sockguard supports two configuration methods: YAML config files and environment variables.
YAML Config (recommended)
listen:
address: 127.0.0.1:2375
insecure_allow_plain_tcp: false
tls:
cert_file: /run/secrets/sockguard/server-cert.pem
key_file: /run/secrets/sockguard/server-key.pem
client_ca_file: /run/secrets/sockguard/client-ca.pem
insecure_allow_body_blind_writes: false
upstream:
socket: /var/run/docker.sock
log:
level: info # debug, info, warn, error
format: json # json, text
output: stderr
access_log: true
response:
deny_verbosity: minimal # minimal (default, production) or verbose (dev/rule-authoring)
redact_container_env: true
redact_mount_paths: true
redact_network_topology: true
request_body:
container_create:
allow_privileged: false # deny HostConfig.Privileged=true (default)
allow_host_network: false # deny HostConfig.NetworkMode=host (default)
allowed_bind_mounts: # host paths allowed as /src:/dst bind mounts
- /srv/containers
- /var/lib/app-data
exec:
allow_privileged: false
allow_root_user: false
allowed_commands:
- ["/usr/local/bin/pre-update", "--check"]
image_pull:
allow_imports: false
allow_all_registries: false
allow_official: true
allowed_registries:
- ghcr.io
build:
allow_remote_context: false
allow_host_network: false
allow_run_instructions: false
clients:
allowed_cidrs: # coarse TCP admission; empty means all allowed
- 172.18.0.0/16
container_labels:
enabled: true # resolve caller by source IP, enforce labels
label_prefix: com.sockguard.allow.
ownership:
owner: ci-job-123 # when set, stamps owner labels and denies cross-owner access
label_key: com.sockguard.owner
allow_unowned_images: true
health:
enabled: true
path: /health
rules:
- match: { method: GET, path: "/_ping" }
action: allow
- match: { method: GET, path: "/version" }
action: allow
- match: { method: GET, path: "/events" }
action: allow
- match: { method: GET, path: "/containers/**" }
action: allow
- match: { method: "*", path: "/**" }
action: deny
reason: "no matching allow rule"The default listener is loopback TCP 127.0.0.1:2375, which keeps the Docker API proxy off the network unless you explicitly choose otherwise.
- For the safest deployment, use
listen.socketand share a unix socket. - For remote or container-network TCP, configure
listen.tlsso Sockguard requires mutual TLS. Sockguard's mTLS server minimum is TLS 1.3, so callers must support TLS 1.3. - Plaintext non-loopback TCP is rejected unless you set
listen.insecure_allow_plain_tcp: true. That mode is only for legacy compatibility on a private, trusted network. POST /containers/createbodies are inspected by default. Sockguard blocksHostConfig.Privileged=true,HostConfig.NetworkMode=host, and bind mount sources outsiderequest_body.container_create.allowed_bind_mounts. Named volumes still work without allowlist entries because they are not host bind mounts.POST /containers/*/execandPOST /exec/*/startare inspected whenrequest_body.exec.allowed_commandsis non-empty. Sockguard denies argv vectors outside that allowlist, denies privileged exec unlessallow_privileged: true, denies root-user exec unlessallow_root_user: true, and re-checksPOST /exec/*/startagainst Docker's stored exec metadata before execution.POST /images/createis inspected by default. Sockguard blocksfromSrcimports unlessrequest_body.image_pull.allow_imports: trueand only allows Docker Hub official images unless you setallow_all_registries: trueor list explicitallowed_registries.POST /buildis inspected by default. Sockguard blocks remote contexts,networkmode=host, and Dockerfiles that containRUNinstructions unless you explicitly allow those behaviors underrequest_body.build.*.insecure_allow_body_blind_writesis now reserved for the writes Sockguard still cannot safely constrain, such as arbitrary exec without anallowed_commandsallowlist and Swarm service management endpoints.response.redact_container_env,response.redact_mount_paths, andresponse.redact_network_topologydefault totrue. Sockguard redactsConfig.EnvonGET /containers/*/json, redacts containerHostConfig.Bindshost paths plusMounts[*].SourceonGET /containers/jsonand inspect responses, redacts volumeMountpointonGET /volumesandGET /volumes/*, and strips container/network address topology fromGET /containers/json,GET /containers/*/json,GET /networks, andGET /networks/*. Disable them only for trusted admin clients that truly need Docker's raw metadata.
Per-Client ACLs And Profiles
clients.allowed_cidrs is a coarse TCP-client gate evaluated before the global rule set. Requests whose source IP is outside every configured CIDR are denied with 403 and never reach the health handler or the rule evaluator.
When clients.container_labels.enabled is true, Sockguard resolves bridge-network callers by source IP through the Docker API and looks for per-client allow labels on the calling container. Each clients.container_labels.label_prefix + <method> label is interpreted as a comma-separated Sockguard glob allowlist for that HTTP method:
com.sockguard.allow.get=/containers/**,/events
com.sockguard.allow.post=/containers/*/restartIf you are migrating from wollomatic, set clients.container_labels.label_prefix: socket-proxy.allow. to reuse existing labels. Callers that cannot be resolved by IP (for example, because they share the host network) fall through to the global rule set unchanged.
Named client profiles turn one Sockguard instance into a shared control plane for multiple consumers. Root-level rules and request_body remain the fallback policy unless clients.default_profile points at a named profile:
clients:
default_profile: readonly
source_ip_profiles:
- profile: watchtower
cidrs:
- 172.18.0.0/16
client_certificate_profiles:
- profile: portainer
common_names:
- portainer-admin
profiles:
- name: readonly
response:
visible_resource_labels:
- com.sockguard.visible=true
rules:
- match: { method: GET, path: "/containers/**" }
action: allow
- match: { method: GET, path: "/events" }
action: allow
- match: { method: "*", path: "/**" }
action: deny
- name: watchtower
response:
visible_resource_labels:
- com.sockguard.client=watchtower
request_body:
image_pull:
allow_all_registries: true
exec:
allowed_commands:
- ["/usr/local/bin/pre-update"]
rules:
- match: { method: GET, path: "/containers/**" }
action: allow
- match: { method: POST, path: "/containers/*/exec" }
action: allow
- match: { method: POST, path: "/exec/*/start" }
action: allow
- match: { method: POST, path: "/images/create" }
action: allow
- match: { method: "*", path: "/**" }
action: denyclients.source_ip_profilesmatches the caller's remote IP against CIDRs in config order.clients.client_certificate_profilesmatches the verified mTLS leaf certificate common name in config order and takes precedence over source-IP assignment.clients.default_profileis the fallback when no specific assignment matches.- Each profile has its own
rulesandrequest_bodypolicy, so one proxy can safely host a read-only dashboard, a container updater, and an admin UI at the same time. clients.client_certificate_profilesrequireslisten.tlsmutual TLS.response.visible_resource_labelsandclients.profiles[].response.visible_resource_labelsenforce read-side visibility on list/events/inspect paths. Selectors use Docker label syntax (keyorkey=value), are ANDed together, and profile selectors are additive with the root response selectors.- Hidden resources disappear from
GET /containers/json,/images/json,/networks,/volumes, and/events, while inspect-style reads for hidden targets return404instead of exposing a policy-specific deny body.
Owner Label Isolation
Setting ownership.owner turns on per-proxy resource ownership isolation. Sockguard will:
- Add
ownership.label_key=ownership.ownertoPOST /containers/create,/networks/create, and/volumes/createrequest bodies - Add the same label to
POST /buildvia thelabelsquery parameter - Inject
label=<owner>filters into list, prune, and events requests so responses only reveal resources owned by this proxy instance - Inspect target resources on individual
GET,POST,PUT, andDELETEpaths and deny access to resources owned by a different identity
allow_unowned_images defaults to true so shared base images can still be pulled and inspected without relabeling. Set it to false in tighter multi-tenant setups.
Rule Matching
Rules are evaluated in order. First match wins. If no rule matches, the request is denied.
Path Patterns
| Pattern | Matches | Does Not Match |
|---|---|---|
/containers/json | /containers/json, /v1.45/containers/json | /containers/abc |
/containers/* | /containers/json, /containers/abc123 | /containers/abc/start |
/containers/** | /containers/json, /containers/abc/start, /containers/abc/logs/stream | /images/json |
/** | Everything | Nothing |
Docker API version prefixes (/v1.45/) are stripped before matching.
Methods
- Exact:
GET,POST,PUT,DELETE,HEAD - Wildcard:
*matches any method
Environment Variables
Every config field can be set via env var with SOCKGUARD_ prefix:
SOCKGUARD_LISTEN_ADDRESS=127.0.0.1:2375
SOCKGUARD_LISTEN_INSECURE_ALLOW_PLAIN_TCP=false
SOCKGUARD_LISTEN_TLS_CERT_FILE=/run/secrets/sockguard/server-cert.pem
SOCKGUARD_LISTEN_TLS_KEY_FILE=/run/secrets/sockguard/server-key.pem
SOCKGUARD_LISTEN_TLS_CLIENT_CA_FILE=/run/secrets/sockguard/client-ca.pem
SOCKGUARD_LISTEN_SOCKET=/var/run/sockguard.sock
SOCKGUARD_INSECURE_ALLOW_BODY_BLIND_WRITES=false
SOCKGUARD_UPSTREAM_SOCKET=/var/run/docker.sock
SOCKGUARD_LOG_LEVEL=debug
SOCKGUARD_LOG_FORMAT=json
SOCKGUARD_RESPONSE_DENY_VERBOSITY=minimal
SOCKGUARD_RESPONSE_REDACT_CONTAINER_ENV=true
SOCKGUARD_RESPONSE_REDACT_MOUNT_PATHS=true
SOCKGUARD_RESPONSE_REDACT_NETWORK_TOPOLOGY=true
SOCKGUARD_REQUEST_BODY_CONTAINER_CREATE_ALLOW_PRIVILEGED=false
SOCKGUARD_REQUEST_BODY_CONTAINER_CREATE_ALLOW_HOST_NETWORK=false
SOCKGUARD_REQUEST_BODY_CONTAINER_CREATE_ALLOWED_BIND_MOUNTS=/srv/containers,/var/lib/app-data
SOCKGUARD_REQUEST_BODY_EXEC_ALLOW_PRIVILEGED=false
SOCKGUARD_REQUEST_BODY_EXEC_ALLOW_ROOT_USER=false
SOCKGUARD_REQUEST_BODY_IMAGE_PULL_ALLOW_IMPORTS=false
SOCKGUARD_REQUEST_BODY_IMAGE_PULL_ALLOW_ALL_REGISTRIES=false
SOCKGUARD_REQUEST_BODY_IMAGE_PULL_ALLOW_OFFICIAL=true
SOCKGUARD_REQUEST_BODY_IMAGE_PULL_ALLOWED_REGISTRIES=ghcr.io,quay.io
SOCKGUARD_REQUEST_BODY_BUILD_ALLOW_REMOTE_CONTEXT=false
SOCKGUARD_REQUEST_BODY_BUILD_ALLOW_HOST_NETWORK=false
SOCKGUARD_REQUEST_BODY_BUILD_ALLOW_RUN_INSTRUCTIONS=false
SOCKGUARD_CLIENTS_ALLOWED_CIDRS=172.18.0.0/16,10.0.0.0/24
SOCKGUARD_CLIENTS_CONTAINER_LABELS_ENABLED=true
SOCKGUARD_CLIENTS_CONTAINER_LABELS_LABEL_PREFIX=com.sockguard.allow.
SOCKGUARD_OWNERSHIP_OWNER=ci-job-123
SOCKGUARD_OWNERSHIP_LABEL_KEY=com.sockguard.owner
SOCKGUARD_OWNERSHIP_ALLOW_UNOWNED_IMAGES=trueresponse.deny_verbosity controls how much metadata Sockguard includes in its own 403 JSON deny responses:
minimal(default): returns only the genericmessage. Never echoes the request method, path, or matched rule reason.verbose: returnsmessage,method,path(with/secrets/*and/swarm/unlockkeypaths redacted), andreason. Intended for rule authoring and dev work only — never a production default because it can leak request details to denied callers.
response.redact_container_env, response.redact_mount_paths, and response.redact_network_topology control response filtering on successful Docker read calls:
redact_container_env(defaulttrue): replacesConfig.Envwith an empty array onGET /containers/*/json.redact_mount_paths(defaulttrue): redacts container mountSourcepaths, host bind sources insideHostConfig.Binds, and volumeMountpointfields on the Docker read endpoints Sockguard currently understands.redact_network_topology(defaulttrue): redacts container and network address topology on container/network list and inspect endpoints, includingHostConfig.NetworkMode, containerNetworkSettings, networkIPAM.Config, connectedContainers, andPeers.
Tecnativa Compatibility
For drop-in migration, sockguard accepts Tecnativa-style env vars:
CONTAINERS=1 # Allow GET /containers/**
IMAGES=1 # Allow GET /images/**
NETWORKS=0 # Deny /networks/**
VOLUMES=0 # Deny /volumes/**
EVENTS=1 # Allow GET /events (default)
PING=1 # Allow GET /_ping (default)
VERSION=1 # Allow GET /version (default)
POST=0 # Deny all write operations (default)Compat env vars only generate rules when no explicit rules: are configured. If rules: is present in YAML, those rules take precedence even when they are identical to Sockguard's built-in defaults.
Granular Operations (LinuxServer compatible)
When POST=1 is set:
ALLOW_START=1 # Allow POST /containers/{id}/start
ALLOW_STOP=1 # Allow POST /containers/{id}/stop
ALLOW_RESTART=1 # Allow POST /containers/{id}/restart
ALLOW_CREATE=0 # Deny POST /containers/create (default)
ALLOW_EXEC=0 # Deny POST /containers/{id}/exec (default)Precedence
CLI flags > environment variables > config file > defaults