Ajouter la GeoIP à Nginx Proxy Manager 1/2

Ajouter la GeoIP à Nginx Proxy Manager 1/2

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.

Ouvrir vos services vers Internet avec Nginx Proxy Manager ou Synology DSM
On vous montre sur ce blog tout un tas de services à héberger chez vous ou sur un serveur dans le cloud, et vous pouvez y accéder quand vous êtes sur votre réseau local. Mais comment faire pour les rendre disponibles quand vous êtes en déplacement ou n’y êtes simplement

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 n'intégrant pas les modules nécessaires dans l'image officielle, il va nous falloir mettre les mains dans le cambouis pour compiler et intégrer ces modules.

💡
Attention : Cet article ne fonctionne qu'avec une version de NPM inférieure ou égale à 2.9.20. La mise à jour à une version ultérieure pose problèmes.

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:2.9.20
    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.

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.

Préparations pour la compilation des modules

Afin de stocker les modules que nous allons compiler et le fichier de configuration nginx qui nous permettra de les charger, nous allons créer un dossier "modules" que l'on va monter comme volume dans le conteneur.
Voici la partie volumes modifiée de notre fichier docker-compose.yml :

...
    volumes:
      - /dockers/nginx-proxym/data:/data
      - /dockers/nginx-proxym/letsencrypt:/etc/letsencrypt
      - /dockers/nginx-proxym/modules:/etc/nginx/modules
...

Nous allons créer un script compile-geoip2.sh que nous allons placer dans le dossier data de NPM. Il va nous permettre d'installer les packages et fichiers nécessaires puis de compiler les modules pour notre version de Nginx.

#!/bin/bash
apt-get install -y wget libpcre3 libpcre3-dev libssl-dev zlib1g-dev

ngxversion=openresty-$(/etc/nginx/bin/openresty -v 2>&1|cut -d "/" -f2)

mkdir /tmp/compile && cd /tmp/compile
wget https://openresty.org/download/$ngxversion.tar.gz
tar xvf $ngxversion.tar.gz

mkdir /tmp/compile/$ngxversion/modules
cd /tmp/compile/$ngxversion/modules
git clone https://github.com/leev/ngx_http_geoip2_module.git

cd ../bundle/nginx-$(/etc/nginx/bin/openresty -v 2>&1|cut -d "/" -f2|grep -oP '^\d*\.\d*\.\d*')
export LUAJIT_LIB="/etc/nginx/luajit/lib/"
export LUAJIT_INC="../LuaJIT-*/src/"
COMPILEOPTIONS=$(/etc/nginx/bin/openresty -V 2>&1|grep -i "arguments"|cut -d ":" -f2-)
eval ./configure $COMPILEOPTIONS --add-dynamic-module=../../modules/ngx_http_geoip2_module
make

cp -f objs/ngx_stream_geoip2_module.so /etc/nginx/modules/
cp -f objs/ngx_http_geoip2_module.so /etc/nginx/modules/
rm -f /etc/nginx/modules/ngx_geoip2_*
touch /etc/nginx/modules/ngx_geoip2_$ngxversion

rm -rf /tmp/compile	
compile-geoip2.sh

On rend le script exécutable :chmod +x /dockers/nginx-proxym/data/compile-geoip2.sh.

Modification de l'image NPM avec docker-compose

Afin de pouvoir compiler et exécuter les modules Geoip2 pour la version de Nginx présente dans l'image officielle de NPM, nous avons besoin d'installer quelques paquets systèmes supplémentaires : libmaxminddb0 et libmaxminddb-dev.

Pour intégrer des paquets à un conteneur, deux possibilités existent :

  • Faire notre propre image à partir de l'officielle (build) et y intégrer tout ce qu'il nous faut.
  • Ou modifier le script de lancement du conteneur afin qu'il fasse l'installation des paquets avant le lancement de NPM.

J'ai opté pour la 2ème option, certes cela rallonge un petit peu le démarrage du conteneur, mais on a l'avantage de rester sur l'image officielle, ce qui est plus pratique pour les mises à jour (via watchtower notamment ...).

Nous allons donc créer un autre script entrypoint.sh que nous allons placer dans le dossier data de NPM. Il va nous permettre, à chaque lancement de NPM, d'installer ces paquets et de lancer la compilation des modules s'ils ne sont pas présents où si la version de Nginx a changée lors d'une mise à jour de l'image officielle.

#!/bin/bash
apt-get update
apt-get install -y libmaxminddb0 libmaxminddb-dev

echo "=>Check for GeoIP modules files and version flag..."
set -- /etc/nginx/modules/ngx_geoip2_*
if [[ -f /etc/nginx/modules/ngx_http_geoip2_module.so && -f /etc/nginx/modules/ngx_stream_geoip2_module.so && -f "$1" ]]; then
        moduleversion=$(echo $1|cut -d "-" -f2|grep -oP '^\d*\.\d*\.\d*')
        ngxversion=$(/etc/nginx/bin/openresty -v 2>&1|cut -d "/" -f2|grep -oP '^\d*\.\d*\.\d*')
        if [ "$moduleversion" != "$ngxversion" ]; then
                echo "!=>GeoIP modules ($moduleversion) and nginx ($ngxversion) version mismatch !"
                echo "!=>Starting compilation !"
                /data/compile-geoip2.sh
        else
                echo "=>GeoIP modules found and version match nginx !"
        fi
else
        echo "!=>No GeoIP module found !"
        echo "!=>Starting compilation !"
        /data/compile-geoip2.sh
fi

apt-get clean
rm -rf /var/lib/apt/lists/*

set -- /etc/nginx/modules/ngx_geoip2_*
moduleversion=$(echo $1|cut -d "-" -f2|grep -oP '^\d*\.\d*\.\d*')
ngxversion=$(/etc/nginx/bin/openresty -v 2>&1|cut -d "/" -f2|grep -oP '^\d*\.\d*\.\d*')
echo "### GeoIP Modules $moduleversion - Nginx $ngxversion ###"
echo "### Starting NPM orignal entrypoint ###"
/init
entrypoint.sh

On rend le script exécutable : chmod +x /dockers/nginx-proxym/data/entrypoint.sh.

On modifie notre fichier docker-compose.yml pour écraser la configuration de l'entrypoint de l'image NPM. Pour cela, on ajoute une instruction :

...
  nginx-proxym:
    container_name: nginx-proxym
    image: jc21/nginx-proxy-manager:2.9.20
    entrypoint: "/data/entrypoint.sh"
    restart: always
...
Note : Si vous avez configuré un healthcheck pour ce conteneur et utilisez un conteneur de redémarrage automatique comme deunhealth ou autoheal, il est conseillé d'augmenter la start_period du healthcheck (Par exemple 120s) pour les fois où la compilation se lancera.

On relance le conteneur pour prendre en compte les modifications du compose et lancer la première compilation des modules : docker-compose restart

Il est possible de suivre les actions effectuées au lancement en regardant le log du conteneur : docker logs nginx-proxym

Chargement des modules

Une fois le conteneur lancé et la compilation finie, on retrouve nos modules dans le dossier /dockers/nginx-proxym/modules.

/dockers/nginx-proxym/modules#  ls -l
total 208
-rw-r--r-- 1 root   root       0 déc.  13 23:59 ngx_geoip2_openresty-1.19.3.1
-rwxr-xr-x 1 root   root  116032 déc.  13 23:59 ngx_http_geoip2_module.so
-rwxr-xr-x 1 root   root   89632 déc.  13 23:59 ngx_stream_geoip2_module.so

Pour charger les modules compilés dans NPM, il suffit de créer un fichier geoip2.conf dans le dossier modules :

load_module /etc/nginx/modules/ngx_http_geoip2_module.so;
load_module /etc/nginx/modules/ngx_stream_geoip2_module.so;
geoip2.conf

Puis on recharge la configuration de nginx : docker exec -it nginx-proxym nginx -s reload

Note : Pour voir la version de nginx dans le conteneur : docker exec -it nginx-proxym nginx -v
Pour voir la version de nginx utilisée lors de la compilation des modules, il suffit de regarder le nom du 3ème fichier présent dans le dossier modules.

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.

Attention ! Notez bien l'Account ID et la Licence Key (celle-ci ne sera plus visible par la suite).

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:2.9.20
    entrypoint: "/data/entrypoint.sh"
    restart: always
    volumes:
      - /dockers/nginx-proxym/data:/data
      - /dockers/nginx-proxym/letsencrypt:/etc/letsencrypt
      - /dockers/nginx-proxym/modules:/etc/nginx/modules
    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

Conclusion

Dans ce premier article, nous avons vu comment compiler les modules geoip2 pour la version nginx présente dans Nginx Proxy Manager et les charger afin de pouvoir les utiliser par la suite. Nous avons aussi vu comment télécharger et mettre à jour les bases de données GeoIP_Lite de Maxmind.

Tout est prêt pour utiliser les informations de GeoIP dans Ngnix Proxy Manager à des fins de blocage et/ou log. La configuration de tout ceci est abordée dans l'article suivant :

Ajouter la GeoIP à Nginx Proxy Manager 2/2
Lors du premier article, nous avons compilé et chargé les modules GeoIP dans Nginx Proxy Manager et nous avons téléchargé les bases de données GeoLite2 de Maxmind. Nous pouvons maintenant configurer NPM pour utiliser tout ça, afin de bloquer et/ou loguer en se servant des informations GeoIP. Pour ce…

Si vous avez des questions ou simplement souhaitez échanger avec nous, n'hésitez pas à laisser des commentaires ou à venir sur notre groupe Telegram.