Reverse Proxy Nginx

De Justine's wiki
Version datée du 13 novembre 2019 à 16:09 par Justine (discussion | contributions) (Page créée avec « = Reverse Proxy = == Présentation des reverse-proxies == Les reverse-proxies servent d'intermédiaire entre les utilisateurs externes et le réseau local. Ils ont le r... »)
(diff) ← Version précédente | Voir la version actuelle (diff) | Version suivante → (diff)
Aller à la navigation Aller à la recherche

Reverse Proxy

Présentation des reverse-proxies

Les reverse-proxies servent d'intermédiaire entre les utilisateurs externes et le réseau local. Ils ont le rôle de passerelle entre les réseaux externes et le réseau local, et permettent de contrôler les flux à destination des serveurs web internes (au contraire des proxies qui contrôlent le flux depuis l'intérieur vers l'extérieur). Implémenter des reverse-proxies présente plusieurs avantages :

  • Sécuriser les serveurs web en les protégeant des attaques directes depuis l'extérieur, et en masquant l'architecture
  • Mettre en cache des données statiques
  • Faire du Load-balancing
  • Faire du Fail-Over
  • Compresser les données

Paramétrage des clients

Les clients n'ont pas besoin de configuration particulière, le reverse-proxy est transparent pour eux. On en rencontre en réalité souvent sur Internet, sans en avoir conscience.

Côté serveur

Côté serveur, il est nécessaire d'avoir un cluster de serveurs web. La répartition de charge permet de répartir les requêtes, pour éviter les ralentissements ou plantages serveur. Le Failover quant à lui, se fait parce que le reverser-proxy communique en permanence avec les serveurs internes pour connaître leur disponibilité; le reverse-proxy s'adapte et relaie les requêtes vers les serveurs qui sont fonctionnels (pas de coupure de service !). Pour ce qui est du DNS, la résolution de nom renvoie vers le proxy (et pas un des serveurs web). Le proxy redistribue alors les requêtes.

L'exemple de NginX

Nginx est à la fois un serveur web, et un reverse-proxy. Nous allons ici voir comment le configurer en tant que reverse-proxy pour le web (http et https). Il est capable de renvoyer les requêtes vers un autre serveur web local (qui écoute sur un port autre que 80) ou vers d'autres serveurs de son réseau interne.

Installation et commandes de pilotage de Nginx

L'installation est basique :

sudo apt install nginx

Les commandes de pilotage sont les suivantes :

service nginx {configtest|force-reload|reload|restart|rotate|start|status|stop|upgrade}

On peut tester sa configuration avec :

nginx -t

Configuration de Nginx

À partir d'ici, on verra comment mettre en place le reverse-proxy pour deux sites hébergés sur deux serveurs différents, ainsi que la mise en place du cache et de la compression des données entre le reverse-proxy et le client distant.

Les fichiers de configuration sont placés par défaut dans /etc/nginx.
On va commencer par la configuration globale, dans nginx.conf :

user www-data;                                     # user qui fait tourner nginx
worker_processes 4;                                # nb d'instance nginx (à adapter, 1 par core CPU)
    
# fichier dans lequel est enregistre le pid de nginx (celui du master process, qui lance les workers)
pid /run/nginx.pid;

events {
        worker_connections 1024;                   # nb de connexion max par worker (à adapter)
}
http {
        sendfile on;                               # cf complement pour plus d'infos
        tcp_nopush on;                             # cf complement pour plus d'infos
        tcp_nodelay on;                            # cf complement pour plus d'infos
        server_tokens off;                          # masque la version de nginx dans les messages
        
        # delai d'attente en seconde avant la fermeture d'une connexion   
        keepalive_timeout 65;               
        include /etc/nginx/mime.types;             # liste des types MIME (type de fichier par extension)
        default_type application/octet-stream;     # type par defaut des fichiers non repertories dans mime.type
        access_log /var/log/nginx/access.log;      # fichier de log des acces
        error_log /var/log/nginx/error.log;        # fichier de log des erreurs
        include /etc/nginx/conf.d/*.conf;          # inclusion des fichiers de configuration (on trouvera notamment                                                     le fichier de conf pour le reverse proxy)
        include /etc/nginx/sites-enabled/*;        # configuration des sites web actifs
}

Pour plus d'explications sur les directives : http://nginx.org/en/docs/http/ngx_http_core_module.html

Quelques détails supplémentaires  (from : https://thoughts.t37.net/optimisations-nginx-bien-comprendre-sendfile-tcp-nodelay-et-tcp-nopush-2ab3f33432ca):

  • tcp_nodelay : Par défaut, un paquet TCP peut attendre jusqu'à 0.2 secondes avant d'être envoyé, si il est trop petit, par exemple. Cette option force nginx à envoyer directement les données, sans attendre de faire des paquets tcp assez gros. En effet, ce comportement par défaut cause une latence sur le dernier paquet : il est rare qu'un fichier fasse un nombre exact de paquets, et le dernier se retrouve en général à être un peu petit (genre, un octet de données pour 40 octets de headers TCP + IP).
  • tcp_nopush : Cette directive fait un peu le contraire de tcp_nodelay : au lieu d'optimiser les délais d'envoi, elle optimise la quantité d'informations envoyées en une seule fois. tcp_nopush existe dans pile TCP de FreeBSD, mais sous Linux, elle correspond à TCP_CORK. CORK oblige tcp à envoyer des paquets qui font la taille de la MSS (Maximum Segment Size), soit la MTU moins les en-têtes TCP et IP.
  • sendfile : Cette directive fait la force de Nginx avec les deux autres. Elle oblige l'utilisation de l'appel système sendfile pour tous les envois de fichiers. Je passe les détails incompréhensibles, mais en gros, c'est plus efficace.

Compression des données

Pour le moment, le cache et la compression des données ne sont pas configurés. On va donc configurer la compression des données (la configuration du cache se fera dans le fichier de configuration proxy.conf).

Pour gérer la compression des données, on rajoute les lignes suivantes dans la partie http du fichier de configuration :

gzip on;                                             # active la compression
gzip_comp_level 5;                                    # niveau de compression (de 1 a 9, 9 pour la compression max)
gzip_http_version 1.0;                                 # version HTTP minimum pour que la compression soit active
gzip_proxied any;                                     # active la compression pour les requetes faites par les clients qui passent par un proxy
gzip_types text/plain text/html text/css image/x-icon application/x-javascript;   # types MIME pour lesquels la compression est active
gzip_disable "MSIE [1-6]\.(?!.*SV1)";                # désactive la compression pour les navigateurs qui ne supportent pas

Plus de détails sur la compression : http://nginx.org/en/docs/http/ngx_http_gzip_module.html

Reverse-proxy

Pour les directives relatives au reverse-proxy, on va, pour plus de clarté, créer un fichier à part. On y définira aussi les options de cache. On va créer le fichier /etc/nginx/conf.d/proxy.conf et y mettre les lignes suivantes :

proxy_redirect          off;                                                  # desactive la reecriture d'url (inutile ici, les sites sont heberges sur d'autres serveurs)
proxy_set_header        Host            $host;                                # permet de modifier l'entete HTTP
proxy_set_header        X-Real-IP       $remote_addr;                         # permet de modifier l'entete HTTP
proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;           # permet de modifier l'entete HTTP
proxy_hide_header       X-Powered-By;                                # cache le champ X-Powered-By de l'entête HTTP
proxy_intercept_errors on;                                  # permet d'intercepter les réponses avec un code de retour supérieur ou égal à 300

proxy_cache_path /var/cache/nginx/site1 levels=1:2 keys_zone=site1:10m inactive=7d max_size=700m;   
                      # definit le chemin et les parametres du cache pour le site 1

proxy_cache_path /var/cache/nginx/site2 levels=1:2 keys_zone=site2:10m inactive=7d max_size=700m;  
                      # definit le chemin et les parametres du cache pour le site 2

proxy_cache_path /var/cache/nginx/site3 levels=1:2 keys_zone=site3:10m inactive=7d max_size=700m;  
                      # definit le chemin et les parametres du cache pour le site 3

Ici, les directives proxy_set_header permettent de réécrire le header pour que les serveurs web voient les requêtes arriver du client, plutôt que du proxy.

Déclaration des serveurs arrières (backend)

On ne prendra ici pour exemple qu'un seul serveur arrière.

Deux dossiers importants :

  • /etc/nginx/sites-available : Contient les configurations des sites (tous les sites, même inactifs)
  • /etc/nginx/sites-enabled : contient des liens symboliques vers les fichiers de configuration des sites actifs (même principe qu'Apache).

On va ici voir l'exemple du fichier de site reverse-site1. C'est un fichier de configuration standard de Nginx. Il est à placer dans available avec un lien dans enable, comme pour Apache. Attention, ce fichier est bien sur le proxy, par sur les serveurs webs en eux-même !

server {
        listen 80;                                         # port d'ecoute
        server_name www.site1.domain site1.domain;         # nom du serveur arriere (ici repondra  sites www.site1.domain et site1.domain

        if ($request_method !~ ^(GET|HEAD|POST)$ ) {       # Si la requete n'est pas de type GET ou HEAD ou POST
                return 444;                                # on renvoie le code 444 (no data), permet de fermer la connexion
        }

        location / {                                        
                proxy_cache site1;                         # nom de la zone de cache
                proxy_cache_valid 200 302 2d;              # duree du cache pour les codes de retour 200 et 302 (ici 2 jours)
                proxy_cache_valid 404 1m;                  # duree du cache pour les code de retour 404 (ici 1 minute)
                proxy_pass http://www.site1.domain/;            # url du serveur arriere
        }

        access_log /var/log/nginx/site1-access.log;        # fichier de log des acces
        error_log /var/log/nginx/site1-error.log;          # fichier de log des erreurs
}

Load Balancing

Le load balancing se fait avec la directive upstream. Par défaut, Nginx fonctionne en mode round-robin (chaque serveur est interrogé à tour de rôle). Un exemple ici de la doc (http://nginx.org/en/docs/http/ngx_http_upstream_module.html#upstream)

upstream '''backend''' {
    server backend1.example.com       weight=5; 
    server backend2.example.com:8080;
    server unix:/tmp/backend3;

    server backup1.example.com:8080   backup;
    server backup2.example.com:8080   backup;
}

server {
    location / {
        proxy_pass http://'''backend''';
    }
}

 Quelques explications :

  • server suivi d'une IP:un nom de domaine et d'un port (éventuel) permet d'indiquer un serveur backend;
  • weight correspond au poids du serveur (par défaut, 1). Avec l'exemple du dessus, pour 7 requêtes, 5 vont au premier serveur, une au deuxième, et une au troisième.
  • backup marque le serveur en question comme serveur de backup, utilisé seulement si els serveurs primaires sont indisponibles.
  • Bien d'autres options sont disponibles.

Mise en place d'un reverse-proxy

Nous allons mettre en place un reverse-proxy, en nous basant sur cette architecture :

RTENOTITLE

 

Le serveur proxy sera la même machine que j'ai utilisé pour Squid plus tôt; l'utilisateur externe sera en fait ma machine hôte, avec l'IP donnée par le DHCP de la box.

Je commence par mettre en place mes 4 serveurs web, des Debian avec Apache. Rien de compliqué, mais je m'assure que le site01 et le site02 aient une page d'acceuil différente (je ne crée pas d'hôte virtuel : les sites doivent répondre directement depuis leur IP). Une fois que c'est fait et que je les ai testés (avec un client...), je passe à la configuration du reverse-proxy.

C'est parti pour les différents fichiers !

/etc/nginx.conf

#/etc/nginx/nginx.conf

user www-data;                                     # user qui fait tourner nginx
worker_processes 2;                                # nb d'instances nginx (ici, ma VM a deux coeurs CPU)
    
# fichier dans lequel est enregistre le pid de nginx (celui du master process, qui lance les workers)
pid /run/nginx.pid;

events {
        worker_connections 4;                   # nb de connexion max par instance (j'en mets peu, pour l'exemple)
}
http {
        sendfile on;                               # Optimisation des performances
        tcp_nopush on;                             # Optimisation des performances
        tcp_nodelay on;                            # Optimisation des performances
        server_tokens off;                         # masque la version de nginx dans les messages
        
           
        keepalive_timeout 65;                      # delai d'attente en seconde avant la fermeture d'une connexion               
        include /etc/nginx/mime.types;             # liste des types MIME (type de fichier par extension)
        default_type application/octet-stream;     # type par defaut des fichiers non repertories dans mime.type
        access_log /var/log/nginx/access.log;      # fichier de log des acces
        error_log /var/log/nginx/error.log;        # fichier de log des erreurs
        include /etc/nginx/conf.d/*.conf;          # inclusion des fichiers de configuration (on trouvera notamment proxy.conf)
        include /etc/nginx/sites-enabled/*;        # configuration des sites web actifs

###Compression

gzip on;                                                                          # active la compression
gzip_comp_level 5;                                                                # niveau de compression (de 1 a 9, 9 pour la compression max)
gzip_http_version 1.0;                                                            # version HTTP minimum pour que la compression soit active
gzip_proxied any;                                                                 # active la compression pour les requetes faites par les clients qui passent par un proxy
gzip_types text/plain text/html text/css image/x-icon application/x-javascript;   # types MIME pour lesquels la compression est active
gzip_disable "MSIE [1-6]\.(?!.*SV1)";                                             # désactive la compression pour les navigateurs qui ne supportent pas

}

/etc/nginx/conf.d/proxy.conf

#/etc/nginx/conf.d/proxy.conf

#Ici se trouve la configuration générale du reverse-proxy

###Options générales
proxy_redirect          off;                                                  # desactive la reecriture d'url (inutile ici, les sites sont heberges sur d'autres serveurs)
proxy_set_header        Host            $host;                                # permet de modifier l'entete HTTP
proxy_set_header        X-Real-IP       $remote_addr;                         # permet de modifier l'entete HTTP
proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;           # permet de modifier l'entete HTTP
proxy_hide_header       X-Powered-By;                                         # cache le champ X-Powered-By de l'entête HTTP
proxy_intercept_errors on;                                                    # permet d'intercepter les réponses avec un code de retour supérieur ou égal à 300

###Options de cache

proxy_cache_path /var/cache/nginx/site01 levels=1:2 keys_zone=site1:10m inactive=7d max_size=700m;   
                      # definit le chemin et les parametres du cache pour le site 1

proxy_cache_path /var/cache/nginx/site02 levels=1:2 keys_zone=site2:10m inactive=7d max_size=700m;  
                      # definit le chemin et les parametres du cache pour le site 2

 

/etc/nginx/sites-available/site01.conf

#/etc/nginx/sites-available/site01.conf

upstream site01 {                #Ici, je configure mes deux serveurs backend
    server 192.168.56.101:80 weight=2;    #Répartition de charge : ce serveur (srv1-backend) recoit 2/3 des requêtes
    server 192.168.56.102:80 weight=1;    #Répartition de charge : ce serveur (srv2-backend) recoit 1/3 des requêtes
}

server {                                #Ici, je configure le site en lui même
    listen        80;                                             #Le port sur lequel Nginx écoute
    server_name    www.site01.mondomaine site01.mondomaine;    #L'adresse de ce site pour le client

    location / {                            #Ce bloc contient les directives de fichiers...
        proxy_cache site1;                    #Le cache est "site1" défini dans proxy.conf
        proxy_cache_valid 200 302 2d;                #Cache valide 1 jour pour les pages qui fonctionnent
        proxy_cache_valid 404 1m;                #Cache valide 1 minute pour les erreurs 404
        proxy_pass http://site01;                #On renvoie vers le bloc "upstream site01", soit vers nos serveurs backend
    }
}

 

/etc/nginx/sites-available/site02.conf

#/etc/nginx/sites-available/site02.conf

upstream site02 {                         #Ici, je configure mes deux serveurs backend, à poids égal (par défaut, weight=1)
        server 192.168.56.103:80;         #Répartition de charge : ce serveur (srv3-backend) reçoit la moitié des requêtes
        server 192.168.56.104:80;         #Répartition de charge : ce serveur (srv3-backend) reçoit la moitié des requêtes
}

server {                                                                #Ici, je configure le site en lui même
        listen          80;                                             #Le port sur lequel Nginx écoute
        server_name      www.site02.mondomaine site02.mondomaine;       #L'adresse de ce site pour le client

        location / {                                                    #Ce bloc contient les directives de fichiers...
                proxy_cache site2;                                      #Le cache est "site1" défini dans proxy.conf
                proxy_cache_valid 200 302 2h;                           #Cache valide 1 jour pour les pages qui fonctionnent
                proxy_cache_valid 404 1m;                               #Cache valide 1 minute pour les erreurs 404
                proxy_pass http://site02;                               #On renvoie nos deux serveurs backend
        }
}

Création du dossier de cache

Il faut créer le dossier de cache et lui filer les droits :

mkdir /var/cache/nginx
cd /var/cache/nginx
mkdir site01 site 02
cd ..
chown -R www-data:www-data nginx

Tests et activation

Notre site est enfin en place ! Il n'y a plus qu'à tester. D'abord les fichiers de configurations :

nginx -t #Doit dire que c'est bon

Ensuite, on redémarre nginx :

systemctl restart nginx
systemctl status nginx #Très important : nginx -t ne renvoie pas toutes les erreurs !

Plus qu'à se rendre sur un navigateur client (côté WAN) pour tester.

Si le cache pose problème, il est possible de vider ses dossiers !