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.
- Aplikacja bierze peera transportowego (adres, który faktycznie otworzył połączenie).
- 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ługafrontend/caddyjest 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. - 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ńcuchX-Forwarded-Forprzeglą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 czytaCF-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
.mmdbmoż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:
- Ustaw
GEOIP_ENABLED=falsew środowisku backendu. - Zrestartuj backend (to wyłącza egzekucję GeoIP, zanim jakakolwiek polityka zostanie oceniona).
- Popraw lub wyczyść listę dozwolonych krajów, następnie ustaw
GEOIP_ENABLED=truei 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.