- Minimale Web-UI (app/web/, vanilla, same-origin -> kein CORS): Text-Prompt + Mikrofon-Button (Aufnahme im Browser -> /ws/voice -> Antwort wird vorgelesen), Token-Streaming, PCM-Wiedergabe, Identitaet/Logout/Admin im Menue - Forward-/Trusted-Header-Auth (app/auth.py): Identitaet aus SSO-Header, nur von TRUSTED_PROXY_IPS akzeptiert; sonst Token/Anonymous-Fallback. Auto-Provisioning via store.get_or_create_user_by_external_id (+ external_id-Spalte/Migration) - /api/me um is_admin + sso_logout_url erweitert; GET /api/admin/users (Liste) und GET /api/admin/request-headers (SSO-Header-Discovery), Admin-gated - StaticFiles-Mount; Config: TRUSTED_AUTH_HEADER/_PROXY_IPS, ADMIN_USERS, SSO_LOGOUT_URL - WS-Auth liest Identitaet aus dem Handshake-Header - Deploy: nginx-Vorlage (WS-Upgrade!) + deploy/README.md (HTTPS/SSO/Firewall/Discovery) - Tests: Forward-Auth (Provisioning, Admin-Flag, Proxy-IP-Trust, 401/403, Static) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2.6 KiB
Remote-Betrieb über YunoHost (assistent.linix.de)
Web-UI + Mikrofon vom Handy/Browser, abgesichert per HTTPS und YunoHost-SSO.
Topologie: https://assistent.linix.de → nginx@YunoHost (TLS, SSO) → über LAN →
http://<GPU-Box>:8003 (Gateway). Der interne LAN-Hop ist unverschlüsselt
(vertrautes LAN, wie bei Ollama).
Warum HTTPS Pflicht ist: Browser geben das Mikrofon (
getUserMedia) nur in einem „secure context" frei – also HTTPS oderhttp://localhost. Über einfacheshttp://vom Handy gibt es kein Mikrofon.
1. Gateway auf der GPU-Box
deploy/voice-assistant.env.example → /etc/voice-assistant/voice-assistant.env anpassen:
HOST= LAN-IP der GPU-Box (nicht0.0.0.0),PORT=8003AUTH_ENABLED=trueTRUSTED_AUTH_HEADER(nach Discovery, s. u.),TRUSTED_PROXY_IPS= LAN-IP von linix.deADMIN_USERS=atoor,dieterschlueter,dschlueter,SSO_LOGOUT_URL=…
Kein
--forwarded-allow-ipssetzen: Das Identitäts-Vertrauen prüft die direkte Quell-IP (= der Proxy). Würde uvicorn den Client ausX-Forwarded-Forüberschreiben, schlüge der Proxy-IP-Check fehl.
2. Drei Sicherheits-Pflichten
- Bind: Gateway nur an die LAN-IP (Schritt 1).
- Firewall: Port 8003 der GPU-Box nur von der linix.de-IP erlauben, z. B.:
sudo ufw allow from <linix.de_LAN_IP> to any port 8003 proto tcp sudo ufw deny 8003 - Header überschreiben: nginx setzt den Identitäts-Header selbst; Client-Eingaben werden verworfen (siehe nginx-Conf). App-seitig zusätzlich der Proxy-IP-Check.
3. nginx auf YunoHost
deploy/assistent.linix.de.nginx.conf als Vorlage → auf dem YunoHost-Server unter
/etc/nginx/conf.d/assistent.linix.de.d/assistant.conf ablegen, GPU_BOX_LAN_IP
eintragen, die map $http_upgrade … einmalig im http{}-Kontext anlegen, dann
nginx -t && systemctl reload nginx. Subdomain assistent.linix.de in YunoHost
anlegen (Let's Encrypt) und per SSO schützen (nur erlaubte Tester/Gruppe).
4. Discovery: richtigen Identitäts-Header bestimmen
Als Admin hinter dem SSO aufrufen:
https://assistent.linix.de/api/admin/request-headers
Dort den Header finden, der den eingeloggten SSO-Usernamen trägt (z. B. X-Remote-User,
Remote-User, Auth-User …). Diesen Namen in TRUSTED_AUTH_HEADER und in der
nginx-proxy_set_header …-Zeile eintragen, Gateway + nginx neu laden.
5. Test
https://assistent.linix.de/lädt die UI, links „Angemeldet als ".- Text-Chat funktioniert; Mikrofon-Button nimmt auf und spielt die Antwort ab.
- Admins (
ADMIN_USERS) sehen den Admin-Bereich (Nutzerliste).