← Back to PRs

#22682: fix(gateway): [P0] status probe ignores gateway.tls.enabled — hardcoded ws:// + self-signed cert rejection

by mahsumaktas open 2026-02-21 14:10 View on GitHub →
gateway cli commands size: XS
## Severity: P0 — Breaks core CLI functionality for all TLS users `openclaw gateway status` **always** reports `RPC probe: failed` when `gateway.tls.enabled: true`. This affects every user with TLS enabled (including the default self-signed cert setup). The gateway itself runs fine — only the CLI status/probe is broken, which is confusing and makes users think the gateway is down. ## Root Cause Two independent bugs that combine to make TLS status probing completely non-functional: ### Bug 1: Probe URL construction ignores `gateway.tls.enabled` Six code sites construct WebSocket URLs with hardcoded `ws://` instead of checking the TLS config: | File | Function/Location | What's hardcoded | |------|-------------------|-----------------| | `src/cli/daemon-cli/status.gather.ts` | `probeUrl` construction | `` `ws://${probeHost}:${daemonPort}` `` | | `src/commands/gateway-status/helpers.ts` | `resolveTargets()` | `` `ws://127.0.0.1:${port}` `` | | `src/commands/gateway-status/helpers.ts` | `buildNetworkHints()` | `localLoopbackUrl`, `localTailnetUrl` | | `src/commands/gateway-status.ts` | SSH tunnel URL | `` `ws://127.0.0.1:${tunnel.localPort}` `` | | `src/commands/gateway-status.ts` | Discovery beacon `wsUrl` | `` `ws://${host}:${port}` `` | | `src/cli/gateway-cli/register.ts` | `discover --json` wsUrl | `` `ws://${host}:${port}` `` | The correct pattern already exists in `src/gateway/call.ts` (`buildGatewayConnectionDetails`): ```typescript const tlsEnabled = config.gateway?.tls?.enabled === true; const scheme = tlsEnabled ? "wss" : "ws"; ``` These six sites simply never got the same treatment. **Effect:** When TLS is enabled, probing `ws://` against a `wss://` listener either triggers a `SECURITY ERROR` (blocked by `isSecureWebSocketUrl()`) or fails to connect. ### Bug 2: `GatewayClient` rejects self-signed certs without fingerprint In `src/gateway/client.ts`, `rejectUnauthorized: false` is only set when `tlsFingerprint` is provided: ```typescript // Before: only accepts self-signed certs WITH fingerprint pinning if (url.startsWith("wss://") && this.opts.tlsFingerprint) { wsOptions.rejectUnauthorized = false; // ... fingerprint verification } // No else → Node.js default rejectUnauthorized=true → self-signed cert rejected ``` Since the gateway auto-generates self-signed certs by default and `tlsFingerprint` is optional (and most users don't configure it), the WebSocket handshake fails with `1006 abnormal closure` for every default TLS installation. ## Fix **Bug 1** — Read `gateway.tls.enabled` to select `ws://` vs `wss://` at each site: ```typescript const scheme = cfg.gateway?.tls?.enabled === true ? "wss" : "ws"; ``` **Bug 2** — Accept self-signed certs when no fingerprint is configured: ```typescript } else if (url.startsWith("wss://")) { wsOptions.rejectUnauthorized = false; } ``` ## Changes | File | Lines | Description | |------|-------|-------------| | `src/cli/daemon-cli/status.gather.ts` | +2 −1 | TLS-aware `probeUrl` | | `src/commands/gateway-status/helpers.ts` | +5 −3 | TLS-aware `resolveTargets()` + `buildNetworkHints()` | | `src/commands/gateway-status.ts` | +4 −2 | TLS-aware SSH tunnel + discovery beacon URLs | | `src/cli/gateway-cli/register.ts` | +2 −1 | TLS-aware `discover --json` output | | `src/gateway/client.ts` | +6 −0 | Accept self-signed certs without fingerprint | | **Total** | **+19 −7** | | ## Verified Tested locally with `gateway.tls.enabled: true`, `gateway.bind: "lan"`, no `tlsFingerprint`: **Before:** ``` Probe target: ws://192.168.1.39:28643 RPC probe: failed SECURITY ERROR: Gateway URL "ws://192.168.1.39:28643" uses plaintext ws:// ``` **After:** ``` Probe target: wss://192.168.1.39:28643 RPC probe: ok ``` `openclaw gateway health` also confirms the gateway is healthy. ## Related - Fixes #22678 - Supersedes #14277 (fixes only `status.gather.ts`, stale since Feb 11) - Supersedes #21842 (fixes only `register.ts` discover JSON path) - This PR covers all six affected sites + the self-signed cert issue that neither PR addresses ## Non-breaking - When `gateway.tls.enabled` is `false` or unset, all URLs remain `ws://` — no behavior change for non-TLS users - The `rejectUnauthorized: false` fallback only applies to `wss://` connections without fingerprint pinning; fingerprint verification is unaffected <!-- greptile_comment --> <h3>Greptile Summary</h3> This PR fixes a critical P0 bug where `openclaw gateway status` always fails when TLS is enabled. The fix correctly addresses the root cause by reading `gateway.tls.enabled` to construct `wss://` URLs instead of hardcoded `ws://` URLs across 6 code sites. **What changed:** - TLS-aware URL scheme selection (`wss://` vs `ws://`) in status probe, gateway discovery, and network hints - Self-signed certificate acceptance for `wss://` connections without fingerprint pinning **Critical security issue found:** The `rejectUnauthorized: false` change in `src/gateway/client.ts:161-166` introduces a MITM vulnerability for non-loopback connections. When connecting over LAN or tailnet without fingerprint pinning, any attacker on the network can intercept credentials. The fix should scope `rejectUnauthorized: false` to loopback addresses only (using `isLoopbackHostname()`), requiring valid certs or fingerprint pinning for network connections. **Other observations:** - The `register.ts` change correctly prioritizes the beacon's `gatewayTls` field over local config, which is appropriate for discovery - All TLS scheme logic follows the established pattern from `buildGatewayConnectionDetails()` <h3>Confidence Score: 2/5</h3> - This PR has a critical security vulnerability that must be addressed before merging - Score reflects the MITM vulnerability introduced by disabling certificate validation for all non-loopback `wss://` connections. While the URL scheme fixes are correct and necessary, the security regression in `client.ts` creates a vulnerability that could expose user credentials to network attackers. The issue is fixable by scoping `rejectUnauthorized: false` to loopback addresses only. - `src/gateway/client.ts` requires immediate attention to fix the MITM vulnerability before this can be safely merged <sub>Last reviewed commit: 4029a97</sub> <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs