haproxy est ce qu'on appelle un load-balancer, ie un service permettant de dispatcher les requêtes TCP/HTTP vers plusieurs serveurs, appelés les backends. Mais haproxy peut aussi servir comme premier rempart contre les attaques sur les sites et applications Web.
Il est principalement utilisé en tant que load-balancer http vers des backends Apache/NginX/... mais peut aussi être utilisé pour faire du load-balancing vers d'autres services (mysql,redis,node.js ...).
Architecture
Pour ma maquette j'ai 5 vms sur mon serveur Proxmox :
- un proxy en frontal avec une ip publique et une ip privée
- deux serveurs web sur le réseau privé Apache + PHP 5.6
- 1 serveur SQL sur le réseau privé
- 1 serveur NFS sur le réseau privé
Un petit schéma pour illustrer tout cela. Cette architecture est très perfectible, elle présente plusieurs SPOF :
- le proxy (on peut penser mettre en place un cluster actif/passif pacemaker)
- le serveur sql (réplication du serveur sql et cluster actif/passif ou load-balancing haproxy)
- le stockage nfs (stockage réseau répliqué CEPH, GluterFS ...)
Installation
A partir de maintenant toutes mes installations se feront sur la nouvelle version majeure de Debian, ie Debian Jessie (version 8). Cependant pour bénéficier de la dernière version de haproxy, il faut mettre en place les backports jessie :
# echo "deb http://httpredir.debian.org/debian jessie-backports main contrib non-free" > /etc/apt/sources.list.d/backports.list
# apt-get update
On peut alors installer haproxy :
# apt-get install -t jessie-backports haproxy
Configuration
Le fichier de configuration est le suivant /etc/haproxy/haproxy.cfg. Faites un backup du fichier de base et voici ma conf, on en reparle après :
global
log 127.0.0.1 local0
log 127.0.0.1 local1 notice
user haproxy
group haproxy
defaults
log global
mode http
option httplog
option dontlognull
retries 3
option redispatch
maxconn 2000
timeout connect 5s
timeout client 30s
timeout server 10s
timeout http-request 5s
errorfile 400 /etc/haproxy/errors/400.http
errorfile 403 /etc/haproxy/errors/403.http
errorfile 408 /etc/haproxy/errors/408.http
errorfile 500 /etc/haproxy/errors/500.http
errorfile 502 /etc/haproxy/errors/502.http
errorfile 503 /etc/haproxy/errors/503.http
errorfile 504 /etc/haproxy/errors/504.http
listen webstats IPSRV:8080
stats enable
stats hide-version
stats scope webfarm
stats scope webservers
stats uri /
stats realm Haproxy\ Statistics
stats auth haproxy:secret
stats refresh 10s
listen webfarm IPSRV:80
default_backend webservers
backend webservers
mode http
balance roundrobin
cookie LBN insert indirect nocache
option httpclose
option forwardfor
server web01 192.168.14.41:80 weight 55 cookie web01 check inter 1s
server web02 192.168.14.42:80 weight 45 cookie web02 check inter 1s
On redémarre haproxy pour vérifier que tout fonctionne :
# service haproxy restart
# # netstat -tpnl |grep haproxy
tcp 0 0 IPSRV:80 0.0.0.0:* LISTEN 5845/haproxy
tcp 0 0 IPSRV:8080 0.0.0.0:* LISTEN 5845/haproxy
NB : remplacer IPSRV par l'ip publique du serveur.
Explications
global
Paramètres globaux de haproxy, les logs (dans syslog), l'utilisateur et le group de fonctionnement du service.
Il est possible aussi si le serveur héberge plusieurs services de chrooter haproxy.
defaults
Un certains nombre de paramètres importants dont :
- mode : mode de fonctionnement par défaut du load-balancer : http donc load-balancing sur le niveau 7 du modèle OSI ;
- maxconn : nombre de connexion maximum acceptée sur le frontal, cette option permet de limiter les connexions vers les backend et donc les attaques de type (D)DOS, si le nombre de connexion dépasse cette limite les nouvelles connexions seront refusées ;
- timeout : différents timeout permettant de couper les connexions trop longues, ce qui permet par exemple de limiter les attaques de type slowloris ;
- errorfile : il est possible de définir des pages d'erreur générique
stats
haproxy met à disposition une page de stats accessible, ici par exemple cette page sera accessible sur l'ip publique et le port 8080 avec une authentification assez complexe !!!
Visuellement cela nous donne ceci :
frontend / backends
Les deux dernières parties concernent la configuration du frontend et backend pour l'accès aux services
Côté frontend, on écoute sur l'IP publique et le port 80 et on fait référence au backend "webservers" :
listen webfarm IPSRV:80
default_backend webservers
Côté backend on défini les paramètres d'accès aux serveurs web :
backend webservers
mode http
balance roundrobin
cookie LBN insert indirect nocache
option httpclose
option forwardfor
server web01 192.168.14.41:80 weight 55 cookie web01 check inter 1s
server web02 192.168.14.42:80 weight 45 cookie web02 check inter 1s
Définition du backend "webservers" qui fonctionne en mode http (vu plus haut)
Il existe plusieurs façon de répartir les requêtes :
- roudnrobin : les requêtes sont réparties les unes après les autres sur chaque serveur de façon uniforme (modulo le paramètre weight) ;
- leastconn : les requêtes sont envoyées vers le serveur le moins chargé ;
- source : les requêtes seront toujours envoyées vers le même serveur backend pour la même source. Ce qui permet par exemple d'être sur de renvoyer vers le bon serveur web si les sessions sont stockées en dur
Les deux lignes "server ..." permettent de définir les paramètres de chaque serveur :
- nom IP:port : le nom du serveur, son IP et son port ;
- weight : on est en mode roundrobin mais on peut affecter un poids aux serveurs, par exemple si un est plus puissant que l'autre il aura un poids supérieur, la somme des poids doit faire 100 ;
- cookie nom : nom du cookie, ce paramètre est en relation avec la ligne cookie. Cela permet de faire plus ou moins la même chose que pour le mode source. Le proxy va positionner un cookie ici nommé LBN avec pour contenu le nom du serveur backend ;
- check inter 1s : vérifie toutes les secondes que le serveur est disponible
Basique mais utile
Cette configuration reste basique mais permet déjà d'assurer une fonction de répartition de charge vers les serveurs web, ainsi qu'un premier rempart de sécurité, outre les timeout et le nombre limite de connexion haproxy rejette toutes requêtes http mal formée.
Une petite lecture complémentaire :