← Back to PRs

#21741: fix(gateway): allow plaintext ws:// for Docker/private network addresses

by Joe3112 open 2026-02-20 09:35 View on GitHub →
docs gateway docker size: S
## 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