[Bug] Tunnel-Connect-Befehl nutzt globales SERVER_URL als Host → falsche Adresse (gleiches Muster wie #191/#189) #202

Closed
opened 2026-06-29 21:01:46 +00:00 by chinux · 2 comments
Owner

Bug (live beobachtet)

Der generierte Tunnel-Connect-Befehl zeigt die falsche Host-Adresse: z. B. ssh root@172.16.1.14 -p 50004, obwohl der Nutzer das Backend nicht ueber diese (interne/VPN-)IP erreicht. Port-Forwarding (bind_port) ist korrekt — nur die Host-Adresse im Link stimmt nicht.

Ursache

server/tunneling/manager.py:42-46:

_TUNNEL_PUBLIC_HOST = os.getenv("TUNNEL_PUBLIC_HOST", "")
if not _TUNNEL_PUBLIC_HOST:
    _TUNNEL_PUBLIC_HOST = urlparse(os.getenv("SERVER_URL","")).hostname or "localhost"

Der Connect-Host wird aus dem globalen SERVER_URL abgeleitet (Fallback) und in to_dict() (:96) fuer JEDEN Tunnel verwendet. → bei VPN-SERVER_URL (172.16.1.14) bekommt man immer die interne IP, auch wenn man public/ueber Domain zugreift.

Architektur-Klarstellung (verifiziert)

Der Tunnel-Listener laeuft auf dem Backend (manager.py:4-9: "backend-side TCP listeners that relay through WS"). Datenpfad: Browser → Backend-Listener:bind_port → WS → Agent → Ziel. Die Connect-Adresse ist also die Backend-Adresse, unter der der NUTZER das Backend erreicht — nicht die Node-Adresse, nicht zwingend SERVER_URL.

Bezug zu #189/#191 (gleiches Wurzelmuster)

Wie bei #191 (regenerate-token) wird eine transport-/erreichbarkeitsabhaengige Adresse faelschlich aus dem globalen SERVER_URL abgeleitet. Es braucht dieselbe transport-bewusste Auswahl:

  • Nutzer-Zugriff public → oeffentliche Domain (SERVER_URL_PUBLIC).
  • Nutzer-Zugriff VPN → VPN-Adresse (SERVER_URL_VPN).
  • TUNNEL_PUBLIC_HOST (explizit gesetzt) gewinnt immer.

Fix

  • TUNNEL_PUBLIC_HOST-Ableitung von SERVER_URL auf die #189-Adress-Logik umstellen (Public/VPN). Idealerweise den in #191 vorgeschlagenen gemeinsamen Helper (resolve_install_base/eine resolve_public_host-Variante) wiederverwenden statt erneut SERVER_URL zu parsen.
  • Wenn der Nutzer-Zugriffsweg nicht serverseitig bestimmbar ist: TUNNEL_PUBLIC_HOST als expliziten Konfigwert dokumentieren/empfehlen (er gewinnt bereits) — und im UI/Doku klarstellen, dass dieser fuer korrekte Connect-Strings gesetzt sein sollte.
  • Niemals stillschweigend localhost ausliefern (aktueller Last-Fallback) — das ist nie ein gueltiger Connect-Host fuer den Nutzer.

Akzeptanz

  • Connect-Befehl zeigt die Adresse, unter der der Nutzer das Backend tatsaechlich erreicht (public Domain bzw. VPN-IP), nicht die interne SERVER_URL-IP bei abweichendem Zugriffsweg.
  • TUNNEL_PUBLIC_HOST (explizit) hat Vorrang.
  • Kein localhost-Fallback in der Ausgabe.

Branch

fix/tunnel-connect-host-transport-aware

## Bug (live beobachtet) Der generierte Tunnel-Connect-Befehl zeigt die **falsche Host-Adresse**: z. B. `ssh root@172.16.1.14 -p 50004`, obwohl der Nutzer das Backend nicht ueber diese (interne/VPN-)IP erreicht. Port-Forwarding (bind_port) ist korrekt — nur die Host-Adresse im Link stimmt nicht. ## Ursache `server/tunneling/manager.py:42-46`: ``` _TUNNEL_PUBLIC_HOST = os.getenv("TUNNEL_PUBLIC_HOST", "") if not _TUNNEL_PUBLIC_HOST: _TUNNEL_PUBLIC_HOST = urlparse(os.getenv("SERVER_URL","")).hostname or "localhost" ``` Der Connect-Host wird aus dem **globalen SERVER_URL** abgeleitet (Fallback) und in `to_dict()` (:96) fuer JEDEN Tunnel verwendet. → bei VPN-SERVER_URL (172.16.1.14) bekommt man immer die interne IP, auch wenn man public/ueber Domain zugreift. ## Architektur-Klarstellung (verifiziert) Der Tunnel-Listener laeuft auf dem **Backend** (manager.py:4-9: "backend-side TCP listeners that relay through WS"). Datenpfad: Browser → Backend-Listener:bind_port → WS → Agent → Ziel. Die Connect-Adresse ist also die **Backend-Adresse, unter der der NUTZER das Backend erreicht** — nicht die Node-Adresse, nicht zwingend SERVER_URL. ## Bezug zu #189/#191 (gleiches Wurzelmuster) Wie bei #191 (regenerate-token) wird eine transport-/erreichbarkeitsabhaengige Adresse faelschlich aus dem globalen SERVER_URL abgeleitet. Es braucht dieselbe **transport-bewusste Auswahl**: - Nutzer-Zugriff public → oeffentliche Domain (SERVER_URL_PUBLIC). - Nutzer-Zugriff VPN → VPN-Adresse (SERVER_URL_VPN). - `TUNNEL_PUBLIC_HOST` (explizit gesetzt) gewinnt immer. ## Fix - `TUNNEL_PUBLIC_HOST`-Ableitung von SERVER_URL auf die #189-Adress-Logik umstellen (Public/VPN). Idealerweise den in #191 vorgeschlagenen gemeinsamen Helper (`resolve_install_base`/eine `resolve_public_host`-Variante) wiederverwenden statt erneut SERVER_URL zu parsen. - Wenn der Nutzer-Zugriffsweg nicht serverseitig bestimmbar ist: `TUNNEL_PUBLIC_HOST` als expliziten Konfigwert dokumentieren/empfehlen (er gewinnt bereits) — und im UI/Doku klarstellen, dass dieser fuer korrekte Connect-Strings gesetzt sein sollte. - Niemals stillschweigend `localhost` ausliefern (aktueller Last-Fallback) — das ist nie ein gueltiger Connect-Host fuer den Nutzer. ## Akzeptanz - Connect-Befehl zeigt die Adresse, unter der der Nutzer das Backend tatsaechlich erreicht (public Domain bzw. VPN-IP), nicht die interne SERVER_URL-IP bei abweichendem Zugriffsweg. - TUNNEL_PUBLIC_HOST (explizit) hat Vorrang. - Kein localhost-Fallback in der Ausgabe. ## Branch `fix/tunnel-connect-host-transport-aware`
Author
Owner

Umgesetzt in bd2bbae (auf main).

tunneling/manager.py leitete TUNNEL_PUBLIC_HOST einmalig beim Import aus SERVER_URL ab und nutzte den einen globalen Wert für jeden Tunnel → falscher Connect-Host, wenn das Backend nicht über SERVER_URL erreichbar ist.

Fix per-Node:

  • _spawn löst den Public-Host via resolve_install_base(node) auf (transport-bewusst, gleicher Helper) und speichert ihn auf der Session (public_host). Defensiv — fehlende VPN-Adresse blockt das Tunnel-Öffnen NICHT (HTTPException gefangen → None).
  • to_dict: ENV TUNNEL_PUBLIC_HOST gewinnt → sonst self.public_host → sonst leer + public_host_unset=True (UI: „Host manuell setzen"). Kein SERVER_URL-Global, kein localhost/0.0.0.0 in der Ausgabe.
  • Modul-Level SERVER_URL-Fallback entfernt (reines ENV).

Akzeptanz verifiziert (Container): per-node → http://172.16.1.14:port; kein Host → leer + public_host_unset; ENV → Override gewinnt. Listener bleibt backend-seitig (Datenpfad Browser→Backend:bind_port→WS→Agent), daher per-Node-Backend-Adresse korrekt.

Bleibt offen wie gewünscht.

Umgesetzt in `bd2bbae` (auf `main`). `tunneling/manager.py` leitete `TUNNEL_PUBLIC_HOST` einmalig beim Import aus `SERVER_URL` ab und nutzte den einen globalen Wert für jeden Tunnel → falscher Connect-Host, wenn das Backend nicht über `SERVER_URL` erreichbar ist. **Fix per-Node:** - `_spawn` löst den Public-Host via `resolve_install_base(node)` auf (transport-bewusst, gleicher Helper) und speichert ihn auf der Session (`public_host`). Defensiv — fehlende VPN-Adresse blockt das Tunnel-Öffnen NICHT (HTTPException gefangen → None). - `to_dict`: ENV `TUNNEL_PUBLIC_HOST` gewinnt → sonst `self.public_host` → sonst **leer** + `public_host_unset=True` (UI: „Host manuell setzen"). Kein `SERVER_URL`-Global, kein `localhost`/`0.0.0.0` in der Ausgabe. - Modul-Level `SERVER_URL`-Fallback entfernt (reines ENV). **Akzeptanz verifiziert** (Container): per-node → `http://172.16.1.14:port`; kein Host → leer + `public_host_unset`; ENV → Override gewinnt. Listener bleibt backend-seitig (Datenpfad Browser→Backend:bind_port→WS→Agent), daher per-Node-Backend-Adresse korrekt. Bleibt offen wie gewünscht.
Author
Owner

Verifiziert → erfüllt, wird geschlossen. manager.py: globaler SERVER_URL-Fallback entfernt (:41-44); Connect-Host = TUNNEL_PUBLIC_HOST-Override → sonst per-Node aufgelöst (sess.public_host = urlparse(resolve_install_base(node)['base']).hostname, :416-424) → sonst LEER mit UI-Hinweis 'Host manuell setzen' (:96-105). Kein localhost-Fallback mehr. Commit bd2bbae.

**Verifiziert → erfüllt, wird geschlossen.** manager.py: globaler SERVER_URL-Fallback entfernt (:41-44); Connect-Host = `TUNNEL_PUBLIC_HOST`-Override → sonst per-Node aufgelöst (`sess.public_host = urlparse(resolve_install_base(node)['base']).hostname`, :416-424) → sonst LEER mit UI-Hinweis 'Host manuell setzen' (:96-105). Kein localhost-Fallback mehr. Commit bd2bbae.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
chinux/theProx#202
No description provided.