Haproxy : load balancer et plus
Haproxy : load balancer et plus jppHaproxy a pour but premier d'être un "load balancer" mais il permet beaucoup plus et permet de mettre un serveur Apache un peu plus à l'abri.
Ici sont présentées quelques exemple d'application de cet outil presque universel.
- Limitation du nombre de connexions à un serveur (Web ou autres) qui permet d'éviter la saturation du serveur.
- Il permet aussi de réaliser certains "filtrages", attention ce n'est pas un WAF, mais il permet de réaliser pas mal de choses.
- Depuis assez longtemps maintenant il permet de gérer le HTTPS et de "comprendre" ce que lui envoie un client et de l'orienter là où il faut. Il permet aussi de "prendre" la communication HTTPS et de la relayer en HTTP vers les serveurs WEB.
En bref c'est un outil indispensable sur tout serveur HTTP.
Il est aussi utilisable en load balancer sur différents protocoles TCP et permet aussi de protéger un peu (notamment des surcharges) les services "cachés" derrière.
Note 2024 : par ailleurs c'est un logiciel bien maintenu et qui propose régulièrement de nouvelles versions depuis plus de 10 ans.
Haproxy : anti DDOS
Haproxy : anti DDOS jppHAPROXY est, entre autres choses, un load-balancer très souple qui peut apporter en outre un gain en sécurité par une gestion spécifique des connexions. Il "protège" les serveurs Web en limitant le nombre des connexions simultanées afin d'empêcher la saturation du serveur. De nombreux tests montrent que si Haproxy est bien réglé on peut garder un accès (pas exemple ssh) sur un serveur surchargé de demandes d'accès HTTP (qui a dit malicieuses ?), cela semble notamment efficace contre les attaques de style «Killer2 / Slowloris» voir les sites : Securityvibes et Phrack à ce sujet.
Tous les exemples fournis dans cet article sont fondés sur le protocole HTTP, mais Haproxy permet aussi de gérer d'autres protocoles, en fait pratiquement tous les protocoles TCP, je l'ai vu utilisé pour un proxy Mysql.
Son empreinte mémoire et sa consommation CPU réduite ne chargent que peu les serveurs, c'est donc un investissement d'un très bon rapport qualité/coût.
- nombre de requêtes, d'octets «in», d'octets «out» par «frontend»
- nombre de requêtes, d'octets «in», d'octets «out» par «backend»
Le paramétrage relativement simple et compréhensible, est divisé en «blocs» délimités par des mots clefs:
- global
- defaults
- frontend nom_du_frontend
- backend nom_du_backend
- listen nom_du_listen
Section «global».
Ici sont définis principalement les paramètres communs à tous les «frontend» et «backend».
log, nombre total de connexions gérées :
log 127.0.0.1 local0
maxconn 512
Lancement en «démon» (arrière plan) cette option est obligatoire pour que le processus se «détache» de la console, l'utilisateur et le groupe sous lequel le programme doit tourner, l'option «chroot» qui permet de mieux protéger le système en isolant le processus dans une cage :
chroot nom_du_répertoire_cage
user haproxy
group haproxy
daemon
L'option "chroot" est fortement conseillée, le répertoire indiqué peut être «vide» puisque, une fois lancé le processus ne gère que des accès réseau.
On peut par ailleurs utiliser les options «debug» ou «quiet» pour augmenter ou diminuer le niveau de log.
Section «defaults».
Ici sont définis divers paramètres permettant de limiter la durée des connexions afin de protéger le serveur et les services WEB. Sont définis aussi les protocoles suivis (pas uniquement du http). En utilisant «http» il est possible d'utiliser des outils d'analyse des requêtes, par exemple pour rediriger des domaines différents sur des serveurs (ou ports) différents et «éliminer» les demandes de sites non gérés.
Voici un exemple des valeurs adoptées :
timeout client 15s
timeout server 15s
timeout queue 15s
timeout connect 8s
timeout http-request 8s
Ces valeurs, bien sûr, sont à adapter selon les services demandés et une option spécifique http:
option forwardfor
Section «frontend».
C'est ici que l'on peut «repérer» les motifs qui nous intéressent dans les flux HTTP, par exemple le nom du site demandé pour effectuer les bonnes redirections.
Choisir adresse/port :
bind 0.0.0.0:80
La distribution vers différents «backend» s'effectue avec l'aide de directives «ACL», exemple:
acl PRA hdr_beg(host) siteA
acl PRB hdr_beg(host) siteB
il existe un très grand nombre de fonctions de détection, celle utilisée ici (hdr_beg) teste le début du nom de site. Il suffit ensuite de diriger la requête en fonction de l'acl vers le bon «backend» par une directive telle que celle ci dessous :
use_backend BACKA if PRA
use_backend BACKB if PRB
et prévoir une sortie spécifique aux requêtes non sélectionnées :
default_backend POUBEL
dans lequel sont traitées les demandes «résiduelles».
Il est parfaitement possible de créer plusieurs «Frontend» écoutant sur plusieurs couples adresse/port différents. Il est ainsi possible de différencier des accès à la même application par le couple adresse/port pour, par exemple, avoir des statistiques différenciées pour chaque partie de l'application en utilisant un couple adresse/port différent pour les accès «administratifs». Les statistiques du site ne seront ainsi pas «polluées» par des accès liés à l'administration et l'on pourra aussi avoir des statistiques sur les accès administratifs. On peut faire de même pour des accès intranet/extranet afin de disposer de statistiques plus précises.
Section «backend».
C'est ici qu'est fait le lien vers le service sélectionné, celui-ci peut alors être envoyé sur un ou des serveurs spécifiques avec une fonction de répartition de charge :
option httpchk
balance roundrobin
cookie SERVERID rewrite
server local 127.0.0.1:84 maxconn 127
option httpclose
option abortonclose
retries 2
La directive «server» peut être répétée pour décrire les différents serveurs entre lesquels doivent être réparties les requêtes exemple:
server srv1 192.168.1.201:81 cookie srv1 weight 2 check
server srv2 192.168.1.201:81 cookie srv2 weight 1 check
Ce groupe de directives permet de répartir sur srv1 et srv2 en considérant que srv1 doit recevoir deux fois plus de requêtes que srv2.
Ce logiciel est facile à employer et bourré de fonctionnalités intéressantes.
Un petit exemple des statistiques disponibles :
Haproxy : quelques précisions
Haproxy : quelques précisions jppAfin de vérifier les interactions entre HAPROXY et APACHE et aussi essayer de trouver un «bon» réglage j'ai décidé de faire quelques essais de performance.
J'ai «cloné» le site (celui-ci) sur une machine virtuelle (cela ne doit pas beaucoup changer puisque Gandi fournit des machines virtuelles XEN) afin de pouvoir effectuer des tests de saturation et de mesure des temps de réponse en fonction du nombre de connexions en lecture seule.
Pour «charger» le service au maximum j'ai utilisé le programme «Siege» disponible dans Debian) qui permet de simuler un grand nombre de connexions à partir d'une simple liste d'URL. Comme source j'ai utilisé les URL fournies par le plan du site afin d'avoir un maximum de pages, le site n'étant pas immense j'ai obtenu #150 pages différentes pour mes tests. J'ai d'abord effectué quelques tests de charge en faisant varier quelques paramètres «Apache» :
- StartServers : nombre de processus lancés au démarrage
- MinSpareServers : nombre mini de serveurs qui doivent être disponibles
- MaxSpareServers : nombre maxi de serveurs qui peuvent être libres (si plus des processus sont tués)
- MaxClients : nombre de processus à ne pas dépasser.
Le paramètre le plus critique pour l'utilisation mémoire maximale est «MaxClient» (il pourra y avoir MaxClient processus Apache au maximum), les autres sont surtout destinés à l'optimisation de l'occupation mémoire moyenne. Pour simplifier je positionne «StartServers» = «MaxClient» = MaxSpareServers et «MinSpareServers» à 1.
Après quelques tests j'ai obtenu le meilleur fonctionnement en fixant «MaxClient» à 30 ce qui donne une utilisation mémoire de #150Mo (y compris Mysql). J'ai donc ensuite effectué des tests de «charge» avec «Siege» réglé pour envoyer les URL à la vitesse maximale depuis 10 <= N <= 140 «clients» différents et simultanés.
Le schéma des machines utilisées est le suivant :
- une machine « injecteur », c'est une machine physique
- une machine virtuelle « haproxy »
- un ou deux serveurs Apache + Mysql dans deux machines virtuelles sur deux serveurs différents.
Les résultats en fonction du nombre de requêtes en parallèle sont les suivants :
Apache + Haproxy | Apache (2 serveurs)+ Haproxy | ||||||||||
Flux | Req. | Moy. | mini | Maxi | Vol | Req. | Moy. | mini | Maxi | Vol | |
nombre | (sec) | (sec) | (sec) | MO/sec | (sec) | (sec) | (sec) | MO/sec | |||
10 | 9585 | 0,09 | 0,02 | 0,35 | 4,12 | 14825 | 0,06 | 0,01 | 1,52 | 6,36 | |
9637 | 0,09 | 0,02 | 0,34 | 4,12 | 15754 | 0,06 | 0,01 | 1,07 | 6,71 | ||
9611 | 0,09 | 0,02 | 0,35 | 4,12 | 15290 | 0,06 | 0,01 | 1,30 | 6,54 | ||
20 | 9616 | 0,19 | 0,04 | 0,54 | 4,12 | 15760 | 0,11 | 0,01 | 1,71 | 6,69 | |
9628 | 0,19 | 0,05 | 0,51 | 4,14 | 16117 | 0,11 | 0,01 | 1,61 | 6,88 | ||
9622 | 0,19 | 0,05 | 0,53 | 4,13 | 15939 | 0,11 | 0,01 | 1,66 | 6,79 | ||
40 | 9487 | 0,38 | 0,08 | 0,92 | 4,09 | 15417 | 0,23 | 0,01 | 2,98 | 6,55 | |
9491 | 0,38 | 0,05 | 0,78 | 4,06 | 14902 | 0,24 | 0,01 | 2,76 | 6,39 | ||
9489 | 0,38 | 0,07 | 0,85 | 4,08 | 15160 | 0,24 | 0,01 | 2,87 | 6,47 | ||
60 | 9583 | 0,56 | 0,18 | 0,88 | 4,02 | 14294 | 0,37 | 0,01 | 5,72 | 6,04 | |
9566 | 0,56 | 0,15 | 0,98 | 4,09 | 14659 | 0,37 | 0,02 | 8,02 | 6,17 | ||
9575 | 0,56 | 0,17 | 0,93 | 4,06 | 14477 | 0,37 | 0,02 | 6,87 | 6,11 | ||
80 | 9372 | 0,76 | 0,20 | 1,49 | 3,98 | 13446 | 0,54 | 0,03 | 6,37 | 5,34 | |
9588 | 0,74 | 0,06 | 1,18 | 4,11 | 13455 | 0,53 | 0,03 | 7,88 | 5,33 | ||
9480 | 0,75 | 0,13 | 1,34 | 4,05 | 13451 | 0,54 | 0,03 | 7,13 | 5,34 | ||
100 | 9274 | 0,96 | 0,19 | 4,02 | 4,01 | 13446 | 0,66 | 0,05 | 7,64 | 5,21 | |
9439 | 0,95 | 0,55 | 1,44 | 4,05 | 13272 | 0,67 | 0,10 | 5,69 | 5,04 | ||
9357 | 0,96 | 0,37 | 2,73 | 4,03 | 13359 | 0,67 | 0,08 | 6,67 | 5,13 | ||
120 | 9504 | 1,13 | 0,22 | 4,26 | 4,04 | 13870 | 0,77 | 0,17 | 5,36 | 5,43 | |
9622 | 1,11 | 0,27 | 4,31 | 4,12 | 14174 | 0,76 | 0,20 | 6,50 | 5,50 | ||
9563 | 1,12 | 0,25 | 4,29 | 4,08 | 14022 | 0,77 | 0,19 | 5,93 | 5,47 | ||
140 | 14473 | 0,87 | 0,11 | 6,03 | 5,58 | ||||||
14174 | 0,76 | 0,20 | 6,50 | 5,50 | |||||||
14324 | 0,82 | 0,16 | 6,27 | 5,54 |
On remarque que le nombre de requêtes traitées ne varie que peu en fonction du nombre d'injecteurs utilisés en parallèle. En effet dans tous les cas les serveurs sont à peu près à 100% de CPU puisque l'injection est effectuée à vitesse maxi, dès qu'une requête est revenue la suivante est envoyée. Les seules valeurs qui varient de manière significative sont celles correspondant aux temps de réponse moyen, mini et maxi. L'apport d'un deuxième serveur donne, en dehors de la fiabilité, un gain de 40 à 60%.
Avec deux serveurs Apache on n'obtient pas des performances doubles mais l'analyse des statistiques par serveur (merci le tableau récap de Haproxy) montre que l'un des serveurs traite plus de 8500 requêtes et l'autre seulement de 6 à 7000 sans que j'aie pu trouver une explication évidente. Toutefois la machine d'injection ne dispose que d'un accès réseau à 100Mbits qui permet un débit d'environ 9Mo/s vers la machine virtuelle supportant Haproxy. Le deuxième serveur Apache était situé sur une machine physique différente de celle supportant Haproxy et le premier serveur Apache.
Je vais tenter quelques tests réseau pour essayer de comprendre ces limitations.
Tests réseau réalisés avec "Iperf" installé sur toutes les machines physiques et virtuelles, résultats "bruts" :
- Injecteur <--> machine "Haproxy" : 94Mbits/s soit # 12Mo/s
- Machine Haproxy <--> serveur "rapide" : 160Mbits/s soit#20Mo/s
- Machine Haproxy <--> serveur "lent" : 100Mbits/s soit#12Mo/s
Seul l'injecteur est une machine physique. Au vu des performances réseau (maxi < 12Mo/s) le débit maximum aux environs de 6,5Mo/s est pratiquement dans les limites puisqu'il dépasse 50% du débit maxi constaté auquel il faut ajouter le débit inverse (5 à 10%) pour le contrôle de la transmission (ACK).
HAPROXY : berner les malins
HAPROXY : berner les malins jppHaproxy, un peu d'humour, Haproxy pour berner les malins (vous avez dit malicieux ?).
J'ai constaté dans les logs d'un serveur WEB un grand (très, trop ?) nombre de tentatives de connexion sur des URL de type :
- "http://......../Phpmyadmin" avec en suffixe toutes les variantes de version possibles et inimaginables,
- "http://....../default/......" avec un grand nombre de patterns différents
On peut utiliser "Haproxy" pour berner ce genre de petits malins (vous avez dit pernicieux ?) de la façon suivante :
- Créer un "backend" spécial contenant une redirection vers un autre site :
mode http
balance source
option nolinger
server web 127.0.0.1:8080 redir http://www.gotohell.com - backend SALE
Un backend bidon ... - Créer quelques règles sélectives renvoyant vers ce "back end" :
acl SALE path_beg /php
acl SALE path_beg /def
acl SALE path_end .php
use_backend SALE if SALE
Toutes les URL dont la partie "chemin" (après le "/") commence par "/php" ou "/def" ou dont la partie droite se termine par ".php" sont redirigées vers le site indiqué.
Et oui, les logs sont réduits et plus digestes ... Haproxy, oui, cela peut être amusant !
Haproxy : ajouter IPV6
Haproxy : ajouter IPV6 jppNote 2024 : ceci a été testé chez Gandi, mais je n'ai pas encore mis en route IPV6 à la maison.
Haproxy et IPV6, mais c'est très simple .....
Vous avez une belle installation de HAPROXY qui fonctionne à merveille en IPV4, votre serveur, Apache par exemple est bien à l'abri derrière et vous désirez ajouter un accès IPV6.
Mais, c'est très simple il suffit d'ajouter un nouveau "FRONTEND" presque semblable à celui de IPV4 mis à part :
- le nom
- l'adresse d'écoute.
Le FRONTEND original :
frontend PUBLIC
bind 12.34.56.78:80
#
acl PR2 path_end XXXXXX
use_backend TOTO if PR2
# OK
acl PR1 hdr_beg(host) exemple
use_backend EXEMPLE if PR1
default_backend apache
A ceci il suffit d'ajouter une copie conforme :
frontend PUBLICV6
bind 2001:xxxx:xxx:xx:xxx:4aff:fe01:a1d1:80
#
acl PR2 path_end XXXXXX
use_backend TOTO if PR2
# OK
acl PR1 hdr_beg(host) exemple
use_backend EXEMPLE if PR1
default_backend apache
Vous relancez Haproxy et ... votre site est accessible en IPV6, simple non ?
Allez il ne vous reste plus qu'à disposer d'un accès IPV6 et à le décrire pour Apache.
Haproxy : passage en HTTPS
Haproxy : passage en HTTPS jppHAPROXY et HTTPS.
Depuis les événements frappant Internet (Snowden …) tout le monde veut passer en HTTPS avec des éléments récents (Heartbleed est passé par là).
J'utilise « Haproxy » pour gérer des sites et j'attendais avec impatience qu'il supporte SSL, ce qui semble fait dans les versions 1.5. Malheureusement il n'existe pas encore de version « stable » il faut donc passer par les versions « dev ». Il faudra suivre le rythme des développeurs pour rester à la pointe en attendant la version stable. Quelques séances de test en perspective à chaque avancée et de la doc à lire pour exploiter les nouvelles fonctionnalités.
L'avantage d'utiliser Haproxy pour gérer le SSL c'est qu'il n'y a rien à modifier dans la configuration Apache !
Pour les tests j'ai utilisé la dernière version de HAPROXY disponible, ici la version 1.5-dev24 à compiler avec :
make TARGET=linux2628 USE_OPENSSL=1 USE_ZLIB=1
Ce sont les paramètres « mini » permettant l'utilisation de SSL et de la compression.
Aux paramètres existants dans mon « haproxy.cfg » il m'a suffi d'ajouter les sections traitant le SSL (FRONTEND et BACKEND) :
frontend SSL-PUBLIC
bind *:443 ssl crt /etc/haproxy/ssl/ssl-cert-snakeoil.pem
#
mode http
option httpclose
option forwardfor
reqadd X-Forwarded-Proto:\ https
# OK
acl SS-PR1 hdr_beg(host) performance
use_backend SS-PERF if SS-PR1
# OK
acl SS-CN1 hdr_beg(host) consult
use_backend CONS if SS-CN1
# OK
acl SS-GN1 hdr_beg(host) gandizzop
use_backend GAND if SS-GN1
# OK
acl SS-GAL hdr_beg(host) galerie
use_backend GALERIE if SS-GAL
#
backend SS-PERF
option httpchk balance roundrobin
cookie SERVERID rewrite
# cookie SERVERID insert indirect nocache
server local 127.0.0.1:82 maxconn 127
#
option httpclose
option abortonclose
retries 2
compression algo gzip
compression offload
backend SS-CONS
option httpchk
balance roundrobin
cookie SERVERID rewrite
# cookie SERVERID insert indirect nocache
server local 127.0.0.1:83 maxconn 127
#
option httpclose
option abortonclose
retries 2
compression algo gzip
compression offload
Rien à changer du coté de la configuration Apache …. au passage on compresse les données envoyées avec gzip.
Dans le cas de Drupal 7 (7.27 dans mon cas) l'accès est anormal, la première page est accédée normalement mais certains éléments, dont les CSS et JS, sont accédés en HTTP simple ce qui provoque un « blocage » de ces ressource au niveau de Firefox ou Chrome. Il m'a été nécessaire de modifier un fichier de configuration (sites/default/settings.php) pour y ajouter la ligne suivante :
$base_url = 'https://le_nom_du_site'; // NO trailing slash!
Dans mon cas cette ligne avait été mise en commentaire et cela ne gênait pas du tout le fonctionnement en HTTP. Après l'ajout de cette ligne le fonctionnement en HTTP reste parfaitement OK et l'accès en HTTPS est enfin correct !
Haproxy : ssl version 2016
Haproxy : ssl version 2016 jppAprès de nombreuses années j'ai décidé de passer ce site en HTTPS exclusif, là encore Haproxy m'a simplifié la tâche car il permet de :
- rediriger le HTTP vers le HTTPS (envoi d'un code 301)
- gérer la connexion HTTPS
Par contre pour ne pas "perdre" l'information IP du client d'origine il faut veiller à bien positionner l'option "forwardfor" et à configurer le log de Apache (ici 2.4.x) pour récupérer l'information du header X-Forwarded-For inséré par Haproxy.
Redirection HTTP --> HTTPS.
Le "Backend" de la partie HTTP devient :
#-------------------------------------------------------------
backend PERF
mode http
option forwardfor
http-request redirect scheme https code 301
On ne peut guère faire plus simple !
Partie HTTPS, frontend et backend.
J'ai choisi de rester en HTTPS pour "sortir" de Haproxy et aller sur le serveur WEB car Haproxy est installé sur un "frontal" alors que le serveur Apache est "derrière" (c'est en fait une Machine Virtuelle KVM).
Frontend.
Il faut positionner un certain nombre de caractéristiques liées à SSL, par exemple éviter SSLV3 ! Cette partie a été copiée (sauf "tune.ssl.default-dh-param 2048") du site Haproxy.
# Default ciphers to use on SSL-enabled listening sockets.
# For more information, see ciphers(1SSL). This list is from:
# https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
ssl-default-bind-ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA
ssl-default-bind-options no-sslv3
tune.ssl.default-dh-param 2048