Zum Inhalt springen
Q
QuoteNode

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.

  1. Die Anwendung nimmt den Transport-Peer (die Adresse, die die Verbindung tatsächlich geöffnet hat).
  2. 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ündelte frontend/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.
  3. 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 die X-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 niemals CF-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:

  1. Setzen Sie GEOIP_ENABLED=false in der Backend-Umgebung.
  2. Starten Sie das Backend neu (das deaktiviert die GeoIP-Durchsetzung, bevor eine Richtlinie ausgewertet wird).
  3. Korrigieren oder leeren Sie die Länder-Erlaubnisliste, setzen Sie dann GEOIP_ENABLED=true und 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.

Last reviewed: Recently