feat(vpn): expose Athena services directly over WireGuard

This commit is contained in:
Mikei386
2026-08-24 07:33:16 +02:00
parent b997fcb9f7
commit c402ea79f7
13 changed files with 130 additions and 56 deletions
+7 -6
View File
@@ -55,17 +55,18 @@ Neustart an; danach wird derselbe Befehl erneut ausgeführt.
| llama.cpp | nur Docker-intern | Inferenz und integrierte Vision |
| Profile Controller | nur Docker-intern | eng begrenzter Profil-/FLUX-Hot-Swap |
| FLUX Worker | nur Docker-intern, normalerweise gestoppt | Bildgenerierung auf RTX 5080 |
| XTTS-v2 | nur Docker-intern, RTX 3060 | primäre mehrsprachige Sprachausgabe |
| TTS Gateway | nur Docker-intern | Annmarie Nele, Queue und Piper-Fallback |
| Piper | nur Docker-intern, CPU | ausfallsichere deutsche Ersatzstimme |
| MCP-Tool-Stack | nur Docker-intern | Athena-Kontext, Athena Operator, Web, GitHub, Home Assistant, ARR, Unraid und Navidrome |
| XTTS-v2 | `<WG-IP>:8092`, RTX 3060 | primäre mehrsprachige Sprachausgabe |
| TTS Gateway | `<WG-IP>:8085` | Annmarie Nele, Queue und Piper-Fallback |
| Piper | `<WG-IP>:8091`, CPU | ausfallsichere deutsche Ersatzstimme |
| MCP-Tool-Stack | `<WG-IP>:8201-8208` | Athena-Kontext, Athena Operator, Web, GitHub, Home Assistant, ARR, Unraid und Navidrome |
XTTS-v2, TTS-Gateway, Piper-Fallback und der FLUX.2-Klein-Hot-Swap sind
reproduzierbare Kerndienste; STT
bleibt optional. Web-, Home-Assistant-,
GitHub-, ARR-, Unraid- und Navidrome-Werkzeuge besitzen dagegen bereits getrennte Container unter
`platform/mcp/`. Open WebUI erreicht sie ausschließlich über das interne
`mike-ai-tools`-Netz; llama.cpp erhält keine MCP-Konfiguration und keine
`platform/mcp/`. Open WebUI erreicht sie über das interne `mike-ai-tools`-Netz;
Pi, Hermes und andere Clients verwenden die direkten WireGuard-Ports aus
`docs/VPN_SERVICE_PORTS.md`. llama.cpp erhält keine MCP-Konfiguration und keine
Infrastruktur-Secrets. Die Bildanalyse ist Bestandteil des multimodalen
Qwen-Modells.
+2
View File
@@ -48,6 +48,8 @@ services:
networks:
frontend:
ipv4_address: 172.30.10.254
tools:
ipv4_address: 172.30.40.254
tools-egress:
ipv4_address: 172.30.50.254
security_opt: ["no-new-privileges:true"]
+2
View File
@@ -33,6 +33,8 @@ SSH_KEY_ONLY=true
WIREGUARD_MODE=container
WIREGUARD_CONFIG_FILE=/etc/mike-ai/wireguard/fritz-athena.conf
WIREGUARD_ENABLE=false
# Stable Fritzbox/WireGuard address used by direct VPN clients such as Pi or Hermes.
VPN_SERVICE_IP=192.168.1.212
# The values below are only used by the legacy host-terminated mode. The
# default container mode takes address, peer and routes from the Fritzbox file.
AI_BIND_ADDRESS=10.77.0.2
+10 -12
View File
@@ -15,6 +15,7 @@ Heimnetz / VPN-Clients
|
<Fritz-VPN-IP>:8080 Open WebUI
<Fritz-VPN-IP>:8081 Profile Router API
<Fritz-VPN-IP>:8201-08 direkte MCP-Endpunkte
|
Docker-intern
+-- Profile Controller -- Docker Socket (feste Allowlist)
@@ -39,7 +40,7 @@ Heimnetz / VPN-Clients
| Komponente | Außen erreichbar | Aufgabe |
|---|---|---|
| WireGuard Gateway | VPN-Adresse, Port 8080/8081 | Tunnel und eng begrenzte TCP-Proxys |
| WireGuard Gateway | VPN-Adresse, feste Portmatrix | Tunnel und direkte TCP-Proxys für UI, API und Werkzeuge |
| Open WebUI | nur Docker-intern | Chat-Oberfläche |
| Profile Router | nur Docker-intern | OpenAI-API und Profilwahl |
| Profile Controller | nein | startet ausschließlich fest erlaubte Profile |
@@ -47,7 +48,7 @@ Heimnetz / VPN-Clients
| XTTS-v2 | nein | primäre deutsche/englische Text-to-Speech-Ausgabe auf RTX 3060 |
| TTS-Gateway | nein | serialisiert XTTS, segmentiert Sprachwechsel und fällt auf Piper zurück |
| Piper | nein | CPU-basierte Text-to-Speech-Rückfallebene |
| MCP-Tool-Stack | nein | voneinander getrennte Werkzeugbereiche |
| MCP-Tool-Stack | über feste VPN-Ports | voneinander getrennte Werkzeugbereiche für OpenWebUI, Pi und Hermes |
| SearXNG/TinySearch | nein | private Suche, Crawl4AI-Extraktion und lokales Reranking |
Nur der Profile Controller erhält den Docker-Socket. Der Router erhält weder
@@ -90,7 +91,7 @@ Ultra bleibt für maximalen Kontext bewusst text-only.
- Docker-Netze liegen ausschließlich unter `172.30.0.0/16`.
- Open WebUI und Router besitzen keine Docker-Host-Portfreigabe.
- Der Gateway lauscht in seinem eigenen Namespace auf der Fritz-VPN-IP und
leitet nur 8080/8081 zu den internen Diensten weiter.
leitet die dokumentierte Portmatrix zu UI, API, TTS und MCPs weiter.
- Quellrouting schickt `172.30.10.0/24` und `172.30.50.0/24` zum Gateway;
Regeln für `172.30.0.0/16` bewahren rein internen Docker-Verkehr.
- Die verschlüsselten äußeren Gateway-Pakete sind eng von diesen Quellregeln
@@ -132,12 +133,11 @@ werden eindeutig buchstabiert.
## Zentrale MCP-Werkzeugebene
Werkzeuge werden nicht in llama.cpp eingebaut. Sie laufen als eigene,
zentrale MCP-Container. Open WebUI greift intern darauf zu. Für externe Clients
wie Hermes wird später ein authentifizierter MCP-Gateway über WireGuard
vorgeschaltet; die unauthentifizierten internen Ports werden niemals direkt
veröffentlicht. So können alle Oberflächen dieselben geprüften Werkzeuge
verwenden, ohne Secrets zu duplizieren.
Werkzeuge werden nicht in llama.cpp eingebaut. Sie laufen als eigene, zentrale
MCP-Container. Open WebUI greift intern darauf zu; Pi, Hermes und andere
Clients verwenden ihre festen Ports direkt auf Athenas WireGuard-Adresse. Ein
zusätzliches MCP-Gateway ist nicht erforderlich. So können alle Oberflächen
dieselben Werkzeuge verwenden, ohne Secrets zu duplizieren.
Die Trenneinheit ist **ein Container pro Fachbereich und Vertrauensstufe** –
nicht ein Container pro einzelner Funktion und nicht ein gemeinsamer
@@ -153,7 +153,7 @@ Open WebUI ── internes Netz ───────────┬── web-m
└── unraid-mcp-read
Hermes Agent ─ WireGuard ─┐
weitere MCP-Clients ──────┴── mcp-gateway (später) ── dasselbe interne Netz
Pi / weitere MCP-Clients ─┴── feste VPN-Ports 8201-8208 ── MCP-Container
```
| Container | Werkzeugbereich | Standardrecht |
@@ -169,8 +169,6 @@ weitere MCP-Clients ──────┴── mcp-gateway (später) ── das
| `unraid-mcp-read` | System-, Container- und begrenzte Logdiagnose | nur lesen |
| `unraid-mcp-admin` | eng definierte Verwaltungsaktionen | bewusst aktivieren |
| `sandbox-mcp` | temporäre Code- und Dateiarbeit | isolierter Arbeitsraum |
| `mcp-gateway` | Auth, Routing, Limits und Werkzeugauswahl | keine Fach-Secrets |
Read- und Write-Instanzen dürfen dasselbe Image verwenden, laufen aber mit
unterschiedlichen Tokens, Netzwerkzugriffen und Werkzeug-Allowlisten. Der
Gateway besitzt keine HA-, ARR- oder Unraid-Secrets. Er authentifiziert Clients,
+4 -2
View File
@@ -116,8 +116,10 @@ laufen, sondern alle fachlichen Funktionen geprüft wurden.
## Phase F – Sicherheitsprüfung
- [ ] Port 8080/8081 an der physischen Hostadresse nicht erreichbar
- [ ] beide Ports über die Fritz-VPN-Adresse erreichbar
- [ ] alle Anwendungsports aus `VPN_SERVICE_PORTS.md` an der physischen
Hostadresse nicht erreichbar
- [ ] OpenWebUI, Router und alle gestarteten MCPs über die Fritz-VPN-Adresse
erreichbar
- [ ] gestopptes WireGuard-Gateway blockiert Container-Egress
- [ ] Hilfsports nur localhost
- [ ] Router nur aus erlaubtem Netz erreichbar
+5 -3
View File
@@ -180,9 +180,11 @@ Auf einer frischen Open-WebUI-Datenbank werden die internen MCP-Adressen über
`TOOL_SERVER_CONNECTIONS` vorbelegt. Bei einer übernommenen Datenbank müssen
die Einträge einmal unter **Admin-Einstellungen → Externe Werkzeuge** geprüft
oder importiert werden. Die Endpunkte stehen in `platform/mcp/README.md`.
Kein MCP-Port wird auf dem Host veröffentlicht. Externe Clients wie Hermes
benötigen später den authentifizierten WireGuard-Gateway und dürfen nicht
direkt auf das interne Werkzeugnetz zugreifen.
Kein MCP-Port wird auf der physischen Universitätsadresse veröffentlicht.
OpenWebUI nutzt intern weiterhin die Docker-Namen; Pi, Hermes und andere
Clients greifen direkt über die festen WireGuard-Adressen aus
[VPN_SERVICE_PORTS.md](VPN_SERVICE_PORTS.md) zu. Ein zusätzliches MCP-Gateway
oder ein weiterer Auth-Layer innerhalb des Heim-VPNs ist nicht vorgesehen.
Die vollständige Wiederherstellung einschließlich OpenWebUI, MCP-Secrets und
Navidrome/Last.fm ist unter
+5 -3
View File
@@ -129,7 +129,8 @@ Docker-Netze verwenden ausschließlich `172.30.0.0/16`. Wichtige Netze:
Open WebUI und Router haben keine normalen Host-Portfreigaben. Der
WireGuard-Gateway-Container beendet den Fritzbox-Clienttunnel und veröffentlicht
innerhalb des VPN nur Open WebUI auf Port 8080 und die Router-API auf Port 8081.
innerhalb des VPN OpenWebUI, Router, TTS und die festen MCP-Ports aus
`VPN_SERVICE_PORTS.md`.
Die KI- und Werkzeugcontainer erreichen Heimnetz und Internet über diesen
Gateway. Quellrouting sorgt dafür, dass sie bei Tunnelausfall nicht über das
Universitätsgateway ausweichen. Das gewünschte Verhalten ist fail-closed.
@@ -227,8 +228,9 @@ Fallback, internem Endpunkt, reproduzierbarer Version und Hörtest aus.
## 9. MCP-Werkzeuge
MCP-Werkzeuge gehören nicht in llama.cpp-Startparameter. Jeder Fachbereich
läuft in einem getrennten Container mit eigener Secret-Datei und minimalem
Netzzugriff. Kein MCP-Port wird am Host veröffentlicht.
läuft in einem getrennten Container mit eigener Secret-Datei. Auf der
Universitätsadresse wird kein MCP-Port veröffentlicht; über die
WireGuard-Adresse sind alle Fach-MCPs direkt erreichbar.
| Bereich | Aufgabe | Rechte |
|---|---|---|
+8 -6
View File
@@ -3,8 +3,9 @@
## Netzgrenze
- Open WebUI und Router veröffentlichen keinerlei Host-Ports.
- Ein dedizierter WireGuard-Container stellt auf seiner VPN-Adresse nur 8080
und 8081 bereit.
- Ein dedizierter WireGuard-Container stellt OpenWebUI, Router und die
Werkzeugdienste direkt auf seiner VPN-Adresse bereit. Die verbindliche
Portmatrix steht in [VPN_SERVICE_PORTS.md](VPN_SERVICE_PORTS.md).
- Die egressfähigen Docker-Netze verwenden eigene Routingtabellen.
- Heimnetz- und optionaler Internetverkehr laufen über WireGuard.
- Die Tabellen zeigen ausschließlich zum Gateway-Container und besitzen keine
@@ -22,13 +23,14 @@ physischen Schnittstelle erreichbar.
- Profile Controller: einzige Socket-Ausnahme; feste Profile und nur
List/Start/Stop, keine frei wählbaren Images, Befehle oder Mounts.
- Open WebUI: einziges persistentes Chat-Volume.
- MCP-Fachcontainer: intern, getrennte Secrets und keine Host-Ports.
- MCP-Fachcontainer: intern verbunden und über feste WireGuard-Ports direkt
für OpenWebUI, Pi, Hermes und andere Heim-VPN-Clients erreichbar.
- TinySearch/SearXNG: intern, Suchanfragen ohne Chatverlauf.
llama.cpp bekommt weder MCP-Konfiguration noch HA-, ARR- oder Unraid-Secrets.
Open WebUI kennt nur interne MCP-URLs; Authentisierung zu den Zielsystemen
findet im jeweiligen Fachcontainer statt. Externe MCP-Clients werden erst über
einen authentifizierten WireGuard-Gateway zugelassen.
Open WebUI kann weiterhin die internen MCP-Namen verwenden. Andere Clients
nutzen ohne zusätzliches Gateway die festen MCP-Ports der WireGuard-Adresse.
Authentisierung zu den Zielsystemen findet im jeweiligen Fachcontainer statt.
Ein Docker-Socket bleibt grundsätzlich privilegiert. Der Controller reduziert
die erreichbare Funktion stark, ersetzt aber keine zusätzliche Socket-Proxy-
+37
View File
@@ -0,0 +1,37 @@
# Dienste direkt über das Heim-VPN
Athena behandelt die von der Fritzbox zugewiesene WireGuard-Adresse als ihr
normales Anwendungsnetz. OpenWebUI, die Router-API und die nützlichen
Werkzeugdienste sind dort direkt erreichbar. Auf der physischen
Universitätsadresse werden diese Ports nicht veröffentlicht.
Aktuelle VPN-Adresse: `192.168.1.212`
| Port | Dienst | Adresse |
|---:|---|---|
| 22 | SSH zum Athena-Host | `ssh root@192.168.1.212` |
| 8080 | OpenWebUI | `http://192.168.1.212:8080` |
| 8081 | OpenAI-kompatible Router-API | `http://192.168.1.212:8081/v1` |
| 8085 | TTS-Gateway | `http://192.168.1.212:8085` |
| 8091 | Piper direkt | `http://192.168.1.212:8091` |
| 8092 | XTTS direkt | `http://192.168.1.212:8092` |
| 8201 | Athena Platform Context MCP | `http://192.168.1.212:8201/mcp` |
| 8202 | Athena Operator MCP | `http://192.168.1.212:8202/mcp` |
| 8203 | Web-MCP | `http://192.168.1.212:8203/mcp` |
| 8204 | GitHub-MCP | `http://192.168.1.212:8204/mcp` |
| 8205 | Home-Assistant-MCP | `http://192.168.1.212:8205/mcp` |
| 8206 | ARR-MCP | `http://192.168.1.212:8206/mcp` |
| 8207 | Navidrome-MCP | `http://192.168.1.212:8207/mcp` |
| 8208 | Unraid-SSH-MCP, falls aktiviert | `http://192.168.1.212:8208/mcp` |
| 8210 | SearXNG-Diagnoseoberfläche | `http://192.168.1.212:8210` |
| 8211 | TinySearch-MCP direkt | `http://192.168.1.212:8211/mcp` |
Pi, Hermes und andere MCP-Clients tragen diese URLs direkt ein. Ein
zusätzliches MCP-Gateway ist nicht erforderlich. Nicht gestartete optionale
Container führen am jeweiligen Port lediglich zu einer nicht erreichbaren
Verbindung; nach ihrem Start funktioniert derselbe Port automatisch.
Die Portweiterleitungen laufen ausschließlich im Netzwerk-Namespace des
WireGuard-Containers und binden explizit an dessen VPN-Adresse. Deshalb sind
sie nicht über `172.21.117.202` erreichbar. SSH bleibt davon unabhängig auch
auf der Universitätsadresse zulässig.
+7 -5
View File
@@ -14,8 +14,10 @@ Verschlüsselung. Eine LAN-zu-LAN-Konfiguration ist für diesen Host nicht nöti
Der Tunnel endet im Container `mike-ai-wireguard-gateway`. Nur dieser Container
erhält `NET_ADMIN` und `/dev/net/tun`. Open WebUI und Router veröffentlichen
keine Host-Ports; der Gateway stellt ausschließlich Port 8080 und 8081 auf der
von der Fritzbox zugeteilten VPN-Adresse bereit.
keine Ports auf der physischen Hostadresse. Der Gateway stellt OpenWebUI,
Router und die Werkzeugdienste auf der von der Fritzbox zugeteilten
VPN-Adresse bereit. Die vollständige Liste steht in
[VPN_SERVICE_PORTS.md](VPN_SERVICE_PORTS.md).
## Split der Verantwortlichkeiten
@@ -38,13 +40,13 @@ systemctl status mike-ai-container-vpn-guard
Der Healthcheck verlangt einen Handshake, der jünger als drei Minuten ist.
Open WebUI liegt unter `http://<VPN-IP>:8080`, der Router unter
`http://<VPN-IP>:8081`. An der physischen Standortadresse dürfen beide Ports
nicht antworten.
`http://<VPN-IP>:8081/v1`; die MCPs liegen auf 8201 bis 8208. An der
physischen Standortadresse dürfen diese Anwendungsports nicht antworten.
## Getestetes Verhalten am 22. August 2026
- Fritzbox-Handshake und Datenverkehr in beide Richtungen: erfolgreich
- Open WebUI und Router über die VPN-Adresse: HTTP 200
- Open WebUI, Router und direkte MCP-Endpunkte über die VPN-Adresse: erreichbar
- dieselben Ports über die physische Hostadresse: geschlossen
- VPN-Gateway gestoppt: ausgehender Open-WebUI-Test blockiert (fail-closed)
- Gateway erneut gestartet: automatischer aktueller Handshake
+37 -15
View File
@@ -46,7 +46,7 @@ ip -4 route replace default dev wg0
ip -6 route replace default dev wg0 2>/dev/null || true
cleanup() {
kill "${proxy_ui_pid:-}" "${proxy_router_pid:-}" "${proxy_ssh_pid:-}" 2>/dev/null || true
[ -z "${proxy_pids:-}" ] || kill $proxy_pids 2>/dev/null || true
wg-quick down "$RUNTIME" 2>/dev/null || true
}
trap cleanup EXIT INT TERM
@@ -58,20 +58,42 @@ iptables -A FORWARD -o wg0 -j ACCEPT
iptables -A FORWARD -i wg0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADE
# Nothing is published on the physical host. These listeners exist only in
# the WireGuard container namespace and forward VPN clients to internal names.
socat TCP-LISTEN:8080,bind=0.0.0.0,reuseaddr,fork TCP:open-webui:8080 &
proxy_ui_pid=$!
socat TCP-LISTEN:8081,bind=0.0.0.0,reuseaddr,fork TCP:router:8081 &
proxy_router_pid=$!
# Emergency SSH path for unattended operation. Bind explicitly to WireGuard's
# IPv4 address, never to a Docker-facing interface or the physical host. The
# target is the host-side gateway of the fixed frontend bridge. Host sshd still
# enforces its normal key-only authentication policy.
wg_ipv4=$(ip -4 -o address show dev wg0 | awk 'NR == 1 { split($4, address, "/"); print address[1] }')
[ -n "$wg_ipv4" ] || { echo "WireGuard IPv4 address is missing" >&2; exit 1; }
socat TCP-LISTEN:22,bind="$wg_ipv4",reuseaddr,fork TCP:172.30.10.1:22 &
proxy_ssh_pid=$!
wait "$proxy_ui_pid"
# The VPN is Athena's normal application network. Nothing below is published
# on the physical university interface: every listener is bound inside this
# namespace to the Fritzbox-assigned WireGuard address. Clients on the home
# VPN may use OpenWebUI, the router and every useful MCP directly.
proxy_pids=""
start_proxy() {
listen_port=$1
target=$2
socat "TCP-LISTEN:${listen_port},bind=${wg_ipv4},reuseaddr,fork" "TCP:${target}" &
proxy_pids="$proxy_pids $!"
}
start_proxy 22 172.30.10.1:22
start_proxy 8080 open-webui:8080
start_proxy 8081 router:8081
start_proxy 8085 tts-gateway:8085
start_proxy 8091 piper:8085
start_proxy 8092 xtts:80
# MCP endpoints. Optional services keep their listener even while stopped and
# begin working automatically as soon as their container is started.
start_proxy 8201 mcp-platform-context:8000
start_proxy 8202 mcp-athena-operator:8000
start_proxy 8203 mcp-web:8000
start_proxy 8204 mcp-github:8000
start_proxy 8205 mcp-homeassistant:8000
start_proxy 8206 mcp-arr:8000
start_proxy 8207 mcp-navidrome:3000
start_proxy 8208 mcp-unraid-ssh:8000
# Search backends are also directly available for diagnostics and alternative
# clients. Normal chat clients should prefer the MCP endpoint on 8203.
start_proxy 8210 searxng:8080
start_proxy 8211 tinysearch:8000
wait $(printf '%s\n' "$proxy_pids" | awk '{print $2}')
+3 -1
View File
@@ -104,7 +104,9 @@ oder ein anderes Werkzeug benötigt wird.
## Sicherheitsmodell
- Kein MCP-Port wird auf eine Host-Adresse veröffentlicht.
- Kein MCP-Port wird auf der physischen Universitätsadresse veröffentlicht.
Über Athenas WireGuard-Adresse sind die Fach-MCPs direkt auf den in
`docs/VPN_SERVICE_PORTS.md` dokumentierten Ports erreichbar.
- Nur Clients im privaten Docker-Netz `mike-ai-tools` erreichen die Endpunkte.
- Secrets bleiben in Dateien unter `/etc/mike-ai` und werden read-only
eingehängt. Sie gehören weder in Git noch in OpenWebUI-Tooldefinitionen.
+3 -3
View File
@@ -141,9 +141,9 @@ services:
MCP_TRANSPORT: http
MCP_HTTP_EXPOSE: "true"
MCP_HTTP_PORT: "3000"
# The endpoint is not published on the host. Host filtering still makes
# accidental access from any other Docker name fail closed.
MCP_HTTP_ALLOWED_HOSTS: "mike-ai-mcp-navidrome:3000,mike-ai-mcp-navidrome"
# OpenWebUI uses the Docker name; Pi/Hermes may reach the same endpoint
# directly through Athena's WireGuard address and VPN port 8207.
MCP_HTTP_ALLOWED_HOSTS: "mike-ai-mcp-navidrome:3000,mike-ai-mcp-navidrome,${VPN_SERVICE_IP:-192.168.1.212}:8207,${VPN_SERVICE_IP:-192.168.1.212}"
WEBUI_ENABLED: "false"
tmpfs:
- /tmp:rw,noexec,nosuid,nodev,size=64m