Przejdź do treści
Q
QuoteNode

Wiki

GeoIP i geograficzna kontrola dostępu

Jak QuoteNode używa lokalnej bazy GeoIP do kontroli dostępu na poziomie krajów i do śledzenia otwarć ofert.

GeoIP i geograficzna kontrola dostępu

QuoteNode zawiera opcjonalną geograficzną kontrolę dostępu i śledzenie kraju na poziomie państw z użyciem lokalnej bazy GeoIP (domyślnie keyless DB-IP Country Lite lub MaxMind GeoLite2).

Dwa zastosowania GeoIP

1. Kontrola dostępu do aplikacji

Po włączeniu filtr GeoIP może ograniczyć dostęp do aplikacji do wybranych krajów. Żądania z krajów spoza listy dozwolonych otrzymują HTTP 403 (Forbidden).

Przydaje się to organizacjom, które:

  • działają wyłącznie w określonych jurysdykcjach,
  • chcą ograniczyć ekspozycję na zautomatyzowane ataki z niektórych regionów,
  • muszą spełniać wymagania dotyczące rezydencji danych lub kontroli dostępu.

2. Śledzenie interakcji z ofertą

Gdy klient otwiera publiczny link do oferty, system zapisuje kod kraju klienta (ISO 3166-1 alpha-2) razem ze zdarzeniem interakcji. Daje to geograficzny kontekst dla analityki ofert:

  • „Twoja oferta została otwarta z Niemiec” — potwierdza zasięg międzynarodowy,
  • „Wielokrotne otwarcia z różnych krajów” — może wskazywać, że oferta została przekazana dalej.

Jak to działa

Baza danych

Rozpoznanie GeoIP wykorzystuje lokalną bazę krajów w formacie MaxMind DB (.mmdb) — a nie zewnętrzne API. Wszystkie zapytania odbywają się w pamięci, bez połączeń sieciowych.

Domyślnie jest to keyless DB-IP Country Lite (licencja CC-BY-4.0, bez konta). Kontener pobiera ją automatycznie przy starcie (oraz w codziennym harmonogramie), jeśli jej brakuje lub jest przeterminowana — niezależnie od GEOIP_ENABLED, sterowane przez GEOIP_AUTO_UPDATE (domyślnie true). Baza może więc być gotowa, gdy egzekwowanie krajów jest wyłączone. Nie ma ręcznego pobierania ani montowania pliku. Aby zamiast tego użyć MaxMind GeoLite2, ustaw MAXMIND_ACCOUNT_ID / MAXMIND_LICENSE_KEY (darmowe konto MaxMind); to ten sam format .mmdb. Plik możesz też uzupełnić ręcznie skryptem scripts/update-geoip.sh.

Wykrywanie adresu IP

Adres IP klienta jest ustalany w całości przez aplikację — nie jest pobierany bezkrytycznie z nagłówków żądania. Sfałszowany X-Forwarded-For, X-Real-IP lub CF-Connecting-IP od klienta bezpośredniego nigdy nie podszyje się pod sprawdzanie kraju.

  1. Aplikacja bierze peera transportowego (adres, który faktycznie otworzył połączenie).
  2. Peer jest zaufany tylko wtedy, gdy jest jawnie zadeklarowanym proxy — wpisem w SECURITY_TRUSTED_PROXIES (rozdzielone przecinkami literalne IP/CIDR i/lub nazwy usług Compose). Wbudowana usługa frontend/caddy jest deklarowana automatycznie, więc standardowe stacki Docker/Coolify działają bez konfiguracji. Adres prywatny/loopback/CGNAT nie jest zaufany tylko dlatego, że jest spoza globalnej puli.
  3. Jeśli peer nie jest zaufanym proxy, wszystkie nagłówki przekazujące są ignorowane, a klientem jest sam peer. Od zaufanego proxy klientem jest pojedynczy, autorytatywny X-Real-IP, w przeciwnym razie łańcuch X-Forwarded-For przeglądany od prawej do lewej z pominięciem wyłącznie zaufanych skoków (pierwszy niezaufany adres od prawej jest klientem, nawet jeśli jest prywatny). Akceptowane są wyłącznie literalne adresy IP (bez DNS). Backend nigdy nie czyta CF-Connecting-IP — Cloudflare jest obsługiwany na krawędzi (poniżej).

Za Cloudflare (automatycznie). Gdy wdrożenie stoi za Cloudflare, ścisły peer transportowy to adres krawędzi Cloudflare, a nie odwiedzającego. Wbudowana krawędź Caddy (Caddy usługi frontend w Coolify oraz Caddy w wariancie standalone) normalizuje to automatycznie: żądanie jest traktowane jako Cloudflare tylko gdy adres łączący się mieści się w oficjalnych opublikowanych zakresach Cloudflare oraz CF-Connecting-IP jest pojedynczym, poprawnym adresem IP; wtedy X-Real-IP jest ustawiany na tego prawdziwego odwiedzającego, a CF-Connecting-IP jest usuwany przed backendem. Wartość zduplikowana, listowa lub nieprawidłowa zawodzi bezpiecznie (fail-closed) do adresu krawędzi. Nie dodajesz zakresów Cloudflare do SECURITY_TRUSTED_PROXIES, a backend nadal nigdy nie ufa CF-Connecting-IP dostarczonemu przez klienta. Lista zakresów jest wersjonowana i odświeżana przez scripts/sync-cloudflare-ips.sh, pilnowana przez cotygodniowy drift check.

Za innym CDN. CDN inny niż Cloudflare nie jest rozpoznawany automatycznie. Znormalizuj IP klienta na własnej krawędzi w ten sam sposób — ustaw X-Real-IP na prawdziwego odwiedzającego i odrzuć niezaufane nagłówki przekazujące — zanim ruch dotrze do backendu.

IP klienta to zgrubny filtr „best-effort”, a nie uwierzytelnianie. Ktoś, kto dosięgnie origin bezpośrednio (z pominięciem CDN/proxy), mógłby podać wybrany nagłówek przekazujący; zalecane utwardzenie to firewall ograniczający origin tak, by docierał do niego tylko Twój CDN/proxy.

Adresy klientów spoza globalnej puli routowalnej (loopback, prywatne/RFC 1918, link-local, CGNAT, IPv6 ULA) nigdy nie są geo-blokowane — nie da się ich zlokalizować — dzięki czemu health-checki z localhost oraz wdrożenia LAN/same-host działają, gdy GeoIP jest aktywne. Lista dozwolonych IP i uwierzytelnianie nadal ich obejmują.

Ścieżki wyłączone

Następujące ścieżki są zawsze wyłączone z restrykcji geograficznych, aby pozostały osiągalne na całym świecie:

  • publiczne linki do ofert (/offer/public/*) i publiczny branding (/api/v1/public/branding/*),
  • akcje z linków e-mail: wypisanie z subskrypcji (/api/v1/notifications/unsubscribe-token/*) i publiczne preferencje (/api/v1/notifications/preferences-public/*),
  • endpointy infrastruktury/health (/health*, /actuator/health, /actuator/info).

Uwierzytelnianie administratora (/api/v1/auth/login, 2FA) oraz uwierzytelnione zasoby pozostają objęte kontrolą geo.

Konfiguracja

Zmienna Domyślnie Opis
GEOIP_ENABLED false Przełącznik wyłącznie egzekwowania krajów — zarazem przełącznik awaryjny. Nie wpływa na ładowanie bazy: baza nadal się ładuje i aktualizuje (zob. GEOIP_AUTO_UPDATE), więc może być gotowa, gdy egzekwowanie jest wyłączone. false niezawodnie wyłącza całe egzekwowanie krajów (patrz odzyskiwanie poniżej).
GEOIP_DB_PATH /app/data/geoip/GeoLite2-Country.mmdb Ścieżka do pliku bazy .mmdb (uzupełniana automatycznie, gdy GeoIP jest włączone).
GEOIP_AUTO_UPDATE true Automatyczne pobranie bazy przy starcie (domyślnie keyless DB-IP; MaxMind gdy ustawione MAXMIND_*), gdy brakuje lub jest starsza niż GEOIP_AUTO_UPDATE_MAX_AGE_DAYS (domyślna wartość aplikacji to 35; szablon produkcyjny ustawia jawnie 25). Ustaw false, by zarządzać plikiem samodzielnie.
GEOIP_AUTO_UPDATE_MAX_AGE_DAYS 35 Wiek, po którym baza uchodzi za nieświeżą i jest pobierana ponownie. To fallback aplikacji; infra/.env.prod.example nadpisuje go jawną wartością 25, więc wdrożenie bez tego klucza dostaje 35.
GEOIP_UPDATE_SCHEDULER_ENABLED true Kill-switch okresowego sprawdzania świeżości. Domyślnie włączony, bo nieświeża baza krajów jest stanem niebezpiecznym. Wyłączenie nie rusza odświeżania przy starcie ani ręcznej aktualizacji.
GEOIP_UPDATE_CRON 0 15 3 * * * Kiedy uruchamia się okresowe sprawdzenie, w strefie czasowej serwera (UTC, jeśli nie ustawisz TZ). Codzienny harmonogram to codzienne sprawdzenie świeżości, a nie codzienne pobieranie — plik ściągany jest tylko gdy go brak albo jest nieświeży. Wspierany model to jeden aktualizator na jedną ścieżkę bazy.
SECURITY_GEOIP_ALLOWED_COUNTRIES (puste) Rozdzielone przecinkami kody krajów ISO 3166-1 alpha-2 (np. PL,DE,FR). Kody są normalizowane do wielkich liter i walidowane; nieprawidłowe są odrzucane. Puste = brak ograniczeń. Gdy ustawione, ta wartość operatora jest nadrzędna wobec ustawienia tenanta w aplikacji.

Gdy żadna lista krajów nie jest skonfigurowana, filtr GeoIP działa pasywnie — rozpoznaje kody krajów do celów śledzenia, ale niczego nie blokuje. Włączenie filtrowania krajów w panelu administratora korzysta z walidowanego selektora krajów (ISO 3166-1 alpha-2) i wymaga co najmniej jednego kraju oraz załadowanej bazy.

Odświeżanie bazy: automatyczne pobranie przy starcie utrzymuje ją w miarę aktualną, ale po ręcznej podmianie pliku .mmdb możesz przeładować go bez restartu w Administracja → Bezpieczeństwo → stan efektywny → Przeładuj bazę (przeładowanie strzeżone — uszkodzony lub brakujący kandydat zachowuje aktualnie załadowaną bazę). Restart backendu również wczytuje nowy plik. Panel stanu efektywnego pokazuje, czy baza jest załadowana, oraz jej znaczniki czasu zbudowania/wczytania.

Odzyskiwanie po blokadzie GeoIP

Jeśli lista dozwolonych krajów przypadkowo wykluczy Twój własny kraj, otrzymasz HTTP 403 ze wskazówką odzyskiwania GEO_BLOCKED. Aby odzyskać dostęp:

  1. Ustaw GEOIP_ENABLED=false w środowisku backendu.
  2. Zrestartuj backend (to wyłącza egzekucję GeoIP, zanim jakakolwiek polityka zostanie oceniona).
  3. Popraw lub wyczyść listę dozwolonych krajów, następnie ustaw GEOIP_ENABLED=true i zrestartuj ponownie.

Jest to niezależne od przełącznika odzyskiwania listy dozwolonych IP — odzyskanie jednego nigdy nie wyłącza po cichu drugiego.

Aspekty prywatności

  • Rozpoznanie GeoIP jest bezstanowe — żadne mapowanie IP→kraj nie jest trwale przechowywane.
  • Dla śledzenia ofert zapisywany jest tylko kod kraju (np. „PL”), a nie sam adres IP.
  • Adresy IP zapisane w zdarzeniach webowych ofert podlegają konfigurowalnemu zadaniu anonimizacji IP, które haszuje je po skonfigurowanej liczbie dni na potrzeby zgodności z RODO.

Last reviewed: Recently