mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-07 10:34:44 +00:00
docs: clarify trusted-proxy websocket scopes (#85950)
This commit is contained in:
@@ -780,16 +780,19 @@ rather than the pre-handshake defaults.
|
||||
- `gateway.controlUi.allowInsecureAuth=true` for localhost-only insecure HTTP compatibility.
|
||||
- successful `gateway.auth.mode: "trusted-proxy"` operator Control UI auth.
|
||||
- `gateway.controlUi.dangerouslyDisableDeviceAuth=true` (break-glass, severe security downgrade).
|
||||
- direct-loopback `gateway-client` backend RPCs authenticated with the shared
|
||||
gateway token/password.
|
||||
- Omitting device identity has scope consequences. When a Control UI connection
|
||||
lacks device identity, `shouldClearUnboundScopesForMissingDeviceIdentity`
|
||||
clears self-declared scopes to an empty set for token, password, and
|
||||
trusted-proxy auth. The connection is allowed on explicit trust paths, but
|
||||
scope-gated methods fail. The exception is local Control UI token/password
|
||||
sessions with `allowInsecureAuth`, which preserve scopes. For other cases,
|
||||
set `gateway.controlUi.dangerouslyDisableDeviceAuth=true` only as a
|
||||
break-glass scope-preservation path.
|
||||
- direct-loopback `gateway-client` backend RPCs on the reserved internal
|
||||
helper path.
|
||||
- Omitting device identity has scope consequences. When a device-less operator
|
||||
connection is allowed through an explicit trust path, OpenClaw still clears
|
||||
self-declared scopes to an empty set unless that path has a named
|
||||
scope-preservation exception. Scope-gated methods then fail with
|
||||
`missing scope`.
|
||||
- `gateway.controlUi.dangerouslyDisableDeviceAuth=true` is a Control UI
|
||||
break-glass scope-preservation path. It does not grant scopes to arbitrary
|
||||
custom backend or CLI-shaped WebSocket clients.
|
||||
- The reserved direct-loopback `gateway-client` backend helper path preserves
|
||||
scopes only for internal local control-plane RPCs; custom backend IDs do not
|
||||
receive this exception.
|
||||
- All connections must sign the server-provided `connect.challenge` nonce.
|
||||
|
||||
### Device auth migration diagnostics
|
||||
|
||||
@@ -53,24 +53,23 @@ Use `trusted-proxy` auth mode when:
|
||||
|
||||
When `gateway.auth.mode = "trusted-proxy"` is active and the request passes trusted-proxy checks, Control UI WebSocket sessions can connect without device pairing identity.
|
||||
|
||||
Scope implications:
|
||||
|
||||
- Device-less Control UI WebSocket sessions connect but receive no operator scopes by default. OpenClaw clears the requested scope list to `[]` so a session that is not bound to an approved paired device/token cannot self-declare permissions.
|
||||
- If methods fail with `missing scope` after a successful WebSocket connect, use HTTPS so the browser can generate device identity and complete pairing. See [Control UI insecure HTTP](/web/control-ui#insecure-http).
|
||||
- Break-glass only: `gateway.controlUi.dangerouslyDisableDeviceAuth=true` preserves requested scopes even without device identity. This is a severe security downgrade; revert quickly. See [Control UI insecure HTTP](/web/control-ui#insecure-http).
|
||||
|
||||
Reverse-proxy scope capping:
|
||||
|
||||
- If your proxy sends `x-openclaw-scopes` on the Control UI WebSocket upgrade request, OpenClaw caps the session scopes to the intersection of the requested scopes and the declared scopes. This header does not grant scopes; it only narrows what the session can hold.
|
||||
|
||||
Implications:
|
||||
|
||||
- Pairing is no longer the primary gate for Control UI access in this mode.
|
||||
- Your reverse proxy auth policy and `allowUsers` become the effective access control.
|
||||
- Keep gateway ingress locked to trusted proxy IPs only (`gateway.trustedProxies` + firewall).
|
||||
|
||||
**Scope clearing without device identity:** Because the browser over plain HTTP
|
||||
cannot create the device identity that OpenClaw uses to bind operator scopes,
|
||||
trusted-proxy WebSocket connections that lack device identity have their
|
||||
self-declared scopes cleared to an empty set. The connection is allowed, but
|
||||
scope-gated methods (`operator.read`, `operator.write`, etc.) fail with
|
||||
`missing scope`.
|
||||
|
||||
To preserve operator scopes on trusted-proxy WebSocket connections without
|
||||
device identity, set `gateway.controlUi.dangerouslyDisableDeviceAuth: true`.
|
||||
This is a break-glass flag (`openclaw security audit` reports it as critical).
|
||||
Use it only when the reverse proxy is the sole path to the Gateway and device
|
||||
identity cannot be established.
|
||||
Custom WebSocket clients are not Control UI sessions. `gateway.controlUi.dangerouslyDisableDeviceAuth` does not grant scopes to arbitrary `client.mode: "backend"` or CLI-shaped clients. Custom automation should use device identity/pairing, the reserved direct-local `client.id: "gateway-client"` backend helper path, or the [admin HTTP RPC plugin](/plugins/admin-http-rpc) when an HTTP request/response surface is a better fit.
|
||||
|
||||
## Configuration
|
||||
|
||||
@@ -322,12 +321,9 @@ Loopback trusted-proxy identity headers still fail closed: same-host callers are
|
||||
|
||||
## Operator scopes header
|
||||
|
||||
Trusted-proxy auth is an **identity-bearing** HTTP mode, so callers may optionally declare operator scopes with `x-openclaw-scopes`.
|
||||
Trusted-proxy auth is an **identity-bearing** HTTP mode, so callers may optionally declare operator scopes with `x-openclaw-scopes` on HTTP API requests.
|
||||
|
||||
Note: `x-openclaw-scopes` applies to HTTP endpoints only. WebSocket scopes are
|
||||
determined by the Gateway protocol handshake and device identity binding. For
|
||||
WebSocket scope behavior with trusted-proxy, see
|
||||
[Control UI pairing behavior](#control-ui-pairing-behavior).
|
||||
Note: WebSocket scopes are determined by the Gateway protocol handshake and device identity binding. On Control UI WebSocket upgrade requests, `x-openclaw-scopes` is only a cap on the negotiated session scopes, not a grant. For WebSocket scope behavior with trusted-proxy, see [Control UI pairing behavior](#control-ui-pairing-behavior).
|
||||
|
||||
Examples:
|
||||
|
||||
@@ -342,6 +338,7 @@ Behavior:
|
||||
- When the header is absent, normal identity-bearing HTTP APIs fall back to the standard operator default scope set.
|
||||
- Gateway-auth **plugin HTTP routes** are narrower by default: when `x-openclaw-scopes` is absent, their runtime scope falls back to `operator.write`.
|
||||
- Browser-origin HTTP requests still have to pass `gateway.controlUi.allowedOrigins` (or deliberate Host-header fallback mode) even after trusted-proxy auth succeeds.
|
||||
- For Control UI WebSocket sessions, `x-openclaw-scopes` is a scope cap when present on the upgrade request. An empty value yields no scopes.
|
||||
|
||||
Practical rule: send `x-openclaw-scopes` explicitly when you want a trusted-proxy request to be narrower than the defaults, or when a gateway-auth plugin route needs something stronger than write scope.
|
||||
|
||||
@@ -427,17 +424,20 @@ The audit checks for:
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Connection succeeds but methods report missing scope">
|
||||
The WebSocket connects, but `chat.history` or `sessions.list` fails with
|
||||
`missing scope: operator.read`.
|
||||
The WebSocket connects, but `chat.history`, `sessions.list`, or
|
||||
`models.list` fails with `missing scope: operator.read`.
|
||||
|
||||
This is expected for trusted-proxy WebSocket connections without device
|
||||
identity. Connections lacking device identity have their scopes cleared. The
|
||||
browser cannot generate device identity over plain HTTP.
|
||||
Common causes:
|
||||
|
||||
- Device-less Control UI session: trusted-proxy auth can admit the WebSocket connection without device identity, but OpenClaw clears scopes on device-less sessions by design.
|
||||
- Custom backend client: `gateway.controlUi.dangerouslyDisableDeviceAuth` is Control UI scoped and does not grant scopes to arbitrary backend or CLI-shaped WebSocket clients.
|
||||
- Overly narrow `x-openclaw-scopes`: if your proxy injects this header on the Control UI WebSocket upgrade request, the session scopes are capped to that set. An empty header value yields no scopes.
|
||||
|
||||
Fix:
|
||||
|
||||
- Set `gateway.controlUi.dangerouslyDisableDeviceAuth: true` to preserve operator scopes on trusted-proxy WebSocket connections, or
|
||||
- Use device identity pairing so scopes are bound to the device token.
|
||||
- For Control UI, use HTTPS so the browser can generate device identity and complete pairing.
|
||||
- For custom automation, use device identity/pairing, the reserved direct-local `gateway-client` backend helper path, or [admin HTTP RPC](/plugins/admin-http-rpc).
|
||||
- Use `gateway.controlUi.dangerouslyDisableDeviceAuth: true` only as a temporary Control UI break-glass path.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="WebSocket still failing">
|
||||
|
||||
Reference in New Issue
Block a user