Configurer un reverse proxy avec haproxy (HTTP/HTTPS)

Ou : utilisons un logiciel dont c’est le taf de proxyfier 🙂

Un article pour configurer apache en mode reverse proxy.

Je vous invite Ă  le relire rapidement (au moins le chapĂŽ 😉 ) pour savoir de quoi l’on parle avec le terme reverse proxy.

Bien que ça fonctionne trĂšs bien, il existe des logiciels dĂ©diĂ©s Ă  la mise en place d’un reverse proxy, Ă  mettre devant le serveur web.

Le logiciel auquel nous allons nous intĂ©resser aujourd’hui est haproxy et est justement dĂ©diĂ© Ă  cet usage.

À noter que nous allons l’utiliser ici en tant que proxy HTTP/HTTPS, mais que haproxy peut servir de proxy pour n’importe quoi, du serveur de messagerie Ă  un serveur mysql, et globalement Ă  tout ce qui utilise TCP.

Pourquoi que donc ?

Avoir un reverse proxy devant son serveur web présente plusieurs avantages.

  • Ne pas avoir le serveur web publiquement accessible (par exemple pour sa protection)
  • Faire des prĂ©-traitement sur les requĂȘtes (nombre de sessions, limitation de bande passante, etc)
  • Balancer les requĂȘtes entre plusieurs serveurs web
  • Centraliser les accĂšs publics (pour n’utiliser qu’une IP publique avec diffĂ©rents serveurs web derriĂšre)

Une fois n’est pas coutume, attaquons le concret 🙂

Pré-requis

Pour que cela fonctionne, il vous faut au minimum un serveur web fonctionnel. Il peut ĂȘtre sous apache, nginx, ou mĂȘme IIS pourquoi pas.

C’est vers lui que nous redirigerons les requĂȘtes.

Il vous faut Ă©galement un serveur oĂč installer haproxy.

Dans cet exemple, nous prendrons un serveur dĂ©diĂ© Ă  cet usage. Cependant, il est aussi possible d’installer haproxy SUR le serveur hĂ©bergeant le serveur web (s’il est sous Linux). Ce n’est pas le sujet ici 🙂

Nous allons donc partir sur un serveur web, et un serveur “passerelle” qui possĂ©dera l’adresse publique de votre site, et sur lequel nous installerons haproxy.

Dans notre exemple, ce serveur passerelle sera sous debian 8.

Soit vous avez des serveurs physiques, soit vous les virtualisez.

Si vous vous auto-hĂ©bergez, vous ne pourrez pas utiliser votre adresse publique sur votre serveur passerelle. Dans ce cas, mettez en place une redirection de port vers votre serveur passerelle sur votre machinbox (ou routeur). Les ports 80 et 443 suffiront 🙂

Terminologie

Haproxy utilise des termes assez classiques dans sa configuration, mais que nous allons tout de mĂȘme revoir ici afin de bien savoir de quoi l’on parle dans la suite.

  • bind : attacher en Anglais, permet de dire sur quelle IP et quel port haproxy va Ă©couter. Par exemple, 192.168.1.1 sur le port 80
  • frontend : c’est un bloc de configuration qui permet de dĂ©finir toutes les rĂšgles qui s’appliqueront (domaines Ă©coutĂ©s, limitations, etc). Un frontend peut s’appliquer Ă  un ou plusieurs bind.
  • backend : c’est un autre bloc de configuration, que l’on place derriĂšre un frontend. Si le frontend gĂšre ce qui est publique (Ă  “l”avant” du serveur), le backend gĂšre “l’arriĂšre”. C’est lĂ  que vous dĂ©finirez les serveurs web vers lesquels envoyer les requĂȘtes.
  • acl : une “access control list” permet de dĂ©finir des conditions dans un bloc, par exemple “si le domaine contient site1, alors faire cela, si la requĂȘte est en https, alors faire ceci”

Aller go : le cambouis c’est pas si salissant

Installation

On commence par installer haproxy. Il est de base dans debian (depuis la version 6)

Si vous voulez faire de l’https, il vous faudra la version au moins 1.5 de haproxy. (disponible dans les backports debian 7 ou nativement sous debian 8)

Si vous voulez plus de détails sur les versions à installer, rendez vous sur http://haproxy.debian.net/

aptitude update
aptitude install haproxy

Global et default

Les 2 sections global et default permettent de définir des variables qui seront appliquées à tout le reste de la configuration (sauf redéfinition plus précise dans un sous bloc).

Les paramĂštres dĂ©finit Ă  l’installation sont corrects, vous pouvez donc laisser ces sections ainsi pour l’instant.

OĂč configurer

Le fichier de configuration de haproxy est placĂ© dans /etc/haproxy/haproxy.cfg. NĂ©anmoins, pour que cela soit plus pratique Ă  configurer, vous pouvez Ă©crire vos configurations personnalisĂ©es dans un fichier /etc/haproxy/haproxy.local, cela Ă©vitera d’avoir Ă  modifier le fichier par dĂ©faut.

Configuration du frontend

Nous allons voir ici une configuration de frontend basique. Il est possible de faire des choses trĂšs compliquĂ©es, mais on va rester simple pour l’instant 🙂

frontend http-in
bind IP_PUBLIQUE:80
mode http
option httplog
acl your_acl hdr(host) votresiteweb.tld
use_backend backend1 if your_acl

Attention, l’indentation est importante !

Tous les paramĂštres du bloc doivent ĂȘtre dĂ©calĂ©s en dessous pour que haproxy voit que ces paramĂštres font parti du bloc. En gĂ©nĂ©ral, une tabulation ou X espaces, selon les prĂ©fĂ©rences.

Vous définissez :

  • frontend http-in : le mot clef frontend indique la prĂ©sence d’un bloc de configuration frontend. ici, http-in est un nom pour ce frontend, choisi arbitrairement. Vous pouvez nommer le frontend comme vous le souhaitez, une bonne pratique est de prendre un nom clair et pas trop Ă  rallonge 🙂
  • IP_PUBLIQUE : l’adresse IP sur laquelle haproxy va Ă©couter. Vous pouvez prĂ©ciser une IP prĂ©cise, ou bien 0.0.0.0 pour Ă©couter sur toutes les IP prĂ©sentes sur le serveur. Vous pouvez aussi mettre plusieurs lignes l’une en dessous de l’autre pour ajouter des IP prĂ©cises
  • mode http : on dĂ©finit que ce frontend va traiter uniquement le protocole HTTP (et donc aussi HTTPS). Cela permet dĂ©jĂ  Ă  haproxy d’analyser les requĂȘtes, et de rejeter tout ce qui n’est pas formatĂ© correctement vis Ă  vis des RFC
  • option httplog : permet de logguer le dĂ©tail des requĂȘtes http. Cela permet d’avoir plus d’informations dans les logs haproxy (headers, session http, …).
  • acl : on dĂ©finit une ACL, qui sera reconnue si la partie HOST de la requĂȘte http correspond exactement Ă  votresiteweb.tld. Il est aussi possible de chercher la fin d’un host (tout ce qui termine par votresiteweb.tld), commence par, contient tel mot, etc. Ici si ça correspond, l’acl se nommera your_acl, nous pourrons la rĂ©utiliser dans la suite du bloc.
  • use_backend : on dĂ©finit ici qu’on va utiliser le backend backend1 SI l’acl your_acl est active. Donc dans notre cas, si la requĂȘte traitĂ©e contient exactement votresiteweb.tld dans la partie HOST, l’acl est active. Tout se recoupe !

Et voila, en 5 lignes, vous avez configurĂ© un premier frontend pour rediriger votresiteweb.tld vers un backend (qu’il reste Ă  Ă©crire)

On voit déjà que la syntaxe est trÚs simple par rapport à apache (plus de ProxyPass avec des paramÚtres pas toujours clairs).

On a ici une configuration trĂšs simpliste. Je prĂ©senterai quelques options montrant la puissance d’haproxy Ă  la fin de cet article.

Configuration du backend

Maintenant que notre frontend est prĂȘt Ă  recevoir les requĂȘtes publiques, il faut crĂ©er le backend qui sera Ă  mĂȘme de savoir oĂč envoyer ces requĂȘtes.

backend backend1
mode http
option httpchk
option forwardfor except 127.0.0.1
http-request add-header X-Forwarded-Proto https if { ssl_fc }
server web-server1 IP_SERVEUR_WEB:80 maxconn 32

De la mĂȘme maniĂšre que pour le frontend, attention Ă  l’indentation pour que les paramĂštres soient dĂ©calĂ©s en dessous du mot clef backend

Ici vous définissez :

  • backend backend1 : le mot clef backend permet d’indiquer le dĂ©but d’un bloc de backend. Le nom backend1 est au choix, comme pour le nom du frontend. Il sera celui Ă  utiliser dans le frontend au moment d’Ă©crire le use_backend
  • mode http : comme pour le frontend, cela indique que ce backend s’occupera d’http, et permet d’utiliser diverses options pratiques (rĂ©Ă©criture de header notamment)
  • option httpchk : le httpchk permet de faire en sorte que haproxy vĂ©rifie Ă  tout moment l’Ă©tat des serveurs web derriĂšre lui. Il peut ainsi savoir si le serveur est prĂȘt Ă  recevoir des requĂȘtes, basculer vers un serveur de secours, afficher une page d’erreur en cas de panne, etc. De base, c’est un simple check HTTP sur le / qui est effectuĂ©, mais il est possible par exemple de spĂ©cifier un script ou un chemin prĂ©cis
  • forwardfor except 127.0.0.1 : cette option va permettre d’ajouter un en tĂȘte xforwardfor dans les requĂȘtes qui passent par le backend, en tĂȘte contenant la vĂ©ritable adresse IP du visiteur. En effet, les requĂȘtes passant par le proxy, c’est son IP qui sera vu niveau rĂ©seau par le serveur web ce qui peut ĂȘtre gĂȘnant pour faire des statistiques de visites par exemple, car vous auriez l’impression que toutes les visites viennent du serveur proxy… Le except 127.0.0.1 permet d’Ă©viter d’ajouter cet en tĂȘte si c’est 127.0.0.1 qui a gĂ©nĂ©rĂ© la requĂȘte.
  • server web-server1 : cette dĂ©finition va permettre d’indiquer le serveur vers lequel transmettre les requĂȘtes. IP_SERVEUR_WEB est bien sĂ»r l’adresse IP du serveur web. :80 permet d’indiquer le port ou transmettre. Il est possible d’indiquer plusieurs lignes pour dĂ©finir plusieurs serveur web et faire de la rĂ©partition de charge.
  • maxconn 32 : permet de limiter le nombre maximum de connexions gĂ©rĂ©es par ce serveur, ici 32. Cela permet d’Ă©viter de surcharger le serveur web au dessus de sa capacitĂ© par exemple, et de mitiger directement et Ă  peu de coĂ»t une attaque.

A partir de lĂ , c’est tout bon. Vous avez une configuration, certes basique, qui vous permettra de recevoir des requĂȘtes pour votre site, et les transmettre vers votre serveur web 🙂

Bien sûr, redémarrez haproxy pour appliquer la configuration.

systemctl restart haproxy

Un peu plus loin : https

frontend

Jusqu’ici, le frontend Ă©tait uniquement http, pas de certificat.

Mettre en place un frontend https est tout ce qu’il y a de plus simple. On va tout simplement crĂ©er un deuxiĂšme frontend, cette fois bindĂ© sur le port 443

frontend https-in
bind IP_PUBLIQUE:443 ssl crt /etc/haproxy/cert/ no-sslv3
mode http
option httplog
acl your_acl hdr(host) votresiteweb.tld
use_backend backend1 if your_acl

On voit quelques différences par rapport à la configuration définie plus haut.

  • https-in : le nom est lĂ  encore au choix, mais il faut qu’il soit diffĂ©rent du premier, sinon haproxy renverra une erreur au dĂ©marrage.
  • bind : la ligne bind change. On voit qu’on va cette fois se mettre sur le port 443 au lieu de 80, l’HTTPS Ă©tant par dĂ©faut sur le port 443. Rien ne vous empĂȘche de mettre un autre port selon vos besoins.
  • ssl : ce mot clef (utilisable sur la mĂȘme ligne que bind) permet d’indiquer Ă  haproxy qu’il va devoir faire du SSL sur ce bind
  • crt /etc/haproxy/cert/ : dĂ©finit le rĂ©pertoire dans lequel vous mettre vos certificats. haproxy gĂšre les certificats au format pem, que vous pouvez simplement crĂ©er de la façon suivante en mergeant le .crt et le .key :
cat domain.tld.crt domain.tld.key > domain.tld.pem
  • no-sslv3 : cela permet de spĂ©cifier Ă  haproxy de refuser d’utiliser le protocole sslv3, considĂ©rĂ© dĂ©sormais comme non sĂ©curisĂ©.

Toutes les autres options sont les mĂȘme que pour le frontend HTTP (acl, use_backend, …)

Note sur le dossier des certificats

haproxy gÚre de maniÚre trÚs efficace les certificats. Vous pouvez les mettre en vrac dans le répertoire, haproxy les parse au démarrage et utilise les certificat de la maniÚre la plus précise possible.

Si par exemple vous possédez un certificat wildcard (*.domain.tld) et un certificat plus précis (sous.domain.tld)

  • Les visites sur sous.domain.tld utiliseront le certificat de sous.domain.tld
  • Les visites sur domain.tld utiliseront le certificat wildcard
  • Les visites sur toto.domain.tld utiliseront le certificat wildcard
  • etc 🙂

Backend

Comme c’est le frontend qui gĂšre la partie HTTPS, vous pouvez utiliser exactement les mĂȘme backend pour le frontend http et le frontend https. Rien Ă  faire Ă  ce niveau lĂ  donc, tout est dĂ©jĂ  bon dans la configuration.

RedĂ©marrez haproxy, et votre site est prĂȘt Ă  fonctionner en HTTPS !

Encore plus loin : options sympathiques

Cette liste n’est clairement pas exhaustive, haproxy disposant d’une foison incroyable de possibilitĂ©s. Je ne liste ici que celles qui me semblent les plus utiles de maniĂšre gĂ©nĂ©rales (en gros, celles que j’utilise moi 😀 )

Pour une liste complÚte, je vous invite à vous référer à la documentation haproxy qui est vraiment trÚs claire et détaillée avec tous les exemples nécessaires à la compréhension.

Plusieurs sites dans un frontend

Comme vous ne pouvez crĂ©er qu’un seul frontend Ă©coutant sur le port 80 (ou 443) sur une adresse IP, il va falloir utiliser le mĂȘme frontend pour gĂ©rer plusieurs sites.

Cela se fait trĂšs simplement, en utilisant plusieurs acl dans le frontend, par exemple :

acl site1 hdr(host) site1.tld
acl sous-domaine hdr(host) sousdomain.site1.tld
acl toto-site2 hdr(host) toto.site2.tld

Vous pouvez ensuite utiliser un ou plusieurs backend selon les acl :

use_backend backend1 if site1 or sous-domaine or toto-site2

Ou encore :

use_backend backend1 if site1 or sous-domaine
use_backend backend2 if toto-site2

Test de domaine dans un frontend

Dans mes exemples, j’ai Ă  chaque fois utilisĂ© hdr(host) dans mes acl, qui permet de chercher le contenu exact de la variable HOST de la requĂȘte HTTP.

Néanmoins, il est possible de faire plus général, comme par exemple créer des acl en se basant sur la fin du HOST, le début, voir chercher si HOST contient telle ou telle chaßne de caractÚre.

Cela peut permettre de créer une acl qui gérera tout ce qui se termine par site.tdl, tout ce qui contient www., etc

acl test1 hdr_beg(host) www. #On match ce qui commence par www.
acl test2 hdr_end(host) domain.tld #On match tout ce qui termine par domain.tld
acl test3 hdr_reg(host) REGEX #On match tout ce qui correspond à l'expression réguliÚre REGEX

Vous ĂȘtes ensuite libre d’utiliser ces acl dans les backend que vous voulez.

Backend par défaut, aussi nommé la poubelle

Une fonctionnalitĂ© que je trouve assez intĂ©ressante est de prĂ©voir un backend par dĂ©faut qui servira de poubelle. Toutes les visites arrivant sur votre serveur et qui ne matchent pas une acl peuvent ĂȘtre considĂ©rĂ©es comme de la poubelle, voire comme une attaque potentielle, et donc autant ne pas les rediriger vers un site par dĂ©faut (comme le fait par exemple de base le vhost apache default)

Pour cela, il suffit d’ajouter dans le frontend le mot clef default_backend qui permet de dĂ©finir un backend qui sera utilisĂ© pour les requĂȘtes n’ayant matchĂ© aucune acl dans le frontend, et de crĂ©er un backend qui renverra par exemple une erreur 403 “accĂšs interdit” :

frontend http-in
[...]
default_backend poubelle

backend poubelle
mode http
http-request deny

RĂ©partition de charge avec un backend

Si vous dĂ©finissez plusieurs lignes de server dans un backend, alors haproxy va automatiquement rĂ©partir (via le protocole round robin) les requĂȘtes entrantes de maniĂšre Ă©quitables entre les serveurs.

Mais il est Ă©galement possible de dĂ©finir des poids pour ces serveurs, si par exemple l’un d’eux est plus puissant, plus Ă  mĂȘme de recevoir des requĂȘtes

backend backend1
[...]
server server1 ip1:80 weight 1
server server2 ip2:80 weight 2

Ici server2 encaissera 2 fois plus de requĂȘtes que server1

Vous pouvez utiliser le mot clef backup :

backend backend1
[...]
server server1 ip1:80
server server2 ip2:80 backup

Ici, server1 recevra toutes les requĂȘtes, mais si haproxy dĂ©tecte qu’il n’est plus accessible, alors il enverra automatiquement toutes les requĂȘtes vers server2

Il est possible aussi de combiner ces 2 techniques, faire de la rĂ©partition de charge entre plusieurs serveurs tout en ayant d’autres disponibles en backup.

Forcer l’https

Il est possible de dire Ă  haproxy, dans votre frontend sur le port 80, que tel site doit absolument utiliser l’HTTPS. Auquel cas, si haproxy reçoit une requĂȘte en HTTP, alors il redirigera immĂ©diatement le visiteur vers exactement la mĂȘme requĂȘte mais en HTTPS :

frontend http-in
[...]
# On a plusieurs acl
acl site1 hdr(host) site1.tld
acl sous-domaine hdr(host) sousdomaine.site1.tld
acl toto-site2 hdr(host) toto.site2.tld

redirect scheme https code 301 if !{ ssl_fc } site1.tld or sous-domaine
use_backend backend2 if toto-site2

Ici, on va forcer via un code 301 (redirection permanente) la redirection vers l’HTTPS (on dĂ©tecte que le protocole ssl n’est pas utilisĂ©) pour les accĂšs sur site1.tld et sousdomaine.site1.tld, mais on reste en HTTP et on utilise backend2 si la visite est sur toto.site2.tld

RĂ©Ă©crire des en-tĂȘtes dans le backend

On a vu un exemple de rĂ©Ă©criture d’en tĂȘte dans la partie sur les backend avec le xForwarFor.

Il est possible dans un backend d’Ă©crire tous les en tĂȘtes que vous voulez.

Par exemple, si le frontend devant le backend est en https, vous pouvez ajouter un en-tĂȘte qui indiquera au serveur web derriĂšre qu’il y a eu traitement https, ce mĂȘme si le serveur web ne voit jamais le certificat puisque c’est haproxy qui s’en occupe :

backend backend1
[...]
http-request add-header X-Forwarded-Proto https if { ssl_fc }

Vous pouvez ainsi Ă©crire (ou rĂ©Ă©crire) n’importe quel en-tĂȘte.

Par exemple pour rĂ©Ă©crire l’en tĂȘte serveur :

backend backend1
[...]
rspidel ^server #On commence par effacer l'en tĂȘte serveur dĂ©jĂ  prĂ©sent
rspadd Server:\ Vous\ utilisez\ mon\ super\ serveur\ ! #On rajoute un nouvel en-tete Server (attention à bien protéger les espaces par des \)

Voila pour ce tour d’horizon (pas si) rapide sur haproxy !

J’espĂšre que cela vous permettra de vous familiariser avec ce logiciel, qui n’a Ă©tĂ© ici qu’Ă  peine effleurĂ© 🙂

Comme toujours, n’hĂ©sitez pas Ă  poser vos questions dans les commentaires !

Sources

Laisser un commentaire