Haproxy : load balancer et plus

Haproxy : load balancer et plus jpp

Logo HAPROXY

Haproxy 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 jpp

HAPROXY 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 jpp

Afin 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
FluxReq.Moy.miniMaxiVol Req.Moy.miniMaxiVol
nombre (sec)(sec)(sec)MO/sec  (sec)(sec)(sec)MO/sec
1095850,090,020,354,12 148250,060,011,526,36
 96370,090,020,344,12 157540,060,011,076,71
 96110,090,020,354,12 152900,060,011,306,54
2096160,190,040,544,12 157600,110,011,716,69
 96280,190,050,514,14 161170,110,011,616,88
 96220,190,050,534,13 159390,110,011,666,79
4094870,380,080,924,09 154170,230,012,986,55
 94910,380,050,784,06 149020,240,012,766,39
 94890,380,070,854,08 151600,240,012,876,47
6095830,560,180,884,02 142940,370,015,726,04
 95660,560,150,984,09 146590,370,028,026,17
 95750,560,170,934,06 144770,370,026,876,11
8093720,760,201,493,98 134460,540,036,375,34
 95880,740,061,184,11 134550,530,037,885,33
 94800,750,131,344,05 134510,540,037,135,34
10092740,960,194,024,01 134460,660,057,645,21
 94390,950,551,444,05 132720,670,105,695,04
 93570,960,372,734,03 133590,670,086,675,13
12095041,130,224,264,04 138700,770,175,365,43
 96221,110,274,314,12 141740,760,206,505,50
 95631,120,254,294,08 140220,770,195,935,47
140      144730,870,116,035,58
       141740,760,206,505,50
       143240,820,166,275,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 jpp

Haproxy, 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 :

  1. 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
  2. backend SALE 
    Un backend bidon ...        
  3. 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 jpp

Note 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 jpp

HAPROXY 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 jpp

Aprè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