rd13_tile_server/docs/requirements/REQUIREMENTS.md

242 lines
8.3 KiB
Markdown
Raw Normal View History

# Anforderungen rd13_tile_server
Letzte Aktualisierung: 2026-06-22
---
## Architektur-Entscheidung
**Stack:** Martin (Vektor-API) + tileserver-gl (Raster-Renderer)
Rationale: [siehe ADR-001](../adr/001-martin-tileserver-gl-stack.md)
- Martin: Vektor-Tiles (PBF) API-Foundation für zukünftige MapLibre GL Integration
- tileserver-gl: PNG-Rendering Kartographer + externe Clients (Home Assistant, Nextcloud)
- Tile-Updates: manuell 24×/Jahr (nicht live)
- Styles: Anpassbar über JSON-Style-Definitionen in `config/styles/`
**⛔ Hardware-Einschränkung (dauerhaft):**
Kein Planetiler-Self-Build auf dieser Hardware. Kein Pre-Built für Europa/DE z16 verfügbar.
→ Begründung: [ADR-002](../adr/002-no-highzoom-no-satellite-build.md)
---
## REQ-001 Tile Server öffentlich erreichbar
**Status:** ✅ umgesetzt
Der Tile Server muss über HTTPS unter einer öffentlichen Domain erreichbar sein,
damit externe Dienste (MediaWiki, Home Assistant, Nextcloud) Karten laden können.
**Akzeptanzkriterien:**
2026-06-10 08:30:08 +00:00
- Erreichbar unter `https://tiles.rd13server.de`
- SSL-Terminierung via NPM
- Health-Endpunkt antwortet mit HTTP 200
---
## REQ-002 Rate Limiting für den öffentlichen Tile-Endpunkt
**Status:** 🔴 offen Implementierung erforderlich vor Live-Gang
2026-06-10 08:30:08 +00:00
Da der Server unter `https://tiles.rd13server.de` öffentlich erreichbar ist, muss
er gegen unautorisierten Massenkonsum und DoS-Angriffe geschützt werden.
### Anforderungen
| ID | Anforderung | Priorität |
|---|---|---|
| REQ-002.1 | Rate Limiting: max. 10 Tile-Requests/Sekunde pro IP, Burst 60 | hoch |
| REQ-002.2 | Verbindungslimit: max. 20 gleichzeitige Verbindungen pro IP | hoch |
| REQ-002.3 | WebUI (`/`) nur aus LAN (192.168.178.0/24) erreichbar | mittel |
| REQ-002.4 | `/health` und `/catalog` nur aus LAN erreichbar | mittel |
| REQ-002.5 | HTTP-Antwort 429 bei Überschreitung mit Retry-After-Header | mittel |
| REQ-002.6 | Caching-Header für Tiles (max-age 24h) zur Lastreduzierung | mittel |
### Implementierungsplan
**Schicht 1 NPM Advanced Config (primärer Schutz)**
Rate Limiting passiert im Nginx Proxy Manager auf Proxy-Host-Ebene.
NPM erlaubt freie nginx-Direktiven im Feld „Advanced".
```nginx
# -- Rate Limiting --
# Speicher: 10 MB reicht fuer ~160.000 IP-Adressen
limit_req_zone $binary_remote_addr zone=tiles:10m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=tile_conn:10m;
# Burst von 60 erlaubt schnellen Kartenaufbau beim ersten Laden
limit_req zone=tiles burst=60 nodelay;
limit_conn tile_conn 20;
# 429 statt 503 zurueckgeben
limit_req_status 429;
limit_conn_status 429;
# -- Caching-Header fuer Tile-Endpunkte --
add_header Cache-Control "public, max-age=86400, stale-while-revalidate=3600" always;
add_header Vary "Accept-Encoding" always;
# -- Sicherheits-Header --
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
```
**Schicht 2 Interne Endpunkte abschirmen (im selben NPM Advanced Block)**
WebUI, Health und Catalog sollen von außen nicht erreichbar sein.
Da NPM keinen bedingten Block pro Location direkt unterstuetzt, wird ein
zweiter Proxy-Host fuer interne Endpunkte empfohlen:
```
2026-06-10 08:30:08 +00:00
Oeffentlicher Proxy-Host: tiles.rd13server.de -> http://HOST:9982
→ nur Tile-Pfade freigeben, alle anderen 403
→ Rate Limiting aktiv
Interner Proxy-Host (kein SSL nötig):
tiles-internal.lan -> http://192.168.178.5:9982
→ kein Rate Limiting
→ nur aus LAN erreichbar (Firewall / Access List im NPM)
```
Alternativ: Einzelner Proxy-Host mit NPM Access List „LAN only" fuer `/` und `/catalog`.
**Schicht 3 fail2ban (optionaler zweiter Layer)**
```ini
# /etc/fail2ban/filter.d/nginx-tile-ratelimit.conf
[Definition]
failregex = limiting requests, excess.* by zone "tiles".* client: <HOST>
ignoreregex =
# /etc/fail2ban/jail.local
[nginx-tile-ratelimit]
enabled = true
port = http,https
filter = nginx-tile-ratelimit
logpath = /var/log/nginx/access.log
maxretry = 10
findtime = 60
bantime = 3600
```
### Reihenfolge der Umsetzung
2026-06-10 08:30:08 +00:00
1. NPM Proxy-Host fuer `tiles.rd13server.de` anlegen (falls noch nicht vorhanden)
2. Rate-Limiting-Block in NPM Advanced Config eintragen
3. NPM Access List „LAN" anlegen und auf `/` + `/catalog` anwenden
2026-06-10 08:30:08 +00:00
4. Test: `ab -n 200 -c 10 https://tiles.rd13server.de/osm/10/0/0` → 429 nach Burst
5. fail2ban optional installieren und konfigurieren
---
## REQ-003 Caching (nicht-funktional)
**Status:** 🔴 offen
Tiles sollen gecacht werden, um wiederholte Requests zu vermeiden und
die Last auf den Server zu reduzieren.
**Akzeptanzkriterien:**
- Cache-Control-Header auf allen Tile-Responses: `public, max-age=86400`
- NPM leitet Header durch (keine Überschreibung)
---
## REQ-004 MediaWiki Integration
**Status:** ✅ umgesetzt (LocalSettings konfiguriert)
2026-06-10 08:30:08 +00:00
Kartographer nutzt `https://tiles.rd13server.de` als Tile-Server.
---
## REQ-005 Home Assistant Integration
**Status:** 📋 dokumentiert, nicht getestet
Satellit-Tiles als Raster-Layer in HA-Map-Card einbindbar.
**Satellit-Download-Entscheidung (2026-06-13):**
- Kein Pre-Built Planet-Satellit-MBTiles verfügbar → [ADR-002](../adr/002-no-highzoom-no-satellite-build.md)
- Download z011 (~38 GB, ~24h) via `scripts/download-satellite.py` möglich wenn gewünscht
- z12+ auf dieser Hardware nicht praktikabel (140 GB+, 35 Tage)
```bash
# Satellit z0-11 starten (optional, bei Bedarf):
SAT_MAX_ZOOM=11 python3 scripts/download-satellite.py data/satellite.mbtiles
```
---
## REQ-006 Satellit-Hybrid und Luftbild-Detailtiefe
**Status:** 🧊 **AUF EIS** Entscheidung getroffen, Implementierung folgt bei Bedarf
**Dezision:** 2026-06-22 umfangreiche Provider-Analyse durchgeführt. Feststellung: Mit freien Datenquellen ist Vollabdeckung z13/z14+ nicht praktikabel; empfohlen wird MapTiler On-Prem für Private Nutzung.
### Aktuelle Situation (z11 Sentinel-2)
Der aktuelle Satelliten-Datensatz (`data/satellite.mbtiles`) basiert auf
Sentinel-2 cloudless 2021 und endet real bei `maxzoom=11` (20 GB, 5.6 Mio Tiles).
Bei `z=14` oder `z=16` wird das Raster nur hochskaliert. Häuser, kleine Straßen
und Luftbilddetails sind nicht vorhanden.
```text
satellite.mbtiles
format: jpg
minzoom: 0
maxzoom: 11
size: ~20 GB (JPEG)
quality: 1060m Pixel (Sentinel-2)
description: Sentinel-2 cloudless 2021 (EOX CC BY 4.0)
```
### Provider-Matrix (Private Offline-Nutzung)
| **Quelle** | **Qualität** | **Lizenz** | **Kosten** | **Self-Hosting** | **Speicher z16** |
|---|---|---|---|---|---|
| **MapTiler On-Prem** | ⭐⭐⭐⭐⭐ (z1617) | ✅ Explizit erlaubt | €1.500 (One-Time) | ✅ JA | 200500 GB |
| **Sentinel-2 (EOX)** | ⭐⭐⭐ (z011) | ✅ CC BY 4.0 | Kostenlos | ✅ JA | N/A |
| **Google Maps API** | ⭐⭐⭐⭐⭐ | ❌ Offline verboten | €6001.200/Mo | ❌ NEIN | N/A |
| **Mapbox** | ⭐⭐⭐⭐ | ❌ Bulk-DL verboten | $600800/Mo | ❌ NEIN | N/A |
| **Sentinel Hub Free** | ⭐⭐⭐ | ✅ Kostenlos | Kostenlos | ⚠️ Script | 1.333+ Jahre¹ |
¹ Kostenlos: 100 PU/Mo = 100 z14-Tiles/Mo. Deutschland z14 = 1,6 Mio Tiles = 16.000 Monate = **1.333 Jahre Betrieb**.
### Kostenvergleich (3 Jahre, Private Nutzung)
| Szenario | **Jahr 1** | **Jahr 2+** | **Speicher** | **Gesamt** |
|---|---|---|---|---|
| Status Quo (z11) | €0 | €0 | 20 GB | ✅ **€0** |
| **MapTiler z14** | €500800 | €0 | 3050 GB | ✅ **€500800** |
| **MapTiler z16** | €1.2001.500 | €0 | 200300 GB | ✅ **€1.2001.500** |
| Sentinel Hub Free | €0 | €0 | 20 GB | ❌ **1.333 Jahre Betrieb** |
### Technische Integration (Option: MapTiler On-Prem)
Falls Upgrade beschlossen:
| Datei | Änderung | Impact |
|---|---|---|
| `docker-compose.yml` | Volume `/data/satellite-hq.mbtiles` | 1 Zeile |
| `config/tileserver.json` | Source `satellite-hq` eintragen | 3 Zeilen |
| `config/styles/satelite-world.json` | Layer hinzufügen | 5 Zeilen |
**Aufwand:** Niedrig (~2 Std Setup + Testing).
### Next Steps
1. **Phase 1 (Jetzt):** z11 Sentinel-2 in Produktion testen (24 Wochen)
- Frage: Reicht z11 für MediaWiki/HA/Nextcloud aus?
2. **Phase 2 (Falls nötig):** MapTiler On-Prem kaufen
- Kontakt: sales@maptiler.com
- Budget: €5001.500 (One-Time)
- Lieferzeit: 15 Tage
3. **Phase 3 (Integration):** 4 Dateien anpassen, `docker compose up -d`
4. **Phase 4 (Doku):** ADR-003 erstellen (Satellite-Entscheidungslog)