Wiki
GeoIP und geografische Zugriffskontrolle
Wie QuoteNode eine lokale GeoIP-Datenbank zur länderbasierten Zugriffskontrolle und zum Tracking von Angebotsöffnungen verwendet.
GeoIP und geografische Zugriffskontrolle
QuoteNode enthält eine optionale geografische Zugriffskontrolle und länderbezogenes Tracking auf Basis einer lokalen GeoIP-Länderdatenbank (standardmäßig keyless DB-IP Country Lite oder MaxMind GeoLite2).
Zwei Verwendungen von GeoIP
1. Anwendungs-Zugriffskontrolle
Wenn aktiviert, kann der GeoIP-Filter den Anwendungszugriff auf bestimmte Länder beschränken. Anfragen aus nicht erlaubten Ländern erhalten HTTP 403 (Forbidden).
Das ist nützlich für Organisationen, die:
- nur in bestimmten Jurisdiktionen tätig sind,
- die Angriffsfläche für automatisierte Angriffe aus bestimmten Regionen verringern möchten,
- Vorgaben zur Datenresidenz oder Zugriffskontrolle einhalten müssen.
2. Tracking von Angebotsinteraktionen
Wenn ein Kunde einen öffentlichen Angebotslink öffnet, erfasst das System den Ländercode des Kunden (ISO 3166-1 alpha-2) zusammen mit dem Interaktionsereignis. Das liefert geografischen Kontext für die Angebotsanalyse:
- „Ihr Angebot wurde aus Deutschland geöffnet“ — bestätigt internationale Reichweite,
- „Mehrere Öffnungen aus verschiedenen Ländern“ — kann darauf hindeuten, dass das Angebot weitergeleitet wurde.
Funktionsweise
Datenbank
Die GeoIP-Auflösung verwendet eine lokale Länderdatenbank im MaxMind-DB-Format (.mmdb) — keine externe API. Alle Abfragen erfolgen im Arbeitsspeicher ohne Netzwerkaufrufe.
Standardmäßig ist das die keyless DB-IP Country Lite (Lizenz CC-BY-4.0, kein Konto nötig). Der Container lädt sie beim Start automatisch herunter (und nach einem Tagesplan), wenn sie fehlt oder veraltet ist — unabhängig von GEOIP_ENABLED, gesteuert durch GEOIP_AUTO_UPDATE (Standard true). Die Datenbank kann also bereit sein, während die Länder-Durchsetzung aus ist. Kein manuelles Herunterladen oder Einbinden der Datei. Um stattdessen MaxMind GeoLite2 zu verwenden, setzen Sie MAXMIND_ACCOUNT_ID / MAXMIND_LICENSE_KEY (ein kostenloses MaxMind-Konto); es ist dasselbe .mmdb-Format. Die Datei lässt sich auch manuell mit scripts/update-geoip.sh befüllen.
IP-Erkennung
Die Client-IP wird vollständig von der Anwendung aufgelöst — sie wird nicht blind aus Request-Headern übernommen. Ein gefälschter X-Forwarded-For-, X-Real-IP- oder CF-Connecting-IP-Header von einem direkten Client kann die Länderprüfung niemals täuschen.
- Die Anwendung nimmt den Transport-Peer (die Adresse, die die Verbindung tatsächlich geöffnet hat).
- Der Peer wird nur dann als vertrauenswürdig behandelt, wenn er ein explizit deklariertes Proxy ist — ein Eintrag in
SECURITY_TRUSTED_PROXIES(durch Kommas getrennte literale IP/CIDR und/oder Compose-Service-Namen). Der gebündeltefrontend/caddy-Service wird automatisch deklariert, sodass die Standard-Stacks von Docker/Coolify ohne Konfiguration funktionieren. Eine private/Loopback-/CGNAT-Adresse ist nicht allein deshalb vertrauenswürdig, weil sie nicht global routbar ist. - Ist der Peer kein vertrauenswürdiges Proxy, werden alle Weiterleitungs-Header ignoriert und der Peer selbst ist der Client. Von einem vertrauenswürdigen Proxy ist der Client der einzelne, maßgebliche
X-Real-IP, andernfalls dieX-Forwarded-For-Kette, von rechts nach links durchlaufen und nur vertrauenswürdige Hops überspringend (die erste nicht vertrauenswürdige Adresse von rechts ist der Client, auch wenn sie privat ist). Es werden nur literale IPs akzeptiert (kein DNS). Das Backend liest niemalsCF-Connecting-IP— Cloudflare wird an der Edge behandelt (unten).
Hinter Cloudflare (automatisch). Wenn das Deployment hinter Cloudflare steht, ist der strikte Transport-Peer eine Cloudflare-Edge-Adresse, nicht der Besucher. Die gebündelte Caddy-Edge (der frontend-Caddy in Coolify und der Standalone-Caddy) normalisiert dies automatisch: Eine Anfrage wird nur dann als Cloudflare behandelt, wenn die verbindende IP innerhalb der offiziellen veröffentlichten Cloudflare-Bereiche liegt und CF-Connecting-IP eine einzelne wohlgeformte IP ist; dann wird X-Real-IP auf diesen echten Besucher gesetzt und CF-Connecting-IP vor dem Backend entfernt. Ein doppelter, aufgelisteter oder fehlerhafter Wert schlägt sicher fehl (fail-closed) auf die Edge-Adresse. Sie fügen Cloudflares Bereiche nicht zu SECURITY_TRUSTED_PROXIES hinzu, und das Backend vertraut weiterhin niemals einem client-gelieferten CF-Connecting-IP. Die Bereichsliste wird versioniert und von scripts/sync-cloudflare-ips.sh aktualisiert, abgesichert durch einen wöchentlichen Drift-Check.
Hinter einem anderen CDN. Ein CDN, das nicht Cloudflare ist, wird nicht automatisch erkannt. Normalisieren Sie die Client-IP an Ihrer eigenen Edge auf dieselbe Weise — setzen Sie X-Real-IP auf den echten Besucher und verwerfen Sie nicht vertrauenswürdige Weiterleitungs-Header — bevor der Verkehr das Backend erreicht.
Die Client-IP ist ein grober Best-Effort-Filter — keine Authentifizierung. Wer den Ursprung direkt erreichen kann (unter Umgehung von CDN/Proxy), könnte einen gewählten Weiterleitungs-Header vortäuschen; empfohlene Härtung ist, den Ursprung per Firewall so zu beschränken, dass nur Ihr CDN/Proxy ihn erreicht.
Nicht global routbare Client-Adressen (Loopback, privat/RFC 1918, Link-Local, CGNAT, IPv6 ULA) werden nie geo-blockiert — sie lassen sich nicht geolokalisieren — sodass localhost-Health-Checks und LAN-/Same-Host-Deployments funktionieren, während GeoIP aktiv ist. Die IP-Whitelist und die Authentifizierung gelten weiterhin für sie.
Ausgenommene Pfade
Die folgenden Pfade sind immer ausgenommen von geografischen Beschränkungen, damit sie weltweit erreichbar bleiben:
- öffentliche Angebotslinks (
/offer/public/*) und öffentliches Branding (/api/v1/public/branding/*), - E-Mail-Link-Aktionen: Abmeldung (
/api/v1/notifications/unsubscribe-token/*) und öffentliche Präferenzen (/api/v1/notifications/preferences-public/*), - Infrastruktur-/Health-Endpunkte (
/health*,/actuator/health,/actuator/info).
Admin-Authentifizierung (/api/v1/auth/login, 2FA) und authentifizierte Assets bleiben geo-geschützt.
Konfiguration
| Variable | Standard | Beschreibung |
|---|---|---|
GEOIP_ENABLED |
false |
Nur Schalter für die Länder-Durchsetzung — zugleich Notfallschalter. Beeinflusst nicht das Laden der Datenbank: Die DB wird weiterhin geladen und aktualisiert (siehe GEOIP_AUTO_UPDATE), kann also bereit sein, während die Durchsetzung aus ist. false deaktiviert die gesamte Länder-Durchsetzung zuverlässig (siehe Wiederherstellung unten). |
GEOIP_DB_PATH |
/app/data/geoip/GeoLite2-Country.mmdb |
Pfad zur .mmdb-Länderdatenbankdatei (wird bei aktiviertem GeoIP automatisch befüllt). |
GEOIP_AUTO_UPDATE |
true |
Datenbank beim Start automatisch herunterladen (standardmäßig keyless DB-IP; MaxMind, wenn MAXMIND_* gesetzt sind), wenn sie fehlt oder älter als GEOIP_AUTO_UPDATE_MAX_AGE_DAYS ist (Anwendungsstandard 35; die Produktionsvorlage setzt explizit 25). false setzen, um die Datei selbst zu verwalten. |
GEOIP_AUTO_UPDATE_MAX_AGE_DAYS |
35 |
Alter, ab dem die Datenbank als veraltet gilt und neu geladen wird. Das ist der Anwendungs-Fallback; infra/.env.prod.example überschreibt ihn mit einem expliziten 25, eine Installation ohne diesen Schlüssel erhält also 35. |
GEOIP_UPDATE_SCHEDULER_ENABLED |
true |
Kill-Switch für die periodische Aktualitätsprüfung. Standardmäßig an, weil eine veraltete Länderdatenbank der unsichere Zustand ist. Ausschalten lässt die Aktualisierung beim Start und manuelle Updates unberührt. |
GEOIP_UPDATE_CRON |
0 15 3 * * * |
Wann die periodische Prüfung läuft, in der Zeitzone des Servers (UTC, sofern TZ nicht gesetzt ist). Ein täglicher Zeitplan bedeutet eine tägliche Prüfung, keinen täglichen Download — geladen wird nur, wenn die Datei fehlt oder veraltet ist. Unterstützt wird ein Updater pro Datenbankpfad. |
SECURITY_GEOIP_ALLOWED_COUNTRIES |
(leer) | Durch Kommas getrennte ISO-3166-1-alpha-2-Ländercodes (z. B. PL,DE,FR). Codes werden in Großbuchstaben normalisiert und validiert; ungültige Codes werden abgelehnt. Leer = keine Einschränkung. Wenn gesetzt, ist dieser Operatorwert maßgeblich gegenüber der In-App-Einstellung des Tenants. |
Wenn keine Länderliste konfiguriert ist, arbeitet der GeoIP-Filter passiv — er löst Ländercodes für Tracking-Zwecke auf, blockiert aber keine Anfragen. Das Aktivieren der Länderfilterung im Admin-Bereich verwendet einen validierten Länderwähler (ISO 3166-1 alpha-2) und erfordert mindestens ein Land sowie eine geladene Datenbank.
Aktualisieren der Datenbank: der automatische Download beim Start hält sie hinreichend aktuell, aber nach dem manuellen Austausch der
.mmdb-Datei können Sie sie ohne Neustart unter Administration → Sicherheit → effektiver Zustand → Datenbank neu laden neu laden (ein abgesichertes Neuladen — eine beschädigte oder fehlende Kandidatendatei behält die aktuell geladene Datenbank). Ein Backend-Neustart übernimmt die neue Datei ebenfalls. Das Panel des effektiven Zustands zeigt, ob die Datenbank geladen ist, samt Build-/Ladezeitstempel.
Wiederherstellung nach einer GeoIP-Sperre
Wenn eine Länder-Erlaubnisliste versehentlich Ihr eigenes Land ausschließt, erhalten Sie HTTP 403 mit dem Wiederherstellungshinweis GEO_BLOCKED. So stellen Sie den Zugriff wieder her:
- Setzen Sie
GEOIP_ENABLED=falsein der Backend-Umgebung. - Starten Sie das Backend neu (das deaktiviert die GeoIP-Durchsetzung, bevor eine Richtlinie ausgewertet wird).
- Korrigieren oder leeren Sie die Länder-Erlaubnisliste, setzen Sie dann
GEOIP_ENABLED=trueund starten Sie erneut neu.
Dies ist unabhängig vom Wiederherstellungsschalter der IP-Whitelist — die Wiederherstellung des einen deaktiviert nie stillschweigend das andere.
Datenschutzhinweise
- Die GeoIP-Auflösung ist zustandslos — es wird keine dauerhafte IP-zu-Land-Zuordnung gespeichert.
- Für das Angebots-Tracking wird nur der Ländercode erfasst (z. B. „DE“), nicht die IP-Adresse selbst.
- In Angebots-Web-Ereignissen erfasste IP-Adressen unterliegen dem konfigurierbaren IP-Anonymisierungs-Job, der sie nach einer konfigurierbaren Anzahl von Tagen für die DSGVO-Konformität hasht.