Aller au contenu
Q
QuoteNode

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.

  1. L’application prend le pair de transport (l’adresse qui a réellement ouvert la connexion).
  2. Le pair n’est considéré comme de confiance que s’il est un proxy explicitement déclaré — une entrée dans SECURITY_TRUSTED_PROXIES (IP/CIDR littéraux séparés par des virgules et/ou noms de service Compose). Le service intégré frontend/caddy est déclaré automatiquement, de sorte que les stacks standard de Docker/Coolify fonctionnent sans configuration. Une adresse privée/loopback/CGNAT n’est pas de confiance du simple fait qu’elle n’est pas routable globalement.
  3. Si le pair n’est pas un proxy de confiance, tous les en-têtes de transfert sont ignorés et le pair lui-même est le client. Depuis un proxy de confiance, le client est l’unique et autoritatif X-Real-IP ; sinon la chaîne X-Forwarded-For parcourue de droite à gauche en ne sautant que les sauts de confiance (la première adresse non fiable en partant de la droite est le client, même si elle est privée). Seules les IP littérales sont acceptées (pas de DNS). Le backend ne lit jamais CF-Connecting-IP — Cloudflare est géré à l’edge (ci-dessous).

Derrière Cloudflare (automatique). Lorsque le déploiement est derrière Cloudflare, le pair de transport strict est une adresse edge de Cloudflare, pas le visiteur. L’edge Caddy intégré (le Caddy du frontend dans Coolify et le Caddy autonome) le normalise automatiquement : une requête n’est traitée comme Cloudflare que lorsque l’IP qui se connecte se trouve dans les plages officielles publiées de Cloudflare et que CF-Connecting-IP est une unique IP bien formée ; alors X-Real-IP est défini sur ce vrai visiteur et CF-Connecting-IP est supprimé avant le backend. Une valeur dupliquée, en liste ou mal formée échoue de manière sûre (fail-closed) vers l’adresse edge. Vous n’ajoutez pas les plages de Cloudflare à SECURITY_TRUSTED_PROXIES, et le backend ne fait toujours jamais confiance à un CF-Connecting-IP fourni par le client. La liste des plages est versionnée et rafraîchie par scripts/sync-cloudflare-ips.sh, protégée par un contrôle de dérive hebdomadaire.

Derrière un autre CDN. Un CDN autre que Cloudflare n’est pas reconnu automatiquement. Normalisez l’IP du client à votre propre edge de la même manière — définissez X-Real-IP sur le vrai visiteur et éliminez les en-têtes de transfert non fiables — avant que le trafic n’atteigne le backend.

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 en-tête de transfert choisi ; 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 (valeur par défaut de l’application 35 ; le modèle de production fixe explicitement 25). Mettre false pour gérer le fichier vous-même.
GEOIP_AUTO_UPDATE_MAX_AGE_DAYS 35 Âge à partir duquel la base est considérée comme périmée et retéléchargée. C’est la valeur de repli de l’application ; infra/.env.prod.example la remplace par un 25 explicite, donc un déploiement qui omet la clé obtient 35.
GEOIP_UPDATE_SCHEDULER_ENABLED true Interrupteur de la vérification périodique de fraîcheur. Activé par défaut, car une base pays périmée est l’état dangereux. Le désactiver laisse intacts le rafraîchissement au démarrage et les mises à jour manuelles.
GEOIP_UPDATE_CRON 0 15 3 * * * Quand la vérification périodique s’exécute, dans le fuseau horaire du serveur (UTC sauf si vous définissez TZ). Une planification quotidienne est une vérification quotidienne, pas un téléchargement quotidien : le fichier n’est récupéré que s’il manque ou est périmé. Le modèle pris en charge est un seul metteur à jour par chemin de base.
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 :

  1. Définissez GEOIP_ENABLED=false dans l’environnement du backend.
  2. Redémarrez le backend (cela désactive l’application de GeoIP avant l’évaluation de toute politique).
  3. Corrigez ou videz la liste de pays autorisés, puis définissez GEOIP_ENABLED=true et 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.

Last reviewed: Recently