210 lines
6.8 KiB
Markdown
210 lines
6.8 KiB
Markdown
# Anforderungen – rd13_tile_server
|
||
|
||
Letzte Aktualisierung: 2026-06-13
|
||
|
||
---
|
||
|
||
## 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 2–4×/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:**
|
||
- 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
|
||
|
||
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:
|
||
|
||
```
|
||
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
|
||
|
||
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
|
||
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)
|
||
|
||
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 z0–11 (~38 GB, ~24h) via `scripts/download-satellite.py` möglich wenn gewünscht
|
||
- z12+ auf dieser Hardware nicht praktikabel (140 GB+, 3–5 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:** 🔴 offen – Next Step
|
||
|
||
Der aktuelle Satelliten-Datensatz (`data/satellite.mbtiles`) basiert auf
|
||
Sentinel-2 cloudless 2021 und endet real bei `maxzoom=11`. Bei `z=14` oder
|
||
`z=16` wird das Raster deshalb nur hochskaliert. Häuser, kleine Straßen und
|
||
feine Luftbilddetails sind in diesem Datensatz nicht enthalten und können durch
|
||
Style-Anpassungen nicht nachträglich erzeugt werden.
|
||
|
||
### Festgestellte Grenze
|
||
|
||
```text
|
||
satellite.mbtiles
|
||
format: jpg
|
||
minzoom: 0
|
||
maxzoom: 11
|
||
description: Sentinel-2 cloudless 2021 (EOX CC BY 4.0)
|
||
```
|
||
|
||
### Next Steps
|
||
|
||
1. **Satellite-Hybrid-Style bauen**
|
||
- `satellite` als Raster-Hintergrund verwenden
|
||
- `osm` oder `osm-europe` als Vektor-Overlay ergänzen
|
||
- zunächst Straßen, Wege und Gebäudeumrisse über das Satellitenbild legen
|
||
- Labels nur aktivieren, wenn Font/Glyph-Rendering stabil funktioniert
|
||
|
||
2. **Höher aufgelösten Luftbild-Datensatz evaluieren**
|
||
- Ziel: mindestens `z16`, besser `z18+`
|
||
- erst Lizenz, Abdeckung, Datenvolumen und Aktualisierungsweg klären
|
||
- ohne höher aufgelöste Rasterdaten sind echte Haus-/Straßendetails im
|
||
Satellitenbild nicht erreichbar
|
||
|
||
3. **Aktuelle Satellitenkarte nicht über `z11` bewerten**
|
||
- für Übersichtskarten ist der Datensatz nutzbar
|
||
- für Gebäude- und Straßendetails muss der Hybrid-Style oder ein neuer
|
||
Luftbilddatensatz genutzt werden
|