From 855cc0333eb46a2474116574584414ddc8818265 Mon Sep 17 00:00:00 2001 From: duffyduck Date: Thu, 24 Sep 2026 17:16:45 +0200 Subject: [PATCH] =?UTF-8?q?feat(host-agent):=20RVS=5FSNI=20=E2=80=94=20auf?= =?UTF-8?q?=20interne=20IP=20verbinden,=20aber=20Cert-Hostname=20im=20TLS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fuer Agenten im selben Netz wie der RVS: direkt auf die interne IP verbinden (kein NAT-Hairpin ueber den externen Hostnamen), aber im TLS-Handshake den Namen praesentieren, fuer den das Caddy-Zertifikat gilt. Ohne das schickt der Client die IP als SNI -> Caddy findet kein Cert -> 'tlsv1 alert internal error'. RVS_HOST= + RVS_SNI=. server_hostname im ssl-Context. Leeres RVS_SNI = Verhalten wie bisher. Robuster als /etc/hosts (ueberlebt Reboots, wichtig auf Live-ISOs). Co-Authored-By: Claude Opus 4.8 --- host-agent/.env.example | 6 ++++++ host-agent/host_agent.py | 19 ++++++++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/host-agent/.env.example b/host-agent/.env.example index 430a639..f9c0de5 100644 --- a/host-agent/.env.example +++ b/host-agent/.env.example @@ -7,6 +7,12 @@ RVS_HOST=rvs.example.de RVS_PORT=443 RVS_TLS=true RVS_TOKEN= +# 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, +# damit der TLS-Handshake (SNI) passt. Sonst leer lassen. +# RVS_HOST=10.0.0.2 +# RVS_SNI=example.com +RVS_SNI= # ─── Identitaet dieses Rechners ──────────────────────────────────── # HOST_ID = technisch eindeutig (a-z0-9-_), Default = Hostname-Slug. diff --git a/host-agent/host_agent.py b/host-agent/host_agent.py index 1e4c147..22d414b 100644 --- a/host-agent/host_agent.py +++ b/host-agent/host_agent.py @@ -99,6 +99,11 @@ RVS_HOST = os.environ.get("RVS_HOST", "") RVS_PORT = int(os.environ.get("RVS_PORT", "443") or "443") RVS_TLS = _env_bool("RVS_TLS", True) RVS_TOKEN = os.environ.get("RVS_TOKEN", "") +# 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, +# das Caddy-Zertifikat gilt aber fuer den Namen. Dann: RVS_HOST=, +# RVS_SNI=. Leer = SNI = RVS_HOST (Normalfall). +RVS_SNI = os.environ.get("RVS_SNI", "").strip() HOST_ID = (os.environ.get("HOST_ID") or _default_id()).strip() HOST_NAME = (os.environ.get("HOST_NAME") or HOST_ID).strip() @@ -400,13 +405,21 @@ class HostAgent: logger.error("RVS_HOST und RVS_TOKEN sind Pflicht (siehe .env.example).") return backoff = 1 + # SNI-/Cert-Override: auf eine IP verbinden, aber den Namen im TLS- + # Handshake praesentieren (Caddy-Cert gilt fuer den Namen). + connect_kwargs = {"max_size": 16 * 1024 * 1024, + "ping_interval": 20, "ping_timeout": 20} + if RVS_TLS and RVS_SNI: + import ssl + connect_kwargs["ssl"] = ssl.create_default_context() + connect_kwargs["server_hostname"] = RVS_SNI while True: proto = "wss" if RVS_TLS else "ws" url = f"{proto}://{RVS_HOST}:{RVS_PORT}?token={RVS_TOKEN}" try: - logger.info("Verbinde mit RVS %s://%s:%s …", proto, RVS_HOST, RVS_PORT) - async with websockets.connect(url, max_size=16 * 1024 * 1024, - ping_interval=20, ping_timeout=20) as ws: + logger.info("Verbinde mit RVS %s://%s:%s%s …", proto, RVS_HOST, RVS_PORT, + f" (SNI {RVS_SNI})" if RVS_SNI else "") + async with websockets.connect(url, **connect_kwargs) as ws: self.ws = ws backoff = 1 await self._hello(log=True)