Skip to content

feat(dind): Docker API proxy for rootful DinD security #144

@thejoeejoee

Description

@thejoeejoee

Context

PR #143 switches DinD to rootless mode, which prevents inner containers from flushing outer iptables rules via user namespace isolation. This is the current solution.

However, rootless DinD has known limitations (overlay2 driver restrictions, UID mapping edge cases, compatibility with certain images). This issue documents what would be needed to securely run rootful DinD instead — a Docker API filtering proxy between the opencode container and the DinD sidecar.

Required components

1. Docker API filtering proxy

A reverse proxy (jailoc system dind-proxy hidden subcommand) that intercepts Docker Engine API calls and blocks dangerous operations:

  • /containers/create — reject Privileged, NetworkMode: host, dangerous CapAdd (NET_ADMIN, NET_RAW, SYS_ADMIN, SYS_PTRACE), bind mounts to sensitive paths (/proc, /sys, host Docker socket)
  • /containers/{id}/exec and /exec/{id}/start — block entirely (exec can escalate to root inside containers)
  • /containers/{id}/update — block (can re-add capabilities post-creation)
  • /containers/{id}/archive PUT — block (can overwrite arbitrary files in containers)
  • /build — allow, but stream tar context without buffering
  • Versioned paths — handle /v1.47/... prefix stripping

Errors returned as Docker-format JSON: {"message": "blocked by jailoc proxy: ..."}.

2. mTLS on both hops

  • opencode ↔ proxy: proxy presents DinD's server cert (extend DinD SAN with DNS:dind-proxy)
  • proxy ↔ DinD: proxy uses DinD client certs for upstream connection
  • New dind-certs-server named volume to persist DinD's /certs/server/ (currently only ca and client are volumes)

3. Network-level bypass prevention

iptables rule in opencode's entrypoint.sh to block direct access to dind:2376, forcing all traffic through the proxy:

iptables -I JAILOC-OUTPUT -d dind -p tcp --dport 2376 -j DROP
iptables -I JAILOC-OUTPUT -d dind -p tcp --dport 2375 -j DROP

4. Compose template changes

  • Add dind-proxy service (runs jailoc system dind-proxy)
  • Mount jailoc binary into the proxy container (or build/download a Linux binary)
  • Route DOCKER_HOST from opencode to dind-proxy:2376 instead of dind:2376
  • Add dind-certs-server volume

5. Binary delivery problem

The jailoc binary on the host may be Darwin (macOS). The proxy runs in a Linux container. Options:

  • Download Linux binary from GitHub Releases at container start
  • Build from source inside the container (slow)
  • Publish a separate proxy container image

This is the main unresolved design question.

Blocklist vs allowlist

The proposed approach is blocklist (allow all API calls, block known-dangerous ones). This is pragmatic but has an inherent risk of missing new dangerous endpoints in future Docker API versions. An allowlist approach would be safer but significantly more restrictive and harder to maintain.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions