blob: f1b08e87b4a60c95858d5fdd5f91c72550d92c6b [file] [log] [blame]
H A - P r o x y
---------------
version 1.1.27
willy tarreau
2003/10/27
================
| Introduction |
================
HA-Proxy 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 entê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.
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>
-N <nombre maximal de connexions simultanées par proxy>
-d active le mode debug
-D passe en daemon
-p <fichier> indique au processus père qu'il doit écrire les PIDs de ses
fils dans ce fichier en mode démon.
-s affiche les statistiques (si option compilée)
-l ajoute des informations aux statistiques
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'entêtes HTTP
sont affichés.
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.
============================
| 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>
- chroot <répertoire>
- nbproc <nombre>
- daemon
- debug
- quiet
- pidfile <fichier>
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. 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). Dans une future version, haproxy sera
capable de positionner lui-même cette limite.
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. 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: 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 :
---------
global
uid 30000
gid 30000
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.
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'.
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)
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
- supervision
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 entê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.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 SIG_USR1
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. La
valeur par défaut 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
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
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 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.
- 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. En version 1.1.9,
seul 'roundrobin' est géré, et c'est aussi la valeur implicite par défaut. 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
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
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>
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. 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.
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.
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
# 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
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.
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, et de
la réécriture des entêtes.
4.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.2) Journalisation des connexions
----------------------------------
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.
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 :
---------
listen http_proxy 0.0.0.0:80
mode http
option httplog
option dontlognull
log 192.168.2.200 local3
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/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.
- 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 entê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'entê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). On peut donc en déduire
Td, le temps de transfert des données, en excluant les autres temps :
Td = Tt - (Tq + Tc + Tr)
Les temps rapportés à '-1' sont simplement à éliminer de cette équation.
En mode TCP ('option tcplog'), seuls les deux indicateurs 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/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/Tt : la connexion n'a pas pu s'établir vers le serveur (refus ou
time-out au bout de Tt-Tq ms).
Tq/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+Tc) ms.
4.2.4) Conditions de déconnexion
--------------------------------
Les logs TCP et HTTP fournissent un indicateur de complétude de la session.
C'est un champ de 4 caractères (2 en TCP) précédant la requête HTTP, indiquant :
- sur le premier caractère, un code précisant le premier événement qui a causé
la terminaison de la session :
C : fermeture de la session TCP de la part du client
S : fermeture de la session TCP de la part du serveur, ou refus de connexion
P : terminaison prématurée des sessions par le proxy, pour cas d'erreur
interne ou de configuration (ex: filtre d'URL)
c : expiration du délai d'attente côté client : clitimeout
s : expiration du délai d'attente côté serveur: srvtimeout et contimeout
- : terminaison normale.
- sur le second caractère, l'état d'avancement de la session HTTP lors de la
fermeture :
R : terminaison en attendant la réception totale de la requête du client
C : terminaison en attendant la connexion vers le serveur
H : terminaison en attendant la réception totale des entêtes du serveur
D : terminaison durant le transfert des données du serveur vers le client
L : terminaison durant le transfert des dernières données du proxy vers
le client, alors que le serveur a déjà fini.
- : 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é.
I : le client a présenté un cookie ne correspondant à aucun serveur
connu.
D : le client a présenté un cookie correspondant à un serveur hors
d'usage. 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
- 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 persistence n'a été fourni par le serveur.
P : un cookie de persistence a été fourni par le serveur et transmis
tel quel.
I : aucun cookie n'a été fourni par le serveur, il a été inséré par le
proxy.
D : le cookie présenté par le serveur a été supprimé par le proxy pour
ne pas être retourné au client.
R : le cookie retourné par le serveur a été modifié par le proxy.
- : non appliquable
Le mot clé "capture" permet d'ajouter dans des logs HTTP des informations
capturées dans les échanges. La version 1.1.17 supporte uniquement une capture
de cookies client et serveur, ce qui permet dans bien des cas, de reconstituer
la session d'un utilisateur. La syntaxe est la suivante :
capture cookie <préfixe_cookie> len <longueur_capture>
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.
4.2.5) Exemples de logs
-----------------------
- haproxy[674]: 127.0.0.1:33319 [15/Oct/2003:08:31:57] relais-http Srv1 6559/7/147/6723 200 243 - - ---- "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[18113]: 127.0.0.1:34548 [15/Oct/2003:15:18:55] relais-http <NOSRV> -1/-1/-1/8490 -1 0 - - CR-- ""
=> 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'entê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/50001 408 0 - - cR-- ""
=> 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'entê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 ('c----') au bout
de 5s.
- haproxy[18989]: 10.0.0.1:34552 [15/Oct/2003:15:26:31] relais-http Srv1 3183/-1/-1/11215 503 0 - - SC-- "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.
4.3) Modification des entê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 entête valide <search>
reqiallow <search> idem sans distinction majuscules/minuscules
reqdeny <search> interdire la requête si un entête valide <search>
reqideny <search> idem sans distinction majuscules/minuscules
reqpass <search> inhibe ces actions sur les entêtes validant <search>
reqipass <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
<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 1 à 9, et sont codées par un '\'
suivi du chiffre désiré. 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
entêtes inutiles avant les ajouts.
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\?)
# 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
reqidel ^Connection:
rspidel ^Connection:
reqadd Connection:\ close
rspadd Connection:\ close
# change the server name
rspidel ^Server:\
rspadd Server:\ Formilux/0.1.8
Enfin, 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.
Exemple :
---------
listen http_proxy 0.0.0.0:80
mode http
log global
option httplog
option dontlognull
option forwardfor
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 192.168.1.1:80 cookie server01 check
server 192.168.1.2:80 cookie server02 check
4.5) 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 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
4.6) 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.23, 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, httplog,
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.
=======================
| 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 0 > /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 --