Skip to content

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

Merged
vincentkoc merged 33 commits intoopenclaw:mainfrom
dashed:alberto/ws-private-network
Mar 2, 2026
Merged

fix(gateway): allow ws:// to private network addresses#28670
vincentkoc merged 33 commits intoopenclaw:mainfrom
dashed:alberto/ws-private-network

Conversation

@dashed
Copy link
Contributor

@dashed dashed commented Feb 27, 2026

Summary

Extends isSecureWebSocketUrl() to accept ws:// connections to RFC 1918 private network addresses, not just loopback. This fixes WebSocket connectivity in Docker, Kubernetes, WSL2, and Tailscale environments that was broken by the CWE-319 security fix (#20803).

Problem

The CWE-319 fix (PR #20803) blocks all ws:// connections to non-loopback addresses. This is correct for the public internet, but breaks legitimate private network communication:

  • Kubernetes: Pod-to-pod WebSocket connections (e.g., ws://10.42.1.100:18789)
  • Docker Compose: Cross-service communication (e.g., ws://172.17.0.3:18789)
  • WSL2: Host-to-guest connections (e.g., ws://172.x.x.x:18789)
  • Tailscale: Peer connections via CGNAT addresses (e.g., ws://100.64.0.1:18789)

Reported in: #22104, #22226, #21065, #22047, #21142, #21760, #21158, #21192

Root Cause

isSecureWebSocketUrl() calls isLoopbackHost() to validate ws:// URLs. Loopback is too narrow — private network addresses are equally safe from the CWE-319 threat (cleartext transmission over the public internet).

Fix

  • Add isPrivateOrLoopbackHost() — a thin wrapper around the existing isPrivateOrLoopbackAddress() utility (already used elsewhere in the codebase), with hostname handling (localhost, bracketed IPv6)
  • Change isSecureWebSocketUrl() to call isPrivateOrLoopbackHost() instead of isLoopbackHost()

Security Rationale

CWE-319 targets cleartext transmission over networks where an attacker can intercept traffic. RFC 1918 addresses (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) are non-routable on the public internet by definition. Traffic between private addresses stays within the local network boundary — the same trust domain as loopback.

This fix preserves the security intent of #20803:

  • wss:// — always accepted (TLS)
  • ws://127.0.0.1 — accepted (loopback)
  • ws://10.42.1.100now accepted (private network)
  • ws://192.168.1.100now accepted (private network)
  • ws://100.64.0.1now accepted (CGNAT/Tailscale)
  • ws://remote.example.com — still rejected (hostname, possible DNS rebinding)
  • ws://203.0.113.10 — still rejected (public IP)

Why no config flag?

Several competing PRs propose config flags or env vars to opt into this behavior. A config flag is unnecessary here because:

  1. Private networks are inherently not subject to CWE-319 (traffic never crosses the public internet)
  2. A flag adds friction — users must discover and set it to fix broken deployments
  3. The security boundary is clear and well-defined (RFC 1918 = private, everything else = public)

Test Plan

  • 51 test cases covering all private ranges, public IP rejection, and hostname rejection
  • isPrivateOrLoopbackHost(): loopback, RFC 1918, CGNAT, link-local, IPv6 ULA, public IPs, empty input
  • isSecureWebSocketUrl(): all existing cases preserved + new private address cases
npx vitest run src/gateway/net.test.ts

Closes #22104
Closes #22226
Closes #21065
Ref #22047, #21142, #21760, #21158, #21192, #20803

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ae5c0d26e4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 27, 2026

Greptile Summary

Extends WebSocket URL validation to accept ws:// connections to private network addresses (RFC 1918, CGNAT, link-local), fixing broken connectivity in Docker, Kubernetes, WSL2, and Tailscale environments.

Key Changes:

  • Added isPrivateOrLoopbackHost() wrapper that delegates to existing isPrivateOrLoopbackAddress() utility with hostname handling
  • Modified isSecureWebSocketUrl() to call isPrivateOrLoopbackHost() instead of isLoopbackHost()
  • Preserved DNS rebinding protection by rejecting all hostnames except localhost
  • Maintained CWE-319 protection by continuing to reject ws:// to public IPs

Security Alignment:
This change aligns with the project's documented security model in SECURITY.md, which explicitly acknowledges network-visible deployments on LAN/tailnet as intentional for trusted node scenarios. Private network traffic never crosses the public internet boundary, making it exempt from CWE-319 interception risks.

Confidence Score: 5/5

  • This PR is safe to merge with no identified risks
  • The implementation is well-designed and thoroughly tested. It reuses existing, battle-tested IP validation utilities, maintains strong DNS rebinding protection, and aligns perfectly with the project's documented security model. The test coverage is comprehensive with 51 test cases covering all private ranges, edge cases, and security boundaries.
  • No files require special attention

Last reviewed commit: ae5c0d2

@dashed dashed force-pushed the alberto/ws-private-network branch from ae5c0d2 to 2eec6f2 Compare February 27, 2026 12:54
@openclaw-barnacle openclaw-barnacle bot added docs Improvements or additions to documentation docker Docker and sandbox tooling labels Mar 2, 2026
@vincentkoc vincentkoc merged commit 4495114 into openclaw:main Mar 2, 2026
13 checks passed
@dashed dashed deleted the alberto/ws-private-network branch March 2, 2026 05:25
safzanpirani pushed a commit to safzanpirani/clawdbot that referenced this pull request Mar 2, 2026
* 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>
steipete pushed a commit to Sid-Qin/openclaw that referenced this pull request Mar 2, 2026
* 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>
hanqizheng pushed a commit to hanqizheng/openclaw that referenced this pull request Mar 2, 2026
* 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>
execute008 pushed a commit to execute008/openclaw that referenced this pull request Mar 2, 2026
* 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>
dawi369 pushed a commit to dawi369/davis that referenced this pull request Mar 3, 2026
* 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>
sachinkundu pushed a commit to sachinkundu/openclaw that referenced this pull request Mar 6, 2026
* 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>
zooqueen pushed a commit to hanzoai/bot that referenced this pull request Mar 6, 2026
* 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

commands Command implementations docker Docker and sandbox tooling docs Improvements or additions to documentation gateway Gateway runtime size: M

Projects

None yet

2 participants