-
Notifications
You must be signed in to change notification settings - Fork 81
[bug] WebSocket PTY rejects connections from Docker bridge IPs #149
Description
Bug Description
When CAO runs inside a Docker container and the browser connects from the host, WebSocket connections to the PTY endpoint are rejected because the client IP is a Docker bridge address (e.g. 172.17.0.1) rather than 127.0.0.1.
Steps to Reproduce
- Run
cao-server --host 0.0.0.0inside a Docker container with port 9889 mapped - Open the CAO web UI from the host browser at
http://localhost:9889 - Try to connect to a terminal session via WebSocket
Expected Behavior
WebSocket connection succeeds — the host browser can interact with tmux terminal sessions.
Actual Behavior
WebSocket connection is rejected. The server logs show the client IP as 172.17.0.1 (Docker bridge), which fails the localhost check.
Root Cause
In cli_agent_orchestrator/api/main.py, the WebSocket endpoint has a hardcoded localhost check:
if client_host not in (None, "127.0.0.1", "::1", "localhost"):
await websocket.close(code=1008, reason="Only localhost connections are allowed")
returnDocker bridge IPs (172.x.x.x), which are standard for container-to-host networking, are not in this allowlist.
Proposed Fix
Make the trusted hosts configurable, with sensible defaults that include private networks:
# Option A: Environment variable
TRUSTED_HOSTS = os.environ.get("CAO_TRUSTED_HOSTS", "").split(",") if os.environ.get("CAO_TRUSTED_HOSTS") else []
DEFAULT_HOSTS = {None, "127.0.0.1", "::1", "localhost"}
if client_host not in DEFAULT_HOSTS and client_host not in TRUSTED_HOSTS:
# Also allow RFC 1918 private ranges (Docker bridge, Kubernetes pods, etc.)
is_private = any(client_host and client_host.startswith(prefix)
for prefix in ("172.", "10.", "192.168."))
if not is_private:
await websocket.close(code=1008, reason="Only localhost/private connections allowed")
returnOr, more conservatively, add a --trusted-hosts CLI flag to cao-server.
Workaround
We currently patch this at install time with:
if client_host not in (None, "127.0.0.1", "::1", "localhost") and not (client_host and client_host.startswith("172.")):Environment
- CAO: main branch
- Docker: 27.x
- OS: Linux (Docker container based on python:3.13)
- Browser: Chrome (connecting from Docker host)