Skip to content

Latest commit

 

History

History
138 lines (109 loc) · 5.95 KB

docker.md

File metadata and controls

138 lines (109 loc) · 5.95 KB

Running OpenConcho in Docker

The @openconcho/web SPA ships as a container: a two-stage build (Node + pnpm builds the static bundle, then nginx-unprivileged serves it on port 8080 as a non-root user) that also reverse-proxies the Honcho API under its own origin, so the browser never makes a cross-origin request.

How the proxy works

The browser issues every Honcho call same-origin to /api/* and names the real upstream per request in an X-Honcho-Upstream header (sourced from the active instance's base URL). nginx strips /api, forwards to that upstream server-side, and returns the response. Because the browser→nginx hop is same-origin, no CORS applies; the nginx→Honcho hop is server-side, where CORS is irrelevant. The frontend stays the source of truth for which instance to talk to, so the multi-instance switcher and the Fleet view keep working.

Compose: dev vs prod profiles

One docker-compose.yml with two profiles; the shared config (env, ports, extra_hosts) is defined once via a YAML anchor:

  • dev profile — build: ., runs your local source (make up).
  • prod profile — pulls the published image (ghcr.io/offendingcommit/openconcho-web:latest, pull_policy: always) (make prod).
make up      # build from source + run                → http://localhost:8080
make prod    # pull ghcr…:latest instead of building
make down    # stop + remove (either profile)
make clean   # down + drop the locally built image

make up expands to docker compose --profile dev up -d --build and make prod to docker compose --profile prod up -d. A bare docker compose up (no profile) starts nothing — use the make targets or pass --profile. Set env inline or via a .env file:

OPENCONCHO_DEFAULT_HONCHO_URL=https://honcho.example.net make prod

The published image is multi-arch (amd64 + arm64); the first publish creates a private GHCR package — make it public for unauthenticated pulls.

Add it to an existing Honcho Compose stack

Drop the openconcho service into the project that runs your Honcho api, pointing the seed at the api service (nginx resolves it on the compose network):

services:
  openconcho:
    image: ghcr.io/offendingcommit/openconcho-web:latest
    environment:
      OPENCONCHO_DEFAULT_HONCHO_URL: http://api:8000
    ports:
      - "127.0.0.1:8080:8080"
    depends_on:
      api:
        condition: service_healthy
    restart: unless-stopped

OPENCONCHO_DEFAULT_HONCHO_URL seeds the UI's first instance with an absolute URL. The browser sends that URL in the X-Honcho-Upstream header; nginx (on the compose network) forwards to it — no browser CORS, and the API token never leaves the origin.

Standalone (no compose)

docker run --rm -p 8080:8080 -e OPENCONCHO_DEFAULT_HONCHO_URL=http://host.docker.internal:8000 \
  ghcr.io/offendingcommit/openconcho-web:latest
# → http://localhost:8080  ·  GET /healthz returns "ok"

Runtime knobs (no rebuild needed):

Env Default Meaning
OPENCONCHO_DEFAULT_HONCHO_URL (empty) Absolute URL seeding the first instance; empty = configure in Settings
OPENCONCHO_UPSTREAM_ALLOWLIST (empty) Optional SSRF guard: comma-separated host globs (e.g. honcho.example.net,*.honcho.dev). Empty = forward anywhere
OPENCONCHO_PORT 8080 nginx listen port inside the container (IPv4 and IPv6). Unset or empty keeps 8080. Decimal 1-65535, no leading zeros.

OPENCONCHO_PORT is the process listen port, not the published host port. On a bridge network docker run -p 9090:8080 already remaps localhost without a rebuild. Set the env when the process itself must listen elsewhere (host networking, or a required container port). The image runs as UID 101, so ports below 1024 fail to bind unless the host allows unprivileged low ports. Compose publishes 127.0.0.1:8080:${OPENCONCHO_PORT:-8080} so the target matches the listener; change the left-hand host port separately if you only need a different localhost port.

Hardened run adds --read-only --cap-drop ALL --security-opt no-new-privileges with tmpfs on /tmp, /var/cache/nginx, /var/run, and /etc/nginx/conf.d. Those tmpfs mounts must be writable by UID 101 (uid=101,gid=101 on docker run --tmpfs, or fsGroup: 101 in the chart). The entrypoint writes config.js, the allowlist map, and the listen port at start, which a read-only root blocks without those mounts.

Serving under a sub-path

To host the UI under a path such as https://example.net/honcho/, build with the BASE_PATH build arg (leading and trailing slash required) and let your reverse proxy strip the prefix:

docker build --build-arg BASE_PATH=/honcho/ -t openconcho-web:honcho .
docker run -d -p 127.0.0.1:8080:8080 \
  -e OPENCONCHO_DEFAULT_HONCHO_URL=http://host.docker.internal:8000 openconcho-web:honcho
location = /honcho { return 308 /honcho/; }
location /honcho/ {
    proxy_pass http://127.0.0.1:8080/;   # trailing slash strips /honcho
}

Assets, routes, config.js, and the /api proxy all resolve under the prefix (/honcho/api/*). Outside Docker, pass the same flag to the web build: pnpm --filter @openconcho/web build --base /honcho/.

SSRF: when to set the allowlist

The header-driven proxy forwards to whatever upstream the client names. With the default 127.0.0.1:8080 binding only your own machine can reach nginx, so leaving the allowlist open is fine. Before exposing the proxy (e.g. behind a tunnel), set OPENCONCHO_UPSTREAM_ALLOWLIST to the host globs you trust — non-matching upstreams are rejected with 403 and an X-Honcho-Proxy-Reject: allowlist header.

CORS, the short version

The desktop app routes HTTP through Rust (reqwest) and bypasses browser CORS; the web build solves it with the same-origin /api proxy above — nothing to configure on Honcho. The proxy makes a Honcho-side CORSMiddleware unnecessary regardless of which instance you point at.