• BLOG

  • Ajouter un système de whitelist d'IP sur un frontal nginx-proxy (FR)

    mer. 06 septembre 2017 Dan Lousqui

    Share on: Twitter - Facebook - Google+

    alohomora

    Vous 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 :

    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.

    alohomora

    Et ajoutez des premières adresses IP à whitelister (ou uniquement la vôtre si vous préférez)

    alohomora

    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 :-)

  • Comments