feat(host-agent,satellite): RVS_TLS_FALLBACK wie in den Compute-Bridges
Host-Agent und Satellit hatten keinen TLS-Fallback (nur die vier Compute-Bridges). Jetzt konsistent: bei TLS-Fehlschlag einmal auf ws:// zurueckfallen, danach wieder mit RVS_TLS starten (kein Sticky-Fallback, wie bei den Bridges). uri_host/proto werden pro Versuch aus use_tls berechnet, damit der RVS_SNI-Pfad nur bei wss gilt. Hilft nur wo der RVS plaintext erreichbar ist; gegen Caddy-TLS bleibt wss. RVS_TLS_ FALLBACK in beiden .env.example dokumentiert. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,7 @@
|
|||||||
RVS_HOST=rvs.example.de
|
RVS_HOST=rvs.example.de
|
||||||
RVS_PORT=443
|
RVS_PORT=443
|
||||||
RVS_TLS=true
|
RVS_TLS=true
|
||||||
|
RVS_TLS_FALLBACK=true # bei TLS-Fehlschlag einmal auf ws:// zurueckfallen
|
||||||
RVS_TOKEN=
|
RVS_TOKEN=
|
||||||
# RVS_SNI: nur noetig, wenn RVS_HOST eine IP ist (Agent im selben Netz wie der
|
# RVS_SNI: nur noetig, wenn RVS_HOST eine IP ist (Agent im selben Netz wie der
|
||||||
# RVS, direkt auf die interne IP). Dann hier den Zertifikats-/Hostnamen angeben,
|
# RVS, direkt auf die interne IP). Dann hier den Zertifikats-/Hostnamen angeben,
|
||||||
|
|||||||
@@ -98,6 +98,9 @@ def _default_id() -> str:
|
|||||||
RVS_HOST = os.environ.get("RVS_HOST", "")
|
RVS_HOST = os.environ.get("RVS_HOST", "")
|
||||||
RVS_PORT = int(os.environ.get("RVS_PORT", "443") or "443")
|
RVS_PORT = int(os.environ.get("RVS_PORT", "443") or "443")
|
||||||
RVS_TLS = _env_bool("RVS_TLS", True)
|
RVS_TLS = _env_bool("RVS_TLS", True)
|
||||||
|
# Bei TLS-Fehlschlag einmal auf ws:// zurueckfallen (wie die Compute-Bridges).
|
||||||
|
# Hilft nur, wenn der RVS plaintext erreichbar ist; gegen Caddy-TLS bleibt wss.
|
||||||
|
RVS_TLS_FALLBACK = _env_bool("RVS_TLS_FALLBACK", True)
|
||||||
RVS_TOKEN = os.environ.get("RVS_TOKEN", "")
|
RVS_TOKEN = os.environ.get("RVS_TOKEN", "")
|
||||||
# TLS-Hostname (SNI + Zertifikatspruefung), falls RVS_HOST eine IP ist — z.B. der
|
# TLS-Hostname (SNI + Zertifikatspruefung), falls RVS_HOST eine IP ist — z.B. der
|
||||||
# Agent laeuft im selben Netz wie der RVS und verbindet direkt auf die interne IP,
|
# Agent laeuft im selben Netz wie der RVS und verbindet direkt auf die interne IP,
|
||||||
@@ -417,22 +420,26 @@ class HostAgent:
|
|||||||
logger.error("RVS_HOST und RVS_TOKEN sind Pflicht (siehe .env.example).")
|
logger.error("RVS_HOST und RVS_TOKEN sind Pflicht (siehe .env.example).")
|
||||||
return
|
return
|
||||||
backoff = 1
|
backoff = 1
|
||||||
# RVS_SNI gesetzt (Verbindung auf eine interne IP): die URI nutzt den
|
# use_tls kann bei Fehlschlag einmal auf ws:// fallen (RVS_TLS_FALLBACK),
|
||||||
# HOSTNAMEN, damit Host-Header + SNI + Cert-Pruefung stimmen (sonst weist
|
# danach wieder zurueck auf RVS_TLS (kein Sticky-Fallback).
|
||||||
# Caddy die Verbindung ab -> HTTP 200 statt WS-Upgrade). Der TCP-Connect
|
use_tls = RVS_TLS
|
||||||
# wird per host=/port= auf die IP in RVS_HOST umgebogen.
|
tls_fallback_tried = False
|
||||||
proto = "wss" if RVS_TLS else "ws"
|
|
||||||
uri_host = RVS_SNI if (RVS_TLS and RVS_SNI) else RVS_HOST
|
|
||||||
url = f"{proto}://{uri_host}:{RVS_PORT}?token={RVS_TOKEN}"
|
|
||||||
connect_kwargs = {"max_size": 16 * 1024 * 1024,
|
connect_kwargs = {"max_size": 16 * 1024 * 1024,
|
||||||
"ping_interval": 20, "ping_timeout": 20}
|
"ping_interval": 20, "ping_timeout": 20}
|
||||||
while True:
|
while True:
|
||||||
|
proto = "wss" if use_tls else "ws"
|
||||||
|
# Bei TLS + RVS_SNI: URI nutzt den HOSTNAMEN (Host-Header/SNI/Cert),
|
||||||
|
# getaddrinfo mappt ihn auf die IP in RVS_HOST. Bei ws:// direkt die IP.
|
||||||
|
uri_host = RVS_SNI if (use_tls and RVS_SNI) else RVS_HOST
|
||||||
|
url = f"{proto}://{uri_host}:{RVS_PORT}?token={RVS_TOKEN}"
|
||||||
|
fallback = False
|
||||||
try:
|
try:
|
||||||
logger.info("Verbinde mit RVS %s://%s:%s%s …", proto, uri_host, RVS_PORT,
|
logger.info("Verbinde mit RVS %s://%s:%s%s …", proto, uri_host, RVS_PORT,
|
||||||
f" (TCP {RVS_HOST})" if (RVS_TLS and RVS_SNI) else "")
|
f" (TCP {RVS_HOST})" if (use_tls and RVS_SNI) else "")
|
||||||
async with websockets.connect(url, **connect_kwargs) as ws:
|
async with websockets.connect(url, **connect_kwargs) as ws:
|
||||||
self.ws = ws
|
self.ws = ws
|
||||||
backoff = 1
|
backoff = 1
|
||||||
|
tls_fallback_tried = False
|
||||||
await self._hello(log=True)
|
await self._hello(log=True)
|
||||||
hb = asyncio.create_task(self._heartbeat())
|
hb = asyncio.create_task(self._heartbeat())
|
||||||
try:
|
try:
|
||||||
@@ -442,10 +449,19 @@ class HostAgent:
|
|||||||
hb.cancel()
|
hb.cancel()
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.warning("RVS-Verbindung verloren: %s", exc)
|
logger.warning("RVS-Verbindung verloren: %s", exc)
|
||||||
|
if use_tls and RVS_TLS_FALLBACK and not tls_fallback_tried:
|
||||||
|
logger.info("TLS fehlgeschlagen — Fallback auf ws://")
|
||||||
|
use_tls = False
|
||||||
|
tls_fallback_tried = True
|
||||||
|
fallback = True
|
||||||
finally:
|
finally:
|
||||||
self.ws = None
|
self.ws = None
|
||||||
|
if fallback:
|
||||||
|
continue # sofort erneut mit ws://
|
||||||
await asyncio.sleep(backoff)
|
await asyncio.sleep(backoff)
|
||||||
backoff = min(backoff * 2, 30)
|
backoff = min(backoff * 2, 30)
|
||||||
|
use_tls = RVS_TLS # kein Sticky-Fallback
|
||||||
|
tls_fallback_tried = False
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
RVS_HOST=rvs.example.de
|
RVS_HOST=rvs.example.de
|
||||||
RVS_PORT=443
|
RVS_PORT=443
|
||||||
RVS_TLS=true
|
RVS_TLS=true
|
||||||
|
RVS_TLS_FALLBACK=true # bei TLS-Fehlschlag einmal auf ws:// zurueckfallen
|
||||||
RVS_TOKEN=
|
RVS_TOKEN=
|
||||||
# RVS_SNI: nur noetig, wenn RVS_HOST eine IP ist (Satellit im selben Netz wie der
|
# RVS_SNI: nur noetig, wenn RVS_HOST eine IP ist (Satellit im selben Netz wie der
|
||||||
# RVS, direkt auf die interne IP). Dann hier den Zertifikats-/Hostnamen angeben,
|
# RVS, direkt auf die interne IP). Dann hier den Zertifikats-/Hostnamen angeben,
|
||||||
|
|||||||
+23
-8
@@ -106,6 +106,8 @@ def _default_id() -> str:
|
|||||||
RVS_HOST = os.environ.get("RVS_HOST", "")
|
RVS_HOST = os.environ.get("RVS_HOST", "")
|
||||||
RVS_PORT = int(os.environ.get("RVS_PORT", "443") or "443")
|
RVS_PORT = int(os.environ.get("RVS_PORT", "443") or "443")
|
||||||
RVS_TLS = _env_bool("RVS_TLS", True)
|
RVS_TLS = _env_bool("RVS_TLS", True)
|
||||||
|
# Bei TLS-Fehlschlag einmal auf ws:// zurueckfallen (wie die Compute-Bridges).
|
||||||
|
RVS_TLS_FALLBACK = _env_bool("RVS_TLS_FALLBACK", True)
|
||||||
RVS_TOKEN = os.environ.get("RVS_TOKEN", "")
|
RVS_TOKEN = os.environ.get("RVS_TOKEN", "")
|
||||||
# TLS-Hostname (SNI + Zertifikatspruefung), falls RVS_HOST eine IP ist — z.B. ein
|
# TLS-Hostname (SNI + Zertifikatspruefung), falls RVS_HOST eine IP ist — z.B. ein
|
||||||
# Satellit im selben Netz wie der RVS, der direkt auf die interne IP verbindet
|
# Satellit im selben Netz wie der RVS, der direkt auf die interne IP verbindet
|
||||||
@@ -1332,22 +1334,26 @@ class Satellite:
|
|||||||
_creds_load()
|
_creds_load()
|
||||||
asyncio.create_task(self._periodic_scan())
|
asyncio.create_task(self._periodic_scan())
|
||||||
backoff = 1
|
backoff = 1
|
||||||
# RVS_SNI gesetzt (Verbindung auf eine interne IP): die URI nutzt den
|
# use_tls kann bei Fehlschlag einmal auf ws:// fallen (RVS_TLS_FALLBACK),
|
||||||
# HOSTNAMEN, damit Host-Header + SNI + Cert-Pruefung stimmen (sonst weist
|
# danach wieder zurueck auf RVS_TLS (kein Sticky-Fallback). Bei TLS+RVS_SNI
|
||||||
# Caddy die Verbindung ab -> HTTP 200 statt WS-Upgrade). Der TCP-Connect
|
# nutzt die URI den HOSTNAMEN (Host-Header/SNI/Cert), getaddrinfo mappt ihn
|
||||||
# wird per host=/port= auf die IP in RVS_HOST umgebogen.
|
# auf die IP in RVS_HOST.
|
||||||
proto = "wss" if RVS_TLS else "ws"
|
use_tls = RVS_TLS
|
||||||
uri_host = RVS_SNI if (RVS_TLS and RVS_SNI) else RVS_HOST
|
tls_fallback_tried = False
|
||||||
url = f"{proto}://{uri_host}:{RVS_PORT}?token={RVS_TOKEN}"
|
|
||||||
connect_kwargs = {"max_size": 8 * 1024 * 1024,
|
connect_kwargs = {"max_size": 8 * 1024 * 1024,
|
||||||
"ping_interval": 20, "ping_timeout": 20}
|
"ping_interval": 20, "ping_timeout": 20}
|
||||||
while True:
|
while True:
|
||||||
|
proto = "wss" if use_tls else "ws"
|
||||||
|
uri_host = RVS_SNI if (use_tls and RVS_SNI) else RVS_HOST
|
||||||
|
url = f"{proto}://{uri_host}:{RVS_PORT}?token={RVS_TOKEN}"
|
||||||
|
fallback = False
|
||||||
try:
|
try:
|
||||||
logger.info("Verbinde mit RVS %s://%s:%s%s …", proto, uri_host, RVS_PORT,
|
logger.info("Verbinde mit RVS %s://%s:%s%s …", proto, uri_host, RVS_PORT,
|
||||||
f" (TCP {RVS_HOST})" if (RVS_TLS and RVS_SNI) else "")
|
f" (TCP {RVS_HOST})" if (use_tls and RVS_SNI) else "")
|
||||||
async with websockets.connect(url, **connect_kwargs) as ws:
|
async with websockets.connect(url, **connect_kwargs) as ws:
|
||||||
self.ws = ws
|
self.ws = ws
|
||||||
backoff = 1
|
backoff = 1
|
||||||
|
tls_fallback_tried = False
|
||||||
await self._hello(log=True)
|
await self._hello(log=True)
|
||||||
hb = asyncio.create_task(self._heartbeat())
|
hb = asyncio.create_task(self._heartbeat())
|
||||||
try:
|
try:
|
||||||
@@ -1357,10 +1363,19 @@ class Satellite:
|
|||||||
hb.cancel()
|
hb.cancel()
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.warning("RVS-Verbindung verloren: %s", exc)
|
logger.warning("RVS-Verbindung verloren: %s", exc)
|
||||||
|
if use_tls and RVS_TLS_FALLBACK and not tls_fallback_tried:
|
||||||
|
logger.info("TLS fehlgeschlagen — Fallback auf ws://")
|
||||||
|
use_tls = False
|
||||||
|
tls_fallback_tried = True
|
||||||
|
fallback = True
|
||||||
finally:
|
finally:
|
||||||
self.ws = None
|
self.ws = None
|
||||||
|
if fallback:
|
||||||
|
continue
|
||||||
await asyncio.sleep(backoff)
|
await asyncio.sleep(backoff)
|
||||||
backoff = min(backoff * 2, 30)
|
backoff = min(backoff * 2, 30)
|
||||||
|
use_tls = RVS_TLS
|
||||||
|
tls_fallback_tried = False
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user