docs: Update all documentation with latest fixes - Traefik routing, JWT-cookie auth, install.sh improvements

This commit is contained in:
XPS\Micro 2026-03-19 10:31:49 +01:00
parent 436b1c0b0e
commit 02280cf0b4
3 changed files with 381 additions and 0 deletions

View File

@ -0,0 +1,250 @@
# Kritische Fixes - März 2026
Diese Datei dokumentiert wichtige Bugfixes und Verbesserungen, die seit März 2026 implementiert wurden.
## 1. Traefik Network Routing Fix ✅
**Problem:** User-Container wurden nicht im `web`-Netzwerk registriert, obwohl Labels vorhanden waren.
**Ursache:** Docker SDK Parameter `network=` funktioniert nicht korrekt bei `containers.run()`.
**Lösung:** Explizite Netzwerk-Verbindung nach Container-Start mit `network.connect()`.
```python
# Vorher (funktioniert nicht):
container = client.containers.run(..., network=Config.TRAEFIK_NETWORK)
# Nachher (funktioniert):
container = client.containers.run(...) # Ohne network-Parameter
network = client.networks.get(Config.TRAEFIK_NETWORK)
network.connect(container)
```
**Commits:**
- `65a2a6e`: Connect containers to Traefik network using network.connect()
**Betroffen:** `container_manager.py` - Beide `spawn_container()` und `spawn_multi_container()`
---
## 2. Traefik Router Service-Labels Fix ✅
**Problem:** Traefik erkannte neue Container-Routes nicht, obwohl Labels korrekt waren.
**Ursache:** Router-Labels fehlte die Referenz zum Service. Traefik wusste nicht, zu welchem Service die Route führen soll.
**Lösung:** Router-Labels mit `.service` Claim hinzufügen, die auf den Service zeigen.
```python
# Vorher (unvollständig):
'traefik.http.routers.user1-template-dict.rule': '...',
'traefik.http.services.user1-template-dict.loadbalancer.server.port': '8080'
# Nachher (komplett):
'traefik.http.routers.user1-template-dict.rule': '...',
'traefik.http.routers.user1-template-dict.service': 'user1-template-dict', # ← Wichtig!
'traefik.http.services.user1-template-dict.loadbalancer.server.port': '8080'
```
**Commits:**
- `45bd329`: Add missing router.service labels
**Betroffen:** `container_manager.py` - Labels-Konfiguration
---
## 3. Cookie-basierte JWT-Authentication ✅
**Problem:** User-Container waren öffentlich zugänglich - jeder konnte URL nutzen ohne Login.
**Lösung:** JWT-Token als HttpOnly Cookie, validiert in jedem Container.
**Implementierung:**
### Backend (api.py)
- JWT-Token wird als HttpOnly Cookie nach Login gespeichert
- Cookie wird automatisch bei jedem Request mitgesendet
- Logout löscht den Cookie
```python
def create_auth_response(access_token, user_data, expires_in):
response = make_response(jsonify(response_data))
response.set_cookie(
'spawner_token',
access_token,
max_age=expires_in,
httponly=True, # JavaScript-zugriff blockiert
secure=True, # Nur über HTTPS
samesite='Lax' # CSRF-Schutz
)
return response
```
### Container (z.B. Dictionary-Template)
- `before_request` Hook validiert JWT im Cookie
- Ohne gültigen Token: `403 Forbidden`
- JWT_SECRET wird vom Spawner als Environment-Variable übergeben
```python
@app.before_request
def validate_jwt_token():
# GET / und /health sind öffentlich
if request.path == '/' or request.path == '/health':
return
# Alle API-Calls brauchen gültigen Token
token = request.cookies.get('spawner_token')
if not token:
return jsonify({'error': 'Authentifizierung erforderlich'}), 401
try:
payload = jwt.decode(token, JWT_SECRET, algorithms=['HS256'])
g.user_id = payload.get('sub')
except jwt.InvalidTokenError:
return jsonify({'error': 'Token ungültig'}), 401
```
**Commits:**
- `436b1c0`: Add cookie-based JWT authentication for user containers
**Betroffen:**
- `api.py` - JWT-Cookie setzen/löschen
- `container_manager.py` - JWT_SECRET als Environment-Variable
- `user-template-dictionary/app.py` - JWT-Validierung
- `user-template-dictionary/requirements.txt` - PyJWT hinzugefügt
---
## 4. install.sh Improvements ✅
### 4a. Git-Pull Auto-Fix für Synology
**Problem:** Auf Synology schlugen `git pull` Befehle fehl wegen Dateiberechtigungen.
**Lösung:**
```bash
git config core.filemode false # Ignoriere Berechtigungsbits
git reset --hard origin/main # Force-Sync mit Remote
```
**Commits:**
- `7111d7a`: Auto-fix git pull with core.filemode
### 4b. Update-and-Re-Exec Mechanism
**Problem:** Wenn `install.sh` selbst aktualisiert wird, lädt bash die alte Version weiter.
**Lösung:** Nach `git pull` Checksumme vergleichen und Script neu starten mit `exec bash`.
```bash
BEFORE_HASH=$(md5sum install.sh)
# ... git pull ...
AFTER_HASH=$(md5sum install.sh)
if [ "$BEFORE_HASH" != "$AFTER_HASH" ]; then
export ALREADY_REEXECED="true"
exec bash install.sh
fi
```
**Commits:**
- `bb25750`: Add update-and-re-exec mechanism
### 4c. Alte Container Auto-Cleanup
**Problem:** Nach Code-Updates blieben alte Container und verursachten Traefik-Konflikte.
**Lösung:** `install.sh` löscht alle alten User-Container vor Restart.
```bash
# install.sh Sektion 8:
docker rm -f $(docker ps -a | grep "user-" | awk '{print $1}')
```
**Commits:**
- `7beb1d0`: Add detailed output for old container cleanup
**Betroffen:** `install.sh`
---
## 5. Dictionary Template Routing Fix ✅
**Problem:** `/api/words` Requests return 404 weil Traefik den Pfad-Prefix nicht entfernt.
**Lösung:** API-Base-Pfad aus `window.location.pathname` berechnen.
```javascript
// Vorher (falsch):
const response = await fetch('/api/words')
// Nachher (richtig):
const apiBase = window.location.pathname.replace(/\/$/, ''); // z.B. "/e220dd278a12-template-dictionary"
const response = await fetch(`${apiBase}/api/words`)
```
**Commits:**
- `20a4d60`: Fix API paths in Dictionary template for Traefik routing
**Betroffen:** `user-template-dictionary/templates/index.html`
---
## Zusammengefasst
| Fix | Status | Commits | Wichtigkeit |
|-----|--------|---------|-------------|
| Traefik Network (network.connect) | ✅ | 65a2a6e | 🔴 KRITISCH |
| Traefik Router Service-Labels | ✅ | 45bd329 | 🔴 KRITISCH |
| JWT-Cookie-Auth | ✅ | 436b1c0 | 🔴 KRITISCH (Security) |
| install.sh Git-Fix | ✅ | 7111d7a | 🟡 Wichtig |
| install.sh Re-Exec | ✅ | bb25750 | 🟡 Wichtig |
| install.sh Container-Cleanup | ✅ | 7beb1d0 | 🟡 Wichtig |
| Dictionary API-Paths | ✅ | 20a4d60 | 🟡 Wichtig |
---
## Deployment-Schritte
Nach diesen Fixes sollte auf der Synology folgende Procedure ausgeführt werden:
```bash
bash install.sh
```
Das führt automatisch aus:
1. `git pull` mit Auto-Fix für Berechtigungen
2. Falls install.sh sich geändert hat: Neu starten
3. Alte User-Container löschen (Traefik-Konflikt-Prävention)
4. Alle Templates aus .env bauen
5. Docker-Compose mit neuen Containern starten
---
## Testen nach Deployment
1. **User-Authentication testen:**
```bash
# Mit Browser: https://spawner.wieland.org/
# Login mit Magic Link
```
2. **Container-Routing testen:**
```bash
# Container sollte im web-Netzwerk sein
docker network inspect web | grep user-
```
3. **JWT-Protection testen:**
```bash
# Versuche direkten Container-Zugriff OHNE Login
curl https://spawner.wieland.org/e220dd278a12-template-dictionary/api/words
# → Sollte 401 Unauthorized zurückgeben
```
4. **JWT-Cookie testen:**
```bash
# Nach erfolgreichem Login:
# Browser-DevTools → Application → Cookies
# → spawner_token sollte vorhanden und HttpOnly markiert sein
```

View File

@ -1082,6 +1082,65 @@ df -h /volume1/docker/
---
## Neue Features (März 2026)
### 1. Git-Pull Auto-Fix für Synology
**Problem:** `git pull` schlägt fehl wegen Dateiberechtigungen.
**Lösung:**
```bash
git config core.filemode false # Ignoriere Berechtigungsbits
git reset --hard origin/main # Force-Sync mit Remote
```
**Automatisch aktiviert** wenn `git pull` fehlschlägt.
### 2. Update-and-Re-Exec Mechanism
**Problem:** Wenn `install.sh` selbst aktualisiert wird, lädt bash die alte Version weiter.
**Lösung:**
```bash
# Vor git pull: Checksumme von install.sh speichern
BEFORE_HASH=$(md5sum install.sh)
# Nach git pull: Checksumme vergleichen
AFTER_HASH=$(md5sum install.sh)
# Wenn geändert: Script mit exec neu starten
if [ "$BEFORE_HASH" != "$AFTER_HASH" ]; then
export ALREADY_REEXECED="true"
exec bash install.sh # Neu starten mit neuem Code
fi
```
### 3. Alte User-Container Cleanup
**Problem:** Nach Code-Updates bleiben alte Container und verursachen Traefik-Konflikte.
**Lösung:** Automatisches Löschen aller alten User-Container vor Docker-Compose Restart.
```bash
# Phase 8 in install.sh
docker ps -a | grep "user-" | awk '{print $NF}' # Zeige Container-Namen
docker rm -f $(docker ps -a | grep "user-" | awk '{print $1}') # Lösche alle
```
**Ausgabe:**
```
Räume alte User-Container auf...
Gefunden: 3 alte User-Container:
• user-e220dd278a12-template-dictionary-1
• user-abc123-template-01-2
• user-xyz789-template-next-3
Lösche Container...
✓ Alle alten Container gelöscht
```
---
## Häufige Fragen
**F: Kann ich install.sh mehrmals hintereinander ausführen?**

View File

@ -410,6 +410,78 @@ Falls die Anwendung wächst:
---
## 🔒 Sicherheit & Authentifizierung
### JWT-Cookie Validierung
Das Dictionary-Template ist **obligatorisch geschützt** mit JWT-Token-Validierung:
1. **HttpOnly Cookie `spawner_token`** wird vom Spawner gesetzt
2. **Vor jedem API-Request** wird der Token validiert
3. **Ohne gültigen Token: 403 Forbidden**
### How It Works
```
User Login
Spawner setzt HttpOnly Cookie: spawner_token=<JWT>
Browser sendet Cookie automatisch bei jedem Request
Dictionary-Template validiert JWT in: app.before_request()
Gültig? → Erlauben API-Zugriff
Ungültig? → 403 Forbidden (Authentifizierung erforderlich)
```
### Implementation Details
**Token-Validierung in `app.py`:**
```python
@app.before_request
def validate_jwt_token():
# Öffentliche Endpoints (GET / und /health)
if request.path == '/' or request.path == '/health':
return
# Alle API-Calls brauchen gültigen JWT
token = request.cookies.get('spawner_token')
if not token:
return jsonify({'error': 'Authentifizierung erforderlich'}), 401
# Dekodiere und validiere JWT
payload = jwt.decode(token, JWT_SECRET, algorithms=['HS256'])
g.user_id = payload.get('sub')
```
### Sicherheits-Features
- ✅ **HttpOnly Cookies** - JavaScript kann Token nicht auslesen
- ✅ **Secure Flag** - Nur über HTTPS übertragen
- ✅ **SameSite=Lax** - CSRF-Schutz
- ✅ **Token Expiration** - Standard: 1 Stunde (konfigurierbar)
- ✅ **JWT_SECRET** - Wird vom Spawner übergeben
- ✅ **Logout** - Cookie wird beim Logout gelöscht
### Testing der Sicherheit
```bash
# 1. Versuche direkten Zugriff OHNE Login
curl https://spawner.wieland.org/e220dd278a12-template-dictionary/api/words
# → Sollte 401 Unauthorized zurückgeben
# 2. Nach erfolgreichem Login
curl -b "spawner_token=<JWT>" https://spawner.wieland.org/e220dd278a12-template-dictionary/api/words
# → Sollte Wörter-Liste zurückgeben
# 3. Überprüfe Cookie im Browser
# Browser DevTools → Application → Cookies
# → spawner_token sollte HttpOnly markiert sein
```
---
## Troubleshooting
### Problem: "Datenbankfehler beim Abrufen"