Ajouter la GeoIP à Nginx Proxy Manager
Historique des versions
Version | Date | Commentaires |
---|---|---|
1 | Octobre 2023 | Version initiale |
2 | Août 2025 | Révision pour suivi de la compatibilité suite à la mise à jour de NPM. |
En complément de la mise en place de Nginx Proxy Manager pour gérer vos redirections de sous-domaines vers les services que vous hébergez, nous allons voir comment ajouter les informations GeoIP à NPM, nous permettant de bloquer les visiteurs venant de certains pays et/ou de loguer la provenance de ces visiteurs.
La GeoIP est une technique permettant de localiser un utilisateur en se basant sur son adresse IP. Pour cela, nous nous appuierons sur des bases de données que Maxmind met à disposition gratuitement : GeoLite2. Ces bases gratuites sont moins précises que leurs versions payantes, mais seront largement suffisantes pour notre utilisation.
NPM à l'origine n'intégrait pas les modules nécessaires dans l'image officielle, il fallait donc mettre les mains dans le cambouis pour compiler et intégrer ces modules. Ce qui avait donné lieu à un tuto en 2 parties sur le blog :
Mais depuis quelques versions, ces modules sont intégrés à l'image, ce qui simplifie énormément la configuration et l'utilisation de la GeoIP avec NPM. Voici donc le tutoriel remanié et fonctionnant avec la dernière version de NPM (2.12 à l'écriture de l'article).
Prérequis
Pour cet article, nous allons partir d'un fichier docker-compose.yml
standard pour NPM :
version: '3.9'
services:
nginx-proxym:
container_name: nginx-proxym
image: jc21/nginx-proxy-manager:latest
restart: always
volumes:
- /dockers/nginx-proxym/data:/data
- /dockers/nginx-proxym/letsencrypt:/etc/letsencrypt
ports:
- 80:80
- 443:443
- 81:81
environment:
TZ: "Europe/PARIS"
DB_SQLITE_FILE: "/data/database.sqlite"
Pour les volumes, j'ai opté pour un chemin absolu afin d'être plus clair dans les chemins utilisés, à vous d'adapter en fonction de votre configuration et de vos habitudes.
Vous noterez que le fichier utilise une base SQLite au lieu de MariaDB, c'est juste pour simplifier, tout ce que nous allons faire est indépendant de la base de donnée utilisée et ne remet pas en cause votre installation.
Chargement des modules
Pour charger les modules GeoIP dans NPM, il suffit de créer un fichier root_top.conf
dans le dossier prévu par NPM pour les configurations avancées, à savoir /dockers/nginx-proxym/data/nginx/custom
.
load_module /usr/lib/nginx/modules/ngx_http_geoip2_module.so;
load_module /usr/lib/nginx/modules/ngx_stream_geoip2_module.so;
root_top.conf
Puis on recharge la configuration de nginx : docker exec -it nginx-proxym nginx -s reload
Téléchargement et mise à jour des bases GeoLite
Pour obtenir les bases Geoip2 gratuites de Maxmind, il faut d'abord s'inscrire sur leur site. Ça se passe là : https://www.maxmind.com/en/geolite2/signup.
Créez votre compte et une fois celui-ci validé, vous pouvez vous connecter afin de générer une clé de licence qui servira par la suite : https://www.maxmind.com/en/accounts/current/license-key.
Afin de mettre à jour régulièrement les bases geoip, Maxmind propose une image docker très simple à utiliser. Il suffit de lui monter comme volume le dossier où l'on veut nos bases et de renseigner avec des variables d'environnement les informations de notre clé de licence.
On créé un nouveau répertoire geoip2
pour stocker nos bases dans le dossier data de NPM : /dockers/nginx-proxym/data/geoip2
.
Et voici la partie service que l'on peut rajouter au fichier docker-compose de NPM :
geoip-upd:
container_name: geoip-upd
image: maxmindinc/geoipupdate:latest
restart: unless-stopped
volumes:
- /dockers/nginx-proxym/data/geoip2:/usr/share/GeoIP
environment:
TZ: "Europe/PARIS"
GEOIPUPDATE_ACCOUNT_ID: XXXXXX
GEOIPUPDATE_LICENSE_KEY: "XXXXXXXXXXXXXXXXX"
GEOIPUPDATE_EDITION_IDS: "GeoLite2-City GeoLite2-Country GeoLite2-ASN" # Bases à télécharger
GEOIPUPDATE_FREQUENCY: 12 # Màj toutes les 12h
GEOIPUPDATE_PRESERVE_FILE_TIMES: 1 # Conserver la date originale de création des fichiers.
docker-compose.yml
Il est possible de ne télécharger qu'une base suivant ce que l'on compte en faire :
- GeoLite2-ASN : Informations sur l'ISP à qui appartient l'IP.
- GeoLite2-Country : Geolocalisation de l'IP au niveau pays.
- GeoLite2-City : Geolocalisation de l'IP au niveau ville. (Inclue le niveau pays)
Une fois le conteneur lancé, on retrouve nos fichiers de base de données dans le répertoire /dockers/nginx-proxym/data/geoip2
.
/dockers/nginx-proxym/data/geoip2# ls -l
total 86220
-rw-r--r-- 1 root root 7541424 déc. 10 00:57 GeoLite2-ASN.mmdb
-rw-r--r-- 1 root root 74871506 déc. 10 00:57 GeoLite2-City.mmdb
-rw-r--r-- 1 root root 5862627 déc. 10 00:57 GeoLite2-Country.mmdb
Voici notre fichier docker-compose.yml
final pour la stack NPM + GeoIP :
version: '3.9'
services:
nginx-proxym:
container_name: nginx-proxym
image: jc21/nginx-proxy-manager:latest
restart: always
volumes:
- /dockers/nginx-proxym/data:/data
- /dockers/nginx-proxym/letsencrypt:/etc/letsencrypt
ports:
- 80:80
- 443:443
- 81:81
environment:
TZ: "Europe/PARIS"
DB_SQLITE_FILE: "/data/database.sqlite"
geoip-upd:
container_name: geoip-upd
image: maxmindinc/geoipupdate:latest
restart: unless-stopped
volumes:
- /dockers/nginx-proxym/data/geoip2:/usr/share/GeoIP
environment:
TZ: "Europe/PARIS"
GEOIPUPDATE_ACCOUNT_ID: XXXXXX
GEOIPUPDATE_LICENSE_KEY: "XXXXXXXXXXXXXXXXX"
GEOIPUPDATE_EDITION_IDS: "GeoLite2-City GeoLite2-Country GeoLite2-ASN"
GEOIPUPDATE_FREQUENCY: 12
GEOIPUPDATE_PRESERVE_FILE_TIMES: 1
Nous pouvons maintenant configurer NPM pour utiliser tout ça, afin de bloquer et/ou loguer en se servant des informations GeoIP. Pour cela, nous allons ajouter des fichiers de configuration "custom".
Utilisation de la base de données
Pour charger la base de donnée voulue et définir des variables contenant les informations qui nous intéressent, nous allons créer un fichier http_top.conf
dans le dossier /dockers/nginx-proxym/data/nginx/custom
.
charset utf-8;
geoip2 /data/geoip2/GeoLite2-City.mmdb {
auto_reload 3h;
$geoip2_metadata_country_build metadata build_epoch;
$geoip2_data_country_code default=XX source=$remote_addr country iso_code;
$geoip2_data_country_name default=- country names fr;
$geoip2_data_city_name default=- city names fr;
$geoip2_data_region_name default=- subdivisions 0 names fr;
}
http_top.conf
Cette configuration va permettre de charger le fichier GeoLite2-City.mmdb
précédemment téléchargé et d'extraire dans des variables le code pays, le nom du pays, le nom de la ville et le nom de la région en français.
Pour ne pas avoir de problèmes avec les noms accentués, on définit le charset en UTF8.
Définition des IPs à bloquer suivant la provenance
Pour bloquer les IPs en provenance de certains pays, vous pouvez opter pour deux approches :
- Bloquer tous les pays et n'en autoriser que certains, en ajoutant les lignes suivantes au fichier
http_top.conf
:
geo $allowed_ip {
default no; # On interdit par défaut
192.168.1.0/24 yes; # On autorise le réseau local
}
map $geoip2_data_country_code $allowed_country {
default $allowed_ip;
FR yes; # On autorise les IP Française
BE yes; # On autorise les IP Belges
}
- Autoriser tous les pays et ne bloquer que certains, en ajoutant les lignes suivantes au fichier
http_top.conf
:
geo $allowed_ip {
default yes; # On autorise par défaut
192.168.1.0/24 yes; # On autorise le réseau local
}
map $geoip2_data_country_code $allowed_country {
default $allowed_ip;
CN no; # On interdit les IP Chinoises
RU no; # On interdit les IP Russes
}
Mise en place du blocage
Une fois que nous avons défini ce qu'on veut bloquer/autoriser, il ne reste plus qu'à le rendre effectif.
Il faut pour cela ajouter ces lignes dans un fichier de configuration :
if ($allowed_country = no) {
return 444;
}
Le fichier dans lequel ajouter ces lignes dépend du niveau où l'on veut effectuer le blocage :
- Blocage Général : Il faut créer un fichier
server_proxy.conf
avec ce contenu dans le dossier/dockers/nginx-proxym/data/nginx/custom
- Blocage au niveau de chaque Proxy Host : Il faut ajouter ce contenu dans la configuration avancée (Custom Nginx Configuration) de chaque hôte (Interface web de NPM).
Création de logs avec les infos GeoIP
Pour l'instant, il n'est pas possible dans NPM de modifier le format de log par défaut, nous allons donc devoir créer un autre fichier de log contenant les infos GeoIP en plus.
Pour cela, ajoutons un nouveau format de log dans le fichier http_top.conf
:
log_format proxy_geo escape=json '[$time_local] [Client $remote_addr] [$allowed_country $geoip2_data_country_code $geoip2_data_country_name $geoip2_data_region_name $geoip2_data_city_name] "$http_user_agent" '
'$upstream_cache_status $upstream_status $status - $request_method $scheme $host "$request_uri" [Length $body_bytes_sent] [Gzip $gzip_ratio] [Sent-to $server] "$http_referer"';
Les informations loguées par NPM sont les même que celles par défaut, on y a juste ajouté les infos GeoIP : [$allowed_country $geoip2_data_country_code $geoip2_data_country_name $geoip2_data_region_name $geoip2_data_city_name]
, à savoir, si la requête est autorisée ou non, le code pays sur deux lettres, le nom du pays, le nom de la région et le nom de la ville.
Puis ajoutons dans la configuration avancée (Custom Nginx Configuration) de chaque hôte (Interface web de NPM), le code suivant qui génèrera le fichier de log au bon format à chaque requête sur cet hôte :
access_log /data/logs/proxy-host-%HOSTID%_access-geo.log proxy_geo;
En remplaçant %HOSTID% par l'id du proxy host que vous êtes en train de modifier.
On recharge la configuration de nginx avec la commande docker exec -it nginx-proxym nginx -s reload
.
Et voilà, il n'y a plus qu'à tester la connexion aux hôtes avec un vpn, pour venir de différents pays/villes et voir si le blocage éventuel est bien effectif.
Vous pouvez voir le nouveau fichier de log en direct en utilisant la commande tail -f /dockers/nginx-proxym/data/logs/proxy-host-%HOSTID%_access-geo.log
.
Exemples :
/dockers/nginx-proxym/data/logs# tail -f proxy-host-1_access-geo.log
[15/Dec/2021:14:24:58 +0000] [Client 192.168.1.51] [yes XX - - -] "Uptime-Kuma/1.11.1" 200 200 - GET https mon-hote.tld "/login" [Length 9708] [Gzip ] [Sent-to 192.168.1.69] ""
[15/Dec/2021:14:27:01 +0000] [Client 185.189.113.45] [yes FR France Île-de-France Paris] "Mozilla/5.0 (Linux; Android 10; CLT-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Mobile Safari/537.36" 302 302 - GET https mon-hote.tld "/" [Length 0] [Gzip ] [Sent-to 192.168.1.69] ""
[15/Dec/2021:14:27:47 +0000] [Client 89.40.182.9] [no IT Italie Lombardie Milan] "Mozilla/5.0 (Linux; Android 10; CLT-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Mobile Safari/537.36" 444 - GET https mon-hote.tld "/login" [Length 0] [Gzip ] [Sent-to 192.168.1.69] ""
La première ligne est une connexion provenant du réseau local, la deuxième provenant d'un serveur vpn à Paris, la troisième provenant d'un serveur vpn à Milan. On voit bien que les deux premières ont été acceptées et la troisième rejetée.
Conclusion
Le log de ces informations peut servir à faire des statistiques sur la provenance de vos visiteurs. Nous verrons dans un autre article comment exploiter ces informations. Quand au blocage par pays, il peut aider à sécuriser vos services web en bloquant les connexions venant de pays reconnus comme origine d'un gros pourcentage des attaques mondiales.
J'espère que cet article vous permettra de mettre en place de manière effective la GeoIP sur votre Nginx Proxy Manager.
Si vous avez des questions ou simplement souhaitez échanger avec nous, n'hésitez pas à laisser des commentaires ou à venir sur notre groupe Telegram.