CodesWhat/sockguard
GitHub: CodesWhat/sockguard
一个基于 Go 的安全 Docker 套接字代理,通过默认拒绝与细粒度策略解决容器入口安全与审计问题。
Stars: 4 | Forks: 0
sockguard
**Control what gets through. A security-first Docker socket proxy built in Go.**📑 Contents
- [📖 Documentation](https://getsockguard.com/docs) - [🌐 Website](https://getsockguard.com) - [🚀 Quick Start](#quick-start) - [🤔 Why Sockguard](#why-sockguard) - [✨ Features](#features) - [⚖️ Comparison](#comparison) - [⚙️ Configuration](#configuration) - [🔧 CLI](#cli) - [🔄 Migrating from Tecnativa](#migrating-from-tecnativa) - [🗺️ Roadmap](#roadmap) - [🛠️ Built With](#built-with) - [🤝 Contributing](#contributing) - [🔒 Security](#security)🚀 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 volumes: - /var/run/docker.sock:/var/run/docker.sock:ro environment: - SOCKGUARD_LISTEN_ADDRESS=:2375 - SOCKGUARD_LISTEN_INSECURE_ALLOW_PLAIN_TCP=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 ``` 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** with `SOCKGUARD_LISTEN_INSECURE_ALLOW_PLAIN_TCP=true` so migration from `tecnativa/docker-socket-proxy` and `linuxserver/socket-proxy` still works on a private Docker network. Do not publish that plaintext listener to the host or Internet. 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 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 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"]` 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 - 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.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 - 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: ``` To run fully unprivileged with a unix socket, pre-create a host directory with the uid/gid you want and bind-mount it in place of the named volume.🤔 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. Existing socket proxies (Tecnativa, LinuxServer) filter by URL path only. Sockguard goes further: granular operation control, structured audit logging, and a default-deny posture out of the box.✨ 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. 10 bundled presets. | | 📊 | **Structured Logging** | JSON access logs with method, path, decision, matched rule, latency, client info. | | 🔐 | **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 and client-container label ACLs let one proxy differentiate TCP callers before the global rule set runs. | | 🔍 | **Request Body Inspection** | `POST /containers/create` bodies are inspected to block privileged containers, host networking, and non-allowlisted bind mounts before Docker sees the request. | | 🏷️ | **Owner Label Isolation** | A proxy instance can stamp containers, networks, volumes, and build-produced images with an owner label, auto-filter list/prune/events calls, and deny cross-owner access to labeled resources. | | 🫥 | **Visibility-Controlled Reads** | Redacts env, mount, and network-topology metadata by default and can hide list/events/inspect results behind per-client label visibility rules. | | 🧱 | **Body-Blind Write Guardrail** | Remaining body-sensitive write endpoints such as `exec`, `build`, and Swarm writes still require explicit unsafe opt-in until their request bodies are inspected. | | 🔄 | **Tecnativa Compatible** | Drop-in replacement using the same env vars. `CONTAINERS=1`, `POST=0`, `ALLOW_START=1` all work. | | 🪶 | **Minimal Attack Surface** | Wolfi-based image, ~12MB. 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. | | 🩺 | **Health Check** | `/health` endpoint with cached upstream reachability probes. | | 🧪 | **Battle-Tested** | ~99% statement coverage, race-detector clean, fuzz testing on filter, config, proxy, and hijack paths. |⚖️ Comparison
How sockguard stacks up against other Docker socket proxies: | Feature | Tecnativa | LinuxServer | wollomatic | **Sockguard** | |---------|:---------:|:-----------:|:----------:|:-------------:| | Method + path filtering | ✅ | ✅ | ✅ | ✅ | | Granular POST ops | ❌ | Partial | Via regex | ✅ | | Request body inspection | ❌ | ❌ | ❌ | ✅ (`/containers/create`) | | Per-client policies | ❌ | ❌ | CIDR + client labels | ✅ (CIDR + client labels) | | Response filtering | ❌ | ❌ | ❌ | ✅ (visibility + redaction) | | Structured audit log ❌ | ❌ | ❌ | ✅ | | YAML config | ❌ | ❌ | ❌ | ✅ | | Tecnativa env compat | N/A | ✅ | ❌ | ✅ |⚙️ Configuration
### 环境变量(Tecnativa兼容) ``` CONTAINERS=1 # Allow GET /containers/** IMAGES=0 # Deny /images/** EVENTS=1 # Allow GET /events POST=0 # Read-only mode # 粒度(需要 POST=1) ALLOW_START=1 ALLOW_STOP=1 ALLOW_CREATE=0 ALLOW_EXEC=0 ``` 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. ### YAML 配置(推荐) ``` 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 response: deny_verbosity: minimal # recommended for production; verbose adds method/path/reason for debugging redact_container_env: true redact_mount_paths: true redact_network_topology: true request_body: container_create: allowed_bind_mounts: - /srv/containers - /var/lib/app-data exec: allowed_commands: - ["/usr/local/bin/pre-update", "--check"] image_pull: allow_official: true allowed_registries: - ghcr.io build: allow_remote_context: false allow_host_network: false allow_run_instructions: false clients: allowed_cidrs: - 172.18.0.0/16 container_labels: enabled: true label_prefix: com.sockguard.allow. ownership: owner: ci-job-123 label_key: com.sockguard.owner rules: - match: { method: GET, path: "/_ping" } action: allow - match: { method: GET, path: "/containers/**" } action: allow - match: { method: POST, path: "/containers/*/start" } action: allow - match: { method: "*", path: "/**" } action: deny ``` Trailing `/**` matches both the base path and any deeper path. For example, `/containers/**` matches `/containers` and `/containers/abc/json`. `listen.tls` is only needed when you expose Sockguard on non-loopback TCP. Plaintext non-loopback TCP is rejected unless you set `listen.insecure_allow_plain_tcp: true`, which is intended only for legacy compatibility on a private, trusted network. Allowed `POST /containers/create` requests are inspected by default. Unless you opt out, Sockguard blocks `HostConfig.Privileged=true`, `HostConfig.NetworkMode=host`, and any bind mount source outside `request_body.container_create.allowed_bind_mounts`. Named volumes still work without allowlist entries because they are not host bind mounts. Allowed `POST /containers/*/exec` and `POST /exec/*/start` requests are inspected when `request_body.exec.allowed_commands` is non-empty. Sockguard denies non-allowlisted argv vectors, denies privileged execs unless `request_body.exec.allow_privileged: true`, denies root-user execs unless `request_body.exec.allow_root_user: true`, and re-inspects `POST /exec/*/start` against Docker's stored exec metadata before letting it run. Allowed `POST /images/create` requests are inspected by default. Sockguard denies `fromSrc` image imports unless `request_body.image_pull.allow_imports: true` and only allows Docker Hub official images unless you set `request_body.image_pull.allow_all_registries: true` or list explicit `request_body.image_pull.allowed_registries`. Allowed `POST /build` requests are inspected by default. Sockguard denies remote build contexts, `networkmode=host`, and Dockerfiles containing `RUN` instructions unless you explicitly allow those behaviors under `request_body.build.*`. `clients.allowed_cidrs` is a coarse TCP-client gate. Requests whose source IP falls outside every configured CIDR are denied before `/health` or the global rule set runs. 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 +🔧 CLI
``` 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 `🔄 Migrating from Tecnativa
Replace the image — your env vars work as-is: ``` 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 - CONTAINERS=1 - POST=0 ```🗺️ Roadmap
| Version | Theme | Status | |---------|-------|--------| | **0.1.0** | MVP — drop-in replacement with granular control, YAML config, structured logging | ✅ shipped | | **0.2.0** | mTLS for remote TCP, TLS 1.3 minimum, loopback-by-default listener, body-blind write guardrail | ✅ shipped | | **0.3.0** | Request-body inspection for `/containers/create`, per-proxy owner labels, per-client CIDR + container-label ACLs | ✅ shipped | | **0.4.0** | Profile inheritance, unix peer creds, container/image pattern visibility | 🕒 planned | | **0.5.0** | Operator auditability Prometheus metrics, dedicated audit log schema, stable request IDs, explicit deny reason codes | 🕒 planned | | **0.6.0** | Secure container enforcement — readonly rootfs, resource limits, approved seccomp/AppArmor/SELinux, restricted CapAdd/Devices, image signature verification | 🕒 planned | | **0.7.0** | Abuse controls — per-client rate limits, burst controls, concurrency caps | 🕒 planned | | **0.8.0** | Dynamic configuration — hot reload, admin API, config validation, policy versioning | 🕒 planned |🛠️ Built With
🔒 Security
- **Responsible disclosure** — see [SECURITY.md](SECURITY.md) for scope, supported versions, and how to report a vulnerability privately. - **Image verification** — every release 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](docs/src/content/verification.mdx).
Built by CodesWhat · Licensed under Apache-2.0
标签:API代理, DevOps安全, Docker, Docker守护进程, EVTX分析, GitHub Advanced Security, Go语言, Hakrawler, Streamlit, Tecnativa, Web截图, 兼容, 安全代理, 安全加固, 安全合规, 安全防御评估, 审计日志, 容器安全, 容器防护, 方法过滤, 日志审计, 流量过滤, 程序破解, 结构化日志, 网络代理, 访问控制, 请求响应过滤, 请求拦截, 路径过滤, 默认拒绝