fix(gateway): allow ws:// to private network addresses (#28670)

* fix(gateway): allow ws:// to RFC 1918 private network addresses

resolve ws-private-network conflicts

* gateway: keep ws security strict-by-default with private opt-in

* gateway: apply private ws opt-in in connection detail guard

* gateway: apply private ws opt-in in websocket client

* onboarding: gate private ws urls behind explicit opt-in

* gateway tests: enforce strict ws defaults with private opt-in

* onboarding tests: validate private ws opt-in behavior

* gateway client tests: cover private ws env override

* gateway call tests: cover private ws env override

* changelog: add ws strict-default security entry for pr 28670

* docs(onboard): document private ws break-glass env

* docs(gateway): add private ws env to remote guide

* docs(docker): add private ws break-glass env var

* docs(security): add private ws break-glass guidance

* docs(config): document OPENCLAW_ALLOW_PRIVATE_WS

* Update CHANGELOG.md

* gateway: normalize private-ws host classification

* test(gateway): cover non-unicast ipv6 private-ws edges

* changelog: rename insecure private ws break-glass env

* docs(onboard): rename insecure private ws env

* docs(gateway): rename insecure private ws env in config reference

* docs(gateway): rename insecure private ws env in remote guide

* docs(security): rename insecure private ws env

* docs(docker): rename insecure private ws env

* test(onboard): rename insecure private ws env

* onboard: rename insecure private ws env

* test(gateway): rename insecure private ws env in call tests

* gateway: rename insecure private ws env in call flow

* test(gateway): rename insecure private ws env in client tests

* gateway: rename insecure private ws env in client

* docker: pass insecure private ws env to services

* docker-setup: persist insecure private ws env

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
This commit is contained in:
Alberto Leal
2026-03-01 23:49:45 -05:00
committed by GitHub
parent d76b224e20
commit 449511484d
16 changed files with 272 additions and 14 deletions

View File

@@ -114,10 +114,11 @@ export class GatewayClient {
return;
}
const allowPrivateWs = process.env.OPENCLAW_ALLOW_INSECURE_PRIVATE_WS === "1";
// Security check: block ALL plaintext ws:// to non-loopback addresses (CWE-319, CVSS 9.8)
// This protects both credentials AND chat/conversation data from MITM attacks.
// Device tokens may be loaded later in sendConnect(), so we block regardless of hasCredentials.
if (!isSecureWebSocketUrl(url)) {
if (!isSecureWebSocketUrl(url, { allowPrivateWs })) {
// Safe hostname extraction - avoid throwing on malformed URLs in error path
let displayHost = url;
try {
@@ -130,6 +131,9 @@ export class GatewayClient {
"Both credentials and chat data would be exposed to network interception. " +
"Use wss:// for remote URLs. Safe defaults: keep gateway.bind=loopback and connect via SSH tunnel " +
"(ssh -N -L 18789:127.0.0.1:18789 user@gateway-host), or use Tailscale Serve/Funnel. " +
(allowPrivateWs
? ""
: "Break-glass (trusted private networks only): set OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1. ") +
"Run `openclaw doctor --fix` for guidance.",
);
this.opts.onConnectError?.(error);