From db97899037e8ac4b209b78d842ba4e2562e566f0 Mon Sep 17 00:00:00 2001 From: Vuong Hoang Date: Sat, 13 Jun 2026 14:05:07 -0700 Subject: [PATCH] feat(arbo): disable ENGINE_TOKEN bearer auth on prod (WireGuard = boundary) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Operator decision 2026-06-13 (relayed by comfy-dev, confirmed in-session): turn off the prod arbo engine's bearer auth and rely on the WireGuard perimeter. Reverses ADR-0001's open-auth-hole-closed posture (comfy-dev owns the ADR update on the vh/arbo side). The app's protected-gate no-ops only when ENGINE_TOKEN is ABSENT — an empty string still gates (verified: ENGINE_TOKEN="" -> /workflows 401). So both inject paths are removed: the compose environment line is commented out and the .env line deleted on the host. Result: tokenless GET /workflows 200 (was 401), matching the dev engine. Original token preserved in the host's .env.pre-auth-off.bak for re-enable. playbooks/arbo-disable-engine-token.yaml captures the reversible procedure. --- playbooks/arbo-disable-engine-token.yaml | 67 ++++++++++++++++++++++++ stacks/arbo/.env.example | 10 ++-- stacks/arbo/README.md | 11 ++-- stacks/arbo/compose.yaml | 7 ++- 4 files changed, 88 insertions(+), 7 deletions(-) create mode 100644 playbooks/arbo-disable-engine-token.yaml diff --git a/playbooks/arbo-disable-engine-token.yaml b/playbooks/arbo-disable-engine-token.yaml new file mode 100644 index 0000000..bd93977 --- /dev/null +++ b/playbooks/arbo-disable-engine-token.yaml @@ -0,0 +1,67 @@ +# Disable bearer-token auth on the prod arbo engine (irv-ml1), leaning on +# WireGuard as the access boundary. Operator decision 2026-06-13 (relayed by +# comfy-dev, confirmed in-session). Deliberately reverses ADR-0001's +# "open-auth hole closed (ENGINE_TOKEN minted)" line. +# +# GOTCHA (why .env-only is not enough): the app's `dependencies=protected` +# gate no-ops only when ENGINE_TOKEN is ABSENT from the container env. An +# empty string still gates (verified 2026-06-13: ENGINE_TOKEN="" -> /workflows +# still 401). The var is injected by TWO paths, both must be removed: +# 1. env_file: .env -> delete the ENGINE_TOKEN line from .env +# 2. environment: - ENGINE_TOKEN=${ENGINE_TOKEN} -> commented out in compose +# With both gone the var is unset in the container and the engine serves open, +# exactly like the dev engine on nh3-dev. +# +# Reversible: the pre-change .env (with the real token) is backed up to +# .env.pre-auth-off.bak. To re-lock: restore the ENGINE_TOKEN line in .env, +# un-comment the compose line, `compose up -d`. +# +# No sudo: lkraven owns the compose dir + .env and is in the docker group. + +vars: + dir: /opt/docker/compose/arbo + +steps: + - name: Back up prod .env (preserves the real ENGINE_TOKEN for re-enable) + shell: cp -p {{ dir }}/.env {{ dir }}/.env.pre-auth-off.bak + # creates: guards the FIRST backup — never clobber it on a rerun. + creates: "{{ dir }}/.env.pre-auth-off.bak" + + - name: Remove the ENGINE_TOKEN line from .env entirely (must be ABSENT, not empty) + shell: sed -i '/^ENGINE_TOKEN=/d' {{ dir }}/.env + when: "grep -qE '^ENGINE_TOKEN=' {{ dir }}/.env" + + - name: Push the corrected compose (ENGINE_TOKEN injection commented out) + upload: + src: stacks/arbo/compose.yaml + dest: "{{ dir }}/compose.yaml" + mode: "0644" + + - name: Recreate the engine so ENGINE_TOKEN is absent from its env + shell: docker compose -f {{ dir }}/compose.yaml up -d + +verify: + - name: .env no longer defines ENGINE_TOKEN + shell: "! grep -qE '^ENGINE_TOKEN=' {{ dir }}/.env" + changed_when: "false" + + - name: Backup still carries the original token (reversibility intact) + shell: grep -qE '^ENGINE_TOKEN=.+' {{ dir }}/.env.pre-auth-off.bak + changed_when: "false" + + - name: ENGINE_TOKEN is ABSENT from the running container env + shell: "! docker exec arbo printenv ENGINE_TOKEN >/dev/null 2>&1" + changed_when: "false" + + - name: Protected endpoint serves tokenless after warmup (auth OFF — expect HTTP 200, was 401) + shell: | + port=$(docker port arbo 8200/tcp 2>/dev/null | sed -n 's/.*:\([0-9]\+\)$/\1/p' | head -1) + final=000 + for i in $(seq 1 30); do + code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 5 "http://localhost:${port}/workflows") + if [ "$code" != "000" ]; then final=$code; break; fi + sleep 2 + done + echo "tokenless GET /workflows on :${port} -> HTTP ${final}" + test "$final" = "200" + changed_when: "false" diff --git a/stacks/arbo/.env.example b/stacks/arbo/.env.example index 2c3f64d..881490e 100644 --- a/stacks/arbo/.env.example +++ b/stacks/arbo/.env.example @@ -16,9 +16,13 @@ ARBO_GID=1000 ARBO_CATALOG_DIR=/worktank/arbo/repo # ── Secrets (DO NOT COMMIT REAL VALUES) ────────────────────────────── -# ENGINE_TOKEN: a real bearer token — auth is OPEN until this is set. -# Mint a fresh one: openssl rand -hex 32 -ENGINE_TOKEN= +# ENGINE_TOKEN: bearer auth is intentionally OFF (operator decision 2026-06-13, +# WireGuard = the boundary). Leave it UNSET — the protected-gate no-ops only +# when the var is ABSENT (an empty `ENGINE_TOKEN=` still gates), and the compose +# injection is commented out to match. To re-lock: un-comment the compose line +# `- ENGINE_TOKEN=${ENGINE_TOKEN}`, mint a bearer (`openssl rand -hex 32`), set +# it below, `compose up -d`. +# ENGINE_TOKEN= # GRANITE_KEY: the LiteLLM virtual key scoped to arbo. The 'arbo-prompt-enhance' # vkey (comfy-dev, issued 2026-06-09) is extended to reach BOTH granite-4.1-8b diff --git a/stacks/arbo/README.md b/stacks/arbo/README.md index 1fc5f08..3fd4ec9 100644 --- a/stacks/arbo/README.md +++ b/stacks/arbo/README.md @@ -31,9 +31,14 @@ scripts/deploy-stack.sh irv-ml1 arbo # diffs vs live, prompts y/N Both injected via the on-host **`.env`** (gitignored; never the althing bus), the standard fleet pattern: -- **`ENGINE_TOKEN`** — auth is **OPEN today**. Mint a real bearer - (`openssl rand -hex 32`), put it in `.env`. Closing this is the one security - fix the move buys us. +- **`ENGINE_TOKEN`** — bearer auth is **intentionally OFF** (operator decision + 2026-06-13). Prod is WireGuard-only; the perimeter is the boundary, so the + per-browser token was friction with little added security. The compose + `ENGINE_TOKEN` injection is commented out **and** the `.env` line removed — the + app's protected-gate no-ops only when the var is **absent** (an empty string + still gates). To re-lock: un-comment the compose line, set a bearer in `.env` + (`openssl rand -hex 32`), `compose up -d`. Reverses ADR-0001 (comfy-dev owns + that ADR's update). See `playbooks/arbo-disable-engine-token.yaml`. - **`GRANITE_KEY`** — the LiteLLM **virtual key scoped to arbo**. The `arbo-prompt-enhance` vkey (comfy-dev, issued 2026-06-09) was **extended** to reach **both** `granite-4.1-8b` (prompt-craft / `gen` step) **and** diff --git a/stacks/arbo/compose.yaml b/stacks/arbo/compose.yaml index e952fd2..a4b38cb 100644 --- a/stacks/arbo/compose.yaml +++ b/stacks/arbo/compose.yaml @@ -48,7 +48,12 @@ services: # LiteLLM gateway for the granite hero/metadata calls: - GRANITE_ENDPOINT=http://10.250.50.70:4000/v1 - GRANITE_KEY=${GRANITE_KEY} # arbo-prompt-enhance scoped vkey (.env) - - ENGINE_TOKEN=${ENGINE_TOKEN} # real bearer — closes today's open auth (.env) + # ENGINE_TOKEN intentionally NOT injected — bearer auth is OFF on prod by + # operator decision (2026-06-13); WireGuard is the access boundary. The + # app's protected-gate no-ops only when the var is ABSENT — an empty string + # still gates — so it must not be set here OR in .env. Re-enable: restore + # this line + set ENGINE_TOKEN in .env, then `compose up -d`. Reverses ADR-0001. + #- ENGINE_TOKEN=${ENGINE_TOKEN} volumes: - arbo_db:/data - arbo_heroes:/heroes