#21741: fix(gateway): allow plaintext ws:// for Docker/private network addresses
docs
gateway
docker
size: S
Cluster:
Gateway Resilience and Configuration
## Summary
Closes #21158
- Add `gateway.allowPlaintextWs` config option and `OPENCLAW_ALLOW_PLAINTEXT_WS=1` env var to permit plaintext `ws://` connections to RFC1918 private addresses (Docker bridge networks, LAN IPs) while still blocking public addresses.
- The existing security check (CWE-319) was too strict for Docker environments where containers communicate over private bridge networks (e.g. `172.18.0.2`), breaking `bind: "lan"` setups after the 2026.2.19 security hardening.
- Register `allowPlaintextWs` and `customBindHost` in the Zod config schema (the `.strict()` validator was rejecting the new field without this).
- Add security audit finding (`gateway.allow_plaintext_ws`, severity: warn) when the option is enabled.
## Test plan
- [x] `src/gateway/net.test.ts` — 5 new tests for `isSecureWebSocketUrl` with `allowPrivateNetworks` (Docker IPs, RFC1918, public IPs still blocked, loopback, default behavior unchanged)
- [x] `src/gateway/call.test.ts` — 3 new tests for `buildGatewayConnectionDetails` (config flag, env var, public IPs still blocked)
- [x] All existing tests pass (`net.test.ts` 45/45, `call.test.ts` 32/32, `client.test.ts` 6/6, `audit.test.ts` 75/75, config 412/412)
- [ ] Manual: set `gateway.allowPlaintextWs: true` in Docker, verify `ws://172.18.0.2:18789` connects successfully
- [ ] Manual: verify `ws://` to public IPs is still blocked even with the flag enabled
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
Adds `gateway.allowPlaintextWs` config option and `OPENCLAW_ALLOW_PLAINTEXT_WS=1` environment variable to allow plaintext `ws://` connections to private RFC1918/link-local/CGNAT addresses (Docker bridge networks, LAN IPs) while maintaining security by continuing to block public addresses. This resolves Docker deployment issues introduced by recent security hardening where containers on private bridge networks (e.g. `172.18.0.2`) were unable to connect.
**Key changes:**
- Security validation properly distinguishes between private and public networks using `isPrivateOrLoopbackAddress` (checks RFC1918, link-local, CGNAT, ULA IPv6)
- Hostnames are correctly rejected even with `allowPrivateNetworks=true` since `net.isIP()` returns 0 for non-IP strings
- Comprehensive test coverage (5 new tests in `net.test.ts`, 3 new tests in `call.test.ts`)
- Security audit finding added with severity "warn" when the option is enabled
- Zod schema properly registers the new field (prevents `.strict()` validation errors)
- Documentation updated across gateway config reference, Docker install guide, and security index
<h3>Confidence Score: 5/5</h3>
- This PR is safe to merge with minimal risk
- Score reflects well-implemented security controls with proper boundary validation, comprehensive test coverage (8 new tests covering private networks, public IPs, env vars, config), and defense-in-depth approach. The security check correctly blocks hostnames when allowPrivateNetworks is enabled (net.isIP returns 0 for non-IPs), preventing DNS rebinding attacks. Private address detection properly covers RFC1918, link-local, CGNAT, and IPv6 ULA ranges. Audit trail added with appropriate severity level. No logical errors or security vulnerabilities identified.
- No files require special attention
<sub>Last reviewed commit: 106a0e9</sub>
<!-- greptile_other_comments_section -->
<sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#21436: fix(gateway): plaintext ws:// blocked for Docker bind=lan (SECURITY...
by xinhuagu · 2026-02-19
89.3%
#21784: fix(security): restrict gateway ports to loopback by default
by smilekyra · 2026-02-20
80.7%
#22110: fix(tools): prefer loopback for internal tool-to-gateway RPC calls
by pierreeurope · 2026-02-20
80.6%
#22056: fix(gateway): use loopback for self-connections regardless of bind ...
by usedhonda · 2026-02-20
80.5%
#21697: fix(gateway): unblock local spawn pairing and gated private-LAN ws
by rjuanluis · 2026-02-20
79.6%
#8478: Security: remove --allow-unconfigured from default Dockerfile CMD
by uttej-badwane · 2026-02-04
79.2%
#21233: docs: clarify bind=lan non-loopback access requires wss or tunnel (...
by saurabhchopade · 2026-02-19
78.5%
#18441: feat(gateway): add localNetworks config for subnet-scoped auto-pairing
by JayMishra-source · 2026-02-16
78.2%
#21772: [Bug]: Allow ws:// to Tailscale CGNAT addresses
by AIflow-Labs · 2026-02-20
78.0%
#23364: Gateway: add risk-ack interlock for dangerous Control UI flags
by bmendonca3 · 2026-02-22
77.6%