Title: Configurer un reverse proxy avec haproxy subtitle: (HTTP/HTTPS) Date: 2015-12-11 09:16 Modified: 2015-12-11 09:16 Category: Système Tags: haute disponibilité, hébergement, reverse proxy, nom de domaine, debian, linux, haproxy keywords: haute disponibilité, hébergement, reverse proxy, nom de domaine, debian, linux, haproxy Slug: configurer-reverse-proxy-haproxy Authors: Victor Status: published
[TOC]
(Oui, j'assume le verbe proxyfier, na)
Il y a quelques temps, j'avais fait 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.
Avoir un reverse proxy devant son serveur web présente plusieurs avantages.
Une fois n'est pas coutume, attaquons le concret :)
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 :-)
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.
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
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.
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.
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 :
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.
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 :
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
<p>Jusqu'ici, le frontend était uniquement http, pas de certificat.</p>
<p>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</p>
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
<p>On voit quelques différences par rapport à la configuration définie plus haut.</p>
<ul>
<li>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.</li>
<li>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.</li>
<li>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</li>
<li>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 :</li>
</ul>
cat domain.tld.crt domain.tld.key > domain.tld.pem```
Toutes les autres options sont les même que pour le frontend HTTP (acl, use_backend, ...)
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)
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 !
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 :-D )
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.
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```
<p>Ou encore :</p>
use_backend backend1 if site1 or sous-domaine use_backend backend2 if toto-site2```
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.
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
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.
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
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 !