my_voice_assistant_v3/deploy/README.md
Dieter Schlüter 76df695111 feat(web): Remote-Web-UI mit Mikrofon + Forward-Auth (YunoHost-SSO)
- 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>
2026-06-18 04:37:16 +02:00

2.6 KiB
Raw Blame History

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 oder http://localhost. Über einfaches http:// 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 (nicht 0.0.0.0), PORT=8003
  • AUTH_ENABLED=true
  • TRUSTED_AUTH_HEADER (nach Discovery, s. u.), TRUSTED_PROXY_IPS = LAN-IP von linix.de
  • ADMIN_USERS=atoor,dieterschlueter,dschlueter, SSO_LOGOUT_URL=…

Kein --forwarded-allow-ips setzen: Das Identitäts-Vertrauen prüft die direkte Quell-IP (= der Proxy). Würde uvicorn den Client aus X-Forwarded-For überschreiben, schlüge der Proxy-IP-Check fehl.

2. Drei Sicherheits-Pflichten

  1. Bind: Gateway nur an die LAN-IP (Schritt 1).
  2. 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
    
  3. 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).