La réputation de Nginx comme serveur web n'est plus à faire. Il est également possible de l'utiliser comme reverse-proxy afin de gérer le flux entrant HTTP ou HTTPS de vos applications, et de façon transparente, sur votre serveur. Au cours de cet article, nous allons installer Nginx comme reverse-proxy dans un environnement conteneurisé !

Préambule

Certains aspects des applications Web, comme le chiffrement SSL, la mise en cache des demandes et la découverte de services, peuvent être gérés en dehors de l'application elle-même.

Un reverse-proxy, comme Nginx ou même Traefik, peuvent assumer certaines de ces tâches, cela permet notamment aux développeurs de ne pas se soucier de ces problématiques. Et tout cela peut être réalisé de façon simple avec Docker, docker-compose et dans le cadre de cet article : Nginx.

Pré-requis

Il sera bien sûr nécessaire d'avoir une installation de Docker avec docker-compose fonctionnelle. Vous pouvez suivre ces articles afin de réaliser cette installation :

Installer Docker sur un dédié Kimsufi
Un tutoriel accessible à tous sur l’installation de Docker sur un serveur dédié kimsufi. Cette installation peut - bien sûr - être réalisée chez d’autres hébergeurs.
Docker Compose, indispensable ?
Vous avez installé Docker à l’aide du précédent article [/installation-docker-dedie-kimsufi/] et vous avez découvert comment lancer vos premiers conteneurs. Maintenant vous souhaitez sûrement passer à la vitesse supérieure. Le lancement de multiples conteneurs via docker run commence à vous poser qu…

Mais ne perdons pas plus de temps !

Nginx

Avant tout, une petite note pour les connaisseurs :

Il existe une image Docker, très bien réalisée, que j'ai moi même utilisée pendant plusieurs mois/années : https://hub.docker.com/r/jwilder/nginx-proxy/

Cette image permet de faire de l'auto-découverte de service nécessitant un reverse-proxy en utilisant les variables d'environnement et l'API Docker. Pourquoi ne pas l'utiliser dans cet article ?

Le but de mon article, reste de découvrir comment mettre en place Nginx en reverse proxy de façon basique. Et ainsi de comprendre le fonctionnement de cette mise en place.

Si vous appréhendez ces bases, alors utiliser cette image sera simple et accessible.

Je vais donc utiliser l'image Docker nginx officielle et utiliser un fichier de configuration que nous allons éditer manuellement. Mais commençons par créer un service qui va écouter sur le port 80 et nous afficher quelques informations !

Pour cela, je vais créer un fichier docker-compose.yaml :

version: '3.7'
services:
  whoami:
    image: containous/whoami
    container_name: whoami
    ports:
      - 80:80

On lance notre service :

docker-compose up -d

Et on vérifie le bon fonctionnement :

$ curl localhost:80
Hostname: 3ae1aebfa72b
IP: 127.0.0.1
IP: 172.28.0.2
RemoteAddr: 172.28.0.1:44648
GET / HTTP/1.1
Host: localhost
User-Agent: curl/7.64.0
Accept: */*

Notre application répond mais n'utilise pas de reverse-proxy. Nous allons arrêter nos services et modifier notre fichier afin d'ajouter un service Nginx.

docker-compose down

Et voici mon nouveau fichier docker-compose :

version: '3.7'
services:
  reverse-proxy:
    image: nginx
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
    ports:
      - 80:80

  whoami:
    image: containous/whoami
    container_name: whoami

Ce n'est donc plus mon service whoami qui va gérer les requêtes sur le port 80 mais bien mon reverse-proxy.

Je vais donc utiliser un fichier de configuration que je vais monter dans mon conteneur. Voici le fichier de configuration :

events {

}

http {
  server {
    listen 80;

    location / {
      proxy_pass http://whoami:80;
    }
  }
}

On peut relancer nos services :

docker-compose up -d

et valider le fonctionnement :

$ curl localhost:80
Hostname: b5f0f543b844
IP: 127.0.0.1
IP: 172.28.0.3
RemoteAddr: 172.28.0.2:52562
GET / HTTP/1.1
Host: whoami
User-Agent: curl/7.64.0
Accept: */*
Connection: close

Et si nous ajoutions le SSL à notre application ? En utilisant par exemple let's encrypt.

SSL

Pour cet article, je vais utiliser certbot en standalone pour me fournir un certificat Let's encrypt et l'utiliser avec mes services.
Cela nous permet de revoir les bases de l'utilisation de certbot, mais le downtime que va provoquer cette utilisation ne correspond pas à une utilisation en production.

Pour réaliser l'installation de certbot, vous trouverez plus d'informations ici :

Certbot
Automatically enable HTTPS on your website with EFF’s Certbot, deploying Let’s Encrypt certificates.

Par exemple sur Debian, un simple apt-get install certbot suffira.

Je vais créer un certificat SSL pour mon url : whoami.mydomain.com

certbot certonly --standalone -d whoami.mydomain.com

Les certificats vont être stockés dans /etc/letsencrypt par défaut. Je vais modifier mon service afin de lui fournir un volume avec les certificats :

  ...
  reverse-proxy: 
    image: nginx
    volumes:
      - /etc/letsencrypt/:/etc/letsencrypt/
  ...

Enfin, je vais adapter la configuration de Nginx afin de prendre en compte les certificats, directives ssl_certificate et ssl_certificate_key, et ajouter un port d'écoute pour le HTTPS : le port 443.

  ...
  server {
    server_name whoami.mydomain.com;
    ...
    listen 80;
    listen 443 ssl;
    ...
    ssl_certificate /etc/letsencrypt/live/whoami.mydomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/whoami.mydomain.com/privkey.pem;
  }
  ...

J'ajoute également la directive server_name afin que Nginx traite les requêtes HTTP(S) via mon nom de domaine.

Vous pouvez relancer votre stack et valider le fonctionnement :

$ curl https://whoami.mydomain.com
Hostname: b5f0f543b844
IP: 127.0.0.1
IP: 172.28.0.2
RemoteAddr: 172.28.0.3:45130
GET / HTTP/1.1
Host: whoami
User-Agent: curl/7.64.0
Accept: */*
Connection: close

Et le renouvellement de mon certificat dans tout ça ? Et oui il n'est pas automatique avec cette méthode ! Mais nous pouvons le mettre en œuvre facilement :

certbot renew --pre-hook "docker-compose -f path/to/docker-compose.yaml down" --post-hook "docker-compose -f path/to/docker-compose.yaml up -d"

Vous pouvez très bien mettre cette tâche dans un cron de façon quotidienne.


Nous avons donc pu mettre en place une application web, dans notre exemple whoami, derrière Nginx en mode reverse proxy.  

Cette installation, certes minimaliste, nous permet de voir que cette solution est possible et n'est pas forcément très complexe. Toutefois elle demande d'écrire notre fichier de configuration Nginx pour chaque application que nous allons ajouter. Et ça, sans parler du certificat SSL qui nécessite un arrêt de nos services.

Nous pourrons au cours d'un prochain article, améliorer notre service afin de répondre à ces problématiques. Nous en profiterons pour ajouter quelques en-têtes de sécurité.

Vous arrive t-il d'utiliser Nginx comme reverse proxy pour vos conteneurs Docker ?

En tout cas  n'hésitez pas à m'apporter des remarques ou des commentaires sur Twitter ! C'est toujours un plaisir d'avoir des retours ! 😇