Wiki
GeoIP et contrôle d'accès géographique
Comment QuoteNode utilise une base de données GeoIP locale pour le contrôle d'accès par pays et le suivi des offres.
GeoIP et contrôle d’accès géographique
QuoteNode inclut un contrôle d’accès géographique optionnel et un suivi par pays grâce à une base de données GeoIP locale par pays (DB-IP Country Lite sans clé par défaut, ou MaxMind GeoLite2).
Deux usages de GeoIP
1. Contrôle d’accès à l’application
Lorsqu’il est activé, le filtre GeoIP peut restreindre l’accès à l’application à certains pays. Les requêtes provenant de pays non autorisés reçoivent une réponse HTTP 403 (Forbidden).
C’est utile aux organisations qui :
- n’opèrent que dans des juridictions précises,
- veulent réduire l’exposition aux attaques automatisées de certaines régions,
- doivent respecter des règles de résidence des données ou de contrôle d’accès.
2. Suivi des interactions avec l’offre
Lorsqu’un client ouvre un lien public d’offre, le système enregistre le code pays du client (ISO 3166-1 alpha-2) avec l’événement d’interaction. Cela fournit un contexte géographique à l’analyse des offres :
- « Votre offre a été ouverte depuis l’Allemagne » — confirme une portée internationale,
- « Plusieurs ouvertures depuis différents pays » — peut indiquer que l’offre a été transférée.
Fonctionnement
Base de données
La résolution GeoIP utilise une base de données locale par pays au format MaxMind DB (.mmdb) — et non une API externe. Toutes les recherches se font en mémoire, sans appel réseau.
Par défaut, il s’agit de l’édition DB-IP Country Lite sans clé (licence CC-BY-4.0, aucun compte requis). Le conteneur la télécharge automatiquement au démarrage (et selon une planification quotidienne) si elle est absente ou périmée — indépendamment de GEOIP_ENABLED, gouverné par GEOIP_AUTO_UPDATE (par défaut true). La base peut donc être prête alors que l’application par pays est désactivée. Aucun téléchargement manuel ni montage de fichier. Pour utiliser plutôt MaxMind GeoLite2, définissez MAXMIND_ACCOUNT_ID / MAXMIND_LICENSE_KEY (un compte MaxMind gratuit) ; c’est le même format .mmdb. Vous pouvez aussi remplir le fichier manuellement avec scripts/update-geoip.sh.
Détection de l’IP
L’IP du client est résolue entièrement par l’application — elle n’est pas tirée aveuglément des en-têtes de la requête. Un X-Forwarded-For, X-Real-IP ou CF-Connecting-IP falsifié provenant d’un client direct ne peut jamais usurper le contrôle de pays.
- L’application prend le pair de transport (l’adresse qui a réellement ouvert la connexion).
- Si ce pair n’est pas interne, tous les en-têtes de transfert sont ignorés et le pair est utilisé directement. Un pair est interne lorsqu’il est un proxy de confiance explicite ou toute adresse non routable globalement (privée/RFC 1918, loopback, link-local, CGNAT, IPv6 ULA) — qui, derrière Docker/Coolify, est toujours interne au conteneur.
- Si le pair est interne, le client est dérivé de ses métadonnées de transfert dans l’ordre :
CF-Connecting-IP(afin qu’un CDN comme Cloudflare qui réécritX-Forwarded-Fordonne toujours le vrai client) →X-Forwarded-For(parcouru de droite à gauche, en sautant les sauts internes) →X-Real-IP. Seules les IP littérales sont acceptées (pas de DNS).
Comme les pairs privés et les CDN sont gérés automatiquement, la plupart des déploiements ne nécessitent aucune configuration — le Caddy intégré, une edge Coolify ou un CDN en frontal fonctionnent d’emblée. La variable optionnelle SECURITY_TRUSTED_PROXIES (IP/CIDR littéraux séparés par des virgules et/ou noms d’hôte de service) n’ajoute de la confiance que pour un proxy inhabituel dont le pair de transport est une adresse publique.
L’IP du client est un filtre grossier au mieux, pas une authentification. Quiconque peut atteindre l’origine directement (en contournant le CDN/proxy) pourrait présenter un
CF-Connecting-IPchoisi ; le durcissement recommandé est de restreindre l’origine par pare-feu pour que seul votre CDN/proxy puisse l’atteindre.
Les adresses client non routables globalement (loopback, privées/RFC 1918, link-local, CGNAT, IPv6 ULA) ne sont jamais géo-bloquées — elles ne peuvent pas être géolocalisées — de sorte que les health checks localhost et les déploiements LAN/same-host continuent de fonctionner pendant que GeoIP est actif. La liste blanche d’IP et l’authentification s’appliquent toujours à elles.
Chemins exemptés
Les chemins suivants sont toujours exemptés des restrictions géographiques afin de rester accessibles dans le monde entier :
- liens publics d’offres (
/offer/public/*) et branding public (/api/v1/public/branding/*), - actions de liens e-mail : désabonnement (
/api/v1/notifications/unsubscribe-token/*) et préférences publiques (/api/v1/notifications/preferences-public/*), - endpoints d’infrastructure/santé (
/health*,/actuator/health,/actuator/info).
L’authentification administrateur (/api/v1/auth/login, 2FA) et les ressources authentifiées restent soumises au contrôle géographique.
Configuration
| Variable | Défaut | Description |
|---|---|---|
GEOIP_ENABLED |
false |
Interrupteur d’application par pays uniquement — également l’interrupteur de secours. N’affecte pas le chargement de la base : la base continue de se charger et de se mettre à jour (voir GEOIP_AUTO_UPDATE), elle peut donc être prête alors que l’application est désactivée. false désactive de façon fiable toute l’application par pays (voir la récupération ci-dessous). |
GEOIP_DB_PATH |
/app/data/geoip/GeoLite2-Country.mmdb |
Chemin du fichier de base de données .mmdb par pays (rempli automatiquement quand GeoIP est activé). |
GEOIP_AUTO_UPDATE |
true |
Télécharger automatiquement la base au démarrage (DB-IP sans clé par défaut ; MaxMind si MAXMIND_* sont définis) lorsqu’elle est absente ou plus ancienne que GEOIP_AUTO_UPDATE_MAX_AGE_DAYS (25 par défaut). Mettre false pour gérer le fichier vous-même. |
SECURITY_GEOIP_ALLOWED_COUNTRIES |
(vide) | Codes pays ISO 3166-1 alpha-2 séparés par des virgules (p. ex. PL,DE,FR). Les codes sont normalisés en majuscules et validés ; les codes invalides sont rejetés. Vide = aucune restriction. Lorsqu’elle est définie, cette valeur opérateur prévaut sur le réglage du tenant dans l’application. |
Quand aucune liste de pays n’est configurée, le filtre GeoIP est passif — il résout les codes pays à des fins de suivi mais ne bloque aucune requête. Activer le filtrage par pays dans l’interface d’administration utilise un sélecteur de pays validé (ISO 3166-1 alpha-2) et exige au moins un pays et une base de données chargée.
Rafraîchir la base de données : le téléchargement automatique au démarrage la maintient à jour, mais après avoir remplacé manuellement le fichier
.mmdb, vous pouvez le recharger sans redémarrage depuis Administration → Sécurité → état effectif → Recharger la base de données (un rechargement protégé — un candidat corrompu ou manquant conserve la base actuellement chargée). Un redémarrage du backend prend aussi en compte le nouveau fichier. Le panneau d’état effectif indique si la base est chargée, avec ses horodatages de compilation/chargement.
Récupération après un verrouillage GeoIP
Si une liste de pays autorisés exclut accidentellement votre propre pays, vous recevez une réponse HTTP 403 avec l’indice de récupération GEO_BLOCKED. Pour récupérer l’accès :
- Définissez
GEOIP_ENABLED=falsedans l’environnement du backend. - Redémarrez le backend (cela désactive l’application de GeoIP avant l’évaluation de toute politique).
- Corrigez ou videz la liste de pays autorisés, puis définissez
GEOIP_ENABLED=trueet redémarrez à nouveau.
C’est indépendant de l’interrupteur de récupération de la liste blanche d’IP — récupérer l’un ne désactive jamais l’autre en silence.
Considérations de confidentialité
- La résolution GeoIP est sans état — aucune correspondance IP-pays n’est stockée de façon permanente.
- Pour le suivi des offres, seul le code pays est enregistré (p. ex. « FR »), pas l’adresse IP elle-même.
- Les adresses IP enregistrées dans les événements web d’offres sont soumises au traitement configurable d’anonymisation d’IP, qui les hache après un nombre configurable de jours pour la conformité RGPD.