Add fail-closed WireGuard container gateway
This commit is contained in:
+20
-15
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
@@ -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
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user