feat(vpn): WireGuard-Server — Kunden waehlen sich per UDP 443 ein, ein Verteiler fuer alle

Einstellungen → VPN → WireGuard → Server: Einwahl anlegen (Name,
Kundennetz, LAN-IP der FritzBox); ARIA erzeugt beide Schluesselpaare + PSK,
⬇ liefert die Datei fuer die Gegenseite (FritzBox-Netzwerk-Kopplung oder
Standard-WireGuard). Jede Einwahl = eigener VPN-Satellit-Container mit
fester IP im Netz aria-vpn (10.213.213.0/24, ausserhalb von Dockers Pools).

Neuer Verteiler (satellite/vpn_relay.py, aria-vpn-relay, Host-Netz,
VPN_RELAY_PORT 51820): ordnet jedes Paket per WireGuard-mac1 bzw.
Sitzungsnummer dem richtigen Container zu, ohne zu entschluesseln; Fremdes
wird still verworfen. Anleitung NPM-Stream / ohne Nginx / ufw im ℹ des
Server-Reiters und im satellite/README.

Lokal Ende-zu-Ende getestet: zwei Kunden mit demselben Netz 192.168.178.0/24
gleichzeitig ueber einen Port, jeder erreicht nur sein eigenes LAN; kein Weg
Kunde → ARIA, Kunde 1 → Einwahl 2, Container → Internet; fremder Schluessel
und Muell am Verteiler ohne Antwort.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-01 14:14:34 +01:00
co-authored by Claude Opus 5.5
parent 627b826226
commit 9b25187084
8 changed files with 459 additions and 24 deletions
+5 -1
View File
@@ -1101,7 +1101,8 @@ const credsRevealWaiters = new Map();
function vpnRvs() {
return { host: RVS_HOST, port: RVS_PORT, tls: RVS_TLS, tlsFallback: RVS_TLS_FALLBACK,
token: RVS_TOKEN, sni: process.env.RVS_SNI || "" };
token: RVS_TOKEN, sni: process.env.RVS_SNI || "",
publicPort: process.env.VPN_PUBLIC_PORT || "443" };
}
async function broadcastVpn() {
@@ -1122,6 +1123,9 @@ async function handleVpn(ws, msg) {
case "vpn_get":
reply({ ok: true, connection: vpn.getConf(String(msg.id || "")) });
return;
case "vpn_profile":
reply({ ok: true, profile: vpn.profile(String(msg.id || ""), msg.type === "fritz" ? "fritz" : "standard", vpnRvs()) });
return;
case "vpn_info":
reply({ ok: true, info: await vpn.info(String(msg.id || "")) });
return;