Skip to content

Latest commit

 

History

History
45 lines (33 loc) · 7.26 KB

File metadata and controls

45 lines (33 loc) · 7.26 KB

Known Issues

This document lists known issues and limitations. Click the issue title for full details, workarounds, and discussion.

Issue OS Shell/Terminal Fix Planned?
Brokered Docker socket: chunked-body create requests skip inspection All - Yes
Brokered Docker socket: DinD + airlock mode — Testcontainers cannot reach sibling containers macOS, Windows - Investigating
Brokered Docker socket: broker binds 0.0.0.0 on non-Linux macOS, Windows - By design (host-gateway routing)

Brokered Docker socket (beta)

The --dind flag enables Testcontainers and sibling-container workflows by routing every Docker API call from the workload container through a host-side broker. The broker is part of the copilot_here binary itself, owns the rules, and forwards only requests that match the allowlist. This is meaningfully safer than mounting /var/run/docker.sock directly:

  • The host process stays in control of every call.
  • Dangerous endpoint families are denied by default: swarm, services, tasks, nodes, secrets, configs, plugins, session, distribution, auth, events.
  • POST /containers/create is body-inspected: HostConfig.Privileged=true, host network/PID/IPC namespaces, forbidden bind mounts (/, /etc, /var, /var/run/docker.sock, …), and dangerous Linux capabilities (SYS_ADMIN, SYS_MODULE, …) are all rejected at request time.
  • A strict default-deny image allowlist gates which images may be spawned at all. Empty body_inspection.allowed_images means nothing can spawn until the user enumerates trusted patterns (e.g. mcr.microsoft.com/mssql/server:*).
  • In airlock mode, HostConfig.NetworkMode is rewritten to the airlock compose network. Spawned siblings join the same internal-only network as the workload, which reaches them by Docker DNS instead of crossing the airlock boundary.
  • Rules live in .copilot_here/docker-broker.json (local) or ~/.config/copilot_here/docker-broker.json (global), with enforce and monitor modes mirroring airlock.

Current limitations

  • Chunked-body create requests skip inspection. Body inspection requires a Content-Length header on POST /containers/create. If the client uses Transfer-Encoding: chunked (rare for known-size JSON, common for streaming uploads), the broker forwards the body without inspection because the dechunker isn't implemented yet. The endpoint allowlist still gates the call. Tracked for a follow-up.
  • Bodies larger than 2 MiB skip inspection. Same posture: forwarded without rewriting, endpoint allowlist still applies. Container create payloads are typically a few KB so this is a generous safety margin.
  • Path canonicalization for bind mounts is string-level. IsForbiddenHostPath matches an entry against the deny list as exact-or-subpath, but doesn't follow symlinks or normalize .. segments. A bind like /tmp/../etc/passwd:/mnt would slip through. The Docker daemon does its own canonicalization but the broker should mirror it for defense-in-depth. Tracked for a follow-up.
  • Broker binds 0.0.0.0 on non-Linux hosts. On macOS and Windows the broker listens on TCP (the VM file-sharing layer can't proxy Unix sockets), and it binds 0.0.0.0:<ephemeral> instead of 127.0.0.1. The trigger was #111 on Windows + Podman: the workload reaches the broker via host.docker.internal, which copilot_here wires up with --add-host host.docker.internal:host-gateway. host-gateway resolves to a non-loopback bridge/proxy IP on every runtime that doesn't natively provide host.docker.internal (Linux Docker, Podman + gvproxy on Windows/macOS), so a 127.0.0.1-only listener can't accept that traffic. The integration tests already bound Any for the Linux CI case (see tests/CopilotHere.IntegrationTests/BrokerSmokeTests.cs); this PR brings production in line. Docker Desktop's native host.docker.internal still works against an Any-bound listener, so one default covers both runtimes. Every call still passes through the rule allowlist and body inspector, and the default image allowlist is empty (nothing spawns until you opt in). To force 127.0.0.1, set COPILOT_HERE_BROKER_BIND_LOOPBACK=1 — only affects the non-Linux TCP path; fine on Docker Desktop, breaks Podman Machine. (Linux uses a UDS regardless, so the env var is a no-op there.) Run copilot_here --show-docker-broker-status for a per-host snapshot.
  • Windows upstream is best-effort. The broker connects upstream via Docker Desktop's named pipe (\\.\pipe\docker_engine) or Podman's machine pipe (\\.\pipe\podman-machine-default). If your environment routes the daemon differently, set DOCKER_HOST explicitly or run from a Linux/macOS host.
  • Podman: Works via runtime detection. The broker queries podman info --format '{{.Host.RemoteSocket.Path}}' and falls back to the conventional rootless and rootful socket paths. If your Podman setup doesn't expose Host.RemoteSocket.Path, set DOCKER_HOST=unix:///path/to/podman.sock.
  • OrbStack: Works without configuration. OrbStack exposes the standard /var/run/docker.sock on macOS, so the broker connects to it the same way as Docker Desktop.
  • DinD + airlock (macOS / Windows): The Docker API path works (workload → proxy:2375 → socat → host broker), and sibling containers are created on the airlock compose network. However, Testcontainers data-plane connections fail: the workload sets TESTCONTAINERS_HOST_OVERRIDE=host.docker.internal, but host.docker.internal is unreachable via raw TCP from the internal: true airlock network. Testcontainers tries to connect to <host>:<random_mapped_port> and times out. Workaround: use --dind without --airlock until a fix lands. See #101 for potential solutions and discussion.

Rootless Podman file ownership

  • Rootless Podman can't write bind-mounted host files — fixed automatically. Rootless Podman runs inside a user namespace that maps your host user to root inside the container. A bind-mounted file you own on the host therefore shows up as root-owned inside, while the workload runs as appuser (your host UID, which maps to a subordinate UID). The result: appuser can't write the files you mounted in, so the CLI tool fails to make changes. See #119. copilot_here detects rootless Podman via podman info --format '{{.Host.Security.Rootless}}' and adds --userns=keep-id:uid=<PUID>,gid=<PGID> (plus --user 0:0 so the entrypoint still starts as root for its user setup). keep-id maps your host UID/GID to the same values inside the container, so the mounts show up owned by appuser and stay writable. Docker, OrbStack, and Podman Machine already expose the real host UID, so they're left untouched; rootful Podman is skipped because keep-id is rootless-only.

Reporting New Issues

Found a new issue? Please report it on our GitHub Issues page with:

  • Operating system and version
  • Shell version (e.g., PowerShell 5.1, Bash 5.2, Zsh 5.9)
  • Steps to reproduce
  • Expected vs actual behavior
  • Any error messages or screenshots