| ------------------- |
| HAProxy |
| Manuel de référence |
| ------------------- |
| version 1.3.2 |
| willy tarreau |
| 2006/09/03 |
| |
| ================ |
| | Introduction | |
| ================ |
| |
| HAProxy est un relais TCP/HTTP offrant des facilités d'intégration en |
| environnement hautement disponible. En effet, il est capable de : |
| - effectuer un aiguillage statique défini par des cookies ; |
| - effectuer une répartition de charge avec création de cookies pour assurer |
| la persistence de session ; |
| - fournir une visibilité externe de son état de santé ; |
| - s'arrêter en douceur sans perte brutale de service ; |
| - modifier/ajouter/supprimer des en-têtes dans la requête et la réponse ; |
| - interdire des requêtes qui vérifient certaines conditions ; |
| - utiliser des serveurs de secours lorsque les serveurs principaux sont hors |
| d'usage. |
| - maintenir des clients sur le bon serveur serveur d'application en fonction |
| de cookies applicatifs. |
| - fournir des rapports d'état en HTML à des utilisateurs authentifiés, à |
| travers des URI de l'application interceptées. |
| |
| Il requiert peu de ressources, et son architecture événementielle mono- |
| processus lui permet facilement de gérer plusieurs milliers de connexions |
| simultanées sur plusieurs relais sans effondrer le système. |
| |
| |
| =========================== |
| | Paramètres de lancement | |
| =========================== |
| |
| Les options de lancement sont peu nombreuses : |
| |
| -f <fichier de configuration> |
| -n <nombre maximal total de connexions simultanées> |
| = 'maxconn' dans la section 'global' |
| -N <nombre maximal de connexions simultanées par instance> |
| = 'maxconn' dans les sections 'listen' ou 'default' |
| -d active le mode debug |
| -D passe en daemon |
| -q désactive l'affichage de messages sur la sortie standard. |
| -V affiche les messages sur la sortie standard, même si -q ou 'quiet' sont |
| spécifiés. |
| -c vérifie le fichier de configuration puis quitte avec un code de retour 0 |
| si aucune erreur n'a été trouvée, ou 1 si une erreur de syntaxe a été |
| détectée. |
| -p <fichier> indique au processus père qu'il doit écrire les PIDs de ses |
| fils dans ce fichier en mode démon. |
| -sf specifie une liste de PIDs auxquels envoyer un signal FINISH |
| -st specifie une liste de PIDs auxquels envoyer un signal TERMINATE |
| -s affiche les statistiques (si option compilée) |
| -l ajoute des informations aux statistiques |
| -dk désactive l'utilisation de kqueue() |
| -ds désactive l'utilisation de epoll() speculatif |
| -de désactive l'utilisation de epoll() |
| -dp désactive l'utilisation de poll() |
| -db désactive la mise en arrière-plan (utile pour débugger) |
| -m <megs> applique une limitation de <megs> Mo d'utilisation mémoire |
| |
| Le nombre maximal de connexion simultanées par proxy est le paramètre par |
| défaut pour les proxies pour lesquels ce paramètre n'est pas précisé dans le |
| fichier de configuration. Il s'agit du paramètre 'maxconn' dans les sections |
| 'listen'. |
| |
| Le nombre maximal total de connexions simultanées limite le nombre de |
| connexions TCP utilisables à un instant donné par le processus, tous proxies |
| confondus. Ce paramètre remplace le paramètre 'maxconn' de la section 'global'. |
| |
| Le mode debug correspond à l'option 'debug' de la section 'global'. Dans ce |
| mode, toutes les connexions, déconnexions, et tous les échanges d'en-têtes HTTP |
| sont affichés. |
| |
| Pour debugger, l'option '-db' est très pratique car elle désactive |
| temporairement le mode daemon et le mode multi-processus. Le service peut alors |
| être arrêté par un simple appui sur Ctrl-C, sans avoir à modifier la |
| configuration ni à activer le mode debug complet. |
| |
| Les statistiques ne sont disponibles que si le programme a été compilé avec |
| l'option "STATTIME". Il s'agit principalement de données brutes n'ayant |
| d'utilité que lors de benchmarks par exemple, et sont amenées à disparaitre. |
| |
| Les paramètres '-st' et '-sf' sont utilisés pour la reconfiguration à chaud. |
| Voir la section à ce sujet. |
| |
| ============================ |
| | Fichier de configuration | |
| ============================ |
| |
| Structure |
| ========= |
| |
| L'analyseur du fichier de configuration ignore des lignes vides, les espaces, |
| les tabulations, et tout ce qui est compris entre le symbole '#' (s'il n'est |
| pas précédé d'un '\'), et la fin de la ligne, ce qui constitue un commentaire. |
| |
| Le fichier de configuration est découpé en sections répérées par des mots clés |
| tels que : |
| |
| - 'global' |
| - 'listen' |
| - 'defaults' |
| |
| Tous les paramètres font référence à la section définie par le dernier mot clé |
| reconnu. |
| |
| |
| 1) Paramètres globaux |
| ===================== |
| |
| Il s'agit des paramètres agissant sur le processus, ou bien sur l'ensemble des |
| proxies. Ils sont tous spécifiés dans la section 'global'. Les paramètres |
| supportés sont : |
| |
| - log <adresse> <catégorie> [niveau_max] |
| - maxconn <nombre> |
| - uid <identifiant> |
| - gid <identifiant> |
| - user <nom d'utilisateur> |
| - group <nom de groupe> |
| - chroot <répertoire> |
| - nbproc <nombre> |
| - daemon |
| - debug |
| - nokqueue |
| - nosepoll |
| - noepoll |
| - nopoll |
| - quiet |
| - pidfile <fichier> |
| - ulimit-n <nombre> |
| - tune.maxpollevents <nombre> |
| |
| |
| 1.1) Journalisation des événements |
| ---------------------------------- |
| La plupart des événements sont journalisés : démarrages, arrêts, disparition et |
| apparition de serveurs, connexions, erreurs. Tous les messages sont envoyés en |
| syslog vers un ou deux serveurs. La syntaxe est la suivante : |
| |
| log <adresse_ip> <catégorie> [niveau_max] |
| |
| Les connexions sont envoyées en niveau "info". Les démarrages de service et de |
| serveurs seront envoyés en "notice", les signaux d'arrêts en "warning" et les |
| arrêts définitifs de services et de serveurs en "alert". Ceci est valable aussi |
| bien pour les proxies que pour les serveurs testés par les proxies. Le |
| paramètre optionnel <niveau_max> définit le niveau maximal de traces émises |
| parmi les 8 valeurs suivantes : |
| emerg, alert, crit, err, warning, notice, info, debug |
| |
| Par compatibilité avec les versions 1.1.16 et antérieures, la valeur par défaut |
| est "debug" si l'option n'est pas précisée. |
| |
| Les catégories possibles sont : |
| kern, user, mail, daemon, auth, syslog, lpr, news, |
| uucp, cron, auth2, ftp, ntp, audit, alert, cron2, |
| local0, local1, local2, local3, local4, local5, local6, local7 |
| |
| Conformément à la RFC3164, les messages émis sont limités à 1024 caractères. |
| |
| Exemple : |
| --------- |
| global |
| log 192.168.2.200 local3 |
| log 127.0.0.1 local4 notice |
| |
| 1.2) limitation du nombre de connexions |
| --------------------------------------- |
| Il est possible et conseillé de limiter le nombre global de connexions par |
| processus à l'aide du mot clé global 'maxconn'. Les connexions sont comprises |
| au sens 'acceptation de connexion', donc il faut s'attendre en règle général à |
| avoir un peu plus du double de sessions TCP que le maximum de connexions fixé. |
| C'est important pour fixer le paramètre 'ulimit -n' avant de lancer le proxy. |
| Pour comptabiliser le nombre de sockets nécessaires, il faut prendre en compte |
| ces paramètres : |
| |
| - 1 socket par connexion entrante |
| - 1 socket par connexion sortante |
| - 1 socket par couple adresse/port d'écoute par proxy |
| - 1 socket pour chaque serveur en cours de health-check |
| - 1 socket pour les logs (tous serveurs confondus) |
| |
| Dans le cas où chaque proxy n'écoute que sur un couple adresse/port, |
| positionner la limite du nombre de descripteurs de fichiers (ulimit -n) à |
| (2 * maxconn + nbproxy + nbserveurs + 1). A partir des versions 1.1.32/1.2.6, |
| il est possible de spécifier cette limite dans la configuration à l'aide du |
| mot-clé global 'ulimit-n', à condition bien entendu que le proxy ait été |
| démarré sous le compte root (ou avec des droits suffisants pour élever le |
| nombre de descripteurs de fichiers). Cette solution met un terme au problème |
| récurrent d'incertitude de l'adéquation entre les limites systèmes lors de la |
| dernière relance du proxessus et les limites en nombre de connexions. Noter que |
| cette limite s'applique par processus. |
| |
| Exemple : |
| --------- |
| global |
| maxconn 32000 |
| ulimit-n 65536 |
| |
| |
| 1.3) Diminution des privilèges |
| ------------------------------ |
| Afin de réduire les risques d'attaques dans le cas où une faille non identifiée |
| serait exploitée, il est possible de diminuer les privilèges du processus, et |
| de l'isoler dans un répertoire sans risque. |
| |
| Dans la section 'global', le paramètre 'uid' permet de spécifier un identifiant |
| numérique d'utilisateur. La valeur 0, correspondant normalement au super- |
| utilisateur, possède ici une signification particulière car elle indique que |
| l'on ne souhaite pas changer cet identifiant et conserver la valeur courante. |
| C'est la valeur par défaut. De la même manière, le paramètre 'gid' correspond à |
| un identifiant de groupe, et utilise par défaut la valeur 0 pour ne rien |
| changer. Dans le cas où il ne serait pas possible de spécifier un identifiant |
| numérique pour l'uid, il est possible de spécifier un nom d'utilisateur après |
| le mot-clé 'user'. De la même manière, il est possible de préciser un nom de |
| groupe après le mot-clé 'group'. |
| |
| Il est particulièrement déconseillé d'utiliser des comptes génériques tels que |
| 'nobody' car cette pratique revient à utiliser 'root' si d'autres processus |
| utilisent les mêmes identifiants. |
| |
| Le paramètre 'chroot' autorise à changer la racine du processus une fois le |
| programme lancé, de sorte que ni le processus, ni l'un de ses descendants ne |
| puissent remonter de nouveau à la racine. Ce type de cloisonnement (chroot) est |
| généralement contournable sur certains OS (Linux, Solaris) pour peu que |
| l'attaquant possède des droits 'root' et soit en mesure d'utiliser ou de créer |
| un répertoire. Aussi, il est important d'utiliser un répertoire spécifique au |
| service pour cet usage, et de ne pas mutualiser un même répertoire pour |
| plusieurs services de nature différente. Pour rendre l'isolement plus robuste, |
| il est conseillé d'utiliser un répertoire vide, sans aucun droit, et de changer |
| l'uid du processus de sorte qu'il ne puisse rien faire dans ledit répertoire. |
| |
| Remarque importante : |
| --------------------- |
| Dans le cas où une telle faille serait mise en évidence, il est fort probable |
| que les premières tentatives de son exploitation provoquent un arrêt du |
| programme, à cause d'un signal de type 'Segmentation Fault', 'Bus Error' ou |
| encore 'Illegal Instruction'. Même s'il est vrai que faire tourner le serveur |
| en environnement limité réduit les risques d'intrusion, il est parfois bien |
| utile dans ces circonstances de connaître les conditions d'apparition du |
| problème, via l'obtention d'un fichier 'core'. La plupart des systèmes, pour |
| des raisons de sécurité, désactivent la génération du fichier 'core' après un |
| changement d'identifiant pour le processus. Il faudra donc soit lancer le |
| processus à partir d'un compte utilisateur aux droits réduits (mais ne pouvant |
| pas effectuer le chroot), ou bien le faire en root sans réduction des droits |
| (uid 0). Dans ce cas, le fichier se trouvera soit dans le répertoire de |
| lancement, soit dans le répertoire spécifié après l'option 'chroot'. Ne pas |
| oublier la commande suivante pour autoriser la génération du fichier avant de |
| lancer le programme : |
| |
| # ulimit -c unlimited |
| |
| Exemple : |
| --------- |
| |
| # with uid/gid |
| global |
| uid 30000 |
| gid 30000 |
| chroot /var/chroot/haproxy |
| |
| # with user/group |
| global |
| user haproxy |
| group public |
| chroot /var/chroot/haproxy |
| |
| |
| 1.4) Modes de fonctionnement |
| ---------------------------- |
| Le service peut fonctionner dans plusieurs modes : |
| - avant- / arrière-plan |
| - silencieux / normal / debug |
| |
| Le mode par défaut est normal, avant-plan, c'est à dire que le programme ne |
| rend pas la main une fois lancé. Il ne faut surtout pas le lancer comme ceci |
| dans un script de démarrage du système, sinon le système ne finirait pas son |
| initialisation. Il faut le mettre en arrière-plan, de sorte qu'il rende la main |
| au processus appelant. C'est ce que fait l'option 'daemon' de la section |
| 'global', et qui est l'équivalent du paramètre '-D' de la ligne de commande. |
| |
| Le paramètre de ligne de commande '-db' inhibe les options globales 'daemon' |
| et 'nbproc' pour faire fonctionner le processus en mode normal, avant-plan. |
| |
| Par ailleurs, certains messages d'alerte sont toujours envoyés sur la sortie |
| standard, même en mode 'daemon'. Pour ne plus les voir ailleurs que dans les |
| logs, il suffit de passer en mode silencieux par l'ajout de l'option 'quiet'. |
| Cette option n'a pas d'équivalent en ligne de commande. |
| |
| Enfin, le mode 'debug' permet de diagnostiquer les origines de certains |
| problèmes en affichant les connexions, déconnexions et échanges d'en-têtes HTTP |
| entre les clients et les serveurs. Ce mode est incompatible avec les options |
| 'daemon' et 'quiet' pour des raisons de bon sens. |
| |
| 1.5) Accroissement de la capacité de traitement |
| ----------------------------------------------- |
| Sur des machines multi-processeurs, il peut sembler gâché de n'utiliser qu'un |
| processeur pour effectuer les tâches de relayage, même si les charges |
| nécessaires à saturer un processeur actuel sont bien au-delà des ordres de |
| grandeur couramment rencontrés. Cependant, pour des besoins particuliers, le |
| programme sait démarrer plusieurs processus se répartissant la charge de |
| travail. Ce nombre de processus est spécifié par le paramètre 'nbproc' de la |
| section 'global'. Sa valeur par défaut est naturellement 1. Ceci ne fonctionne |
| qu'en mode 'daemon'. Un usage déjà rencontré pour ce paramètre fut de dépasser |
| la limite de nombre de descripteurs de fichiers allouée par processus sous |
| Solaris. |
| |
| Exemple : |
| --------- |
| |
| global |
| daemon |
| quiet |
| nbproc 2 |
| |
| 1.6) Simplification de la gestion des processus |
| ----------------------------------------------- |
| Haproxy supporte dorénavant la notion de fichiers de pid (-> pidfiles). Si le |
| paramètre '-p' de ligne de commande, ou l'option globale 'pidfile' sont suivis |
| d'un nom de fichier, alors ce fichier sera supprimé puis recréé et contiendra |
| le numéro de PID des processus fils, à raison d'un par ligne (valable |
| uniquement en mode démon). Ce fichier n'est PAS relatif au cloisonnement chroot |
| afin de rester compatible avec un répertoire protégé en lecture seule. Il |
| appartiendra à l'utilisateur ayant lancé le processus, et disposera des droits |
| 0644. |
| |
| Exemple : |
| --------- |
| |
| global |
| daemon |
| quiet |
| nbproc 2 |
| pidfile /var/run/haproxy-private.pid |
| |
| # pour stopper seulement ces processus parmi d'autres : |
| # kill $(</var/run/haproxy-private.pid) |
| |
| # pour recharger une configuration avec un impact minimal sur le service, |
| # et sans casser les sessions existantes : |
| # haproxy -f haproxy.cfg -p $(</var/run/haproxy-private.pid) -st $(</var/run/haproxy-private.pid) |
| |
| 1.7) Mécanismes de traitements des événements |
| --------------------------------------------- |
| A partir de la version 1.2.5, haproxy supporte les mécanismes poll() et |
| epoll(). Sur les systems où select() est limité par FD_SETSIZE (comme Solaris), |
| poll() peut être une alternative intéressante. Des tests de performance |
| montrent que les performances de poll() ne décroissent pas aussi vite que le |
| nombre de sockets augmente, ce qui en fait une solution sûre pour les fortes |
| charges. Cela dit, Soalris utilise déjà poll() pour émuler select(), donc tant |
| que le nombre de sockets ne dépasse pas FD_SETSIZE, poll() ne devrait pas |
| apporter de performances supplémentaires. Sur les systèmes à base Linux |
| incluant le patch epoll() (ou tous les Linux 2.6), haproxy utilisera epoll() |
| qui est extrèmement rapide indépendamment du nombre de sockets. Les tests sur |
| haproxy ont montré une performance constante de 1 à 40000 sessions simultanées. |
| La version 1.3.9 a introduit le support de kqueue() pour FreeBSD/OpenBSD, ainsi |
| qu'une variante appelée "speculative epoll()" consistant à tenter d'effectuer |
| les opérations d'entrées/sorties avant de chaîner les événements par les appels |
| système. |
| |
| Afin d'optimiser la latence, il est désormais possible de limiter le nombre |
| d'événements remontés à chaque appel. La limite par défaut est fixée à 200. Si |
| une latence plus petite est recherchée, il peut être justifié d'abaisser cette |
| limite par l'utilisation du paramètre 'tune.maxpollevents' dans la section |
| 'global'. L'augmenter permettra d'économiser un peu le processeur en présence |
| de très grands nombres de connexions simultanées. |
| |
| Haproxy utilisera kqueue() ou speculative epoll() lorsque ce sera disponible, |
| puis epoll(), et se repliera sur poll(), puis en dernier lieu sur select(). |
| Cependant, si pour une raison quelconque il s'avérait nécessaire de désactiver |
| epoll() ou poll() (p.ex: à cause d'un bug ou juste pour comparer les |
| performances), de nouvelles options globales ont été ajoutées dans ce but : |
| 'nosepoll', 'nokqueue', 'noepoll' et 'nopoll'. |
| |
| Exemple : |
| --------- |
| global |
| # utiliser seulement select() |
| noepoll |
| nopoll |
| tune.maxpollevents 100 |
| |
| Remarque : |
| ---------- |
| Dans le but d'assurer une portabilité maximale des configurations, ces options |
| sont acceptées et ignorées si les mécanismes poll() ou epoll() n'ont pas été |
| activés lors de la compilation. |
| |
| Afin de simplifier la résolution de problèmes, le paramètre '-de' en ligne de |
| commande désactive epoll(), le paramètre '-dp' désactive poll(), '-dk' kqueue() |
| et '-ds' désactive speculative epoll(). Ils sont respectivement équivalents à |
| 'noepoll', 'nopoll', 'nokqueue' et 'nosepoll'. |
| |
| |
| 2) Définition d'un service en écoute |
| ==================================== |
| |
| Les sections de service débutent par le mot clé "listen" : |
| |
| listen <nom_instance> [ <adresse_IP>:<plage_ports>[,...] ] |
| |
| - <nom_instance> est le nom de l'instance décrite. Ce nom sera envoyé dans les |
| logs, donc il est souhaitable d'utiliser un nom relatif au service relayé. |
| Aucun test n'est effectué concernant l'unicité de ce nom, qui n'est pas |
| obligatoire, mais fortement recommandée. |
| |
| - <adresse_IP> est l'adresse IP sur laquelle le relais attend ses connexions. |
| L'absence d'adresse ainsi que l'adresse 0.0.0.0 signifient que les connexions |
| pourront s'effectuer sur toutes les adresses de la machine. |
| |
| - <plage_ports> correspond soit à un port, soit à une plage de ports sur |
| lesquels le relais acceptera des connexions pour l'adresse IP spécifiée. |
| Cette plage peut être : |
| - soit un port numérique (ex: '80') |
| - soit une plage constituée de deux valeurs séparées par un tiret |
| (ex: '2000-2100') représentant les extrémités incluses dans la |
| plage. |
| Il faut faire attention à l'usage des plages, car chaque combinaison |
| <adresse_IP>:<port> consomme une socket, donc un descripteur de fichier. |
| Le couple <adresse_IP>:<port> doit être unique pour toutes les instances |
| d'une même machine. L'attachement à un port inférieur à 1024 nécessite un |
| niveau de privilège particulier lors du lancement du programme |
| (indépendamment du paramètre 'uid' de la section 'global'). |
| |
| - le couple <adresse_IP>:<plage_ports> peut être répété indéfiniment pour |
| demander au relais d'écouter également sur d'autres adresses et/ou d'autres |
| plages de ports. Pour cela, il suffit de séparer les couples par une virgule. |
| |
| Exemples : |
| --------- |
| listen http_proxy :80 |
| listen x11_proxy 127.0.0.1:6000-6009 |
| listen smtp_proxy 127.0.0.1:25,127.0.0.1:587 |
| listen ldap_proxy :389,:663 |
| |
| Si toutes les adresses ne tiennent pas sur une ligne, il est possible d'en |
| rajouter à l'aide du mot clé 'bind'. Dans ce cas, il n'est même pas nécessaire |
| de spécifier la première adresse sur la ligne listen, ce qui facilite parfois |
| l'écriture de configurations : |
| |
| bind [ <adresse_IP>:<plage_ports>[,...] ] |
| |
| Exemples : |
| ---------- |
| listen http_proxy |
| bind :80,:443 |
| bind 10.0.0.1:10080,10.0.0.1:10443 |
| |
| 2.1) Inhibition d'un service |
| ---------------------------- |
| Un service peut être désactivé pour des besoins de maintenance, sans avoir à |
| commenter toute une partie du fichier. Il suffit de positionner le mot clé |
| "disabled" dans sa section : |
| |
| listen smtp_proxy 0.0.0.0:25 |
| disabled |
| |
| Remarque: le mot clé 'enabled' permet de réactiver un service préalablement |
| désactivé par le mot clé 'disabled', par exemple à cause d'une |
| configuration par défaut. |
| |
| 2.2) Mode de fonctionnement |
| --------------------------- |
| Un service peut fonctionner dans trois modes différents : |
| - TCP |
| - HTTP |
| - état de santé |
| |
| Mode TCP |
| -------- |
| Dans ce mode, le service relaye, dès leur établissement, les connexions TCP |
| vers un ou plusieurs serveurs. Aucun traitement n'est effectué sur le flux. Il |
| s'agit simplement d'une association |
| source<adresse:port> -> destination<adresse:port>. |
| Pour l'utiliser, préciser le mode TCP sous la déclaration du relais. |
| |
| Exemple : |
| --------- |
| listen smtp_proxy 0.0.0.0:25 |
| mode tcp |
| |
| Mode HTTP |
| --------- |
| Dans ce mode, le service relaye les connexions TCP vers un ou plusieurs |
| serveurs, une fois qu'il dispose d'assez d'informations pour en prendre la |
| décision. Les en-têtes HTTP sont analysés pour y trouver un éventuel cookie, et |
| certains d'entre-eux peuvent être modifiés par le biais d'expressions |
| régulières. Pour activer ce mode, préciser le mode HTTP sous la déclaration du |
| relais. |
| |
| Exemple : |
| --------- |
| listen http_proxy 0.0.0.0:80 |
| mode http |
| |
| Mode supervision |
| ---------------- |
| Il s'agit d'un mode offrant à un composant externe une visibilité de l'état de |
| santé du service. Il se contente de retourner "OK" à tout client se connectant |
| sur son port. Il peut être utilisé avec des répartiteurs de charge évolués pour |
| déterminer quels sont les services utilisables. Si l'option 'httpchk' est |
| activée, alors la réponse changera en 'HTTP/1.0 200 OK' pour satisfaire les |
| attentes de composants sachant tester en HTTP. Pour activer ce mode, préciser |
| le mode HEALTH sous la déclaration du relais. |
| |
| Exemple : |
| --------- |
| # réponse simple : 'OK' |
| listen health_check 0.0.0.0:60000 |
| mode health |
| |
| # réponse HTTP : 'HTTP/1.0 200 OK' |
| listen http_health_check 0.0.0.0:60001 |
| mode health |
| option httpchk |
| |
| |
| 2.2.1 Supervision |
| ----------------- |
| Les versions 1.1.32 et 1.2.6 apportent une nouvelle solution pour valider le |
| bon fonctionnement du proxy sans perturber le service. Le mot-clé 'monitor-net' |
| a été créé dans le butd de spécifier un réseau d'équipements qui ne PEUVENT PAS |
| utiliser le service pour autre chose que des tests de fonctionnement. C'est |
| particulièrement adapté aux proxies TCP, car cela empêche le proxy de relayer |
| des établissements de connexion émis par un outil de surveillance. |
| |
| Lorsque c'est utilisé sur un proxy TCP, la connexion est acceptée puis refermée |
| et rien n'est logué. C'est suffisant pour qu'un répartiteur de charge en amont |
| détecte que le service est disponible. |
| |
| Lorsque c'est utilisé sur un proxy HTTP, la connexion est acceptée, rien n'est |
| logué, puis la réponse suivante est envoyée et la session refermée : |
| "HTTP/1.0 200 OK". C'est normalement suffisant pour qu'un répartiteur de charge |
| HTTP en amont détecte le service comme opérationnel, aussi bien à travers des |
| tests TCP que HTTP. |
| |
| Les proxies utilisant le mot-clé 'monitor-net' peuvent accessoirement se passer |
| de l'option 'dontlognull', ce qui permettra de loguer les connexions vides |
| émises depuis d'autres adresses que celles du réseau de tests. |
| |
| Exemple : |
| --------- |
| |
| listen tse-proxy |
| bind :3389,:1494,:5900 # TSE, ICA and VNC at once. |
| mode tcp |
| balance roundrobin |
| server tse-farm 192.168.1.10 |
| monitor-net 192.168.1.252/31 # L4 load-balancers on .252 and .253 |
| |
| |
| Lorsque le système effectuant les tests est situé derrière un proxy, le mot-clé |
| 'monitor-net' n'est pas utilisable du fait que haproxy verra toujours la même |
| adresse pour le proxy. Pour pallier à cette limitation, la version 1.2.15 a |
| apporté le mot-clé 'monitor-uri'. Il définit une URI qui ne sera ni retransmise |
| ni logée, mais pour laquelle haproxy retournera immédiatement une réponse |
| "HTTP/1.0 200 OK". Cela rend possibles les tests de validité d'une chaîne |
| reverse-proxy->haproxy en une requête HTTP. Cela peut être utilisé pour valider |
| une combinaision de stunnel+haproxy à l'aide de tests HTTPS par exemple. Bien |
| entendu, ce mot-clé n'est valide qu'en mode HTTP, sinon il n'y a pas de notion |
| d'URI. Noter que la méthode et la version HTTP sont simplement ignorées. |
| |
| Exemple : |
| --------- |
| |
| listen stunnel_backend :8080 |
| mode http |
| balance roundrobin |
| server web1 192.168.1.10:80 check |
| server web2 192.168.1.11:80 check |
| monitor-uri /haproxy_test |
| |
| |
| 2.3) Limitation du nombre de connexions simultanées |
| --------------------------------------------------- |
| Le paramètre "maxconn" permet de fixer la limite acceptable en nombre de |
| connexions simultanées par proxy. Chaque proxy qui atteint cette valeur cesse |
| d'écouter jusqu'à libération d'une connexion. Voir plus loin concernant les |
| limitations liées au système. |
| |
| Exemple : |
| --------- |
| listen tiny_server 0.0.0.0:80 |
| maxconn 10 |
| |
| |
| 2.4) Arrêt en douceur |
| --------------------- |
| Il est possible d'arrêter les services en douceur en envoyant un signal |
| SIGUSR1 au processus relais. Tous les services seront alors mis en phase |
| d'arrêt, mais pourront continuer d'accepter des connexions pendant un temps |
| défini par le paramètre 'grace' (en millisecondes). Cela permet par exemple, |
| de faire savoir rapidement à un répartiteur de charge qu'il ne doit plus |
| utiliser un relais, tout en continuant d'assurer le service le temps qu'il |
| s'en rende compte. |
| |
| Remarque : |
| ---------- |
| Les connexions actives ne sont jamais cassées. Dans le pire des cas, il faudra |
| attendre en plus leur expiration avant l'arrêt total du processus, ou bien tuer |
| manuellement le processus par l'envoi d'un signal SIGTERM. La valeur par défaut |
| du paramètre 'grace' est 0 (pas de grâce, arrêt immédiat de l'écoute). |
| |
| Exemple : |
| --------- |
| # arrêter en douceur par 'killall -USR1 haproxy' |
| # le service tournera encore 10 secondes après la demande d'arrêt |
| listen http_proxy 0.0.0.0:80 |
| mode http |
| grace 10000 |
| |
| # ce port n'est testé que par un répartiteur de charge. |
| listen health_check 0.0.0.0:60000 |
| mode health |
| grace 0 |
| |
| A partir de la version 1.2.8, un nouveau mécanisme de reconfiguration à chaud |
| a été introduit. Il est désormais possible de mettre les proxies en "pause" en |
| envoyant un signal SIGTTOU aux processus. Cela désactivera les sockets d'écoute |
| sans casser les sessions existantes. Suite à cela, l'envoi d'un signal SIGTTIN |
| réactivera les sockets d'écoute. Ceci est très pratique pour tenter de charger |
| une nouvelle configuration ou même une nouvelle version de haproxy sans casser |
| les connexions existantes. Si le rechargement s'effectue correctement, il ne |
| reste plus qu'à envoyer un signal SIGUSR1 aux anciens processus, ce qui |
| provoquera leur arrêt immédiat dès que leurs connexions seront terminées ; en |
| revanche, si le rechargement échoue, il suffit d'envoyer un signal SIGTTIN pour |
| remettre les ports en écoute et rétablir le service immédiatement. Veuillez |
| noter que le paramètre 'grace' est ignoré pour le signal SIGTTOU ainsi que le |
| signal SIGUSR1 une fois le processus en pause. Aussi, il peut s'avérer très |
| utile de sauver le fichier de pid avant de démarrer une nouvelle instance. |
| |
| Ce mécanisme est pleinement exploité à partir de la version 1.2.11 avec les |
| options '-st' et '-sf' (voir ci-dessous). |
| |
| 2.4) Reconfiguration à chaud |
| ---------------------------- |
| Les paramètres '-st' et '-sf' sont utilisés pour informer des processus |
| existants que la configuration va être rechargée. Ils recevront le signal |
| SIGTTOU, leur demandant de libérer les ports en écoute afin que le nouveau |
| processus puisse les prendre. Si quoi que ce soit se passe mal, le nouveau |
| processus leur enverra un signal SIGTTIN pour leur indiquer qu'ils peuvent |
| se remettre en écoute et continuer leur travail. En revanche, si la |
| configuration se charge correctement, alors ils recevront un signal de demande |
| de fin de travail en douceur (-sf), ou de terminaison immédiate (-st) qui |
| coupera les sessions en cours. Un usage typique tel que celui-ci permet de |
| recharger une configuration sans interruption de service : |
| |
| # haproxy -p /var/run/haproxy.pid -sf $(cat /var/run/haproxy.pid) |
| |
| |
| 2.5) Temps d'expiration des connexions |
| -------------------------------------- |
| Il est possible de paramétrer certaines durées d'expiration au niveau des |
| connexions TCP. Trois temps indépendants sont configurables et acceptent des |
| valeurs en millisecondes. Si l'une de ces trois temporisations est dépassée, la |
| session est terminée à chaque extrémité. |
| |
| - temps d'attente d'une donnée de la part du client, ou de la |
| possibilité de lui envoyer des données : "clitimeout" : |
| |
| # time-out client à 2mn30. |
| clitimeout 150000 |
| |
| - temps d'attente d'une donnée de la part du serveur, ou de la |
| possibilité de lui envoyer des données : "srvtimeout" : |
| |
| # time-out serveur à 30s. |
| srvtimeout 30000 |
| |
| - temps d'attente de l'établissement d'une connexion vers un serveur |
| "contimeout" : |
| |
| # on abandonne si la connexion n'est pas établie après 4 secondes |
| contimeout 4000 |
| |
| Remarques : |
| ----------- |
| - "contimeout" et "srvtimeout" n'ont pas d'utilité dans le cas du serveur de |
| type "health". |
| - sous de fortes charges, ou sur un réseau saturé ou défectueux, il est |
| possible de perdre des paquets. Du fait que la première retransmission |
| TCP n'ait lieu qu'au bout de 3 secoudes, fixer un timeout de connexion |
| inférieur à 3 secondes ne permet pas de se rattraper sur la perte |
| de paquets car la session aura été abandonnée avant la première |
| retransmission. Une valeur de 4 secondes réduira considérablement |
| le nombre d'échecs de connexion. |
| |
| 2.6) Tentatives de reconnexion |
| ------------------------------ |
| Lors d'un échec de connexion vers un serveur, il est possible de |
| retenter (potentiellement vers un autre serveur, en cas de répartition |
| de charge). Le nombre de nouvelles tentatives infructueuses avant |
| abandon est fourni par le paramètre "retries". |
| |
| Exemple : |
| --------- |
| # on essaie encore trois fois maxi |
| retries 3 |
| |
| Il est à noter que la tentative de reconnexion peut amener à utiliser un autre |
| serveur si le premier a disparu entre deux tentatives de connexion. |
| |
| |
| 2.7) Adresse du serveur |
| ----------------------- |
| Le serveur vers lequel sont redirigées les nouvelles connexions est défini par |
| le paramètre "dispatch" sous la forme <adresse_ip>:<port>. Il correspond à un |
| serveur d'assignation de cookie dans le cas où le service consiste à assurer |
| uniquement une persistence HTTP, ou bien simplement au serveur destination dans |
| le cas de relayage TCP simple. Cet ancien mode ne permet pas de tester l'état |
| du serveur distant, et il est maintenant recommandé d'utiliser de préférence |
| le mode 'balance'. |
| |
| Exemple : |
| --------- |
| # on envoie toutes les nouvelles connexions ici |
| dispatch 192.168.1.2:80 |
| |
| Remarque : |
| ---------- |
| Ce paramètre n'a pas d'utilité pour un serveur en mode 'health', ni en mode |
| 'balance'. |
| |
| |
| 2.8) Adresse de sortie |
| ---------------------- |
| Il est possible de forcer l'adresse utilisée pour établir les connexions vers |
| les serveurs à l'aide du paramètre "source". Il est même possible de forcer le |
| port, bien que cette fonctionnalité se limite à des usages très spécifiques. |
| C'est particulièrement utile en cas d'adressage multiple, et plus généralement |
| pour permettre aux serveurs de trouver le chemin de retour dans des contextes |
| de routage difficiles. Si l'adresse est '0.0.0.0' ou '*' ou vide, elle sera |
| choisie librement par le systeme. Si le port est '0' ou vide, il sera choisi |
| librement par le système. Il est à noter que depuis la version 1.1.18, les |
| tests de bon fonctionnement des serveurs seront aussi effectués à partir de la |
| source spécifiée par ce paramètre. |
| |
| Exemples : |
| ---------- |
| listen http_proxy *:80 |
| # toutes les connexions prennent l'adresse 192.168.1.200 |
| source 192.168.1.200:0 |
| |
| listen rlogin_proxy *:513 |
| # utiliser l'adresse 192.168.1.200 et le port réservé 900 |
| source 192.168.1.200:900 |
| |
| |
| 2.9) Définition du nom du cookie |
| -------------------------------- |
| En mode HTTP, il est possible de rechercher la valeur d'un cookie pour savoir |
| vers quel serveur aiguiller la requête utilisateur. Le nom du cookie est donné |
| par le paramètre "cookie". |
| |
| Exemple : |
| --------- |
| listen http_proxy :80 |
| mode http |
| cookie SERVERID |
| |
| On peut modifier l'utilisation du cookie pour la rendre plus intelligente |
| vis-à-vis des applications relayées. Il est possible, notamment de supprimer ou |
| réécrire un cookie retourné par un serveur accédé en direct, et d'insérer un |
| cookie dans une réponse HTTP adressée à un serveur sélectionné en répartition |
| de charge, et même de signaler aux proxies amont de ne pas cacher le cookie |
| inséré. |
| |
| Exemples : |
| ---------- |
| |
| Pour ne conserver le cookie qu'en accès indirect, donc à travers le |
| dispatcheur, et supprimer toutes ses éventuelles occurences lors des accès |
| directs : |
| |
| cookie SERVERID indirect |
| |
| Pour remplacer la valeur d'un cookie existant par celle attribuée à un serveur, |
| lors d'un accès direct : |
| |
| cookie SERVERID rewrite |
| |
| Pour créer un cookie comportant la valeur attribuée à un serveur lors d'un |
| accès en répartition de charge interne. Dans ce cas, il est souhaitable que |
| tous les serveurs aient un cookie renseigné. Un serveur non assigné d'un cookie |
| retournera un cookie vide (cookie de suppression) : |
| |
| cookie SERVERID insert |
| |
| Pour réutiliser un cookie applicatif et lui préfixer l'identifiant du serveur, |
| puis le supprimer dans les requêtes suivantes, utiliser l'option 'prefix'. Elle |
| permet d'insérer une instance de haproxy devant une application sans risquer |
| d'incompatibilités dûes à des clients qui ne supporteraient pas d'apprendre |
| plus d'un cookie : |
| |
| cookie JSESSIONID prefix |
| |
| Pour insérer un cookie, en s'assurant qu'un cache en amont ne le stockera pas, |
| ajouter le mot clé 'nocache' après 'insert' : |
| |
| cookie SERVERID insert nocache |
| |
| Pour insérer un cookie seulement suite aux requêtes de type POST, ajouter le |
| mot clé 'postonly' après 'insert' : |
| |
| cookie SERVERID insert postonly |
| |
| |
| Remarques : |
| ----------- |
| - Il est possible de combiner 'insert' avec 'indirect' ou 'rewrite' pour |
| s'adapter à des applications générant déjà le cookie, avec un contenu |
| invalide. Il suffit pour cela de les spécifier sur la même ligne. |
| |
| - dans le cas où 'insert' et 'indirect' sont spécifiés, le cookie n'est jamais |
| transmis au serveur vu qu'il n'en a pas connaissance et ne pourrait pas le |
| comprendre. |
| |
| - il est particulièrement recommandé d'utiliser 'nocache' en mode insertion si |
| des caches peuvent se trouver entre les clients et l'instance du proxy. Dans |
| le cas contraire, un cache HTTP 1.0 pourrait cacher la réponse, incluant le |
| cookie de persistence inséré, donc provoquer des changements de serveurs pour |
| des clients partageant le même cache. |
| |
| - le mode 'prefix' ne nécessite pas d'utiliser 'indirect', 'nocache', ni |
| 'postonly', car tout comme le mode 'rewrite', il s'appuie sur un cookie |
| présenté par l'application qui est censée savoir à quel moment il peut |
| être émis sans risque. Toutefois, comme il nécessite de rectifier le cookie |
| présenté par le client dans chaque requête ultérieure, il est indispensable |
| de s'assurer que le client et le serveur communiqueront sans "keep-alive |
| HTTP". Dans le doute, il est recommandé d'utiliser l'option "httpclose". |
| |
| - lorsque l'application est bien connue, et que les parties nécessitant de la |
| persistence sont systématiquement accédées par un formulaire en mode POST, |
| il est plus efficace encore de combiner le mot clé "postonly" avec "insert" |
| et "indirect", car la page d'accueil reste cachable, et c'est l'application |
| qui gère le 'cache-control'. |
| |
| 2.10) Assignation d'un serveur à une valeur de cookie |
| ---------------------------------------------------- |
| En mode HTTP, il est possible d'associer des valeurs de cookie à des serveurs |
| par le paramètre 'server'. La syntaxe est : |
| |
| server <identifiant> <adresse_ip>:<port> cookie <valeur> |
| |
| - <identifiant> est un nom quelconque de serveur utilisé pour l'identifier dans la |
| configuration et les logs. |
| - <adresse_ip>:<port> est le couple adresse-port sur lequel le serveur écoute. |
| - <valeur> est la valeur à reconnaître ou positionner dans le cookie. |
| |
| Exemple : le cookie SERVERID peut contenir server01 ou server02 |
| --------- |
| listen http_proxy :80 |
| mode http |
| cookie SERVERID |
| dispatch 192.168.1.100:80 |
| server web1 192.168.1.1:80 cookie server01 |
| server web2 192.168.1.2:80 cookie server02 |
| |
| Attention : la syntaxe a changé depuis la version 1.0. |
| ----------- |
| |
| 3) Répartiteur de charge autonome |
| ================================= |
| |
| Le relais peut effectuer lui-même la répartition de charge entre les différents |
| serveurs définis pour un service donné, en mode TCP comme en mode HTTP. Pour |
| cela, on précise le mot clé 'balance' dans la définition du service, |
| éventuellement suivi du nom d'un algorithme de répartition. Jusqu'à la version |
| 1.2.11, seul 'roundrobin' était géré, et c'est aussi la valeur implicite par |
| défaut. Avec la version 1.2.12, le nouveau mot clé 'source' est apparu. La |
| version 1.3.10 a également apporté le mot clé 'uri'. Il est évident qu'en cas |
| d'utilisation du répartiteur interne, il ne faudra pas spécifier d'adresse de |
| dispatch, et qu'il faudra au moins un serveur. |
| |
| Exemple : même que précédemment en répartition interne |
| --------- |
| |
| listen http_proxy :80 |
| mode http |
| cookie SERVERID |
| balance roundrobin |
| server web1 192.168.1.1:80 cookie server01 |
| server web2 192.168.1.2:80 cookie server02 |
| |
| Depuis la version 1.1.22, il est possible de déterminer automatiquement le port |
| du serveur vers lequel sera envoyée la connexion, en fonction du port d'écoute |
| sur lequel le client s'est connecté. En effet, il y a 4 possibilités pour le |
| champ <port> de l'adresse serveur : |
| |
| - non spécifié ou nul : |
| la connexion sera envoyée au serveur sur le même port que celui sur |
| lequel le relais a reçu la connexion. |
| |
| - valeur numérique (seul cas supporté pour les versions antérieures) : |
| le serveur recevra la connexion sur le port désigné. |
| |
| - valeur numérique précédée d'un signe '+' : |
| la connexion sera envoyée au serveur sur le même port que celui sur |
| lequel le relais a reçu la connexion, auquel on ajoute la valeur désignée. |
| |
| - valeur numérique précédée d'un signe '-' : |
| la connexion sera envoyée au serveur sur le même port que celui sur |
| lequel le relais a reçu la connexion, duquel on soustrait la valeur |
| désignée. |
| |
| Exemples : |
| ---------- |
| |
| # même que précédemment |
| |
| listen http_proxy :80 |
| mode http |
| cookie SERVERID |
| balance roundrobin |
| server web1 192.168.1.1 cookie server01 |
| server web2 192.168.1.2 cookie server02 |
| |
| # relayage simultané des ports 80 et 81 et 8080-8089 |
| |
| listen http_proxy :80,:81,:8080-8089 |
| mode http |
| cookie SERVERID |
| balance roundrobin |
| server web1 192.168.1.1 cookie server01 |
| server web2 192.168.1.2 cookie server02 |
| |
| # relayage TCP des ports 25, 389 et 663 vers les ports 1025, 1389 et 1663 |
| |
| listen http_proxy :25,:389,:663 |
| mode tcp |
| balance roundrobin |
| server srv1 192.168.1.1:+1000 |
| server srv2 192.168.1.2:+1000 |
| |
| Comme indiqué précédemment, la version 1.2.12 apporta le nouveau mot clé |
| 'source'. Lorsque celui-ci est utilisé, l'adresse IP du client est hachée et |
| distribuée de manière homogène parmi les serveurs disponibles, de sorte qu'une |
| même adresse IP aille toujours sur le même serveur tant qu'il n'y a aucun |
| changement dans le nombre de serveurs disponibles. Ceci peut être utilisé par |
| exemple pour attacher le HTTP et le HTTPS sur un même serveur pour un même |
| client. Cela peut également être utilisé pour améliorer la persistance |
| lorsqu'une partie de la population des clients n'accepte pas les cookies. Dans |
| ce cas, seuls ces derniers seront perturbés par la perte d'un serveur. |
| |
| NOTE: il est important de prendre en compte le fait que beaucoup d'internautes |
| naviguent à travers des fermes de proxies qui assignent des adresses IP |
| différentes à chaque requête. D'autres internautes utilisent des liens à |
| la demande et obtiennent une adresse IP différente à chaque connexion. De |
| ce fait, le paramètre 'source' doit être utilisé avec une extrème |
| précaution. |
| |
| Exemples : |
| ---------- |
| |
| # assurer qu'une même adresse IP ira sur le même serveur pour tout service |
| |
| listen http_proxy |
| bind :80,:443 |
| mode http |
| balance source |
| server web1 192.168.1.1 |
| server web2 192.168.1.2 |
| |
| # améliorer la persistance par l'utilisation de la source en plus du cookie : |
| |
| listen http_proxy :80 |
| mode http |
| cookie SERVERID |
| balance source |
| server web1 192.168.1.1 cookie server01 |
| server web2 192.168.1.2 cookie server02 |
| |
| De plus, tel qu'indiqué ci-dessus, la version 1.3.10 a introduit le mot clé |
| 'uri'. Il est très pratique dans le cas de répartition de charge entre des |
| reverse-proxy-caches, parce qu'il utilisera le résultat d'un hachage de l'URI |
| pour choisir un serveur, ce qui aura pour effet d'optimiser le taux de cache |
| du fait que la même URI sera toujours envoyée au même cache. Ce mot-clé n'est |
| autorisé qu'en mode HTTP. |
| |
| Example : |
| --------- |
| |
| # Envoie toujours une URI donnée au même serveur |
| |
| listen http_proxy |
| bind :3128 |
| mode http |
| balance uri |
| server squid1 192.168.1.1 |
| server squid2 192.168.1.2 |
| |
| |
| 3.1) Surveillance des serveurs |
| ------------------------------ |
| Il est possible de tester l'état des serveurs par établissement de connexion |
| TCP ou par envoi d'une requête HTTP. Un serveur hors d'usage ne sera pas |
| utilisé dans le processus de répartition de charge interne. Pour activer la |
| surveillance, ajouter le mot clé 'check' à la fin de la déclaration du serveur. |
| Il est possible de spécifier l'intervalle (en millisecondes) séparant deux |
| tests du serveur par le paramètre "inter", le nombre d'échecs acceptés par le |
| paramètre "fall", et le nombre de succès avant reprise par le paramètre "rise". |
| Les paramètres non précisés prennent les valeurs suivantes par défaut : |
| |
| - inter : 2000 |
| - rise : 2 |
| - fall : 3 |
| - port : port de connexion du serveur |
| - addr : adresse de connexion du serveur (par defaut: adresse du serveur) |
| |
| Le mode par défaut consiste à établir des connexions TCP uniquement. Dans |
| certains cas de pannes, des serveurs peuvent continuer à accepter les |
| connexions sans les traiter. Depuis la version 1.1.16, haproxy est en mesure |
| d'envoyer des requêtes HTTP courtes et très peu coûteuses. Les versions 1.1.16 |
| et 1.1.17 utilisent "OPTIONS / HTTP/1.0". Dans les versions 1.1.18 à 1.1.20, |
| les requêtes ont été changées en "OPTIONS * HTTP/1.0" pour des raisons de |
| contrôle d'accès aux ressources. Cependant, cette requête documentée dans la |
| RFC2068 n'est pas comprise par tous les serveurs. Donc à partir de la version |
| 1.1.21, la requête par défaut est revenue à "OPTIONS / HTTP/1.0", mais il est |
| possible de paramétrer la partie URI. Les requêtes OPTIONS présentent |
| l'avantage d'être facilement extractibles des logs, et de ne pas induire |
| d'accès aux fichiers côté serveur. Seules les réponses 2xx et 3xx sont |
| considérées valides, les autres (y compris non-réponses) aboutissent à un |
| échec. Le temps maximal imparti pour une réponse est égal à l'intervalle entre |
| deux tests (paramètre "inter"). Pour activer ce mode, spécifier l'option |
| "httpchk", éventuellement suivie d'une méthode et d'une URI. L'option "httpchk" |
| accepte donc 4 formes : |
| |
| - option httpchk -> OPTIONS / HTTP/1.0 |
| - option httpchk URI -> OPTIONS <URI> HTTP/1.0 |
| - option httpchk METH URI -> <METH> <URI> HTTP/1.0 |
| - option httpchk METH URI VER -> <METH> <URI> <VER> |
| |
| HAProxy est souvent utilisé pour relayer divers protocoles reposant sur TCP, |
| tels que HTTPS, SMTP ou LDAP, le plus commun étant HTTPS. Un problème assez |
| couramment rencontré dans les data centers est le besoin de relayer du trafic |
| vers des serveurs lointains tout en maintenant la possibilité de basculer sur |
| un serveur de secours. Les tests purement TCP ne suffisent pas toujours dans |
| ces situations car l'on trouve souvent, dans la chaîne, des proxies, firewalls |
| ou répartiteurs de charge qui peuvent acquitter la connexion avant qu'elle |
| n'atteigne le serveur. La seule solution à ce problème est d'envoyer des tests |
| applicatifs. Comme la demande pour les tests HTTPS est élevée, ce test a été |
| implémenté en version 1.2.15 sur la base de messages SSLv3 CLIENT HELLO. Pour |
| l'activer, utiliser "option ssl-hello-chk". Ceci enverra des messages SSLv3 |
| CLIENT HELLO aux serveurs, en annonçant un support pour la majorité des |
| algorithmes de chiffrement. Si en retour, le serveur envoie ce qui ressemble à |
| une réponse SSLv3 SERVER HELLO ou ALERT (refus des algorithmes), alors la |
| réponse sera considérée comme valide. Noter qu'Apache ne produit pas de log |
| lorsqu'il reçoit des messages HELLO, ce qui en fait un type de message |
| parfaitement adapté à ce besoin. |
| |
| La version 1.3.10 est accompagnée d'un nouveau test d'état pour le SMTP. Par |
| défaut, il consiste à envoyer "HELO localhost" aux serveurs, et à attendre le |
| message "250" en retour. Notez qu'il peut aussi envoyer une requête plus |
| spécifique : |
| |
| - option smtpchk -> envoie "HELO localhost" |
| - option smtpchk EHLO mail.mydomain.com -> envoie ce message ESMTP |
| |
| Voir les exemples ci-après. |
| |
| Depuis la version 1.1.17, il est possible de définir des serveurs de secours, |
| utilisés uniquement lorsqu'aucun des autres serveurs ne fonctionne. Pour cela, |
| ajouter le mot clé "backup" sur la ligne de définition du serveur. Un serveur |
| de secours n'est appelé que lorsque tous les serveurs normaux, ainsi que tous |
| les serveurs de secours qui le précèdent sont hors d'usage. Il n'y a donc pas |
| de répartition de charge entre des serveurs de secours par défaut. A partir |
| de la version 1.2.9, il est possible de les utiliser simultanément grâce à |
| l'option 'allbackups'. Ce type de serveurs peut servir à retourner des pages |
| d'indisponibilité de service. Dans ce cas, il est préférable de ne pas affecter |
| de cookie, afin que les clients qui le rencontrent n'y soient pas affectés |
| définitivement. Le fait de ne pas mettre de cookie envoie un cookie vide, ce |
| qui a pour effet de supprimer un éventuel cookie affecté précédemment. |
| |
| Depuis la version 1.1.22, il est possible d'envoyer les tests de fonctionnement |
| vers un port différent de celui de service. C'est nécessaire principalement |
| pour les configurations où le serveur n'a pas de port prédéfini, par exemple |
| lorsqu'il est déduit du port d'acceptation de la connexion. Pour cela, utiliser |
| le paramètre 'port' suivi du numéro de port devant répondre aux requêtes. Il |
| est possible d'envoyer les tests de fonctionnement vers une adresse différente |
| de celle de service. Cela permet d'utiliser, sur la machine faisant fonctionner |
| HAproxy, un démon permettant des tests specifiques ( REGEX sur un résultat et |
| basculement de plusieurs fermes en cas d'erreur sur l'une d'elles). |
| |
| Enfin, depuis la version 1.1.17, il est possible de visualiser rapidement |
| l'état courant de tous les serveurs. Pour cela, il suffit d'envoyer un signal |
| SIGHUP au processus proxy. L'état de tous les serveurs de tous les proxies est |
| envoyé dans les logs en niveau "notice", ainsi que sur la sortie d'erreurs si |
| elle est active. C'est une bonne raison pour avoir au moins un serveur de logs |
| local en niveau notice. |
| |
| Depuis la version 1.1.18 (et 1.2.1), un message d'urgence est envoyé dans les |
| logs en niveau 'emerg' si tous les serveurs d'une même instance sont tombés, |
| afin de notifier l'administrateur qu'il faut prendre une action immédiate. |
| |
| Depuis les versions 1.1.30 et 1.2.3, plusieurs serveurs peuvent partager la |
| même valeur de cookie. C'est particulièrement utile en mode backup, pour |
| sélectionner des chemins alternatifs pour un serveur donné, pour mettre en |
| oeuvre l'arrêt en douceur d'un serveur, ou pour diriger les clients |
| temporairement vers une page d'erreur en attendant le redémarrage d'une |
| application. Le principe est que lorsqu'un serveur est détecté comme inopérant, |
| le proxy cherchera le prochain serveur possédant la même valeur de cookie pour |
| chaque client qui le demandera. S'il ne trouve pas de serveur normal, alors il |
| le cherchera parmi les serveurs de backup. Consulter le guide d'architecture |
| pour plus d'informations. |
| |
| Exemples : |
| ---------- |
| # conf du paragraphe 3) avec surveillance TCP |
| listen http_proxy 0.0.0.0:80 |
| mode http |
| cookie SERVERID |
| balance roundrobin |
| server web1 192.168.1.1:80 cookie server01 check |
| server web2 192.168.1.2:80 cookie server02 check inter 500 rise 1 fall 2 |
| |
| # même que précédemment avec surveillance HTTP par 'OPTIONS / HTTP/1.0' |
| listen http_proxy 0.0.0.0:80 |
| mode http |
| cookie SERVERID |
| balance roundrobin |
| option httpchk |
| server web1 192.168.1.1:80 cookie server01 check |
| server web2 192.168.1.2:80 cookie server02 check inter 500 rise 1 fall 2 |
| |
| # même que précédemment avec surveillance HTTP par 'OPTIONS /index.html HTTP/1.0' |
| listen http_proxy 0.0.0.0:80 |
| mode http |
| cookie SERVERID |
| balance roundrobin |
| option httpchk /index.html |
| server web1 192.168.1.1:80 cookie server01 check |
| server web2 192.168.1.2:80 cookie server02 check inter 500 rise 1 fall 2 |
| |
| # idem avec surveillance HTTP par 'HEAD /index.jsp? HTTP/1.1\r\nHost: www' |
| listen http_proxy 0.0.0.0:80 |
| mode http |
| cookie SERVERID |
| balance roundrobin |
| option httpchk HEAD /index.jsp? HTTP/1.1\r\nHost:\ www |
| server web1 192.168.1.1:80 cookie server01 check |
| server web2 192.168.1.2:80 cookie server02 check inter 500 rise 1 fall 2 |
| |
| # répartition avec persistence basée sur le préfixe de cookie, et arrêt en |
| # douceur utilisant un second port (81) juste pour les health-checks. |
| listen http_proxy 0.0.0.0:80 |
| mode http |
| cookie JSESSIONID prefix |
| balance roundrobin |
| option httpchk HEAD /index.jsp? HTTP/1.1\r\nHost:\ www |
| server web1-norm 192.168.1.1:80 cookie s1 check port 81 |
| server web2-norm 192.168.1.2:80 cookie s2 check port 81 |
| server web1-stop 192.168.1.1:80 cookie s1 check port 80 backup |
| server web2-stop 192.168.1.2:80 cookie s2 check port 80 backup |
| |
| # Insertion automatique de cookie dans la réponse du serveur, et suppression |
| # automatique dans la requête, tout en indiquant aux caches de ne pas garder |
| # ce cookie. |
| listen web_appl 0.0.0.0:80 |
| mode http |
| cookie SERVERID insert nocache indirect |
| balance roundrobin |
| server web1 192.168.1.1:80 cookie server01 check |
| server web2 192.168.1.2:80 cookie server02 check |
| |
| # idem avec serveur applicatif de secours sur autre site, et serveur de pages d'erreurs |
| listen web_appl 0.0.0.0:80 |
| mode http |
| cookie SERVERID insert nocache indirect |
| balance roundrobin |
| server web1 192.168.1.1:80 cookie server01 check |
| server web2 192.168.1.2:80 cookie server02 check |
| server web-backup 192.168.2.1:80 cookie server03 check backup |
| server web-excuse 192.168.3.1:80 check backup |
| |
| # relayage SMTP+TLS avec test du serveur et serveur de backup |
| |
| listen http_proxy :25,:587 |
| mode tcp |
| balance roundrobin |
| server srv1 192.168.1.1 check port 25 inter 30000 rise 1 fall 2 |
| server srv2 192.168.1.2 backup |
| |
| # relayage HTTPS avec test du serveur et serveur de backup |
| |
| listen http_proxy :443 |
| mode tcp |
| option ssl-hello-chk |
| balance roundrobin |
| server srv1 192.168.1.1 check inter 30000 rise 1 fall 2 |
| server srv2 192.168.1.2 backup |
| |
| # Utilisation d'un groupe de serveurs pour le backup (nécessite haproxy 1.2.9) |
| listen http_proxy 0.0.0.0:80 |
| mode http |
| balance roundrobin |
| option httpchk |
| server inst1 192.168.1.1:80 cookie s1 check |
| server inst2 192.168.1.2:80 cookie s2 check |
| server inst3 192.168.1.3:80 cookie s3 check |
| server back1 192.168.1.10:80 check backup |
| server back2 192.168.1.11:80 check backup |
| option allbackups # all backups will be used |
| |
| |
| 3.2) Reconnexion vers un répartiteur en cas d'échec direct |
| ---------------------------------------------------------- |
| En mode HTTP, si un serveur défini par un cookie ne répond plus, les clients |
| seront définitivement aiguillés dessus à cause de leur cookie, et de ce fait, |
| définitivement privés de service. La spécification du paramètre 'redispatch' |
| autorise dans ce cas à renvoyer les connexions échouées vers le répartiteur |
| (externe ou interne) afin d'assigner un nouveau serveur à ces clients. |
| |
| Exemple : |
| --------- |
| listen http_proxy 0.0.0.0:80 |
| mode http |
| cookie SERVERID |
| dispatch 192.168.1.100:80 |
| server web1 192.168.1.1:80 cookie server01 |
| server web2 192.168.1.2:80 cookie server02 |
| redispatch # renvoyer vers dispatch si refus de connexion. |
| |
| Par défaut (et dans les versions 1.1.16 et antérieures), le paramètre |
| redispatch ne s'applique qu'aux échecs de connexion au serveur. Depuis la |
| version 1.1.17, il s'applique aussi aux connexions destinées à des serveurs |
| identifiés comme hors d'usage par la surveillance. Si l'on souhaite malgré |
| tout qu'un client disposant d'un cookie correspondant à un serveur défectueux |
| tente de s'y connecter, il faut préciser l'option "persist" : |
| |
| listen http_proxy 0.0.0.0:80 |
| mode http |
| option persist |
| cookie SERVERID |
| dispatch 192.168.1.100:80 |
| server web1 192.168.1.1:80 cookie server01 |
| server web2 192.168.1.2:80 cookie server02 |
| redispatch # renvoyer vers dispatch si serveur HS. |
| |
| |
| 3.3) Assignation de poids différents à des serveurs |
| --------------------------------------------------- |
| Parfois il arrive d'ajouter de nouveaux serveurs pour accroître la capacité |
| d'une ferme de serveur, mais le nouveau serveur est soit beaucoup plus petit |
| que les autres (dans le cas d'un ajout d'urgence de matériel de récupération), |
| soit plus puissant (lors d'un investissement dans du matériel neuf). Pour cette |
| raison, il semble parfois judicieux de pouvoir envoyer plus de clients vers les |
| plus gros serveurs. Jusqu'à la version 1.2.11, il était nécessaire de répliquer |
| plusieurs fois les définitions des serveurs pour augmenter leur poids. Depuis |
| la version 1.2.12, l'option 'weight' est disponible. HAProxy construit alors |
| une vue des serveurs disponibles la plus homogène possible en se basant sur |
| leur poids de sorte que la charge se distribue de la manière la plus lisse |
| possible. Le poids compris entre 1 et 256 doit refléter la capacité d'un |
| serveur par rapport aux autres. Le poids de 1 donne la fréquence d'apparition |
| la plus faible, et 256 la fréquence la plus élevée. De cette manière, si un |
| serveur disparait, les capacités restantes sont toujours respectées. |
| |
| |
| Exemple : |
| --------- |
| # distribution équitable sur 2 opteron and un ancien pentium3 |
| |
| listen web_appl 0.0.0.0:80 |
| mode http |
| cookie SERVERID insert nocache indirect |
| balance roundrobin |
| server pentium3-800 192.168.1.1:80 cookie server01 weight 8 check |
| server opteron-2.0G 192.168.1.2:80 cookie server02 weight 20 check |
| server opteron-2.4G 192.168.1.3:80 cookie server03 weight 24 check |
| server web-backup1 192.168.2.1:80 cookie server04 check backup |
| server web-excuse 192.168.3.1:80 check backup |
| |
| Notes : |
| ------- |
| - lorsque le poids n'est pas spécifié, la valeur par défaut est à 1 |
| |
| - le poids n'impacte pas les tests de fonctionnement (health checks), donc il |
| est plus propre d'utiliser les poids que de répliquer le même serveur |
| plusieurs fois. |
| |
| - les poids s'appliquent également aux serveurs de backup si l'option |
| 'allbackups' est positionnée. |
| |
| - le poids s'applique aussi à la répartition selon la source |
| ('balance source'). |
| |
| - quels que soient les poids, le premier serveur sera toujours assigné en |
| premier. Cette règle facilite les diagnostics. |
| |
| - pour les puristes, l'algorithme de calculation de la vue des serveurs donne |
| une priorité aux premiers serveurs, donc la vue est la plus uniforme si les |
| serveurs sont déclarés dans l'ordre croissant de leurs poids. |
| |
| La distribution du trafic suivra exactement le séquencement suivant : |
| |
| Request| 1 1 1 1 |
| number | 1 2 3 4 5 6 7 8 9 0 1 2 3 |
| --------+--------------------------- |
| p3-800 | X . . . . . . X . . . . . |
| opt-20 | . X . X . X . . . X . X . |
| opt-24 | . . X . X . X . X . X . X |
| |
| |
| 3.4) Limitation du nombre de sessions concurrentes par serveur |
| -------------------------------------------------------------- |
| Certains serveurs web multi-processus tels qu'Apache souffrent dès qu'il y a |
| trop de sessions concurrentes, parce qu'il est très coûteux de faire |
| fonctionner des centaines ou des milliers de processus sur un système. Une |
| solution consiste à augmenter le nombre de serveurs et de répartir la charge |
| entre eux, mais cela pose un problème lorsque le but est uniquement de résister |
| à des pics de charge occasionnels. |
| |
| Pour résoudre ce problème, une nouvelle fonctionnalité a été implémentée dans |
| HAProxy 1.2.13. Il s'agit d'une limite "maxconn" par serveur, associée à une |
| file d'attente par serveur et par proxy. Ceci transforme HAProxy en un tampon |
| entre des milliers de clients et quelques serveurs. Dans bien des cas, le fait |
| de diminuer la valeur maxconn améliorera notablement les performances des |
| serveurs et diminuera les temps de réponse simplement parce que les serveurs |
| seront moins congestionnés. |
| |
| Quand une requête cherche à joindre n'importe quel serveur, le premier serveur |
| non saturé est utilisé, en respectant l'algorithme de répartition de charge. Si |
| tous les serveurs sont saturés, alors la requête sera mise dans la file |
| d'attente globale de l'instance. Elle sortira de cette file d'attente lorsque |
| toutes les requêtes précédentes auront été libérées et qu'un serveur aura été |
| libéré d'une connexion pour la traiter. |
| |
| Si une requête fait référence à un serveur en particulier (p.ex: hachage d'IP |
| source, ou persistance par cookie), et que ce server est saturé, alors la |
| requête sera mise dans la file d'attente dédiée à ce serveur. Cette file |
| d'attente est prioritaire sur la file d'attente globale, de sorte qu'il soit |
| plus facile d'atteindre le site pour les utilisateurs qui s'y trouvent déjà |
| que pour les nouveaux utilisateurs. |
| |
| Pour cela, les logs ont dû être enrichis pour indiquer le nombre de sessions |
| par serveur, la position de la requête dans les files d'attentes, et le temps |
| passé en file d'attente. Ceci aide considérablement à faire de la prévision de |
| capacité. Voir la section 'logs' plus bas pour plus d'informations. |
| |
| Exemple : |
| --------- |
| # Prendre soin du P3 qui n'a que 256 Mo de RAM. |
| listen web_appl 0.0.0.0:80 |
| maxconn 10000 |
| mode http |
| cookie SERVERID insert nocache indirect |
| balance roundrobin |
| server pentium3-800 192.168.1.1:80 cookie s1 weight 8 maxconn 100 check |
| server opteron-2.0G 192.168.1.2:80 cookie s2 weight 20 maxconn 300 check |
| server opteron-2.4G 192.168.1.3:80 cookie s3 weight 24 maxconn 300 check |
| server web-backup1 192.168.2.1:80 cookie s4 check maxconn 200 backup |
| server web-excuse 192.168.3.1:80 check backup |
| |
| Cette option se montra si efficace pour réduire les temps de réponse des |
| serveurs que certains utilisateurs voulaient utiliser des valeurs trop basses |
| pour améliorer les performances de leurs serveurs. Seulement, ils n'étaient |
| alors plus en mesure de supporter de très fortes charges parce qu'il n'était |
| plus possible de les saturer. Pour cette raison, la version 1.2.14 a apporté la |
| limitation dynamique de connexions avec l'addition du paramètre "minconn". |
| Lorsque ce paramètre est associé à "maxconn", il active la limitation dynamique |
| basée sur la charge de l'instance. Le nombre maximal de sessions concurrentes |
| sur un serveur devient alors proportionnel au nombre de sessions de l'instance |
| par rapport à son 'maxconn'. Un minimum de <minconn> sessions sera toujours |
| permis quelle que soit la charge. Ceci assurera que les serveurs travailleront |
| au meilleur de leurs performances sous des charges normales, et qu'ils seront |
| tout de même capables de supporter de fortes pointes lorsque nécessaire. La |
| limite dynamique est calculée comme ceci : |
| |
| srv.dyn_limit = max(srv.minconn, srv.maxconn * inst.sess / inst.maxconn) |
| |
| Exemple : |
| --------- |
| # Prendre soin du P3 qui n'a que 256 Mo de RAM. |
| listen web_appl 0.0.0.0:80 |
| maxconn 10000 |
| mode http |
| cookie SERVERID insert nocache indirect |
| balance roundrobin |
| server pentium3-800 192.168.1.1:80 cookie s1 weight 8 minconn 10 maxconn 100 check |
| server opteron-2.0G 192.168.1.2:80 cookie s2 weight 20 minconn 30 maxconn 300 check |
| server opteron-2.4G 192.168.1.3:80 cookie s3 weight 24 minconn 30 maxconn 300 check |
| server web-backup1 192.168.2.1:80 cookie s4 check maxconn 200 backup |
| server web-excuse 192.168.3.1:80 check backup |
| |
| Dans l'exemple ci-dessus, le serveur "pentium3-800' recevra au plus 100 |
| connexions simultanées lorsque l'instance du proxy en atteindra 10000, et |
| recevra seulement 10 connexions simultanées tant que le proxy sera sous les 1000 |
| sessions. |
| |
| Notes : |
| ------- |
| - la requête ne restera pas indéfiniment en file d'attente, elle est |
| assujétie au paramètre 'contimeout', et si une requête ne peut pas |
| sortir de la file avant ce time-out, soit parce que le serveur est |
| saturé, soit parce qu'il y a trop de requêtes en file d'attente, |
| alors elle expirera avec une erreur 503. |
| |
| - si seul <minconn> est spécifié, il a le même effet que <maxconn> |
| |
| - positionner des valeurs trop basses pour 'maxconn' peut améliorer les |
| performances mais aussi permettre à des utilisateurs trop lents de bloquer |
| un serveur pour les autres utilisateurs. |
| |
| |
| 3.5) Abandon des requêtes abortées |
| ---------------------------------- |
| En présence de très fortes charges, les serveurs mettront un certain temps à |
| répondre. La file d'attente du proxy se remplira, et les temps de réponse |
| suivront une croissance proportionnelle à la taille de file d'attente fois |
| le temps moyen de réponse par session. Lorsque les clients attendront plus de |
| quelques secondes, ils cliqueront souvent sur le bouton 'STOP' de leur |
| navigateur, laissant des requêtes inutiles en file d'attente et ralentissant |
| donc les autres utilisateurs. |
| |
| Comme il n'y a aucun moyen de distinguer un vrai clic sur STOP d'une simple |
| fermeture du canal de sortie sur le client (shutdown(SHUT_WR)), les agents HTTP |
| doivent être conservateurs et considérer que le client n'a probablement fermé |
| que le canal de sortie en attendant la réponse. Toutefois, ceci introduit des |
| risques de congestion lorsque beaucoup d'utilisateurs font de même, et s'avère |
| aujourd'hui complètement inutile car probablement aucun client ne referme la |
| session en attendant la réponse. Certains agents HTTP supportent ceci (Squid, |
| Apache, HAProxy), et d'autres ne le supportent pas (TUX, et la plupart des |
| répartiteurs de charge matériels). Donc la probabilité pour qu'une notification |
| de fermeture d'un canal d'entrée côté client représente un utilisateur cliquant |
| sur 'STOP' est proche de 100%, et il est vraiment tentant d'abandonner la |
| requête prématurément sans polluer les serveurs. |
| |
| Pour cette raison, une nouvelle option "abortonclose" a été introduite en |
| version 1.2.14. Par défaut (sans l'option), le comportement reste conforme à |
| HTTP. Mais lorsque l'option est spécifiée, une session dont le canal entrant |
| est fermé sera abortée si cela est possible, c'est à dire que la requête est |
| soit en file d'attente, soit en tentative de connexion. Ceci réduit |
| considérablement la longueur des files d'attentes et la charge sur les serveurs |
| saturés lorsque les utilisateurs sont tentés de cliquer sur 'STOP', ce qui à |
| son tour, réduit les temps de réponse pour les autres utilisateurs. |
| |
| Exemple : |
| --------- |
| listen web_appl 0.0.0.0:80 |
| maxconn 10000 |
| mode http |
| cookie SERVERID insert nocache indirect |
| balance roundrobin |
| server web1 192.168.1.1:80 cookie s1 weight 10 maxconn 100 check |
| server web2 192.168.1.2:80 cookie s2 weight 10 maxconn 100 check |
| server web3 192.168.1.3:80 cookie s3 weight 10 maxconn 100 check |
| server bck1 192.168.2.1:80 cookie s4 check maxconn 200 backup |
| option abortonclose |
| |
| |
| 4) Fonctionnalités additionnelles |
| ================================= |
| |
| D'autres fonctionnalités d'usage moins courant sont disponibles. Il s'agit |
| principalement du mode transparent, de la journalisation des connexions, de la |
| réécriture des en-têtes, et du statut sous forme de page HTML. |
| |
| |
| 4.1) Fonctionnalités réseau |
| --------------------------- |
| 4.1.1) Fonctionnement en mode transparent |
| --------------------------------------- |
| En mode HTTP, le mot clé 'transparent' permet d'intercepter des sessions |
| routées à travers la machine hébergeant le proxy. Dans ce mode, on ne précise |
| pas l'adresse de répartition 'dispatch', car celle-ci est tirée de l'adresse |
| destination de la session détournée. Le système doit permettre de rediriger les |
| paquets vers un processus local. |
| |
| Exemple : |
| --------- |
| listen http_proxy 0.0.0.0:65000 |
| mode http |
| transparent |
| cookie SERVERID |
| server server01 192.168.1.1:80 |
| server server02 192.168.1.2:80 |
| |
| # iptables -t nat -A PREROUTING -i eth0 -p tcp -d 192.168.1.100 \ |
| --dport 80 -j REDIRECT --to-ports 65000 |
| |
| Remarque : |
| ---------- |
| Si le port n'est pas spécifié sur le serveur, c'est le port auquel s'est |
| adressé le client qui sera utilisé. Cela permet de relayer tous les ports TCP |
| d'une même adresse avec une même instance et sans utiliser directement le mode |
| transparent. |
| |
| Exemple : |
| --------- |
| listen http_proxy 0.0.0.0:65000 |
| mode tcp |
| server server01 192.168.1.1 check port 60000 |
| server server02 192.168.1.2 check port 60000 |
| |
| # iptables -t nat -A PREROUTING -i eth0 -p tcp -d 192.168.1.100 \ |
| -j REDIRECT --to-ports 65000 |
| |
| |
| 4.1.2) Choix d'une adresse source par serveur |
| --------------------------------------------------- |
| Avec les versions 1.1.30 et 1.2.3, il devient possible de spécifier une adresse |
| IP source pour joindre chaque serveur. C'est utile pour joindre des serveurs de |
| backup à partir d'un LAN différent, ou pour utiliser des chemins alternatifs |
| pour joindre le même serveur. C'est également utilisable pour faciliter une |
| répartition de charge selon l'adresse IP source pour des connexions sortantes. |
| Bien entendu, la même adresse est utilisée pour les health-checks. |
| |
| Exemple : |
| --------- |
| # utiliser une adresse particulière pour joindre les 2 serveur |
| listen http_proxy 0.0.0.0:65000 |
| mode http |
| balance roundrobin |
| server server01 192.168.1.1:80 source 192.168.2.13 |
| server server02 192.168.1.2:80 source 192.168.2.13 |
| |
| Exemple : |
| --------- |
| # utiliser une adresse particulière pour joindre chaque serveur |
| listen http_proxy 0.0.0.0:65000 |
| mode http |
| balance roundrobin |
| server server01 192.168.1.1:80 source 192.168.1.1 |
| server server02 192.168.2.1:80 source 192.168.2.1 |
| |
| Exemple : |
| --------- |
| # faire une répartition d'adresse sources pour joindre le même proxy à |
| # travers deux liens WAN |
| listen http_proxy 0.0.0.0:65000 |
| mode http |
| balance roundrobin |
| server remote-proxy-way1 192.168.1.1:3128 source 192.168.2.1 |
| server remote-proxy-way2 192.168.1.1:3128 source 192.168.3.1 |
| |
| Exemple : |
| --------- |
| # forcer une connexion TCP à s'attacher à un port particulier |
| listen http_proxy 0.0.0.0:2000 |
| mode tcp |
| balance roundrobin |
| server srv1 192.168.1.1:80 source 192.168.2.1:20 |
| server srv2 192.168.1.2:80 source 192.168.2.1:20 |
| |
| 4.1.3) Maintien de session TCP (keep-alive) |
| ------------------------------------------- |
| Avec la version 1.2.7, il devient possible d'activer le maintien de session |
| TCP (TCP keep-alive) à la fois côté client et côté serveur. Cela permet |
| d'empêcher des sessions longues d'expirer sur des équipements de niveau 4 |
| externes tels que des firewalls ou des répartiteurs de charge. Cela permet |
| aussi au système de détecter et terminer des sessions figées lorsqu'aucun |
| time-out n'a été positionné (fortement déconseillé). Le proxy ne peut pas |
| positionner l'intervalle entre les annonces ni le nombre maximal, veuillez |
| vous référer au manuel du système d'exploitation pour cela. Il existe 3 options |
| pour activer le maintien de session TCP : |
| |
| option tcpka # active le keep-alive côté client et côté serveur |
| option clitcpka # active le keep-alive côté client |
| option srvtcpka # active le keep-alive côté serveur |
| |
| |
| 4.2) Journalisation des connexions |
| ---------------------------------- |
| |
| L'un des points forts de HAProxy est indéniablement la précision de ses logs. |
| Il fournit probablement le plus fin niveau d'information disponible pour un |
| tel outil, ce qui est très important pour les diagnostics en environnements |
| complexes. En standard, les informations journalisées incluent le port client, |
| les chronométrages des états TCP/HTTP, des états de session précis au moment de |
| la terminaison et sa cause, des informations sur les décisions d'aiguillage du |
| trafic vers un serveur, et bien sûr la possibilité de capturer des en-têtes |
| arbitraires. |
| |
| Dans le but d'améliorer la réactivité des administrateurs, il offre une grande |
| transparence sur les problèmes rencontrés, à la fois internes et externes, et |
| il est possible d'envoyer les logs vers des serveurs différents en même temps |
| avec des niveaux de filtrage différents : |
| |
| - logs globaux au niveau processus (erreurs système, arrêts/démarrages, ...) |
| - erreurs système et internes par instance (manque de ressources, bugs, ...) |
| - problèmes externes par instance (arrêts/relance serveurs, limites, ...) |
| - activité par instance (connexions clients), aussi bien lors de leur |
| établissement qu'à leur terminaison. |
| |
| La possibilité de distribuer différents niveaux de logs à différents serveurs |
| permet à plusieurs équipes de production d'intéragir et de corriger leurs |
| problèmes le plus tôt possible. Par exemple, l'équipe système peut surveiller |
| occasionnellement les erreurs système, pendant que l'équipe application |
| surveille les alertes d'arrêts/démarrages de ses serveurs en temps réel, et |
| que l'équipe sécurité analyse l'activité en différé d'une heure. |
| |
| |
| 4.2.1) Niveaux de log |
| --------------------- |
| Les connexions TCP et HTTP peuvent donner lieu à une journalisation sommaire ou |
| détaillée indiquant, pour chaque connexion, la date, l'heure, l'adresse IP |
| source, le serveur destination, la durée de la connexion, les temps de réponse, |
| la requête HTTP, le code de retour, la quantité de données transmises, et même |
| dans certains cas, la valeur d'un cookie permettant de suivre les sessions. |
| Tous les messages sont envoyés en syslog vers un ou deux serveurs. Se référer à |
| la section 1.1 pour plus d'information sur les catégories de logs. La syntaxe |
| est la suivante : |
| |
| log <adresse_ip_1> <catégorie_1> [niveau_max_1] |
| log <adresse_ip_2> <catégorie_2> [niveau_max_2] |
| ou |
| log global |
| |
| Remarque : |
| ---------- |
| La syntaxe spécifique 'log global' indique que l'on souhaite utiliser les |
| paramètres de journalisation définis dans la section 'global'. |
| |
| Exemple : |
| --------- |
| listen http_proxy 0.0.0.0:80 |
| mode http |
| log 192.168.2.200 local3 |
| log 192.168.2.201 local4 |
| |
| 4.2.2) Format des logs |
| ---------------------- |
| Par défaut, les connexions sont journalisées au niveau TCP dès l'établissement |
| de la session entre le client et le relais. En précisant l'option 'tcplog', |
| la connexion ne sera journalisée qu'en fin de session, ajoutant des précisions |
| sur son état lors de la déconnexion, ainsi que le temps de connexion et la |
| durée totale de la session. Le nombre de sessions restantes après la |
| déconnexion est également indiqué (pour le serveur, l'instance et le process). |
| |
| Exemple de journalisation TCP : |
| ------------------------------- |
| listen relais-tcp 0.0.0.0:8000 |
| mode tcp |
| option tcplog |
| log 192.168.2.200 local3 |
| |
| >>> haproxy[18989]: 127.0.0.1:34550 [15/Oct/2003:15:24:28] relais-tcp Srv1 0/0/5007 0 -- 1/1/1 0/0 |
| |
| Champ Format / Description Exemple |
| |
| 1 nom_processus '[' pid ']:' haproxy[18989]: |
| 2 ip_client ':' port_client 127.0.0.1:34550 |
| 3 '[' date ']' [15/Oct/2003:15:24:28] |
| 4 nom_instance relais-tcp |
| 5 nom_serveur Srv1 |
| 6 temps_file '/' temps_connect '/' temps_total 0/0/5007 |
| 7 octets lus 0 |
| 8 etat_terminaison -- |
| 9 conn_srv '/' conns_inst '/' conns_processus 1/1/1 |
| 10 position en file d'attente srv '/' globale 0/0 |
| |
| Une autre option, 'httplog', fournit plus de détails sur le protocole HTTP, |
| notamment la requête et l'état des cookies. Dans les cas où un mécanisme de |
| surveillance effectuant des connexions et déconnexions fréquentes, polluerait |
| les logs, il suffit d'ajouter l'option 'dontlognull', pour ne plus obtenir une |
| ligne de log pour les sessions n'ayant pas donné lieu à un échange de données |
| (requête ou réponse). |
| |
| Exemple de journalisation HTTP : |
| -------------------------------- |
| listen http_proxy 0.0.0.0:80 |
| mode http |
| option httplog |
| option dontlognull |
| log 192.168.2.200 local3 |
| |
| >>> haproxy[674]: 127.0.0.1:33319 [15/Oct/2003:08:31:57] relais-http Srv1 9/0/7/147/723 200 243 - - ---- 2/3/3 0/0 "HEAD / HTTP/1.0" |
| |
| Exemple plus complet : |
| |
| haproxy[18989]: 10.0.0.1:34552 [15/Oct/2003:15:26:31] relais-http Srv1 3183/-1/-1/-1/11215 503 0 - - SC-- 137/202/205 0/0 {w.ods.org|Mozilla} {} "HEAD / HTTP/1.0" |
| |
| Champ Format / Description Exemple |
| |
| 1 nom_processus '[' pid ']:' haproxy[18989]: |
| 2 ip_client ':' port_client 10.0.0.1:34552 |
| 3 '[' date ']' [15/Oct/2003:15:26:31] |
| 4 nom_instance relais-http |
| 5 nom_serveur Srv1 |
| 6 Tq '/' Tw '/' Tc '/' Tr '/' Tt 3183/-1/-1/-1/11215 |
| 7 Code_retour_HTTP 503 |
| 8 octets lus 0 |
| 9 cookies_requête_capturés - |
| 10 cookies_reponse_capturés - |
| 11 etat_terminaison SC-- |
| 12 conns_srv '/' conns_inst '/' conns_processus 137/202/205 |
| 13 position file serveur '/' globale 0/0 |
| 14 '{' entetes_requête_capturés '}' {w.ods.org|Mozilla} |
| 15 '{' entetes_reponse_capturés '}' {} |
| 16 '"' requête_HTTP '"' "HEAD / HTTP/1.0" |
| |
| Note pour les analyseurs de logs : l'URI est TOUJOURS le dernier champ de la ligne, et |
| commence par un guillemet '"'. |
| |
| Le problème de loguer uniquement en fin de session, c'est qu'il est impossible |
| de savoir ce qui se passe durant de gros transferts ou des sessions longues. |
| Pour pallier à ce problème, une nouvelle option 'logasap' a été introduite dans |
| la version 1.1.28 (1.2.1). Lorsqu'elle est activée, le proxy loguera le plus |
| tôt possible, c'est à dire juste avant que ne débutent les transferts de |
| données. Cela signifie, dans le cas du TCP, qu'il loguera toujours le résultat |
| de la connexion vers le serveur, et dans le cas HTTP, qu'il loguera en fin de |
| traitement des en-têtes de la réponse du serveur, auquel cas le nombre d'octets |
| représentera la taille des en-têtes retournés au client. |
| |
| Afin d'éviter toute confusion avec les logs normaux, le temps total de |
| transfert et le nombre d'octets transférés sont préfixés d'un signe '+' |
| rappelant que les valeurs réelles sont certainement plus élevées. |
| |
| Exemple : |
| --------- |
| |
| listen http_proxy 0.0.0.0:80 |
| mode http |
| option httplog |
| option dontlognull |
| option logasap |
| log 192.168.2.200 local3 |
| |
| >>> haproxy[674]: 127.0.0.1:33320 [15/Oct/2003:08:32:17] relais-http Srv1 9/7/14/+30 200 +243 - - ---- 3/3 "GET /image.iso HTTP/1.0" |
| |
| |
| 4.2.3) Chronométrage des événements |
| ----------------------------------- |
| Pour déceler des problèmes réseau, les mesures du temps écoulé entre certains |
| événements sont d'une très grande utilité. Tous les temps sont mesurés en |
| millisecondes (ms). En mode HTTP, quatre points de mesure sont rapportés sous |
| la forme Tq/Tw/Tc/Tr/Tt : |
| |
| - Tq: temps total de réception de la requête HTTP de la part du client. |
| C'est le temps qui s'est écoulé entre le moment où le client a établi |
| sa connexion vers le relais, et le moment où ce dernier a reçu le dernier |
| en-tête HTTP validant la fin de la requête. Une valeur '-1' ici indique |
| que la requête complète n'a jamais été reçue. |
| |
| - Tw: temps total passé dans les files d'attente avant d'obtenir une place |
| vers un serveur. Ceci tient compte à la fois de la file d'attente globale |
| et de celle du serveur, et dépend du nombre de requêtes dans la file et du |
| temps nécessaire au serveur pour compléter les sessions précédentes. La |
| valeur '-1' indique que la requête a été détruite avant d'atteindre une |
| file. |
| |
| - Tc: temps d'établissement de la connexion TCP du relais vers le serveur. |
| C'est le temps écoulé entre le moment ou le relais a initié la demande de |
| connexion vers le serveur, et le moment où ce dernier l'a acquittée, c'est |
| à dire le temps entre l'envoi du paquet TCP SYN la réception du SYN/ACK. |
| Une valeur '-1' ici indique que la connexion n'a jamais pu être établie |
| vers le serveur. |
| |
| - Tr: temps de réponse du serveur. C'est le temps que le serveur a mis pour |
| renvoyer la totalité des en-têtes HTTP à partir du moment où il a acquitté |
| la connexion. Ca représente exactement le temps de traitement de la |
| transaction sans le transfert des données associées. Une valeur '-1' |
| indique que le serveur n'a pas envoyé la totalité de l'en-tête HTTP. |
| |
| - Tt: durée de vie totale de la session, entre le moment où la demande de |
| connexion du client a été acquittée et le moment où la connexion a été |
| refermée aux deux extrémités (client et serveur). La signification change |
| un peu si l'option 'logasap' est présente. Dans ce cas, le temps correspond |
| uniquement à (Tq + Tw + Tc + Tr), et se trouve préfixé d'un signe '+'. On |
| peut donc déduire Td, le temps de transfert des données, en excluant les |
| autres temps : |
| |
| Td = Tt - (Tq + Tw + Tc + Tr) |
| |
| Les temps rapportés à '-1' sont simplement à éliminer de cette équation. |
| |
| En mode TCP ('option tcplog'), seuls les deux indicateurs Tw, Tc et Tt sont |
| rapportés. |
| |
| Ces temps fournissent de précieux renseignement sur des causes probables de |
| problèmes. Du fait que le protocole TCP définisse des temps de retransmission |
| de 3 secondes, puis 6, 12, etc..., l'observation de temps proches de multiples |
| de 3 secondes indique pratiquement toujours des pertes de paquets liés à un |
| problème réseau (câble ou négociation). De plus, si <Tt> est proche d'une |
| valeur de time-out dans la configuration, c'est souvent qu'une session a été |
| abandonnée sur expiration d'un time-out. |
| |
| Cas les plus fréquents : |
| |
| - Si Tq est proche de 3000, un paquet a très certainement été perdu entre |
| le client et le relais. |
| - Si Tc est proche de 3000, un paquet a très certainement été perdu entre |
| le relais et le serveur durant la phase de connexion. Cet indicateur |
| devrait normalement toujours être très bas (moins de quelques dizaines). |
| - Si Tr est presque toujours inférieur à 3000, et que certaines valeurs |
| semblent proches de la valeur moyenne majorée de 3000, il y a peut-être |
| de pertes entre le relais et le serveur. |
| - Si Tt est légèrement supérieur au time-out, c'est souvent parce que le |
| client et le serveur utilisent du keep-alive HTTP entre eux et que la |
| session est maintenue après la fin des échanges. Voir plus loin pour |
| savoir comment désactiver le keep-alive HTTP. |
| |
| Autres cas ('xx' représentant une valeur quelconque à ignorer) : |
| -1/xx/xx/xx/Tt: le client n'a pas envoyé sa requête dans le temps imparti ou |
| a refermé sa connexion sans compléter la requête. |
| Tq/-1/xx/xx/Tt: Il n'était pas possible de traiter la request, probablement |
| parce que tous les serveurs étaient hors d'usage. |
| Tq/Tw/-1/xx/Tt: la connexion n'a pas pu s'établir vers le serveur (refus ou |
| time-out au bout de Tt-(Tq+Tw) ms). |
| Tq/Tw/Tc/-1/Tt: le serveur a accepté la connexion mais n'a pas répondu dans |
| les temps ou bien a refermé sa connexion trop tôt, au bout |
| de Tt-(Tq+Tw+Tc) ms. |
| |
| 4.2.4) Conditions de déconnexion |
| -------------------------------- |
| Les logs TCP et HTTP fournissent un indicateur de complétude de la session dans |
| le champ 'etat_terminaison', juste avant le nombre de connexions actives. C'est |
| un champ long de 2 caractères en TCP et de 4 caractères en HTTP, chacun ayant |
| une signification précise : |
| |
| - sur le premier caractère, un code précisant le premier événement qui a causé |
| la terminaison de la session : |
| |
| C : fermeture inattendue de la session TCP de la part du client. |
| |
| S : fermeture inattendue de la session TCP de la part du serveur, ou |
| refus explicite de connexion de la part de ce dernier. |
| |
| P : terminaison prématurée des sessions par le proxy, pour cause |
| d'imposition d'une limite sur le nombre de connexions, pour cause |
| de configuration (ex: filtre d'URL), ou parce qu'un contrôle de |
| sécurité a détecté et bloqué une anomalie dans la réponse du |
| serveur qui aurait pu causer une fuite d'informations (par exemple, |
| un cookie cachable). |
| |
| R : une ressource sur le proxy a été épuisée (mémoire, sockets, ports |
| source, ...). Généralement, cela arrive au cours de l'établissement |
| d'une connexion, et les logs système doivent contenir une copie de |
| l'érreur précise. |
| |
| I : une erreur interne a été identifiée par le proxy à la suite d'un |
| auto-contrôle. Ceci ne doit JAMAIS arriver, et vous êtes encouragés |
| à remonter n'importe quel log contenant ceci car il s'agira un bug. |
| |
| c : le délai maximal d'attente du client a expiré (clitimeout). |
| |
| s : le délai maximal d'attente du serveur a expiré (srvtimeout et contimeout) |
| |
| - : terminaison normale de session. |
| |
| - sur le second caractère, l'état d'avancement de la session TCP/HTTP lors de |
| la fermeture : |
| |
| R : attente d'une REQUETE HTTP complète de la part du client. Rien n'a |
| été transmis au serveur. |
| |
| Q : attente en file d'attente (QUEUE) d'une place pour avoir une |
| connexion vers un serveur. Ne peut apparaître que sur un serveur |
| possédant un paramètre 'maxconn'. Aucune connexion n'a été envoyée |
| au serveur. |
| |
| C : attente de l'établissement d'une CONNEXION vers le serveur. Le |
| serveur peut au plus avoir vu la tentative de connexion, mais |
| aucune donnée n'a été échangée. |
| |
| H : attente, réception ou traitement des en-têtes HTTP ("HEADERS"). |
| |
| D : transfert des DONNEES du serveur vers le client. |
| |
| L : transfert des dernières ("LAST") données du proxy vers le client, |
| alors que le serveur a déjà fini. |
| |
| T : requête bloquée en mode "tarpit" par le proxy. Elle a été maintenue |
| ouverte vers le client pendant toute la durée du contimeout ou |
| jusqu'à l'abandon de la part du client. |
| |
| - : terminaison normale, après fin de transfert des données. |
| |
| - le troisième caractère indique l'éventuelle identification d'un cookie de |
| persistence (uniquement en mode HTTP) : |
| |
| N : aucun cookie de persistence n'a été présenté. C'est généralement le |
| cas sur les NOUVELLES connexions clients. |
| |
| I : le client a présenté un cookie INVALIDE ne correspondant à aucun |
| serveur connu. Ceci peut être dû à un changement de configuration |
| récent, à des mélanges de noms de cookies entre sites HTTP/HTTPS, |
| ou à une attaque. |
| |
| D : le client a présenté un cookie correspondant à un serveur hors |
| d'usage ("DOWN"). Suivant l'option 'persist', il a été renvoyé vers |
| un autre serveur ou a tout de même tenté de se connecter sur celui |
| correspondant au cookie. |
| |
| V : le client a présenté un cookie VALIDE et a pu se connecter au |
| serveur correspondant. |
| |
| - : non appliquable (pas de cookie positionné dans la configuration). |
| |
| - le dernier caractère indique l'éventuel traitement effectué sur un cookie de |
| persistence retrourné par le serveur (uniquement en mode HTTP) : |
| |
| N : aucun cookie de persistance n'a été fourni par le serveur, et aucun |
| n'a été inséré. |
| |
| I : aucun cookie de persistance n'a été fourni par le serveur, et le |
| proxy en a INSERE un. |
| |
| P : un cookie de persistence a été fourni par le serveur et transmis |
| tel quel ("PASSIF"). |
| |
| R : le cookie retourné par le serveur a été REECRIT par le proxy. |
| |
| D : le cookie présenté par le serveur a été DETRUIT par le proxy pour |
| ne pas être retourné au client. |
| |
| - : non appliquable |
| |
| |
| La combinaison des deux premiers indicateurs fournit une grande quantitié |
| d'informations sur ce qui se passait lorsque la session s'est terminée. Cela |
| peut notamment aider à détecter une saturation de serveur, des troubles réseau, |
| des épuisements de ressources système locales, des attaques, etc... |
| |
| Les combinaisons d'indicateurs les plus fréquentes sont énumérées ici. |
| |
| Indic Raison |
| CR Le client a abandonné avant d'émettre une requête complète. Il est |
| très probable que la requête ait été tapée à la main dans un client |
| telnet et abortée trop tôt. |
| |
| cR Le temps imparti au client a expiré avant réception d'une requête |
| complète. Ceci est parfois causé par un paramètre TCP MSS trop élevé |
| sur le client pour des réseaux PPPoE sur ADSL qui ne peuvent pas |
| transporter des paquets entiers, ou par des clients qui énvoient des |
| requêtes à la main et ne tapent pas assez vite. |
| |
| SC Le serveur a explicitement refusé la connexion (le proxy a reçu un |
| RST TCP ou un message ICMP en retour). Dans certains cas, cela peut |
| être la couche réseau qui indique au proxy que le serveur n'est pas |
| joignable (p.ex: pas de route, pas de réponse ARP en local, etc...) |
| |
| sC La connexion au serveur n'a pas pu s'établir dans le temps imparti. |
| |
| PC Le proxy a refusé d'établir une connexion au serveur parce que le |
| nombre de connexions a atteint la limite 'maxconn' (global ou de |
| l'instance). Le paramètre 'maxconn' de l'instance pourrait être |
| augmenté, tout comme le paramètre 'maxconn' global. |
| |
| RC Une ressource locale a été épuisée (mémoire, sockets, ports source), |
| empêchant la connexion au serveur de s'établir. Les logs d'erreurs |
| diront précisément ce qui manquait. Dans tous les cas, le seul remède |
| consiste à affiner le paramétrage système. |
| |
| cH Le temps imparti au client a expiré au cours d'une requête POST. Ceci |
| est parfois causé par un paramètre TCP MSS trop élevé sur le client |
| pour des réseaux PPPoE sur ADSL qui ne peuvent pas transporter des |
| paquets entiers. |
| |
| CH Le client a abandonné alors qu'il attendait un début de réponse de la |
| part du serveur. Cela peut être causé par le serveur qui mettait trop |
| de temps à répondre, ou par un client cliquant précipitamment sur le |
| bouton 'Stop'. |
| |
| CQ Le client a abandonné alors que sa session était mise en file |
| d'attente pour obtenir un serveur avec suffisamment de connexions |
| libres pour l'accepter. Cela signifie soit que l'ensemble des |
| serveurs étaient saturés, soit que le serveur assigné a mis trop de |
| temps à répondre. |
| |
| CT Le client a abandonné alors que sa session était bloquée en mode |
| tarpit. |
| |
| sQ La session a attendu trop longtemps en file d'attente et a été |
| expirée. |
| |
| SH Le serveur a aborté brutalement alors qu'il devait envoyer ses |
| en-têtes. En général, cela indique qu'il a crashé. |
| |
| sH Le serveur n'a pas pu répondre durant le temps imparti, ce qui montre |
| des transactions trop longues, probablement causées par un back-end |
| saturé. Les seules solutions sont de corriger le problème sur |
| l'application, d'accroître le paramètre 'srvtimeout' pour supporter |
| des attentes plus longues au risque que les clients abandonnent à |
| leur tour, ou bien d'ajouter des serveurs. |
| |
| PR Le proxy a bloqué une requête du client, soit à cause d'une syntaxe |
| HTTP invalide, auquel cas il a renvoyé une erreur HTTP 400 au client, |
| soit à cause d'une requête validant un filtre d'interdiction, auquel |
| cas le proxy a renvoyé une erreur HTTP 403. |
| |
| PH Le proxy a bloqué la réponse du serveur parce qu'elle était invalide, |
| incomplète, dangereuse ('cache control'), ou parce qu'elle validait |
| un filtre de sécurité. Dans tous les cas, une erreur HTTP 502 est |
| renvoyée au client. |
| |
| PT Le proxy a bloqué une requête du client et a maintenu sa connection |
| ouverte avant de lui retourner une erreur "500 server error". Rien |
| n'a été envoyé au serveur. |
| |
| cD Le client n'a pas lu de données pendant le temps qui lui était |
| imparti. Ceci est souvent causé par des problèmes réseau côté client. |
| |
| CD Le client a aborté sa connection de manière inattendue pendant le |
| transfert des données. Ceci est provoqué soit par le crash d'un |
| navigateur, ou par une session en HTTP keep-alive entre le serveur |
| et le client terminée en premier par le client. |
| |
| sD Le serveur n'a rien fait durant le temps imparti par le paramètre |
| 'srvtimeout'. Ceci est souvent causé par des timeouts trop courts |
| sur des équipements de niveau 4 (firewalls, répartiteurs de charge) |
| situés entre le proxy et le serveur. |
| |
| 4.2.5) Caractères non-imprimables |
| --------------------------------- |
| Depuis la version 1.1.29, les caractères non-imprimables ne sont plus envoyés |
| tels quels dans les lignes de logs, mais inscrits sous la forme de deux chiffres |
| hexadécimaux, préfixés du caractère d'échappement '#'. Les seuls caractères |
| dorénavant logués tels quels sont compris entre 32 et 126. Bien évidemment, le |
| caractère d'échappement '#' est lui-même encodé afin de lever l'ambiguité. Il en |
| est de même pour le caractère '"', ainsi que les caractères '{', '|' et '}' pour |
| les en-têtes. |
| |
| 4.2.6) Capture d'en-têtes HTTP et de cookies |
| -------------------------------------------- |
| La version 1.1.23 a apporté la capture des cookies, et la version 1.1.29 la |
| capture d'en-têtes. Tout ceci est effectué en utilisant le mot-clé 'capture'. |
| |
| Les captures de cookies facilitent le suivi et la reconstitution d'une session |
| utilisateur. La syntaxe est la suivante : |
| |
| capture cookie <préfixe_cookie> len <longueur_capture> |
| |
| Ceci activera la capture de cookies à la fois dans les requêtes et dans les |
| réponses. De cette manière, il devient facile de détecter lorsqu'un utilisateur |
| bascule sur une nouvelle session par exemple, car le serveur lui réassignera un |
| nouveau cookie. |
| |
| Le premier cookie dont le nom commencera par <préfixe_cookie> sera capturé, et |
| transmis sous la forme "NOM=valeur", sans toutefois, excéder <longueur_capture> |
| caractères (64 au maximum). Lorsque le nom du cookie est fixe et connu, on peut |
| le suffixer du signe "=" pour s'assurer qu'aucun autre cookie ne prendra sa |
| place dans les logs. |
| |
| Exemples : |
| ---------- |
| # capture du premier cookie dont le nom commence par "ASPSESSION" |
| capture cookie ASPSESSION len 32 |
| |
| # capture du premier cookie dont le nom est exactement "vgnvisitor" |
| capture cookie vgnvisitor= len 32 |
| |
| Dans les logs, le champ précédant l'indicateur de complétude contient le cookie |
| positionné par le serveur, précédé du cookie positionné par le client. Chacun |
| de ces champs est remplacé par le signe "-" lorsqu'aucun cookie n'est fourni |
| par le client ou le serveur, ou lorsque l'option est désactivée.. |
| |
| Les captures d'en-têtes ont un rôle complètement différent. Elles sont utiles |
| pour suivre un identifiant de requête globalement unique positionné par un |
| autre proxy en amont, pour journaliser les noms de serveurs virtuels, les types |
| de clients web, la longueur des POST, les 'referrers', etc. Dans la réponse, on |
| peut chercher des informations relatives à la longueur annoncée de la réponse, |
| le fonctionnement attendu du cache, ou encore la localisation d'un objet en cas |
| de redirection. Tout comme pour les captures de cookies, il est possible |
| d'inclure les en-têtes de requêtes et de réponse simultanément. La syntaxe est |
| la suivante : |
| |
| capture request header <nom> len <longueur max> |
| capture response header <nom> len <longueur max> |
| |
| Note: Les noms d'en-têtes ne sont pas sensibles à la casse. |
| |
| Exemples: |
| --------- |
| # conserver le nom du serveur virtuel accédé par le client |
| capture request header Host len 20 |
| # noter la longueur des données envoyées dans un POST |
| capture request header Content-Length len 10 |
| |
| # noter le fonctionnement attendu du cache par le serveur |
| capture response header Cache-Control len 8 |
| # noter l'URL de redirection |
| capture response header Location len 20 |
| |
| Les en-têtes non trouvés sont logués à vide, et si un en-tête apparait plusieurs |
| fois, seule la dernière occurence sera conservée. Les en-têtes de requête sont |
| regroupés entre deux accolades '{' et '}' dans l'ordre de leur déclaration, et |
| chacun séparés par une barre verticale '|', sans aucun espace. Les en-têtes de |
| réponse sont présentés de la même manière, mais après un espace suivant le bloc |
| d'en-tête de requête. Le tout précède la requête HTTP. Exemple : |
| |
| Config: |
| |
| capture request header Host len 20 |
| capture request header Content-Length len 10 |
| capture request header Referer len 20 |
| capture response header Server len 20 |
| capture response header Content-Length len 10 |
| capture response header Cache-Control len 8 |
| capture response header Via len 20 |
| capture response header Location len 20 |
| |
| Log : |
| |
| Aug 9 20:26:09 localhost haproxy[2022]: 127.0.0.1:34014 [09/Aug/2004:20:26:09] relais-http netcache 0/0/0/162/+162 200 +350 - - ---- 0/0/0 0/0 {fr.adserver.yahoo.co||http://fr.f416.mail.} {|864|private||} "GET http://fr.adserver.yahoo.com/" |
| Aug 9 20:30:46 localhost haproxy[2022]: 127.0.0.1:34020 [09/Aug/2004:20:30:46] relais-http netcache 0/0/0/182/+182 200 +279 - - ---- 0/0/0 0/0 {w.ods.org||} {Formilux/0.1.8|3495|||} "GET http://w.ods.org/sytadin.html HTTP/1.1" |
| Aug 9 20:30:46 localhost haproxy[2022]: 127.0.0.1:34028 [09/Aug/2004:20:30:46] relais-http netcache 0/0/2/126/+128 200 +223 - - ---- 0/0/0 0/0 {www.infotrafic.com||http://w.ods.org/syt} {Apache/2.0.40 (Red H|9068|||} "GET http://www.infotrafic.com/images/live/cartesidf/grandes/idf_ne.png HTTP/1.1" |
| |
| 4.2.7) Exemples de logs |
| ----------------------- |
| - haproxy[674]: 127.0.0.1:33319 [15/Oct/2003:08:31:57] relais-http Srv1 6559/0/7/147/6723 200 243 - - ---- 1/3/5 0/0"HEAD / HTTP/1.0" |
| => requête longue (6.5s) saisie à la main avec un client telnet. Le serveur a |
| répondu en 147 ms et la session s'est terminée normalement ('----') |
| |
| - haproxy[674]: 127.0.0.1:33319 [15/Oct/2003:08:31:57] relais-http Srv1 6559/1230/7/147/6870 200 243 - - ---- 99/239/324 0/9 "HEAD / HTTP/1.0" |
| => Idem, mais la requête a été mise en attente dans la file globale derrière |
| 9 autres requêtes déjà présentes, et y a attendu 1230 ms. |
| |
| - haproxy[674]: 127.0.0.1:33320 [15/Oct/2003:08:32:17] relais-http Srv1 9/0/7/14/+30 200 +243 - - ---- 1/3/3 0/0 "GET /image.iso HTTP/1.0" |
| => requête pour un long transfert. L'option 'logasap' était spécifiée donc le |
| log a été généré juste avant le transfert de données. Le serveur a répondu |
| en 14 ms, 243 octets d'en-têtes ont été transférés au client, et le temps |
| total entre l'accept() et le premier octet de donnée est de 30 ms. |
| |
| - haproxy[674]: 127.0.0.1:33320 [15/Oct/2003:08:32:17] relais-http Srv1 9/0/7/14/30 502 243 - - PH-- 0/2/3 0/0 "GET /cgi-bin/bug.cgi? HTTP/1.0" |
| => le proxy a bloqué une réponse du serveur soit à cause d'un filtre 'rspdeny' |
| ou 'rspideny', soit parce qu'il a détecté un risque de fuite sensible |
| d'informations risquant d'être cachées. Dans ce cas, la réponse est |
| remplacée par '502 bad gateway'. |
| |
| - haproxy[18113]: 127.0.0.1:34548 [15/Oct/2003:15:18:55] relais-http <NOSRV> -1/-1/-1/-1/8490 -1 0 - - CR-- 0/2/2 0/0 "" |
| => Le client n'a pas envoyé sa requête et a refermé la connexion lui-même |
| ('C---') au bout de 8.5s, alors que le relais attendait l'en-tête ('-R--'). |
| Aucune connexion n'a été envoyée vers le serveur. |
| |
| - haproxy[18113]: 127.0.0.1:34549 [15/Oct/2003:15:19:06] relais-http <NOSRV> -1/-1/-1/-1/50001 408 0 - - cR-- 0/2/2 0/0 "" |
| => Le client n'a pas envoyé sa requête et son time-out a expiré ('c---') au |
| bout de 50s, alors que le relais attendait l'en-tête ('-R--'). Aucune |
| connexion n'a été envoyée vers le serveur, mais le relais a tout de même |
| pu renvoyer un message 408 au client. |
| |
| - haproxy[18989]: 127.0.0.1:34550 [15/Oct/2003:15:24:28] relais-tcp Srv1 0/5007 0 cD |
| => log en mode 'tcplog'. Expiration du time-out côté client ('cD') au bout de |
| 5s. |
| |
| - haproxy[18989]: 10.0.0.1:34552 [15/Oct/2003:15:26:31] relais-http Srv1 3183/-1/-1/-1/11215 503 0 - - SC-- 115/202/205 0/0 "HEAD / HTTP/1.0" |
| => La requête client met 3s à entrer (peut-être un problème réseau), et la |
| connexion ('SC--') vers le serveur échoue au bout de 4 tentatives de 2 |
| secondes (retries 3 dans la conf), puis un code 503 est retourné au |
| client. Il y avait 115 connexions sur ce serveur, 202 connexions sur cette |
| instance, et 205 sur l'ensemble des instances pour ce processus. Il est |
| possible que le serveur ait refusé la connexion parce qu'il y en avait |
| déjà trop d'établies. |
| |
| |
| 4.3) Modification des en-têtes HTTP |
| ---------------------------------- |
| En mode HTTP uniquement, il est possible de remplacer certains en-têtes dans la |
| requête et/ou la réponse à partir d'expressions régulières. Il est également |
| possible de bloquer certaines requêtes en fonction du contenu des en-têtes ou |
| de la requête. Une limitation cependant : les en-têtes fournis au milieu de |
| connexions persistentes (keep-alive) ne sont pas vus car ils sont considérés |
| comme faisant partie des échanges de données consécutifs à la première requête. |
| Les données ne sont pas affectées, ceci ne s'applique qu'aux en-têtes. |
| |
| La syntaxe est : |
| reqadd <string> pour ajouter un en-tête dans la requête |
| reqrep <search> <replace> pour modifier la requête |
| reqirep <search> <replace> idem sans distinction majuscules/minuscules |
| reqdel <search> pour supprimer un en-tête dans la requête |
| reqidel <search> idem sans distinction majuscules/minuscules |
| reqallow <search> autoriser la requête si un en-tête valide <search> |
| reqiallow <search> idem sans distinction majuscules/minuscules |
| reqdeny <search> interdire la requête si un en-tête valide <search> |
| reqideny <search> idem sans distinction majuscules/minuscules |
| reqpass <search> inhibe ces actions sur les en-têtes validant <search> |
| reqipass <search> idem sans distinction majuscules/minuscules |
| reqtarpit <search> bloquer et maintenir une request validant <search> |
| reqitarpit <search> idem sans distinction majuscules/minuscules |
| |
| rspadd <string> pour ajouter un en-tête dans la réponse |
| rsprep <search> <replace> pour modifier la réponse |
| rspirep <search> <replace> idem sans distinction majuscules/minuscules |
| rspdel <search> pour supprimer un en-tête dans la réponse |
| rspidel <search> idem sans distinction majuscules/minuscules |
| rspdeny <search> remplace la réponse par un HTTP 502 si un |
| en-tête valide <search> |
| rspideny <search> idem sans distinction majuscules/minuscules |
| |
| |
| <search> est une expression régulière compatible POSIX regexp supportant le |
| groupage par parenthèses (sans les '\'). Les espaces et autres séparateurs |
| doivent êtres précédés d'un '\' pour ne pas être confondus avec la fin de la |
| chaîne. De plus, certains caractères spéciaux peuvent être précédés d'un |
| backslach ('\') : |
| |
| \t pour une tabulation |
| \r pour un retour charriot |
| \n pour un saut de ligne |
| \ pour différencier un espace d'un séparateur |
| \# pour différencier un dièse d'un commentaire |
| \\ pour utiliser un backslash dans la regex |
| \\\\ pour utiliser un backslash dans le texte |
| \xXX pour un caractère spécifique XX (comme en C) |
| |
| |
| <replace> contient la chaîne remplaçant la portion vérifiée par l'expression. |
| Elle peut inclure les caractères spéciaux ci-dessus, faire référence à un |
| groupe délimité par des parenthèses dans l'expression régulière, par sa |
| position numérale. Les positions vont de 0 à 9, et sont codées par un '\' |
| suivi du chiffre désiré (0 désignant la ligne complète). Il est également |
| possible d'insérer un caractère non imprimable (utile pour le saut de ligne) |
| inscrivant '\x' suivi du code hexadécimal de ce caractère (comme en C). |
| |
| <string> représente une chaîne qui sera ajoutée systématiquement après la |
| dernière ligne d'en-tête. |
| |
| Remarques : |
| ----------- |
| - la première ligne de la requête et celle de la réponse sont traitées comme |
| des en-têtes, ce qui permet de réécrire des URL et des codes d'erreur. |
| - 'reqrep' est l'équivalent de 'cliexp' en version 1.0, et 'rsprep' celui de |
| 'srvexp'. Ces noms sont toujours supportés mais déconseillés. |
| - pour des raisons de performances, le nombre total de caractères ajoutés sur |
| une requête ou une réponse est limité à 4096 depuis la version 1.1.5 (cette |
| limite était à 256 auparavant). Cette valeur est modifiable dans le code. |
| Pour un usage temporaire, on peut gagner de la place en supprimant quelques |
| en-têtes inutiles avant les ajouts. |
| - une requête bloquée produira une réponse "HTTP 403 forbidden" tandis qu'une |
| réponse bloquée produira une réponse "HTTP 502 Bad gateway". |
| - une requête bloquée par 'reqtarpit' sera maintenue pendant une durée égale |
| au paramètre 'contimeout', ou jusqu'à l'abandon du client. Rien ne sera |
| envoyé au serveur. Lorsque le temps alloué expire, le proxy répondra avec |
| une réponse "500 server error" de sorte que l'attaquant ne suspecte pas |
| qu'il ait été bloqué. Les logs rapporteront aussi ce code 500, mais les |
| flags de terminaison indiqueront "PT". |
| |
| Exemples : |
| ---------- |
| ###### a few examples ###### |
| |
| # rewrite 'online.fr' instead of 'free.fr' for GET and POST requests |
| reqrep ^(GET\ .*)(.free.fr)(.*) \1.online.fr\3 |
| reqrep ^(POST\ .*)(.free.fr)(.*) \1.online.fr\3 |
| |
| # force proxy connections to close |
| reqirep ^Proxy-Connection:.* Proxy-Connection:\ close |
| # rewrite locations |
| rspirep ^(Location:\ )([^:]*://[^/]*)(.*) \1\3 |
| |
| ###### A full configuration being used on production ###### |
| |
| # Every header should end with a colon followed by one space. |
| reqideny ^[^:\ ]*[\ ]*$ |
| |
| # block Apache chunk exploit |
| reqideny ^Transfer-Encoding:[\ ]*chunked |
| reqideny ^Host:\ apache- |
| |
| # block annoying worms that fill the logs... |
| reqideny ^[^:\ ]*\ .*(\.|%2e)(\.|%2e)(%2f|%5c|/|\\\\) |
| reqideny ^[^:\ ]*\ ([^\ ]*\ [^\ ]*\ |.*%00) |
| reqideny ^[^:\ ]*\ .*<script |
| reqideny ^[^:\ ]*\ .*/(root\.exe\?|cmd\.exe\?|default\.ida\?) |
| |
| # tarpit attacks on the login page. |
| reqtarpit ^[^:\ ]*\ .*\.php?login=[^0-9] |
| |
| # allow other syntactically valid requests, and block any other method |
| reqipass ^(GET|POST|HEAD|OPTIONS)\ /.*\ HTTP/1\.[01]$ |
| reqipass ^OPTIONS\ \\*\ HTTP/1\.[01]$ |
| reqideny ^[^:\ ]*\ |
| |
| # force connection:close, thus disabling HTTP keep-alive |
| option httpclos |
| |
| # change the server name |
| rspidel ^Server:\ |
| rspadd Server:\ Formilux/0.1.8 |
| |
| |
| De plus, l'option 'forwardfor' ajoute l'adresse IP du client dans un champ |
| 'X-Forwarded-For' de la requête, ce qui permet à un serveur web final de |
| connaître l'adresse IP du client initial. Depuis la version 1.3.8, il est |
| possible de préciser le mot-clé "except" suivi d'une adresse ou un réseau |
| IP source pour lequel l'entête ne sera pas ajouté. C'est très pratique dans le |
| cas où un autre reverse-proxy ajoutant déjà l'entête est installé sur la même |
| machine ou dans une DMZ connue. Le cas le plus fréquent est lié à l'utilisation |
| de stunnel en local. |
| |
| Enfin, l'option 'httpclose' apparue dans la version 1.1.28/1.2.1 supprime tout |
| en-tête de type 'Connection:' et ajoute 'Connection: close' dans les deux sens. |
| Ceci simplifie la désactivation du keep-alive HTTP par rapport à l'ancienne |
| méthode impliquant 4 règles. |
| |
| Exemple : |
| --------- |
| listen http_proxy 0.0.0.0:80 |
| mode http |
| log global |
| option httplog |
| option dontlognull |
| option forwardfor except 127.0.0.1/8 |
| option httpclose |
| |
| Notons que certains serveurs HTTP ne referment pas nécessairement la session |
| TCP en fin de traitement lorsqu'ils reçoivent un entête 'Connection: close', |
| ce qui se traduit par des grands nombres de sessions établies et des temps |
| globaux très longs sur les requêtes. Pour contourner ce problème, la version |
| 1.2.9 apporte une nouvelle option 'forceclose' qui referme la connexion sortant |
| vers le serveur dès qu'il commence à répondre et seulement si le tampon de |
| requête est vide. Attention toutefois à ne PAS utiliser cette option si des |
| méthodes CONNECT sont attendues entre le client et le serveur. L'option |
| 'forceclose' implique l'option 'httpclose'. |
| |
| Exemple : |
| --------- |
| listen http_proxy 0.0.0.0:80 |
| mode http |
| log global |
| option httplog |
| option dontlognull |
| option forwardfor |
| option forceclose |
| |
| |
| 4.4) Répartition avec persistence |
| --------------------------------- |
| La combinaison de l'insertion de cookie avec la répartition de charge interne |
| permet d'assurer une persistence dans les sessions HTTP d'une manière |
| pratiquement transparente pour les applications. Le principe est simple : |
| - attribuer une valeur d'un cookie à chaque serveur |
| - effectuer une répartition interne |
| - insérer un cookie dans les réponses issues d'une répartition uniquement, |
| et faire en sorte que des caches ne mémorisent pas ce cookie. |
| - cacher ce cookie à l'application lors des requêtes ultérieures. |
| |
| Exemple : |
| --------- |
| listen application 0.0.0.0:80 |
| mode http |
| cookie SERVERID insert nocache indirect |
| balance roundrobin |
| server srv1 192.168.1.1:80 cookie server01 check |
| server srv2 192.168.1.2:80 cookie server02 check |
| |
| L'autre solution apportée par les versions 1.1.30 et 1.2.3 est de réutiliser un |
| cookie en provenance du serveur et de lui préfixer l'identifiant du serveur. |
| Dans ce cas, ne pas oublier de forcer le mode "httpclose" pour empêcher le |
| client et le serveur de travailler en mode "keep-alive" afin que le proxy |
| puisse corriger le nom du cookie dans toutes les futures requêtes. |
| |
| listen application 0.0.0.0:80 |
| mode http |
| cookie JSESSIONID prefix |
| balance roundrobin |
| server srv1 192.168.1.1:80 cookie srv1 check |
| server srv2 192.168.1.2:80 cookie srv2 check |
| option httpclose |
| |
| |
| 4.5) Protection contre les fuites d'informations du serveur |
| ----------------------------------------------------------- |
| Dans les versions 1.1.28 et 1.2.1, une nouvelle option 'checkcache' a été |
| créée. Elle sert à inspecter minutieusement les en-têtes 'Cache-control', |
| 'Pragma', et 'Set-cookie' dans les réponses serveur pour déterminer s'il y a |
| un risque de cacher un cookie sur un proxy côté client. Quand cette option est |
| activée, les seules réponses qui peuvent être retournées au client sont : |
| - toutes celles qui n'ont pas d'en-tête 'Set-cookie' ; |
| - toutes celles qui ont un code de retour autre que 200, 203, 206, 300, 301, |
| 410, sauf si le serveur a positionné un en-tête 'Cache-control: public' ; |
| - celles qui font suite à une requête POST, sauf si le serveur a positionné |
| un en-tête 'Cache-control: public' ; |
| - celles qui ont un en-tête 'Pragma: no-cache' ; |
| - celles qui ont un en-tête 'Cache-control: private' ; |
| - celles qui ont un en-tête 'Cache-control: no-store' ; |
| - celles qui ont un en-tête 'Cache-control: max-age=0' ; |
| - celles qui ont un en-tête 'Cache-control: s-maxage=0' ; |
| - celles qui ont un en-tête 'Cache-control: no-cache' ; |
| - celles qui ont un en-tête 'Cache-control: no-cache="set-cookie"' ; |
| - celles qui ont un en-tête 'Cache-control: no-cache="set-cookie,' |
| (autorisant d'autres champs après set-cookie). |
| |
| Si une réponse ne respecte pas ces pré-requis, alors elle sera bloquée de la |
| même manière que s'il s'agissait d'un filtre 'rspdeny', avec en retour un |
| message "HTTP 502 bad gateway". L'état de session montre "PH--" ce qui veut |
| dire que c'est le proxy qui a bloqué la réponse durant le traitement des |
| en-têtes. De plus, un message d'alerte sera envoyé dans les logs de sorte que |
| l'administrateur sache qu'il y a une action correctrice à entreprendre. |
| |
| 4.6) Personalisation des erreurs |
| -------------------------------- |
| Certaines situations conduisent à retourner une erreur HTTP au client : |
| - requête invalide ou trop longue => code HTTP 400 |
| - requête mettant trop de temps à venir => code HTTP 408 |
| - requête interdite (bloquée par un reqideny) => code HTTP 403 |
| - erreur interne du proxy => code HTTP 500 |
| - le serveur a retourné une réponse incomplète ou invalide => code HTTP 502 |
| - aucun serveur disponible pour cette requête => code HTTP 503 |
| - le serveur n'a pas répondu dans le temps imparti => code HTTP 504 |
| |
| Un message d'erreur succint tiré de la RFC accompagne ces codes de retour. |
| Cependant, en fonction du type de clientèle, on peut préférer retourner des |
| pages personnalisées. Ceci est possible de deux manières, l'une reposant sur |
| une redirection vers un serveur connu, et l'autre consistant à retourner un |
| fichier local. |
| |
| 4.6.1) Redirection |
| ------------------ |
| Une redirection d'erreur est assurée par le biais de la commande "errorloc" : |
| |
| errorloc <code_HTTP> <location> |
| |
| Au lieu de générer une erreur HTTP <code_HTTP> parmi les codes cités ci-dessus, |
| le proxy génèrera un code de redirection temporaire (HTTP 302) vers l'adresse |
| d'une page précisée dans <location>. Cette adresse peut être relative au site, |
| ou absolue. Comme cette réponse est traîtée par le navigateur du client |
| lui-même, il est indispensable que l'adresse fournie lui soit accessible. |
| |
| Exemple : |
| --------- |
| listen application 0.0.0.0:80 |
| errorloc 400 /badrequest.html |
| errorloc 403 /forbidden.html |
| errorloc 408 /toolong.html |
| errorloc 500 http://haproxy.domain.net/bugreport.html |
| errorloc 502 http://192.168.114.58/error50x.html |
| errorloc 503 http://192.168.114.58/error50x.html |
| errorloc 504 http://192.168.114.58/error50x.html |
| |
| Note: la RFC2616 stipule qu'un client doit réutiliser la même méthode pour |
| accéder à l'URL de redirection que celle qui l'a retournée, ce qui pose des |
| problèmes avec les requêtes POST. Le code de retour 303 a été créé exprès pour |
| régler ce problème, indiquant au client qu'il doit accéder à l'URL retournée |
| dans le champ Location avec la méthode GET uniquement. Seulement, certains |
| navigateurs antérieurs à HTTP/1.1 ne connaissent pas ce code de retour. De |
| plus, la plupart des navigateurs se comportent déjà avec le code 302 comme ils |
| devraient le faire avec le 303. Donc, dans le but de laisser le choix à |
| l'utilisateur, les versions 1.1.31 et 1.2.5 apportent deux nouvelles commandes |
| visant à remplacer 'errorloc' : 'errorloc302' et 'errorloc303'. |
| |
| Leur usage non ambigü est recommandé à la place de la commande 'errorloc' (qui |
| utilise toujours 302). Dans le doute, préférez l'utilisation de 'errorloc303' |
| dès que vous savez que vos clients supportent le code de retour HTTP 303. |
| |
| 4.6.2) Fichiers locaux |
| ---------------------- |
| Parfois il est souhaitable de changer l'erreur retournée sans recourir à des |
| redirections. La seconde méthode consiste à charger des fichiers locaux lors |
| du démarrage et à les envoyer en guise de pur contenu HTTP en cas d'erreur. |
| C'est ce que fait le mot clé 'errorfile'. |
| |
| Attention, il y a des pièges à prendre en compte : |
| - les fichiers sont chargés durant l'analyse de la configuration, avant de |
| faire le chroot(). Donc ils sont relatifs au système de fichiers réel. Pour |
| cette raison, il est recommandé de toujours passer un chemin absolu vers ces |
| fichiers. |
| |
| - le contenu de ces fichiers n'est pas du HTML mais vraiment du protocole HTTP |
| avec potentiellement un corps HTML. Donc la première ligne et les en-têtes |
| sont obligatoires. Idéalement, chaque ligne dans la partie HTTP devrait se |
| terminer par un CR-LF pour un maximum de compatibilité. |
| |
| - les réponses sont limitées à une taille de buffer (BUFSIZE), généralement 8 |
| ou 16 ko. |
| |
| - les réponses ne devraient pas inclure de références aux serveurs locaux, |
| afin de ne pas risquer de créer des boucles infinies sur le navigateur dans |
| le cas d'une panne locale. |
| |
| Exemple : |
| --------- |
| errorfile 400 /etc/haproxy/errorfiles/400badreq.http |
| errorfile 403 /etc/haproxy/errorfiles/403forbid.http |
| errorfile 503 /etc/haproxy/errorfiles/503sorry.http |
| |
| |
| 4.7) Changement des valeurs par défaut |
| -------------------------------------- |
| Dans la version 1.1.22 est apparue la notion de valeurs par défaut, ce qui |
| évite de répéter des paramètres communs à toutes les instances, tels que les |
| timeouts, adresses de log, modes de fonctionnement, etc. |
| |
| Les valeurs par défaut sont positionnées dans la dernière section 'defaults' |
| précédent l'instance qui les utilisera. On peut donc mettre autant de sections |
| 'defaults' que l'on veut. Il faut juste se rappeler que la présence d'une telle |
| section implique une annulation de tous les paramètres par défaut positionnés |
| précédemment, dans le but de les remplacer. |
| |
| La section 'defaults' utilise la même syntaxe que la section 'listen', aux |
| paramètres près qui ne sont pas supportés. Le mot clé 'defaults' peut accepter |
| un commentaire en guise paramètre. |
| |
| Dans la version 1.1.28/1.2.1, seuls les paramètres suivants peuvent être |
| positionnés dans une section 'defaults' : |
| - log (le premier et le second) |
| - mode { tcp, http, health } |
| - balance { roundrobin } |
| - disabled (pour désactiver toutes les instances qui suivent) |
| - enabled (pour faire l'opération inverse, mais c'est le cas par défaut) |
| - contimeout, clitimeout, srvtimeout, grace, retries, maxconn |
| - option { redispatch, transparent, keepalive, forwardfor, logasap, httpclose, |
| checkcache, httplog, tcplog, dontlognull, persist, httpchk } |
| - redispatch, redisp, transparent, source { addr:port } |
| - cookie, capture |
| - errorloc |
| |
| Ne sont pas supportés dans cette version, les adresses de dispatch et les |
| configurations de serveurs, ainsi que tous les filtres basés sur les |
| expressions régulières : |
| - dispatch, server, |
| - req*, rsp* |
| |
| Enfin, il n'y a pas le moyen, pour le moment, d'invalider un paramètre booléen |
| positionné par défaut. Donc si une option est spécifiée dans les paramètres par |
| défaut, le seul moyen de la désactiver pour une instance, c'est de changer les |
| paramètres par défaut avant la déclaration de l'instance. |
| |
| Exemples : |
| ---------- |
| defaults applications TCP |
| log global |
| mode tcp |
| balance roundrobin |
| clitimeout 180000 |
| srvtimeout 180000 |
| contimeout 4000 |
| retries 3 |
| redispatch |
| |
| listen app_tcp1 10.0.0.1:6000-6063 |
| server srv1 192.168.1.1 check port 6000 inter 10000 |
| server srv2 192.168.1.2 backup |
| |
| listen app_tcp2 10.0.0.2:6000-6063 |
| server srv1 192.168.2.1 check port 6000 inter 10000 |
| server srv2 192.168.2.2 backup |
| |
| defaults applications HTTP |
| log global |
| mode http |
| option httplog |
| option forwardfor |
| option dontlognull |
| balance roundrobin |
| clitimeout 20000 |
| srvtimeout 20000 |
| contimeout 4000 |
| retries 3 |
| |
| listen app_http1 10.0.0.1:80-81 |
| cookie SERVERID postonly insert indirect |
| capture cookie userid= len 10 |
| server srv1 192.168.1.1:+8000 cookie srv1 check port 8080 inter 1000 |
| server srv1 192.168.1.2:+8000 cookie srv2 check port 8080 inter 1000 |
| |
| defaults |
| # section vide qui annule tous les paramètes par défaut. |
| |
| |
| 4.8) Rapport d'état sous forme de page HTML |
| ------------------------------------------- |
| Avec la version 1.2.14, il devient possible pour haproxy d'interceptre des |
| requêtes pour une URI particulière et de retourner un rapport complet d'état de |
| l'activité du proxy, et des statistiques sur les serveurs. Ceci est disponible |
| via le mot clé "stats" associé à n'importe laquelle de ces options : |
| |
| - stats enable |
| - stats uri <uri prefix> |
| - stats realm <authentication realm> |
| - stats auth <user:password> |
| - stats scope <proxy_id> | '.' |
| |
| |
| Par défaut, le rapport est désactivé. Le fait de spécifier une des combinaision |
| ci-dessus active le rapport pour l'instance de proxy qui le référence. La |
| solution la plus simple est d'utiliser "stats enable" qui activera le rapport |
| avec les paramètres par défaut suivant : |
| |
| - default URI : "/haproxy?stats" (CONFIG_STATS_DEFAULT_URI) |
| - default auth : non spécifié (pas d'authentication) |
| - default realm : "HAProxy Statistics" (CONFIG_STATS_DEFAULT_REALM) |
| - default scope : non specifié (accès à toutes les instances) |
| |
| L'option "stats uri <uri_prefix>" permet d'intercepter un autre préfixe d'URI |
| que celui par défaut. Noter que n'importe quelle URI qui COMMENCE avec cette |
| chaîne sera validée. Par exemple, une instance pourrait être dédiée à la page |
| d'état seulement et répondre à toute URI. |
| |
| Example : |
| --------- |
| # intercepte n'importe quelle URI et retourne la page d'état. |
| listen stats :8080 |
| mode http |
| stats uri / |
| |
| |
| L'option "stats auth <user:password>" active l'authentification "Basic" et |
| ajoute un couple "user:password" valide à la liste des comptes autorisés. |
| L'utilisateur <user> et le mot de passe <password> doivent être précisés |
| en clair dans le fichier de configuration, et comme ceci est de |
| l'authentification HTTP "Basic", il faut être conscient qu'ils transitent en |
| clair sur le réseau, donc il ne faut pas utiliser de compte sensible. La liste |
| est illimitée dans le but de pouvoir fournir des accès facilement à des |
| développeurs ou à des clients. |
| |
| L'option "stats realm <realm>" définit le "domaine" ("realm") de validité du |
| mot de passe qui sera présenté dans la boîte de dialogue du navigateur |
| lorsqu'il demandera un compte utilisateur et un mot de passe. Il est important |
| de s'assurer que celui-ci soit différent de ceux utilisés par l'application, |
| autrement le navigateur tentera d'en utiliser un caché depuis l'application. |
| Noter que les espaces dans le nom de "realm" doivent être protégés par un |
| backslash ('\'). |
| |
| L'option "stats scope <proxy_id>" limite la portée du rapport d'état. Par |
| défaut, toutes les instances proxy sont listées. Mais dans certaines |
| circonstances, il serait préférable de ne lister que certains proxies ou |
| simplement le proxy courant. C'est ce que fait cette option. Le nom spécial "." |
| (un simple point) référence le proxy courant. Cette option peut être répétée |
| autant de fois que nécessaire pour autoriser d'autres proxies, même pour des |
| noms référencés plus loin dans la configuration ou bien des noms qui n'existent |
| pas encore. Le nom précisé est celui qui apparait après le mot clé "listen". |
| |
| Exemple : |
| --------- |
| # simple application embarquant la page d'état authentifiée |
| listen app1 192.168.1.100:80 |
| mode http |
| option httpclose |
| balance roundrobin |
| cookie SERVERID postonly insert indirect |
| server srv1 192.168.1.1:8080 cookie srv1 check inter 1000 |
| server srv1 192.168.1.2:8080 cookie srv2 check inter 1000 |
| stats uri /my_stats |
| stats realm Statistics\ for\ MyApp1-2 |
| stats auth guest:guest |
| stats auth admin:AdMiN123 |
| stats scope . |
| stats scope app2 |
| |
| # simple application embarquant la page d'état sans authentification |
| listen app2 192.168.2.100:80 |
| mode http |
| option httpclose |
| balance roundrobin |
| cookie SERVERID postonly insert indirect |
| server srv1 192.168.2.1:8080 cookie srv1 check inter 1000 |
| server srv1 192.168.2.2:8080 cookie srv2 check inter 1000 |
| stats uri /my_stats |
| stats realm Statistics\ for\ MyApp2 |
| stats scope . |
| |
| listen admin_page :8080 |
| mode http |
| stats uri /my_stats |
| stats realm Global\ statistics |
| stats auth admin:AdMiN123 |
| |
| Notes : |
| ------- |
| - les options "stats" peuvent aussi être spécifiées dans une section |
| "defaults", auquel cas la même configuration exactement sera fournie à |
| toutes les instances suivantes, d'où l'utilité du scope ".". Toutefois, si |
| une instance redéfinit n'importe quel paramètre "stats", alors les défauts |
| ne lui seront pas appliqués. |
| |
| - l'authentification "Basic" est très simpliste et non sécurisée contre la |
| capture réseau. Aucun mot de passe sensible ne doit être utilisé, et il |
| est bon de savoir qu'il n'existe pas de moyen de le supprimer du navigateur |
| après usage, donc il sera envoyé tel quel à l'application au cours des |
| accès successifs. |
| |
| - Il est très important de préciser l'option "httpclose", sinon le proxy ne |
| sera pas en mesure de détecter les URI dans les sessions keep-alive |
| maintenues entre le navigateur et les serveurs, donc les URI de stats |
| seront transmises telles quelles aux serveurs comme si l'option n'était |
| pas précisée. |
| |
| |
| 5) Listes d'accès |
| ================= |
| |
| Avec la version 1.3.10, un nouveau concept de listes d'accès (ACL) a vu le |
| jour. Comme il n'était pas nécessaire de réinventer la roue, et du fait que |
| toutes les réflexions passées aboutissaient à des propositions non |
| satisfaisantes, il a finalement été décidé que quelque chose de proche de ce |
| que Squid offre serait un bon compromis entre une richesse fonctionnelle et une |
| facilité d'utilisation |
| |
| Le principe est très simple : les ACLs sont déclarées avec un nom, un test et |
| une liste de valeurs valides à tester. Des conditions sont appliquées sur |
| diverses actions, et ces conditions effectuent un ET logique entre les ACLs. La |
| condition n'est donc validée que si toutes les ACLs sont vraies. |
| |
| Il est également possible d'utiliser le mot réservé "OR" dans les conditions, |
| et il est possible pour une ACL d'être spécifiée plusieurs fois, même avec des |
| tests différents, auquel cas le premier test réussi validera l'ACL. |
| |
| Au stade de la version 1.3.12, seuls les tests suivants ont été implémentés : |
| |
| Niveaux 3/4 : |
| src <ipv4_address>[/mask] ... : match IPv4 source address |
| dst <ipv4_address>[/mask] ... : match IPv4 destination address |
| src_port <range> ... : match source port range |
| dst_port <range> ... : match destination port range |
| dst_conn <range> ... : match #connections on frontend |
| |
| Niveau 7 : |
| method <HTTP method> ... : match HTTP method |
| req_ver <1.0|1.1> ... : match HTTP request version |
| resp_ver <1.0|1.1> ... : match HTTP response version |
| status <range> ... : match HTTP response status code in range |
| url <string> ... : exact string match on URI |
| url_reg <regex> ... : regex string match on URI |
| url_beg <string> ... : true if URI begins with <string> |
| url_end <string> ... : true if URI ends with <string> |
| url_sub <string> ... : true if URI contains <string> |
| url_dir <string> ... : true if URI contains <string> between slashes |
| url_dom <string> ... : true if URI contains <string> between slashes or dots |
| |
| Une plage ('range') est constituée d'un ou deux entiers qui peuvent être |
| préfixés d'un opérateur. La syntaxe est : |
| |
| [<op>] <min>[:<max>] |
| |
| Avec <op> pouvant être : |
| 'eq' : la valeur doit égaler <min> ou être comprise entre <min> et <max> |
| 'le' : la valeur doit être inférieure ou égale à <min> |
| 'lt' : la valeur doit être strictement inférieure à <min> |
| 'ge' : la valeur doit être supérieure ou égale à <min> |
| 'gt' : la valeur doit être strictement supérieure à <min> |
| |
| Lorsqu'aucun opérateur n'est défini, 'eq' est employé. Noter que lorsqu'un |
| opérateur est spécifié, il s'applique à toutes les plages de valeurs suivantes |
| jusqu'à la fin de la ligne ou bien jusqu'à ce qu'un nouvel opérateur soit |
| précisé. Exemple : |
| |
| acl status_error status 400:599 |
| acl saturated_frt dst_conn ge 1000 |
| acl invalid_ports src_port lt 512 ge 65535 |
| |
| D'autres tests arrivent (entêtes, cookies, heure, authentification), c'est |
| juste une question de temps. Il est aussi prévu de permettre de lire les |
| valeurs depuis un fichier, ainsi que d'ignorer la casse pour certains tests. |
| |
| La seule commande supportant les conditions d'ACL à ce jour est la nouvelle |
| commande "block" qui bloque une requête et retourne un statut 403 si sa |
| condition est validée (cas du "if") ou invalidée (cas du "unless"). |
| |
| Exemple : |
| --------- |
| |
| acl options_uris url * |
| acl meth_option method OPTIONS |
| acl http_1.1 req_ver 1.1 |
| acl allowed_meth method GET HEAD POST OPTIONS CONNECT |
| acl connect_meth method CONNECT |
| acl proxy_url url_beg http:// |
| |
| # block if reserved URI "*" used with a method other than "OPTIONS" |
| block if options_uris !meth_option |
| |
| # block if the OPTIONS method is used with HTTP 1.0 |
| block if meth_option !http_1.1 |
| |
| # allow non-proxy url with anything but the CONNECT method |
| block if !connect_meth !proxy_url |
| |
| # block all unknown methods |
| block unless allowed_meth |
| |
| Note: Cette documentation est embryonnaire mais doit permettre de démarrer et |
| surtout d'avancer sur le projet sans être trop ralenti par la documentation. |
| |
| |
| ======================= |
| | Paramétrage système | |
| ======================= |
| |
| Sous Linux 2.4 |
| ============== |
| |
| -- cut here -- |
| #!/bin/sh |
| # set this to about 256/4M (16384 for 256M machine) |
| MAXFILES=16384 |
| echo $MAXFILES > /proc/sys/fs/file-max |
| ulimit -n $MAXFILES |
| |
| if [ -e /proc/sys/net/ipv4/ip_conntrack_max ]; then |
| echo 65536 > /proc/sys/net/ipv4/ip_conntrack_max |
| fi |
| |
| if [ -e /proc/sys/net/ipv4/netfilter/ip_ct_tcp_timeout_fin_wait ]; then |
| # 30 seconds for fin, 15 for time wait |
| echo 3000 > /proc/sys/net/ipv4/netfilter/ip_ct_tcp_timeout_fin_wait |
| echo 1500 > /proc/sys/net/ipv4/netfilter/ip_ct_tcp_timeout_time_wait |
| echo 0 > /proc/sys/net/ipv4/netfilter/ip_ct_tcp_log_invalid_scale |
| echo 0 > /proc/sys/net/ipv4/netfilter/ip_ct_tcp_log_out_of_window |
| fi |
| |
| echo 1024 60999 > /proc/sys/net/ipv4/ip_local_port_range |
| echo 30 > /proc/sys/net/ipv4/tcp_fin_timeout |
| echo 4096 > /proc/sys/net/ipv4/tcp_max_syn_backlog |
| echo 262144 > /proc/sys/net/ipv4/tcp_max_tw_buckets |
| echo 262144 > /proc/sys/net/ipv4/tcp_max_orphans |
| echo 300 > /proc/sys/net/ipv4/tcp_keepalive_time |
| echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle |
| echo 0 > /proc/sys/net/ipv4/tcp_timestamps |
| echo 0 > /proc/sys/net/ipv4/tcp_ecn |
| echo 1 > /proc/sys/net/ipv4/tcp_sack |
| echo 0 > /proc/sys/net/ipv4/tcp_dsack |
| |
| # auto-tuned on 2.4 |
| #echo 262143 > /proc/sys/net/core/rmem_max |
| #echo 262143 > /proc/sys/net/core/rmem_default |
| |
| echo 16384 65536 524288 > /proc/sys/net/ipv4/tcp_rmem |
| echo 16384 349520 699040 > /proc/sys/net/ipv4/tcp_wmem |
| |
| -- cut here -- |
| |
| Sous FreeBSD |
| ============ |
| |
| Un port de HA-Proxy sous FreeBSD est désormais disponible, grâce à |
| Clement Laforet <sheepkiller@cultdeadsheep.org>. |
| |
| Pour plus d'informations : |
| http://www.freebsd.org/cgi/url.cgi?ports/net/haproxy/pkg-descr |
| http://www.freebsd.org/cgi/cvsweb.cgi/ports/net/haproxy/ |
| http://www.freshports.org/net/haproxy |
| |
| |
| -- fin -- |