diff --git a/items/openclaw/components/openclaw.yaml b/items/openclaw/components/openclaw.yaml index 87ab1b1..0744a15 100755 --- a/items/openclaw/components/openclaw.yaml +++ b/items/openclaw/components/openclaw.yaml @@ -7,7 +7,24 @@ spec: kind: SERVICE source: type: IMAGE - # Latest stable calendar release (beta tags are not pinned for the catalog). + # PINNED, and the pin is load-bearing rather than merely current: 2026.7.1 + # is the last stable release in which + # `gateway.controlUi.dangerouslyDisableDeviceAuth` still works, and this + # item depends on it to be reachable at all (see the block above `command`). + # + # Verified by reading `shouldSkipControlUiPairing` in each published + # bundle: 2026.7.1 ends `return role === "operator" && policy.allowBypass`, + # while 2026.8.1 and 2026.8.2 both end `return null`, having dropped that + # branch. Upstream calls the key "a retired break-glass input, now fully + # inert" and has `doctor --fix` delete it. 2026.7.2 exists only as betas, + # which this catalog does not pin. + # + # So bumping this tag re-breaks the deployment silently. Anything past + # 2026.7.1 has to pair devices, which means either re-adding the + # in-container `devices approve` watcher this item used to carry, or + # `gateway.auth.trustedProxy.deviceAutoApprove` (2026.8.x only), which + # needs `gateway.auth.mode: "trusted-proxy"` and therefore a platform edge + # that injects an identity header. Settle that before changing the tag. ref: ghcr.io/openclaw/openclaw:2026.7.1 # Converge config through OpenClaw's own `config set` CLI, then exec the # stock gateway entrypoint (`tini -s --` passes argv through, so `sh -c` works). @@ -28,12 +45,50 @@ spec: # the config lives on a persistent volume, so a guard would pin the very first # boot's origin forever and leave a re-deployed app permanently unreachable. # `config set` is declarative, so re-applying is a no-op. + # + # GATEWAY AUTH IS UNTOUCHED, despite the name of the key below. + # `gateway.auth.mode` stays at its default (`token`), so every Control UI + # connection still has to present OPENCLAW_GATEWAY_TOKEN. Verified by + # reading the connect path in the 2026.7.1 bundle + # (dist/message-handler-*.js), where three gates run in this order: + # + # 1. handleMissingDeviceIdentity() — device identity; allowBypass admits + # 2. `if (!authOk) { rejectUnauthorized(authResult); return; }` + # 3. shouldSkipControlUiPairing() — pairing; allowBypass skips + # + # Gate 2 is unconditional — no isControlUi test, no allowBypass exemption — + # and it sits BETWEEN the two gates the key affects. So the key removes + # device identity and pairing while the token check stays fully enforced. + # + # That is the intended posture for this item: anyone holding the token gets + # in from any browser, and no device is enrolled or allowlisted. Device + # pairing is a second gate that a valid token does not satisfy, it can only + # be cleared by an already-paired admin session, and on a fresh deployment + # nothing is ever paired — so left alone the Control UI is permanently + # unreachable, reporting "device pairing required (requestId: ...)" and + # naming a CLI that a catalog user has no host shell to run. Upstream was + # asked to let a valid token bypass pairing and declined (openclaw#29908, + # closed as not planned). + # + # The alternative, carried here previously, was an in-container watcher that + # approved pending requests via `devices approve`. It worked and kept device + # identity, but it made a background process load-bearing for reachability + # and put a cold CLI start in front of every first connect. Traded away + # deliberately for a declarative key and an instant first load. + # + # What it costs: with no device identity there is no second factor and no + # per-device revocation, so a leaked token is sufficient on its own, and + # browsers re-present the token each session instead of holding a stored + # device token. `openclaw security audit` reports + # `gateway.control_ui.device_auth_disabled` as critical — expected here, not + # a regression. The token is the whole boundary; rotate it if it leaks. command: >- sh -c 'set -e; node /app/dist/index.js config set --batch-json "[{\"path\":\"gateway.mode\",\"value\":\"local\"}, {\"path\":\"gateway.bind\",\"value\":\"lan\"}, - {\"path\":\"gateway.controlUi.allowedOrigins\",\"value\":[\"${OPENCLAW_PUBLIC_ORIGIN%/}\"]}]"; + {\"path\":\"gateway.controlUi.allowedOrigins\",\"value\":[\"${OPENCLAW_PUBLIC_ORIGIN%/}\"]}, + {\"path\":\"gateway.controlUi.dangerouslyDisableDeviceAuth\",\"value\":true}]"; exec node openclaw.mjs gateway' endpoints: primary: diff --git a/items/openclaw/listing.yaml b/items/openclaw/listing.yaml index c1762c1..9514340 100755 --- a/items/openclaw/listing.yaml +++ b/items/openclaw/listing.yaml @@ -36,6 +36,23 @@ spec: keep configuration, session history, and the OAuth-token encryption key across restarts. + ## Access and security + Every connection must present the gateway token, so the Control UI is + never open to an unauthenticated visitor. Any browser holding the token + can connect — there is no per-device approval step to complete first. + + That differs from a default OpenClaw install, which also requires each + new browser to be approved once by running `openclaw devices approve` on + the Gateway host. A one-click deployment gives you no host shell to run + that from, so this item turns the device check off and leaves the token + as the single gate. + + The practical consequence is that the gateway token is the only thing + guarding an admin surface that can run tools and shell commands. There is + no second factor and no per-device revocation, so treat the token as a + secret, don't paste it into shared channels, and rotate it from the + Configuration tab if it leaks. + Messaging-channel onboarding (WhatsApp, Telegram, Discord) requires the interactive CLI and is not available in this deployment yet. category: AI_ML