diff --git a/docs/gateway/protocol.md b/docs/gateway/protocol.md index ccb069ab2ee..fde213bb1f7 100644 --- a/docs/gateway/protocol.md +++ b/docs/gateway/protocol.md @@ -204,8 +204,8 @@ The Gateway treats these as **claims** and enforces server-side allowlists. - **Local** connects include loopback and the gateway host’s own tailnet address (so same‑host tailnet binds can still auto‑approve). - All WS clients must include `device` identity during `connect` (operator + node). - Control UI can omit it **only** when `gateway.controlUi.allowInsecureAuth` is enabled - (or `gateway.controlUi.dangerouslyDisableDeviceAuth` for break-glass use). + Control UI can omit it **only** when `gateway.controlUi.dangerouslyDisableDeviceAuth` + is enabled for break-glass use. - Non-local connections must sign the server-provided `connect.challenge` nonce. ## TLS + pinning diff --git a/docs/gateway/security/index.md b/docs/gateway/security/index.md index 6b7eadbb6f0..517cbaa6821 100644 --- a/docs/gateway/security/index.md +++ b/docs/gateway/security/index.md @@ -127,7 +127,7 @@ High-signal `checkId` values you will most likely see in real deployments (not e | `gateway.http.no_auth` | warn/critical | Gateway HTTP APIs reachable with `auth.mode="none"` | `gateway.auth.mode`, `gateway.http.endpoints.*` | no | | `gateway.tools_invoke_http.dangerous_allow` | warn/critical | Re-enables dangerous tools over HTTP API | `gateway.tools.allow` | no | | `gateway.tailscale_funnel` | critical | Public internet exposure | `gateway.tailscale.mode` | no | -| `gateway.control_ui.insecure_auth` | critical | Token-only over HTTP, no device identity | `gateway.controlUi.allowInsecureAuth` | no | +| `gateway.control_ui.insecure_auth` | critical | Insecure-auth toggle enabled | `gateway.controlUi.allowInsecureAuth` | no | | `gateway.control_ui.device_auth_disabled` | critical | Disables device identity check | `gateway.controlUi.dangerouslyDisableDeviceAuth` | no | | `hooks.token_too_short` | warn | Easier brute force on hook ingress | `hooks.token` | no | | `hooks.request_session_key_enabled` | warn/critical | External caller can choose sessionKey | `hooks.allowRequestSessionKey` | no | @@ -143,9 +143,9 @@ High-signal `checkId` values you will most likely see in real deployments (not e ## Control UI over HTTP The Control UI needs a **secure context** (HTTPS or localhost) to generate device -identity. If you enable `gateway.controlUi.allowInsecureAuth`, the UI falls back -to **token-only auth** and skips device pairing when device identity is omitted. This is a security -downgrade—prefer HTTPS (Tailscale Serve) or open the UI on `127.0.0.1`. +identity. `gateway.controlUi.allowInsecureAuth` does **not** bypass secure-context, +device-identity, or device-pairing checks. Prefer HTTPS (Tailscale Serve) or open +the UI on `127.0.0.1`. For break-glass scenarios only, `gateway.controlUi.dangerouslyDisableDeviceAuth` disables device identity checks entirely. This is a severe security downgrade; diff --git a/docs/web/control-ui.md b/docs/web/control-ui.md index 5050236bcee..4a3decb7e90 100644 --- a/docs/web/control-ui.md +++ b/docs/web/control-ui.md @@ -150,7 +150,7 @@ OpenClaw **blocks** Control UI connections without device identity. - `https:///` (Serve) - `http://127.0.0.1:18789/` (on the gateway host) -**Downgrade example (token-only over HTTP):** +**Insecure-auth toggle behavior:** ```json5 { @@ -162,8 +162,22 @@ OpenClaw **blocks** Control UI connections without device identity. } ``` -This disables device identity + pairing for the Control UI (even on HTTPS). Use -only if you trust the network. +`allowInsecureAuth` does not bypass Control UI device identity or pairing checks. + +**Break-glass only:** + +```json5 +{ + gateway: { + controlUi: { dangerouslyDisableDeviceAuth: true }, + bind: "tailnet", + auth: { mode: "token", token: "replace-me" }, + }, +} +``` + +`dangerouslyDisableDeviceAuth` disables Control UI device identity checks and is a +severe security downgrade. Revert quickly after emergency use. See [Tailscale](/gateway/tailscale) for HTTPS setup guidance. diff --git a/src/config/schema.help.ts b/src/config/schema.help.ts index 95520da2080..1c7dffda860 100644 --- a/src/config/schema.help.ts +++ b/src/config/schema.help.ts @@ -33,7 +33,7 @@ export const FIELD_HELP: Record = { "gateway.controlUi.allowedOrigins": "Allowed browser origins for Control UI/WebChat websocket connections (full origins only, e.g. https://control.example.com).", "gateway.controlUi.allowInsecureAuth": - "Allow Control UI auth over insecure HTTP (token-only; not recommended).", + "Insecure-auth toggle; Control UI still enforces secure context + device identity unless dangerouslyDisableDeviceAuth is enabled.", "gateway.controlUi.dangerouslyDisableDeviceAuth": "DANGEROUS. Disable Control UI device identity checks (token/password only).", "gateway.http.endpoints.chatCompletions.enabled": diff --git a/src/config/schema.labels.ts b/src/config/schema.labels.ts index cd8020b4de8..2a6bf2dbccf 100644 --- a/src/config/schema.labels.ts +++ b/src/config/schema.labels.ts @@ -114,7 +114,7 @@ export const FIELD_LABELS: Record = { "gateway.controlUi.basePath": "Control UI Base Path", "gateway.controlUi.root": "Control UI Assets Root", "gateway.controlUi.allowedOrigins": "Control UI Allowed Origins", - "gateway.controlUi.allowInsecureAuth": "Allow Insecure Control UI Auth", + "gateway.controlUi.allowInsecureAuth": "Insecure Control UI Auth Toggle", "gateway.controlUi.dangerouslyDisableDeviceAuth": "Dangerously Disable Control UI Device Auth", "gateway.http.endpoints.chatCompletions.enabled": "OpenAI Chat Completions Endpoint", "gateway.reload.mode": "Config Reload Mode", diff --git a/src/config/types.gateway.ts b/src/config/types.gateway.ts index 5015286e887..1828063149e 100644 --- a/src/config/types.gateway.ts +++ b/src/config/types.gateway.ts @@ -70,7 +70,11 @@ export type GatewayControlUiConfig = { root?: string; /** Allowed browser origins for Control UI/WebChat websocket connections. */ allowedOrigins?: string[]; - /** Allow token-only auth over insecure HTTP (default: false). */ + /** + * Insecure-auth toggle. + * Control UI still requires secure context + device identity unless + * dangerouslyDisableDeviceAuth is enabled. + */ allowInsecureAuth?: boolean; /** DANGEROUS: Disable device identity checks for the Control UI (default: false). */ dangerouslyDisableDeviceAuth?: boolean; diff --git a/src/gateway/server/ws-connection/message-handler.ts b/src/gateway/server/ws-connection/message-handler.ts index 2f0550854e6..40dcc54e6b7 100644 --- a/src/gateway/server/ws-connection/message-handler.ts +++ b/src/gateway/server/ws-connection/message-handler.ts @@ -341,8 +341,7 @@ export function attachGatewayWsMessageHandler(params: { isControlUi && configSnapshot.gateway?.controlUi?.allowInsecureAuth === true; const disableControlUiDeviceAuth = isControlUi && configSnapshot.gateway?.controlUi?.dangerouslyDisableDeviceAuth === true; - // `allowInsecureAuth` is retained for compatibility, but must not bypass - // secure-context/device-auth requirements. + // `allowInsecureAuth` must not bypass secure-context/device-auth requirements. const allowControlUiBypass = disableControlUiDeviceAuth; const device = disableControlUiDeviceAuth ? null : deviceRaw; @@ -429,7 +428,8 @@ export function attachGatewayWsMessageHandler(params: { const canSkipDevice = sharedAuthOk; if (isControlUi && !allowControlUiBypass) { - const errorMessage = "control ui requires HTTPS or localhost (secure context)"; + const errorMessage = + "control ui requires device identity (use HTTPS or localhost secure context)"; markHandshakeFailure("control-ui-insecure-auth", { insecureAuthConfigured: allowInsecureControlUi, }); diff --git a/src/security/audit.ts b/src/security/audit.ts index 7d79c18a386..294110158f4 100644 --- a/src/security/audit.ts +++ b/src/security/audit.ts @@ -349,9 +349,9 @@ function collectGatewayConfigFindings( findings.push({ checkId: "gateway.control_ui.insecure_auth", severity: "critical", - title: "Control UI allows insecure HTTP auth", + title: "Control UI insecure auth toggle enabled", detail: - "gateway.controlUi.allowInsecureAuth=true is a legacy insecure-auth toggle; Control UI still enforces secure context and device identity unless dangerouslyDisableDeviceAuth is enabled.", + "gateway.controlUi.allowInsecureAuth=true does not bypass secure context or device identity checks; only dangerouslyDisableDeviceAuth disables Control UI device identity checks.", remediation: "Disable it or switch to HTTPS (Tailscale Serve) or localhost.", }); }