Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,13 @@ services:
- "${LIVE_WS_PORT:-20132}:${LIVE_WS_PORT:-20132}"
volumes:
- ./data:/app/data
# SECURITY: mounting the host Docker socket gives this container full
# control over the host Docker daemon — it can create/list/stop/rm any
# container the host runs. It is here so the in-container auto-updater
# (src/lib/system/autoUpdate.ts) can recreate the stack. Only use this
# profile on a single-tenant workstation you trust, and never publish
# its ports beyond 127.0.0.1. See docs/guides/DOCKER_GUIDE.md →
# "Escape hatch: configure the container's own CLIs" for the threat model.
- /var/run/docker.sock:/var/run/docker.sock
- /usr/libexec/docker/cli-plugins:/usr/libexec/docker/cli-plugins:ro
- ${AUTO_UPDATE_HOST_REPO_DIR:-.}:/workspace/omniroute:rw
Expand Down
29 changes: 28 additions & 1 deletion docs/guides/DOCKER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,13 +132,40 @@ A bind mount is what makes the path trustworthy: OmniRoute reads
whose children are mounts, which is exactly the `/host-home` shape above) while
still refusing unmounted ones.

### Escape hatch: configure the container's own CLIs
### Escape hatch: configure the container's own CLIs (use sparingly)

When the CLIs genuinely live inside the container (the `cli` profile), the write
is intentional. Pass `--allow-container-write` to any `setup-*` command, or set
`OMNIROUTE_ALLOW_CONTAINER_CONFIG_WRITE=true` for the server. The write proceeds
with a warning that it will not survive the container.

> **Security warning — `cli` profile + `docker.sock` mount.**
> The `cli` profile bind-mounts `/var/run/docker.sock` so the in-container
> auto-updater can recreate the stack from the host daemon
> (`src/lib/system/autoUpdate.ts` probes for that socket and skips the
> Docker path when it is absent). That socket is **a host-root trust
> boundary**: anything that can reach it drives the host Docker daemon as
> root — it can create, inspect, stop and remove any container on the host.
> Implications:
>
> 1. **Never expose the `cli` profile's port to the network.** Publish
> it on `127.0.0.1` (`ports: "127.0.0.1:${DASHBOARD_PORT:-20128}:..."`)
> — a LAN-reachable `cli` profile turns any dashboard-level RCE into
> full host compromise.
> 2. **Do not bind any extra host directories into the `cli` profile.**
> The Docker socket plus any further mount gives the container full
> read/write to your filesystem and host config. If you need a tool to
> see a project, run it locally with the CLI binary — do not mount it
> into the `cli` container.
>
> If you do not need in-container auto-update, leave the `cli` profile off
> (`COMPOSE_PROFILES=core,redis` or shorter). The other profiles do not
> mount the Docker socket.
>
> See `docs/security/MITM-TPROXY-DECRYPT.md` for the related threat model
> around MITM, and `docs/security/SUPPLY_CHAIN.md` for the
> `codex`/`claude-code`/`droid`/`openclaw` binary provenance chain.

## Redis Sidecar

OmniRoute relies on Redis to back the distributed rate limiter and shared cache. The `redis` service is **always defined** in `docker-compose.yml` (it has no profile gate) and starts alongside any other profile.
Expand Down