Files
ARIA-AGENT/satellite/README.md
T
duffyduckandClaude Opus 5.5 9b25187084 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>
2026-11-01 14:14:34 +01:00

194 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ARIA Satellit 🛰️
Ein eigenständiger **Außenposten-Container** für ein fremdes Netz (Büro, Werkstatt,
Ferienwohnung …). Er verbindet sich als RVS-Client in Stefans Raum und gibt ARIA
**Augen und Hände in genau diesem Netz** — ohne dass der Haupt-Stack dort stehen muss.
- **Augen (Info):** entdeckt Geräte via **mDNS/Zeroconf** (Chromecast, AirPlay, Sonos,
Drucker, NAS …), **SSDP/UPnP + DIAL** (Smart-TVs, Fire TV) und der **ARP-Tabelle**
(rohe Hosts). Meldet ARIA ein Live-Inventar.
- **Hände (Steuerung):** **DIAL-App-Launch** (z.B. YouTube-Video auf dem Fire TV),
**Wake-on-LAN**, generisches **HTTP**. Nur wenn freigeschaltet (siehe Sicherheit).
## ⚠️ Wichtig: der Satellit MUSS im echten Ziel-LAN laufen
Discovery (mDNS/SSDP-Multicast + ARP) funktioniert **nur**, wenn der Prozess
tatsächlich im selben LAN wie die Geräte hängt — z.B. `192.168.0.0/24`, wo der
Fire TV steht.
**Docker Desktop (Mac/Windows) geht NICHT.** Dort ist `network_mode: host` das Netz
der Docker-Linux-VM (NAT, `192.168.65.x` / `172.x`), **nicht** dein echtes LAN.
Der Satellit sieht dann nur Docker-Container statt der echten Geräte. (Der Satellit
erkennt das selbst und meldet eine ⚠-Warnung im Diagnostic + Log.)
Richtig deployen — zwei Wege:
**A) Linux-Box im Ziel-LAN mit Docker Engine** (empfohlen, z.B. Raspberry Pi / NUC im Büro):
```bash
cd satellite
cp .env.example .env # RVS-Zugang + SATELLITE_LOCATION
docker compose up -d --build
docker compose logs -f # "Netz: primary_ip=192.168.0.x" + "[scan] N Geraete"
```
`network_mode: host` (schon gesetzt) gibt hier echtes LAN + Multicast.
> **Fehler `KeyError: 'ContainerConfig'` beim Neubauen?**
> Das ist ein **Bug im alten `docker-compose` 1.29.2** (Python-Version), nicht im
> Satelliten: Beim *Ersetzen* eines bestehenden Containers mit Volume liest es ein
> Feld aus dem Image, das neuere Docker-Versionen nicht mehr schreiben. Zwei Fixes:
> ```bash
> docker compose up -d --build # v2 (mit LEERZEICHEN) — falls vorhanden
> # oder mit der alten Version: alten Container erst weg, dann neu anlegen
> docker-compose rm -sf satellite && docker-compose up -d --build
> ```
> Der `./data`-Ordner (Credentials, ADB-Schlüssel, APK-Cache) bleibt dabei erhalten —
> er ist ein Bind-Mount, kein Container-Volume.
**B) Nativ als Python-Prozess** (für Mac/Windows-Test oder ohne Docker) — läuft direkt
auf einer Maschine im Ziel-LAN. Das Script lädt die `.env` **selbst** (muss im
`satellite/`-Ordner liegen):
```bash
cd satellite
cp .env.example .env # RVS-Zugang + SATELLITE_LOCATION eintragen
pip install -r requirements.txt
python satellite.py # liest .env automatisch
```
(Echte Umgebungsvariablen haben Vorrang — `export RVS_TOKEN=...` überschreibt die
`.env`, falls du das lieber magst.)
`RVS_HOST/PORT/TLS/TOKEN` **identisch** zum Haupt-Stack (gleicher Raum, damit ARIA
den Satelliten erreicht). `SATELLITE_LOCATION` ist der Name, über den ARIA das Netz
anspricht („Büro").
**Satellit im selben Netz wie der RVS** (z.B. Rechenzentrum, direkt auf die
interne IP statt NAT-Hairpin): `RVS_HOST=<interne-ip>` + **`RVS_SNI=<zert-name>`**
(der Name, für den das Caddy-Zertifikat gilt). Ohne das scheitert TLS an
`tlsv1 alert internal error`. Zuhause / normal: `RVS_SNI` leer lassen.
**Kontrolle:** im Log/Diagnostic muss `primary_ip` im Ziel-LAN liegen
(`192.168.0.x`) — steht da `192.168.65.x` oder `172.x`, sitzt der Satellit im
falschen (Docker-)Netz.
## Als Dienst installieren (Autostart, ohne Docker)
Legt automatisch ein `.venv` an, installiert die Requirements und richtet einen
Dienst ein, der bei Boot startet und bei Absturz neu hochkommt. `.env` vorher
anlegen.
**Linux (systemd) / macOS (launchd):**
```bash
cd satellite
bash install.sh # installieren + starten
bash install.sh uninstall # entfernen
```
- Linux-Logs: `journalctl -u aria-satellite -f`
- macOS-Logs: `tail -f satellite.log` (evtl. „Lokales Netzwerk"-Zugriff erlauben)
**Windows (Scheduled Task) — PowerShell als Administrator:**
```powershell
cd satellite
powershell -ExecutionPolicy Bypass -File install.ps1 # installieren + starten
powershell -ExecutionPolicy Bypass -File install.ps1 -Uninstall # entfernen
```
- Log: `Get-Content -Wait satellite.log`
## So nutzt ARIA es
ARIA hat drei Brain-Tools:
- `satellite_list` — welche Netze/Satelliten sind online + was können sie.
- `satellite_devices(satellite)` — Inventar eines Netzes.
- `satellite_command(satellite, device, action, params)` — Aktion ausführen.
Beispiel „YouTube-Video auf dem Büro-Stick":
```
satellite_command(satellite="Büro", device="Fire TV",
action="dial.launch", params={"app":"YouTube","v":"<videoId>"})
```
## Sicherheit
Der Satellit scannt ein Netz **und** ist über einen Cloud-Relay erreichbar — deshalb:
- Reagiert **nur** auf den eigenen RVS-Raum (Token).
- **`CONTROL_ENABLED=false`** = reiner Info-Satellit (steuert nichts). Standard-sicher.
- Bei `true`: nur Aktionen aus **`CONTROL_ALLOWLIST`**, alles andere wird abgelehnt.
- Jede ausgeführte Aktion wird **geloggt**.
- Keine offenen Ports — reiner Client.
Empfehlung: in vertrauenswürdigen Netzen `CONTROL_ENABLED=true` mit enger Allowlist;
sonst `false` und nur beobachten.
## VPN-Satelliten (WireGuard, ohne Installation beim Kunden)
Ein VPN-Satellit ist ein Satellit, der **nicht im Kundennetz** läuft, sondern auf ARIA —
verbunden über einen WireGuard-Tunnel. Angelegt wird er in der Diagnostic unter
**Einstellungen → VPN → WireGuard**; im Reiter Satelliten steht er unter „VPN-Satelliten 🔐“.
**Clients** (ARIA wählt sich beim Kunden ein): die WireGuard-Datei des Kunden hochladen
oder einfügen, Namen vergeben, einschalten.
- **An** = Container mit Neustart-Automatik (verbindet sich nach einem Neustart wieder).
**Aus** = Container weg, Datei und Log bleiben — Kundennetze nur bei Bedarf.
- ✏️ bearbeiten · ⬇ Datei wieder herunterladen · ℹ Verlauf (wann verbunden, wie lange,
Datenmenge) · 🗑 löschen. Log-Aufbewahrung im Reiter Einstellungen.
**Wie es gebaut ist** (`Dockerfile.vpn`, `vpn_entry.py`, `diagnostic/vpn.js`):
- **Ein Container pro Verbindung** (`aria-vpn-<id>`, Netz `aria-vpn`, nur `NET_ADMIN`).
Eigener Netz-Namensraum: überlappende Kundennetze (überall `192.168.178.0/24`) stören
sich nicht, und lokale Satelliten im Host-Netz sehen die VPN-Netze nie.
- **Firewall im Container:** durch den Tunnel nur ausgehend, zurück kommen nur Antworten —
auch wenn die Gegenseite den Tunnel aufgebaut hat, führt keine Verbindung vom Kundennetz
zu ARIA. Ins ARIA-Netz/Internet darf der Container nur zum RVS und zum Tunnel-Endpunkt.
- `wg0` wird selbst aufgesetzt, nicht per `wg-quick`: `DNS`/`PostUp` aus fremden Dateien
werden ignoriert, und bei `AllowedIPs = 0.0.0.0/0` bleibt der Weg zum RVS trotzdem
außerhalb des Tunnels. Endpunkte mit Hostnamen (DynDNS) werden neu aufgelöst, solange
keine Verbindung steht.
- **Scan:** über WireGuard gibt es kein mDNS/SSDP/ARP — der VPN-Satellit klopft die Netze
aktiv ab (`SCAN_NETS`, Standard = AllowedIPs ohne `0.0.0.0/0`; typische Ports, auch
„Verbindung abgelehnt“ zählt als Gerät). FritzBoxen erkennt er an Port 49000.
- Daten pro Verbindung unter `aria-data/config/diag-state/vpn/<id>/` (`tunnel/wg.conf`
mit privatem Schlüssel, `status.json`, `events.jsonl`, `data/` = Zugangsdaten usw.).
**Voraussetzungen auf der ARIA-VM:**
- WireGuard-Kernelmodul: `modprobe wireguard` und dauerhaft
`echo wireguard > /etc/modules-load.d/wireguard.conf`
- Image bauen: `docker compose up -d --build vpn-satellite-image` (baut nur das Image,
der Container beendet sich sofort wieder — das ist richtig so).
**Server** (Kunden wählen sich bei ARIA ein, z.B. per FritzBox): im Reiter Server
„+ Hinzufügen“, Namen, Kundennetz (z.B. `192.168.178.0/24`) und LAN-IP der FritzBox eintragen.
ARIA erzeugt beide Schlüsselpaare und einen Pre-Shared-Key; ⬇ liefert die Datei für die
Gegenseite:
- **FritzBox** (Netzwerk-Kopplung): `Address` = LAN-IP der Box, `AllowedIPs` = ARIAs
Tunnelnetz `10.222.<n>.0/24` (kein `0.0.0.0/0` — das lehnt die FritzBox ab). Import:
Internet → Freigaben → VPN (WireGuard) → Verbindung hinzufügen → Netzwerke koppeln →
Einstellungen aus Datei übernehmen. *Noch nicht an einer echten FritzBox getestet.*
- **Standard-WireGuard** (Linux, andere Router): eigene Tunnel-Adresse `10.222.<n>.2`; das
Kundennetz muss die Gegenseite dann selbst weiterleiten bzw. maskieren.
Alle Einwahlen kommen über **einen** Port: Der **Verteiler** (`vpn_relay.py`, Container
`aria-vpn-relay`, Host-Netz, `VPN_RELAY_PORT`, Standard 51820) ordnet jedes Paket anhand der
WireGuard-Prüfsumme `mac1` der richtigen Verbindung zu — jede Einwahl hat ihr eigenes
Server-Schlüsselpaar und ihren eigenen Container mit fester IP im Netz `aria-vpn`
(`10.213.213.0/24`, änderbar mit `VPN_SUBNET`). Entschlüsselt wird dabei nichts; fremde
Schlüssel und alles andere verwirft er still. Auch zwei Kunden mit demselben Netz
(`192.168.178.0/24`) funktionieren gleichzeitig.
**Erreichbar machen — UDP 443 auf dem RVS-Hostnamen** (TCP 443 bleibt beim RVS):
- *Mit Nginx Proxy Manager:* Hosts → **Streams** → Add Stream: Incoming Port `443`,
Forward Host = ARIA-VM, Forward Port `51820`, **TCP Forwarding aus, UDP Forwarding an**
(TCP würde mit den Proxy-Hosts kollidieren). Im docker-compose des NPM `- '443:443/udp'`
ergänzen und neu starten. Am Router UDP 443 ebenfalls auf den NPM-Rechner leiten.
Hinweis: ARIA sieht dann als Absender den NPM statt der Kunden-IP; die erzeugten Dateien
senden alle 25 s ein Lebenszeichen, damit nginx die Zuordnung behält.
- *Ohne Nginx:* den RVS-Port am Router zusätzlich als **UDP** weiterleiten — direkt auf die
ARIA-VM, Port `51820`.
- *Mit ufw als Firewall:*
```bash
# auf dem NPM-Rechner: UDP 443 aus dem Internet erlauben
ufw allow 443/udp comment 'WireGuard -> ARIA'
# auf der ARIA-VM: Verteiler-Port NUR vom NPM erlauben
ufw allow proto udp from <IP-des-NPM> to any port 51820 comment 'VPN-Verteiler nur vom NPM'
# ohne Nginx statt dessen (der Router leitet direkt auf die VM):
ufw allow 51820/udp
```