Configurer Nginx en tant que reverse proxy

I. Présentation

Lorsque l'on gère une infrastructure web avec plusieurs serveurs et plusieurs sites web, il est souvent utile, pour des raisons de sécurité et de gestion, de mettre en place un reverse proxy.

Un reverse proxy est un serveur faisant tourner un service web, celui-ci va être positionné en frontal d'un ou plusieurs serveurs web.

Note : il ne faut pas confondre proxy et reverse proxy. Un proxy est un serveur qui se positionne généralement entre les utilisateurs et internet et qui permet à ceux-ci d'accéder à internet au travers son service de proxy. Un reverse proxy se positionne en frontal d'un serveur web et permet de rediriger les requêtes venant de l'internet vers des serveurs (web) qui eux, sont en internes du réseau. C'est donc exactement l'inverse.

Sans détailler le fonctionnement exhaustif d'un reverse proxy, il faut savoir que ceux-ci possèdent généralement au moins deux interfaces réseau. La première, destinée à être visible sur internet, va être requêtée par les utilisateurs externes. Le reverse proxy (que l'on peut abrégé "RP") est alors le point d'entrée des requêtes web de tous les serveurs qui sont derrière lui. La seconde interface web est elle orientée vers le réseau interne et permet de communiquer avec le ou les serveurs web qui sont les véritables détenteurs de l'information requêtée par les utilisateurs. Avec un schéma cela sera certainement plus clair :

ansible_linux_architecture02

Le rôle et les intérêts du reverse proxy sont multiple :

  • Permet de gérer la répartition de charge, il entre alors dans un rôle de load-balancer, qui réparti la charge des requêtes sur plusieurs serveurs web
  • Permet de gérer le trafic, pour migrer, éteindre ou gérer la maintenance des serveurs web sans interruption de service
  • Permet de gérer via un point central la configuration et l'accès à plusieurs sites et serveurs web
  • Permet d'optimiser le trafic web, par exemple via du cache et/ou de la compression
  • Ajoute une couche de sécurité, tant au niveau du chiffrement SSL que de la gestion des accès (permet de filtrer des requêtes/URL pour en interdire l'accès par exemple)

Bref, le rôle du proxy est important mais nous ne traiterons pas tous ces sujets dans cet article. Nous allons nous contenter de mettre en place un reverse proxy sous Nginx, celui-ci agira en tant que RP pour deux sites web, situés sur deux serveurs web distincts, exactement comme sur le schéma indiqué plus haut.

Pourquoi Nginx ?

La plupart des services web complets peuvent être configurés en tant que reverse proxy, comme Apache, IIS ou Nginx, alors d'autres services sont uniquement dédié à cette tâche, c'est le cas de HAproxy, comme je l'indique dans ce tuto (qui commence à dater d'ailleurs) : Mise en place d'un serveur HAproxy

Nginx est connu pour être un service web orienté vers la haute performance par rapport à ses concurrents. Le rôle d'un reverse proxy est important et nécessite une machine et un service web très performant car le reverse proxy va devoir encaisser, traiter et gérer la redirection des requêtes de plusieurs sites web, vers plusieurs machines. En cela, il est nécessaire de calibrer la machine reverse proxy correctement en fonction du trafic qu'elle va recevoir.

La position du créateur de Nginx, Igor Sysoev, a dès sa conception en 2002 orienté Nginx vers la performance plutôt que vers la multiplicité des fonctionnalités disponibles. Également, Nginx supporte très bien la monté en charge (au niveau traitement et mémoire). A noter qu'un reverse proxy Apache fera également très bien l'affaire, tout dépend de votre trafic !

SPOF ou Single Point of Failure

Nous l'avons vu, le rôle du reverse proxy est central, en ce sens, il constitue un SPOF ou Single Point of Failure, c'est à dire un point unique de défaillance. Autrement dit, si votre reverse proxy ne fonctionne plus, tous les sites et serveurs web qu'il permettait d'atteindre ne seront plus joignables non plus  ! Je vous oriente vers un autre de mes articles sur ce sujet pour comprendre le problème sous-jacent et envisager des solutions : Qu’est ce qu’un SPOF – Single Point of Failure ?

II. Architecture réseau

Comme je l'ai indiqué plus haut, nous allons dans ce tutoriel travailler avec plusieurs machines. Voici donc le schéma réseau de l'architecture utilisée :

ansible_linux_architecture02

J'ai donc un reverse proxy sous Nginx qui dispose de deux interfaces. Une interface qui va recevoir les requêtes, considérée comme le point d'entrée unique sur internet. Et une autre interface du côté du réseau des serveurs web qui va permettre de discuter avec ces derniers pour leur envoyer leur requêtes respectives.

Un reverse proxy sous Nginx, qui dispose d'une interface réseau avec l'adresse IP "192.168.1.3" et d'une interface réseau avec l'adresse IP "192.168.56.101"

Deux serveurs web Apache2 sous Debian avec les adresses 192.168.56.102 et 192.168.56.103 qui hébergent respectivement "monsite1.com" et "monsite2.com". Dans le cadre d'un RP, on peut appeler ces serveurs les "proxied server"

Vous l'aurez compris ici, les utilisateurs externes aurons comme résolution DNS pour "monsite1.com" et "monsite2.com", l'IP "WAN" de mon reverse proxy, qui gérera ensuite la répartition des requêtes en fonction du nom de domaine visé.

Je considère pour la réalisation de ce tutoriel que  les serveurs web sont déjà opérationnels, ils seront basés sur Apache2 dans mon cas mais le fonctionnement et la configuration d'Nginx est exactement le même quelque soit le ou les services web en backend.

Note : front ou "fronted" désigne dans une architecture web les services et serveurs en qui sont positionnés au plus prés des requêtes clients (en frontal). A l'inverse, les "backend" ou "back" sont les serveurs qui sont situés au plus profond de l’architecture web. Il s'agit souvent des serveurs de traitements, voir des bases de données.

III. Configuration de Nginx

Bien ! Passons maintenant à la configuration qui nous intéresse, celle de Nginx ! Je vais commencer par créer un fichier de configuration par site web (par nom de domaine, pour être plus exacte.) Dans mon cas : /etc/nginx/sites-available/monsite1.com.conf et /etc/nginx/sites-available/monsite2.com.conf

Dans le premier, nous allons inscrire la configuration suivante :

upstream monsite1 {
    server 192.168.56.102;
}

server {
    server_name monsite1.com;

    location / {
        proxy_pass http://monsite1;
        proxy_set_header    Host $host;
        
        proxy_connect_timeout 30;
        proxy_send_timeout 30;
    }
}

Détaillons un peu cela pour mieux comprendre ce que nous faisons. On distingue deux blocs :

  • upstream : Permet de définir un groupe de serveur pouvant répondre à la requête. cette section peut par exemple comprendre des directives indiquant une gestion du load-balancing ou du fail-over, ce qui n'est pas traité dans cet article.
  • server : Contient toutes les options et directives à propos du domaine/site courant.

Dans le bloc "server", nous indiquons le nom du serveur (server_name) auquel Nginx devra réagir. Autrement dit, quand une requête web arrivera sur notre RP avec ce nom de domaine dans le header "Host", cela déclenchera la configuration actuelle.

  • L'option "location" permet de gérer où ces requêtes seront redirigées et dans quelle condition. le "/" après location indique que c'est à la racine du serveur web cible que le site se trouve.
  • proxy_pass : permet d'affecter un ensemble de serveur à cette configuration, le nom à indiqué ici est celui du bloc "upstream", qui, vous vous en doutez, peut contenir plusieurs serveurs cible si notre site web est réparti de façon égale sur plusieurs serveurs.
  • proxy_set_header : permet d'ajouter un header à la requête qui va être passée au serveur web par notre RP. Ici nous ajoutons le header "Host" par exemple, le nom de domaine visé par la reqête initiale
  • proxy_connect_timeout et proxy_send_timeout : plus standard, ces directives permettent de gérer le temps (en seconde) au delà duquel le serveur web (backend) sera considéré comme injoignable/down. Autrement dit si la requête passée par notre RP n'a pas de réponse sous 30 secondes, alors un message d'erreur sera renvoyé à l'utilisateur.

Même chose pour la configuration du deuxième site web (fichier /etc/nginx/sites-available/monsite1.com.conf) :

upstream monsite2 {
    server 192.168.56.103;
}

server {
    server_name monsite2.com;

    location / {
        proxy_pass http://monsite2;
        proxy_set_header    Host $host;
        
        proxy_connect_timeout 30;
        proxy_send_timeout 30;
    }
}

Maintenant que nos configurations sont prêtes, nous allons les passer en mode "enabled", c'est à dire les rendre actives aux yeux de Nginx. Cela se fait en créant un lien symbolique sur le fichier de configuration entre le répertoire des configurations disponibles (sites-available) et des sites actifs (sites-enabled), voyez plutôt :

ln -s /etc/nginx/sites-available/monsite1.com.conf /etc/nginx/sites-enabled/monsite1.com.conf
ln -s /etc/nginx/sites-available/monsite2.com.conf /etc/nginx/sites-enabled/monsite2.com.conf

Il ne nous reste plus qu'à redémarrer Nginx pour que cette configuration soit prise en compte ! 🙂

Ensuite, depuis un poste client qui aura l'IP de notre RP en résolution DNS pour monsite1.com et monsite2.com, nous pourrons nous rendre sur nos deux URL pour constater que nous sommes bien redirigé vers les bons sites respectifs.

Note : A des fins de test, optez simplement pour une page web avec un message différent sur chaque serveur.

Si vous avez besoin de voir cela plus précisément, le plus simple est encore d'afficher le logs de chacun des serveurs web pour voir sur quel serveur atterri votre requête.

IV. Cas spécifique : Ajouter le header X-Forwarded-For

Notre petit reverse proxy est en production et les différentes demandes métiers fusent ! Nous allons ensemble voir un  cas spécifique et courant de modification de la configuration de notre Nginx : l'ajout du header X-Forwarded-For

Le header X-Forwarded-For intervient dans de multiples cas et est particulièrement intéressant. Le reverse proxy agit comme un intermédiaire entre l'utilisateur (client web) et le serveur web. Ainsi, quand une requête arrive au niveau du reverse proxy, celui-ci ne transmet pas cette requête directement au serveur web mais effectue lui même la requête au serveur web pour retranscrire la réponse au client. Autrement dit, il s'agit d'un reverse proxy (niveau HTTP) et non d'un routeur qui transmet de simples paquets.

Par conséquent, la requête web comporte une IP source qui n'est plus la même à l'origine. En effectuant une requête au serveur web, le reverse proxy change l'information de l'IP source de la requête et le serveur web ne peut que croire que c'est le reverse proxy qui a effectué la requête web initialement. Ce qui techniquement est vrai. Cependant, certaines applications web se basent sur l'IP source pour appliquer des restrictions ou des permissions d'accès à certaines zones (par exemple) et l'information "qui souhaite accéder à tel répertoire" doit restée valide. On doit être capable de connaître l'expéditeur originel de la requête : l'IP du client HTTP.

En ce sens, le header "X-Forwarded-For" peut être utilisé et est spécifiquement prévu à cet effet. Il est rajouté par un reverse proxy lorsque celui-ci transmet une requête à un serveur web. Il positionne en valeur de ce header HTTP l'IP du client HTTP. Côté application web, il faut souvent le configurer pour que celui-ci ne prenne non plus l'IP source (au sens réseau) comme source de la requête, mais le X-Forwarded-For.

Afin d'effectuer cet ajout de header, la directive suivante est à ajouter dans la section "location" :

 location / {
        proxy_pass http://monsite1;
        proxy_set_header    Host $host;
        proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_connect_timeout 30;         proxy_send_timeout 30;   
  }

A nouveau, un redémarrage de Nginx est requis pour appliquer cette configuration.

Note : la variable $proxy_add_x_forwarded_for est une variable intégrée et gérée automatiquement par Nginx. A noter que dans la plupart des cas, les variables $proxy_add_x_forwarded_for et $remote_addr contiendront la même valeur.

Néanmoins, si la requête arrive avec un header X-Forwarded-For contenant déjà une valeur, alors, l'avantage de $proxy_add_x_forwarded_for  est que celle-ci va ajouter l'IP source à la suite des valeurs existantes (avec une virgule comme délimiteur) alors que $remote_addr va juste supprimer la ou les valeurs existantes pour y positionner l'IP source actuelle. cela devient technique mais c'est important dans certains cas bien spécifiques 🙂

Nous avons bien fait le tour d'une première configuration d'un reverse proxy, bien que celle-ci puisse être beaucoup plus complète, complexe et efficace. J'attire également votre attention sur la nécessité de prendre en compte la sécurité de votre architecture web et de vos services web dans ce genre de cas, autant au niveau du calibrage de votre architecture (ressources) et de la sécurité de la configuration (gestion des sessions dans un cas de load-balancing, SSL).

N'hésitez pas à partager votre avis/vos conseils dans les commentaires ou à poser vos questions dans notre forum !

Partagez cet article Partager sur Twitter Partager sur Facebook Partager sur Linkedin Partager sur Google+ Envoyer par mail

Mickael Dorigny

Co-fondateur d'IT-Connect.fr. Auditeur/Pentester chez Orange Cyberdéfense.

Nombre de posts de cet auteur : 526.Voir tous les posts

5 thoughts on “Configurer Nginx en tant que reverse proxy

  • J’ai nginx en rp et 1 vm ‘web’ sous apache pour mes différents sites.

    N’as-tu pas de problème pour renouveler tes certificats Let’s encrypt ?

    Répondre
    • Bonjour,

      Personnellement pour renouveler mes certificats justilise certbot et ça fonctionne nikel

      Répondre
    • Merci pour le tuto, j’en ai un aussi, il fonctionne parfaitement, par contre impossible à faire fonctionner avec une ts gateway. Obliger d’utiliser un autre RP iis pour cela. Vous avez deja essayé ?

      Répondre
  • Bonjour, super article, j’ai aussi paramétré un reverse proxy, pour différent nom de domaine, mon problème est comment paramétrer let’s encrypt, pour avoir des certificats valide pour tout mes serveurs

    merci à vous 🙂

    Répondre
  • Je voulais juste rajouter qu’il existe des alternatives à Nginx, Varnish, HaProxy.
    Il y a Kemp avec son logiciel Loadmaster qui existe en version payante ou libre avec toutes les fonctionnalités disponibles dont le WAF. et le support de Let’s Encrypt

    Kemp est un reverse proxy et load balancer, doté de fonctionnalités permettant de sécuriser les sites web (wordpress, Drupal, Joomla…) à des prix défiants toute concurrence :protection DDOS, délégation d’authentification des utilisateurs, IPS, WAF…

    Le WAF est basé sur le système OWASP avec ses règles CRS V3.0, avec une grande simplicité de configuration.

    On peut également créer des solutions de PRA/PCA avec le système GEO GSLB… Ceci permet de facilement de basculer d’un DC à un autre DC en cas de défaillance. Ce point n’a pas été couvert dans ce post…

    Répondre

Répondre à Nono Annuler la réponse

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.