Ajouter un système de whitelist d'IP sur un frontal nginx-proxy (FR)
mer. 06 septembre 2017 Dan LousquiVous avez suivi mon précédent billet et maintenant vous exposez certains services et/ou application (GloWeeChat par exemple) sur votre serveur via Docker en passant par jwilder/nginx-proxy ?
Si tel est le cas, vous protégez bien votre application par HTTPS (SSL/TLS), cependant, l'acccès au service est ouvert sur Internet, et vous souhaitez peut-être ajouter un filtrage par adresse IP afin d'autoriser l'accès uniquement aux personnes légitimes. Pour cela, je vous propose d'utiliser TheBlusky/alohomora.
(Note : si vous utilisez Traefik, cet article ne vous concerne pas. Vous pouvez cependant vous
reporter à l'option traefik.frontend.whitelistSourceRange
dans un label
(documentation 1,
documentation 2))
Présentation d'alohomora
Le nom de ce projet vient (honte à vous si vous l'avez pas deviné) du monde d'Harry Potter :
Le sortilège de Déverrouillage, également appelé l'Ami des Voleurs, est un enchantement qui permet à un sorcier d'ouvrir discrètement une porte verrouillée sans utiliser la clé. Son incantation est Alohomora. (Source)
L'idée derrière alohomora est de permettre d'ajouter un filtrage par IP à certains de vos services, via les fonctionnalités suivantes :
- Générer un fichier
allow.conf
, qui sera à inclure dans une configuration nginx (ou apache) ; - Proposer une interface web pour manager les IPs dans la whitelist ;
- Avoir un interface web protégée par un token / passphrase ;
- Donner la possibilité d'ajouter des éléments dans la whitelist avec une durée d'expiration.
Définition d'une whitelist (ou liste blanche): un ensemble d’entités (personnes, comptes, machines…) auxquelles on attribue un niveau de liberté ou de confiance dans un système particulier ; en opposition à une blacklist (ou liste noire) laquelle définit un état de bannissement, d‘interdiction pour ses membres. (Wikipedia)
Installation
Récupération et création de l'image de alohomora
Comme d'habitude, avec mes projets, il suffit de cloner le repository et de build l'image :
git clone https://github.com/TheBlusky/alohomora.git
cd alohomora
sh build.sh
Cela créera l'image blusky/alohomora
à partir des sources du projet.
Mise en place d'un environement "nginx-proxy"
Pour cette démonstration, nous allons mettre en place un environnement avec trois services derrières jwilder/nginx-proxy, deux seront protégés par alohomora, l'autre ne le sera pas.
Pour plus d'information sur ces commandes, je vous invite à lire mon billet Installer un frontal pour services docker.
Note : Ne pas oublier de remplacer domain.tld
par le bon hostname.
mkdir -p /docker/proxy/
chmod -R 777 /docker/proxy/ # Peut probablement faire mieux, je suis ouvert à toute proposition
docker run -d \
-e DEFAULT_HOST=domain.tld \ # Domaine par défaut
-v "/docker/proxy/certs:/etc/nginx/certs:ro" \ # Certificats
-v "/docker/proxy/vhost.d:/etc/nginx/vhost.d" \ # Configurations
-v "/docker/proxy/html:/usr/share/nginx/html" \ # Letsencrypt challenge
-v "/var/run/docker.sock:/tmp/docker.sock:ro" \ # Docker socket
-l com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy=true \
-p 80:80 \
-p 443:443 \
jwilder/nginx-proxy:latest
docker run -d \
-v "/docker/proxy/certs:/etc/nginx/certs:rw" \
-v "/var/run/docker.sock:/var/run/docker.sock:ro" \
-v "/docker/proxy/vhost.d:/etc/nginx/vhost.d" \
-v "/docker/proxy/html:/usr/share/nginx/html" \
jrcs/letsencrypt-nginx-proxy-companion:latest
docker run -d \
--name service_public \
-e "VIRTUAL_HOST=service-public.domain.tld" \
-e "LETSENCRYPT_HOST=service-public.domain.tld" \
-e "LETSENCRYPT_EMAIL=contact@domain.tld" \
emilevauge/whoami
docker run -d \
--name service_prive_1 \
-e "VIRTUAL_HOST=service-prive-1.domain.tld" \
-e "LETSENCRYPT_HOST=service-prive-1.domain.tld" \
-e "LETSENCRYPT_EMAIL=contact@domain.tld" \
emilevauge/whoami
docker run -d \
--name service_prive_2 \
-e "VIRTUAL_HOST=service-prive-2.domain.tld" \
-e "LETSENCRYPT_HOST=service-prive-2.domain.tld" \
-e "LETSENCRYPT_EMAIL=contact@domain.tld" \
emilevauge/whoami
A ce niveau-là, vous devez avoir un serveur qui expose trois services :
- https://service-public.domain.tld
- https://service-prive-1.domain.tld
- https://service-prive-2.domain.tld
Tous accessibles à tous depuis Internet.
Démarrage d'alohomora
L'image Docker d'alohomora nécessite la variable
d'environnement ALOHOMORA_TOKEN
qui servira à l'authentification sur le service.
De plus, l'image générera sa base de données, ainsi que le fichier de configuration allow.conf
dans /alohomora/data
,
il faudra donc monter ce répertoire comme volume.
On peut donc lancer alohomora avec les commandes suivantes :
mkdir -p /docker/alohomora/
chmod -R 777 /docker/alohomora/ # Peut probablement faire mieux, je suis ouvert à toute proposition
docker run -d \
--name alohomora \
-e "VIRTUAL_HOST=alohomora.domain.tld" \
-e "LETSENCRYPT_HOST=alohomora.domain.tld" \
-e "LETSENCRYPT_EMAIL=contact@domain.tld" \
-e "ALOHOMORA_TOKEN=my_awesome_token" \
-v "/docker/alohomora:/alohomora/data" \
blusky/alohomora
Mise en place de la configuration
A ce niveau-là, vous devrez être capable de vous connectez à alohomora via https://alohomora.domain.tld.
Connectez-vous sur l'application, si vous avez copié/collé mes commandes (bouh !), le token est my_awesome_token
.
Et ajoutez des premières adresses IP à whitelister (ou uniquement la vôtre si vous préférez)
Normalement, sur votre serveur, vous devriez avoir un fichier /docker/alohomora/allow.conf
avec votre configuration.
Il faut maintenant créer un script qui analysera les modifications du fichier allow.conf
, pour les copier dans la
configuration de nginx-proxy et reloader la configuration
nginx le cas échéant.
Nous utiliserons l'outil inotify
pour detecter les modifications du fichierallow.conf
sudo apt-get install inotify-tools
Et le script de monitoring est le suivant :
/docker/alohomora_watcher.sh
#!/bin/sh
while inotifywait -e close_write /docker/alohomora/allow.conf; do
echo "Relaunching nginx";
cat /docker/alohomora/allow.conf > /docker/proxy/vhost.d/allow.conf;
NGINX_IMG=$(docker ps --filter "label=com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy=true" -q)
docker exec $NGINX_IMG nginx -s reload;
done;
On ajoute ensuite des droits d'exécution à ce script, puis on le lance en daemon :
cd /docker
chmod +x alohomora_watcher.sh
setsid ./alohomora_watcher.sh >alohomora.logs 2>&1 < /dev/null &
On crée également une première version de allow.conf
, pour bootstraper l'environnement :
cat /docker/alohomora/allow.conf > /docker/proxy/vhost.d/allow.conf
On devrait avoir le fichier de configuration allow.conf
dans l'image
nginx-proxy à la localisation /docker/proxy/vhost.d/allow.conf
.
Protection des containers
L'un des avantages (à mon sens) de nginx-proxy par rapport à Traefik est de pouvoir surcharger la configuration du reverse proxy par hostname.
C'est ce que nous allons faire pour https://service-prive-1.domain.tld et https://service-prive-2.domain.tld
afin que leur configuration nginx inclut le fichier allow.conf
.
Pour cela, rien de plus simple, il suffit d'éditer les fichiers /docker/proxy/vhost.d/service-prive-1.domain.tld
et
/docker/proxy/vhost.d/service-prive-1.domain.tld
et d'y ajouter les lignes suivantes en fin de fichier :
include /etc/nginx/vhost.d/allow.conf;
deny all;
(Note : A priori, JrCs/docker-letsencrypt-nginx-proxy-companion a déjà créé le fichier pour vous. Si ce n'est pas le cas, ce n'est pas grave, créez le.)
Vous pouvez ensuite relancer nginx-proxy avec les commandes suivantes:
NGINX_IMG=$(docker ps --filter "label=com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy=true" -q)
docker restart $NGINX_IMG
Et voilà ! Vous êtes protégés :-)
(Et vous pouvez même protéger alohomora avec alohomora si vous le souhaitez ! Personnellement, je ne le fais pas, car mon use case est d'ajouter mon IP depuis des localisations inconnues (Wi-Fi d'hôtel, Tethering 3G, ...)
Conclusion
Je suis assez satisfait d'alohomora, il répond à la majorité de mes besoins. Cependant, je suis conscient qu'il reste encore pas mal d'axes d'amélioration :
- Le script d'auto-update est dégueulasse (
inotify
+cat
+docker exec
... on a vomi trois fois), cependant, la seule alternative que j'imagine est de donner le socket de Docker à alohomora, ce qui ne me plait pas ; - L'authentification est très légère, un simple token en variable d'environnement... peut mieux faire ;
- Ça pourrait être intéréssant de donner la possibilité de générer plusieurs fichiers, pour plusieurs niveaux d'accès.
Si vous êtes chaud pour développer ces idées, je suis ouvert aux pull requests :-)