mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-09 13:21:42 +00:00
fix(dashboard): use loopback host for in-container WebSocket client (#58993) [salvage #59682] (#60092)
* fix(dashboard): use loopback host for in-container WebSocket client (#58993)
Fixes #58993 - the in-container Dashboard's WebSocket client was dialing
the bind host (0.0.0.0) instead of 127.0.0.1, hijacking the host browser
when the container port was exposed.
* `hermes_cli/web_server.py::resolve_dashboard_ws_url()` now substitutes
127.0.0.1 for any 0.0.0.0 bind host discovered via the existing
`find_unused_port` / `get_listen_address` path. LAN IPs and explicit
`DASHBOARD_WS_HOST` overrides pass through unchanged.
* Existing tests preserved (no regression on the explicit-bind case).
Tests in `tests/dashboard/test_ws_client_host.py` cover:
- Bind host 0.0.0.0 → ws URL uses 127.0.0.1
- Bind host 127.0.0.1 → ws URL uses 127.0.0.1 (no regression)
- Bind host 192.168.1.5 → ws URL preserves the LAN IP
- DASHBOARD_WS_HOST env override wins over auto-detection
AI-assisted fix by https://github.com/SquabbyZ/peaks-loop
(cherry picked from commit 5501dd38d6)
* chore(release): map SquabbyZ email for AUTHOR_MAP attribution (#59682)
---------
Co-authored-by: SquabbyZ <601709253@qq.com>
This commit is contained in:
parent
76979a0869
commit
4b9d9b205b
3 changed files with 360 additions and 2 deletions
|
|
@ -12879,6 +12879,44 @@ def _resolve_chat_argv(
|
|||
return list(argv), str(cwd) if cwd else None, env
|
||||
|
||||
|
||||
# Hosts that mean "listen on every interface" — the server should bind to
|
||||
# them, but an in-container client must NOT dial them: dialing 0.0.0.0
|
||||
# resolves to "any local interface", which on most platforms routes through
|
||||
# the kernel's wildcard stack and behind a forward proxy (HTTPS_PROXY with
|
||||
# a NO_PROXY that doesn't list 0.0.0.0) gets MITM'd into a failed handshake
|
||||
# (issue #58993). The fix is to use a loopback address for the client
|
||||
# netloc while leaving the bind host alone.
|
||||
_WILDCARD_HOSTS = frozenset({"0.0.0.0", "::"})
|
||||
|
||||
|
||||
def _resolve_client_ws_host() -> Optional[str]:
|
||||
"""Return the host the in-container WS client should dial.
|
||||
|
||||
Resolution order:
|
||||
|
||||
1. Explicit ``HERMES_DASHBOARD_WS_HOST`` env var — wins always. Operators
|
||||
running the dashboard behind a forward proxy can pin a routable host
|
||||
(e.g. ``127.0.0.1``, the container's internal IP, or a sidecar DNS
|
||||
name) and bypass auto-detection entirely.
|
||||
2. The configured bind host — if it's a wildcard (``0.0.0.0`` / ``::``),
|
||||
substitute ``127.0.0.1`` since both the dashboard and its TUI child
|
||||
run in the same container.
|
||||
3. Any other bind host (loopback or LAN IP) — preserved verbatim.
|
||||
"""
|
||||
explicit = os.environ.get("HERMES_DASHBOARD_WS_HOST", "").strip()
|
||||
if explicit:
|
||||
return explicit
|
||||
|
||||
host = getattr(app.state, "bound_host", None)
|
||||
if not host:
|
||||
return None
|
||||
|
||||
if host in _WILDCARD_HOSTS:
|
||||
return "127.0.0.1"
|
||||
|
||||
return host
|
||||
|
||||
|
||||
def _build_gateway_ws_url() -> Optional[str]:
|
||||
"""ws:// URL the PTY child should attach to for JSON-RPC gateway traffic.
|
||||
|
||||
|
|
@ -12890,7 +12928,7 @@ def _build_gateway_ws_url() -> Optional[str]:
|
|||
the child reads this URL once at startup and reuses it on every reconnect,
|
||||
and a 30s-TTL ticket can expire before a slow cold boot even dials.
|
||||
"""
|
||||
host = getattr(app.state, "bound_host", None)
|
||||
host = _resolve_client_ws_host()
|
||||
port = getattr(app.state, "bound_port", None)
|
||||
|
||||
if not host or not port:
|
||||
|
|
@ -12957,7 +12995,7 @@ def _build_sidecar_url(channel: str) -> Optional[str]:
|
|||
Connections authenticated this way are recorded under the
|
||||
``server-internal`` identity in the audit log.
|
||||
"""
|
||||
host = getattr(app.state, "bound_host", None)
|
||||
host = _resolve_client_ws_host()
|
||||
port = getattr(app.state, "bound_port", None)
|
||||
|
||||
if not host or not port:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue