Add fail-closed WireGuard container gateway

This commit is contained in:
Mikei386
2026-08-22 20:48:49 +02:00
parent 2f3bdde8b0
commit 0e5876f1a9
17 changed files with 365 additions and 109 deletions
+20 -15
View File
@@ -3,18 +3,18 @@
## Grundsatz
Der Host läuft auf Debian 13. Das Betriebssystem darf im Universitätsnetz
administrierbar bleiben; die KI-Plattform wird ausschließlich an die
WireGuard-Adresse gebunden. KI-Container erreichen Heimnetz und Internet über
den Heim-WireGuard-Peer. Bei Tunnelausfall verhindert eine Blackhole-Route den
unbeabsichtigten Rückfall auf das Universitätsgateway.
administrierbar bleiben. Ein eigener Docker-Gateway beendet WireGuard und ist
der einzige von außen erreichbare Einstieg in die KI-Plattform. KI-Container
erreichen Heimnetz und Internet über die Fritzbox; Quellrouting zum Gateway
verhindert bei Tunnelausfall einen Rückfall auf das Universitätsgateway.
```text
Heimnetz / VPN-Clients
|
WireGuard
|
10.77.0.2:8080 Open WebUI
10.77.0.2:8081 Profile Router API
<Fritz-VPN-IP>:8080 Open WebUI
<Fritz-VPN-IP>:8081 Profile Router API
|
Docker-intern
+-- Profile Controller -- Docker Socket (feste Allowlist)
@@ -35,8 +35,9 @@ Heimnetz / VPN-Clients
| Komponente | Außen erreichbar | Aufgabe |
|---|---|---|
| Open WebUI | nur WireGuard, Port 8080 | Chat-Oberfläche |
| Profile Router | nur WireGuard, Port 8081 | OpenAI-API und Profilwahl |
| WireGuard Gateway | VPN-Adresse, Port 8080/8081 | Tunnel und eng begrenzte TCP-Proxys |
| Open WebUI | nur Docker-intern | Chat-Oberfläche |
| Profile Router | nur Docker-intern | OpenAI-API und Profilwahl |
| Profile Controller | nein | startet ausschließlich vier bekannte Profile |
| llama.cpp Profile | nein | Inferenz, Tool Calling, integrierte Vision |
| Piper | nein | lokale deutsche Text-to-Speech-Ausgabe |
@@ -79,15 +80,19 @@ auf der 3060. Ultra bleibt für maximalen Kontext bewusst text-only.
## Netzwerk
- Docker-Netze liegen ausschließlich unter `172.30.0.0/16`.
- Open WebUI und Router binden an `AI_BIND_ADDRESS`, die WireGuard-IP.
- Quellrouting schickt KI-Container in Tabelle 51820 über WireGuard.
- Eine Blackhole-Default-Route bleibt als Fail-Closed-Fallback bestehen.
- Firewallregeln gestatten aus dem VPN nur die beiden veröffentlichten Ports.
- 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.
- 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
ausgenommen und verlassen Athena über die normale Standortverbindung.
- Bleibt die Gateway-Adresse aus, existiert keine alternative Route für die
Anwendungscontainer (fail-closed).
- Der Host routet weder Universitätsverkehr ins Heimnetz noch Heimverkehr ins
Universitätsnetz.
- Das Heimnetz muss die Rückroute zur WireGuard-Adresse kennen. Soll auch der
Internetzugang der KI über zuhause laufen, braucht der Heim-Peer zusätzlich
IP-Forwarding und NAT ins Heim-WAN.
- Adressen, Heimrouten, Full-Tunnel und Keepalive stammen aus dem root-only
Fritzbox-Clientexport; der Debian-Host übernimmt dessen Default-Route nicht.
## Optionale Erweiterungen
+3 -1
View File
@@ -81,7 +81,9 @@ laufen, sondern alle fachlichen Funktionen geprüft wurden.
## Phase F – Sicherheitsprüfung
- [ ] Port 8080 von normalen Clients nicht erreichbar
- [ ] Port 8080/8081 an der physischen Hostadresse nicht erreichbar
- [ ] beide Ports über die Fritz-VPN-Adresse erreichbar
- [ ] gestopptes WireGuard-Gateway blockiert Container-Egress
- [ ] Hilfsports nur localhost
- [ ] Router nur aus erlaubtem Netz erreichbar
- [ ] Dienste laufen mit minimalen Rechten
+17 -19
View File
@@ -11,8 +11,8 @@ startet den Stack.
1. Die Universität muss den ausgehenden WireGuard-Tunnel erlauben.
2. Heimnetz, Universitätsnetz und Docker-Netz dürfen sich nicht überschneiden.
3. Der WireGuard-Heim-Peer braucht eine feste öffentliche Adresse oder DNS.
4. Für KI-Internetzugang über zuhause: Forwarding und NAT am Heim-Peer.
3. In der Fritzbox eine Konfiguration für **einen einzelnen Client** exportieren.
4. Der Fritzbox-Zugang muss Heimnetz und gewünschten Internetverkehr erlauben.
5. Das private Repository muss auf dem neuen Host lesbar sein.
## Debian installieren
@@ -32,10 +32,16 @@ chmod 600 config/install.env
editor config/install.env
```
Mindestens `ADMIN_USER`, `AI_BIND_ADDRESS`, `WG_ADDRESS`,
`WG_PEER_PUBLIC_KEY`, `WG_ENDPOINT` und `WG_HOME_SUBNET` anpassen. Private
WireGuard-, Router- und WebUI-Schlüssel werden lokal erzeugt und nur unter
`/etc/mike-ai` gespeichert.
Mindestens `ADMIN_USER`, Netzwerkschnittstellen, GPU-Zuordnung und Modellwerte
prüfen. Die Fritzbox-Datei vor dem Start root-only ablegen:
```bash
sudo install -d -m 700 /etc/mike-ai/wireguard
sudo install -m 600 fritz-athena.conf /etc/mike-ai/wireguard/fritz-athena.conf
```
Router- und WebUI-Schlüssel werden lokal erzeugt und nur unter `/etc/mike-ai`
gespeichert. Der Installer gibt keine privaten WireGuard-Werte aus.
## Installation starten
@@ -50,16 +56,7 @@ Beim ersten Stackstart lädt der interne Piper-Container die konfigurierte
deutsche Stimme in sein persistentes Volume. Dadurch kann seine erste
Bereitschaft je nach Internetverbindung etwas länger dauern.
Der Installer zeigt nur den öffentlichen WireGuard-Schlüssel. Diesen am
Heim-Peer eintragen:
```ini
[Peer]
PublicKey = <AUSGABE-DES-INSTALLERS>
AllowedIPs = 10.77.0.2/32
```
Erst wenn der Tunnel steht, kann der Bootstrap fortfahren. API-Schlüssel
Der Compose-Start wartet auf einen aktuellen WireGuard-Handshake. API-Schlüssel
werden nicht ausgegeben. Sie liegen root-only unter `/etc/mike-ai`.
## Ergebnis und Abnahme
@@ -69,7 +66,8 @@ werden nicht ausgegeben. Sie liegen root-only unter `/etc/mike-ai`.
- llama.cpp-WebUI: absichtlich deaktiviert und nicht veröffentlicht
```bash
sudo systemctl status wg-quick@wg0 mike-ai-network-guard
sudo systemctl status mike-ai-container-vpn-guard
sudo docker inspect -f '{{.State.Health.Status}}' mike-ai-wireguard-gateway
sudo docker compose --env-file /etc/mike-ai/stack.env \
-f /opt/mike-ai/stack/compose.yaml ps
curl http://<WIREGUARD-IP>:8081/health
@@ -91,8 +89,8 @@ curl -fsS http://127.0.0.1:8081/v1/audio/speech \
-o /tmp/athena-piper-test.mp3
```
Zusätzlich prüfen: Uni-LAN sieht keine KI-Ports; Heimnetz erreicht beide;
gestopptes WireGuard lässt KI-Container nicht ins Internet; jeder Profilwechsel
Zusätzlich prüfen: Standort-LAN sieht keine KI-Ports; Heimnetz erreicht beide;
gestopptes VPN-Gateway lässt KI-Container nicht ins Internet; jeder Profilwechsel
startet exakt einen llama-Container; Text, Tool Call, Bild und Sprachausgabe funktionieren.
Nach dem ersten Anlegen des OpenWebUI-Administrators werden Filter, Quick
+2 -2
View File
@@ -103,7 +103,7 @@ erfolgreich. Medium ist das Start- und Standardprofil.
Clients verbinden sich mit:
```text
http://HOST:8081/v1
http://<FRITZ-VPN-IP>:8081/v1
```
Als API-Key verwenden sie den Inhalt von `/etc/mike-ai/router-api-key` über
@@ -120,7 +120,7 @@ Vision, Bildgenerierung, STT und TTS umgehen.
- `GET /ready`: Router und Textmodell sind einsatzbereit
- `GET /status`: authentifizierter Detailstatus, Profil, Upstream, aktive Jobs
- `GET /v1/models`: virtuelle Modelle
- llama.cpp-Metriken: Port 8080, nur im administrativen Netz freigeben
- llama.cpp-Metriken: ausschließlich Docker-intern abfragen
- systemd-Journal: nur Metadaten und Fehler prüfen; keine Promptinhalte sammeln
## Upgrade-Regel
+6 -2
View File
@@ -114,13 +114,17 @@ Zielmatrix:
| Port | Zugriff |
|---:|---|
| 22 | nur Administration |
| 8080 | localhost |
| 8081 | vertrauenswürdiges LAN/VPN |
| 8080 | ausschließlich WireGuard-Gateway (Open WebUI) |
| 8081 | ausschließlich WireGuard-Gateway (Router) |
| 8084 | localhost |
| 8085 | localhost |
| 8000 | localhost |
| 5240 | optional nur Administration |
Open WebUI und Router selbst besitzen keine Host-Portfreigaben. Zusätzlich zum
Repository muss die verschlüsselt gesicherte Fritzbox-Clientdatei als
`/etc/mike-ai/wireguard/fritz-athena.conf` (0600) wiederhergestellt werden.
## 6. Speicherlayout – offen
Vor dem Neuaufbau festlegen:
+9 -4
View File
@@ -29,11 +29,12 @@ müssen aktiviert sein.
Vor dem Transport prüfen:
```bash
systemctl is-enabled ssh docker wg-quick@wg0
systemctl is-active ssh docker wg-quick@wg0
systemctl is-enabled ssh docker mike-ai-container-vpn-guard
systemctl is-active ssh docker mike-ai-container-vpn-guard
ethtool enp7s0 | grep Wake-on
systemctl show -p RuntimeWatchdogUSec
wg show
docker inspect -f '{{.State.Health.Status}}' mike-ai-wireguard-gateway
docker exec mike-ai-wireguard-gateway wg show wg0 latest-handshakes
```
Erwartet werden `enabled`, `active`, `Wake-on: g`, ein Watchdog-Wert von einer
@@ -43,6 +44,9 @@ Minute sowie ein aktueller WireGuard-Handshake.
- Der physische Anschluss bezieht seine Adresse per DHCP; im Standortnetz muss
dafür eine Freigabe bzw. Registrierung existieren.
- SSH bleibt auf der Standort-Schnittstelle erreichbar, akzeptiert aber nur
Public-Key-Anmeldungen. Vor dem Transport muss der Schlüsselzugriff getestet
werden.
- Der WireGuard-Tunnel muss **vor dem Transport** erfolgreich aufgebaut und von
zuhause erreichbar getestet sein.
- Für WireGuard braucht Athena keine eingehende Portfreigabe am Standort: Der
@@ -59,7 +63,8 @@ Minute sowie ein aktueller WireGuard-Handshake.
2. Rechner sauber herunterfahren und per Wake-on-LAN einschalten.
3. Netzspannung bei laufendem Rechner trennen, 30 Sekunden warten, wieder
einschalten: Athena bootet automatisch vollständig hoch.
4. WireGuard aus- und wieder einschalten und Fail-closed-Routing prüfen.
4. `mike-ai-wireguard-gateway` stoppen: Container-Egress muss scheitern;
Gateway wieder starten und aktuellen Handshake prüfen.
5. Von zuhause aus ausschließlich über die spätere VPN-Adresse zugreifen.
Ohne erfolgreich getesteten WireGuard-Tunnel und die UEFI-Stromoptionen gilt der
+9 -8
View File
@@ -2,17 +2,18 @@
## Netzgrenze
- KI-Ports binden ausschließlich an die WireGuard-IP.
- Docker-Netze `172.30.0.0/16` verwenden eine eigene Routingtabelle.
- Open WebUI und Router veröffentlichen keinerlei Host-Ports.
- Ein dedizierter WireGuard-Container stellt auf seiner VPN-Adresse nur 8080
und 8081 bereit.
- Die egressfähigen Docker-Netze verwenden eigene Routingtabellen.
- Heimnetz- und optionaler Internetverkehr laufen über WireGuard.
- Eine Blackhole-Default-Route verhindert Fail-open bei Tunnelverlust.
- `DOCKER-USER` erlaubt nur etablierte Verbindungen, KI→WireGuard und
WireGuard→Open-WebUI/Router.
- Die Tabellen zeigen ausschließlich zum Gateway-Container und besitzen keine
Route über das Standortgateway. Das ist der Fail-Closed-Mechanismus.
- Der Host ist kein Router zwischen Universitäts- und Heimnetz.
Docker-publizierte Ports können gewöhnliche Host-Firewallregeln umgehen.
Darum setzt der Installer seine Regeln ausdrücklich in `DOCKER-USER` und
verlässt sich nicht allein auf UFW.
Da keine KI-Ports publiziert werden, kann Docker die Host-Firewall an dieser
Stelle nicht umgehen. SSH bleibt davon getrennt und schlüsselbasiert auf der
physischen Schnittstelle erreichbar.
## Containergrenzen
+40 -42
View File
@@ -1,55 +1,53 @@
# WireGuard-Heimseite
# WireGuard über die Fritzbox
Der Installer kann nur den KI-Host konfigurieren. Einmalig muss der vorhandene
WireGuard-Router im Heimnetz den neuen Peer kennen. Ohne diesen externen Schritt
kann kein automatisches Skript auf dem Uni-Host den Tunnel fertigstellen.
## Gewählter Betriebsmodus
## Peer ergänzen
Athena verwendet die Fritzbox-Konfiguration für **einen einzelnen
WireGuard-Client**. Die unveränderte Exportdatei wird als root-only Secret nach
Beispiel mit KI-WireGuard-Adresse `10.77.0.2/32`:
```ini
[Peer]
PublicKey = <PUBLIC-KEY-DES-KI-HOSTS>
AllowedIPs = 10.77.0.2/32
```text
/etc/mike-ai/wireguard/fritz-athena.conf
```
Das Heimgerät muss das LAN `192.168.1.0/24` zum Tunnel routen können. Geräte im
Heimnetz benötigen entweder eine Route für `10.77.0.2/32` über den
WireGuard-Router oder der Router maskiert den VPN-Verkehr passend.
kopiert (`chmod 600`). Sie gehört weder ins Git-Repository noch in Backups ohne
Verschlüsselung. Eine LAN-zu-LAN-Konfiguration ist für diesen Host nicht nötig.
## KI-Internetzugang über zuhause
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.
Wenn `WG_ROUTE_AI_INTERNET=true` gesetzt ist, muss der Heim-Peer IPv4-Forwarding
und NAT ins WAN erlauben. Das wird auf dem Heimrouter eingerichtet, nicht auf
dem Universitätsnetz. Beispielprinzip für nftables:
## Split der Verantwortlichkeiten
```nft
table inet mike_ai {
chain forward {
type filter hook forward priority 0; policy accept;
iifname "wg0" ip saddr 10.77.0.2 accept
oifname "wg0" ip daddr 10.77.0.2 ct state established,related accept
}
}
- Debian, Paketverwaltung und SSH benutzen die normale Standortverbindung.
- Die Docker-Netze `frontend` und `tools-egress` werden per Quellrouting zum
WireGuard-Gateway geschickt.
- Interner Docker-Verkehr bleibt lokal und durchquert den Tunnel nicht.
- Der Fritzbox-Export darf `0.0.0.0/0` und `::/0` enthalten. Das ändert **nicht**
die Default-Route des Debian-Hosts, sondern nur die des Gateway-Namespace.
- Fällt WireGuard aus, bleibt die Quellroute auf den dann unerreichbaren
Gateway zeigen: Anwendungscontainer fallen geschlossen aus.
table ip mike_ai_nat {
chain postrouting {
type nat hook postrouting priority 100; policy accept;
ip saddr 10.77.0.2 oifname "<HEIM-WAN-INTERFACE>" masquerade
}
}
## Kontrolle ohne Geheimnisse auszugeben
```bash
docker inspect -f '{{.State.Health.Status}}' mike-ai-wireguard-gateway
docker exec mike-ai-wireguard-gateway wg show wg0 latest-handshakes
systemctl status mike-ai-container-vpn-guard
```
Die tatsächlichen Interface-Namen und die bestehende Firewall des Heimrouters
gehen vor. Regeln nicht blind neben eine bereits verwaltete Firewall kopieren.
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.
## Sicherheitsprüfung
## Getestetes Verhalten am 22. August 2026
1. Vom Heimnetz `10.77.0.2` erreichen.
2. Open WebUI auf `10.77.0.2:8080` erreichen.
3. Aus dem Universitäts-LAN Port 8080/8081 nicht erreichen.
4. `wg0` am KI-Host stoppen: KI-Container dürfen nun weder Heimnetz noch
Internet erreichen.
5. Der Debian-Host selbst darf weiterhin nur die ausdrücklich gewünschte
Administration über das Uni-LAN anbieten.
- Fritzbox-Handshake und Datenverkehr in beide Richtungen: erfolgreich
- Open WebUI und Router über die VPN-Adresse: HTTP 200
- dieselben Ports über die physische Hostadresse: geschlossen
- VPN-Gateway gestoppt: ausgehender Open-WebUI-Test blockiert (fail-closed)
- Gateway erneut gestartet: automatischer aktueller Handshake
Die Exportdatei muss Bestandteil des verschlüsselten Disaster-Recovery-Satzes
sein. Ohne sie kann ein neuer Host den Heimtunnel nicht rekonstruieren.