
         		     H A - P r o x y
         		     ---------------
         		      version 1.1.5
			      willy tarreau
			       2002/04/03

================
| 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 :
  - assurer un aiguillage statique défini par des cookies ;
  - assurer 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.

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
    -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.

Le nombre maximal total de connexions simultanées limite le nombre de
connexions TCP utilisables à un instant par le processus, tous proxies
confondus.

============================
| Fichier de configuration |
============================


Commentaires
============

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.


Serveur
=======

Le fichier de configuration contient des sections repérées par le mot
clé "listen" :

    listen <nom_instance> <adresse_IP>:<port>

<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'adresse 0.0.0.0 signifie que les connexions pourront
s'effectuer sur toutes les adresses de la machine.

<port> est le numéro de port TCP sur lequel le relais attend ses
connexions. 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.

Exemple :
---------
    listen http_proxy 127.0.0.1:80


Inhibition
==========

Un serveur 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

Mode
====

Un serveur 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
<adresse_source:port_source> <adresse_destination:port_destination>.
Pour l'utiliser, préciser le mode TCP sous la déclaration du relais :

    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 :

    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. Pour activer ce mode, préciser le mode HEALTH sous la
déclaration du relais :

    listen health_check 0.0.0.0:60000
	mode health


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:

     maxconn 16000


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).

Exemple :
---------

    # 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

    listen health_check 0.0.0.0:60000
	mode health
	grace 0


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 client à 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 3 secondes
	contimeout	3000

Remarque: "contimeout" et "srvtimeout" n'ont pas d'utilité dans le cas
du serveur de type "health".

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" :

	# on essaie encore trois fois maxi
	retries 3

Adresse du serveur
==================

Le serveur vers lequel sont redirigées les connexions est défini par
le paramètre "dispatch" sous la forme <adresse_ip>:<port> :

   	# 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".

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" :

    listen http_proxy 0.0.0.0: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 orientée vers un serveur
sélectionné en répartition de charge.

Pour ne conserver le cookie qu'en accès indirect, donc à travers le
dispatcheur, et le supprimer lors des accès directs :

	cookie SERVERID indirect

Pour réécrire le nom du serveur dans un cookie lors d'un accès direct :

	cookie SERVERID rewrite

Pour créer un cookie comportant le nom du serveur lors d'un accès en
répartition de charge interne. Dans ce cas, il est indispensable que tous les
serveurs aient un cookie renseigné.

	cookie SERVERID insert

Remarque: 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.

Assignation d'un serveur à une valeur de cookie
===============================================

En mode HTTP, il est possible d'associer des serveurs à des valeurs de
cookie 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 (erreurs...).
<adresse_ip>:<port> le couple adresse-port sur lequel le serveur écoute.
<valeur> est la valeur trouvée dans le cookie,

Exemple : le cookie SERVERID peut contenir server01 ou server02
-------
    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

Attention : la syntaxe a changé depuis la version 1.0.
---------

Répartiteur de charge interne
=============================

Le relais peut effectuer lui-même la répartition de charge entre les
différents serveurs décrits 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.0, 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 0.0.0.0: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


Surveillance des serveurs
=========================

A cette date, l'état des serveurs n'est testé que par établissement
de connexion TCP toutes les 2 secondes, avec 3 essais pour déclarer
un serveur en panne, 2 pour le déclarer utilisable. 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

Exemple : même que précédemment avec surveillance
-------

    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


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 serveur HS.

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

Journalisation des connexions
=============================

Les connexions TCP et HTTP peuvent donner lieu à une journalisation
sommaire indiquant, pour chaque connexion, la date, l'heure, les adresses
IP source et destination, et les ports source et destination qui la
caractérisent. Ultérieurement, les URLs seront loguées en mode HTTP,
tout comme les arrêts de service. Tous les messages sont envoyés en
syslog vers un ou deux serveurs. La syntaxe est la suivante :

    log <adresse_ip> <facility>

Exemple :
---------
    listen http_proxy 0.0.0.0:80
	mode http
	log 192.168.2.200 local3
	log 192.168.2.201 local4

Les connexions sont envoyées en niveau "info". Les démarrages de
service seront envoyés en "notice", les signaux d'arrêts en "warning"
et les arrêts définitifs en "alert". Ceci est valable aussi bien
pour les proxies que pour les serveurs testés au sein des proxies.

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


Modification des entêtes HTTP
=============================

En mode HTTP uniquement, il est possible de remplacer certains entêtes
dans la requête et/ou la réponse à partir d'expressions régulières. Une
limitation cependant : les entêtes fournis au milieu de connexions
persistentes (keep-alive) ne sont pas vus. Les données ne sont pas
affectées, ceci ne s'applique qu'aux entêtes.

La syntaxe est :
   reqadd <string>             pour ajouter un entête dans la requête
   reqrep <search> <replace>   pour modifier la requête
   reqrep <search>             pour supprimer un entête dans la requête

   rspadd <string>             pour ajouter un entête dans la réponse
   rsprep <search> <replace>   pour modifier la réponse
   rsprep <search>             pour supprimer un entête dans la réponse


<search> est une expression régulière compatible GNU 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 un backslash
  \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'entête.

Remarques :
---------
  - la première ligne de la requête et celle de la réponse sont traitées comme
    des entê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 :
--------
	reqrep	^(GET.*)(.free.fr)(.*) \1.online.fr\3
	reqrep	^(POST.*)(.free.fr)(.*) \1.online.fr\3
	reqrep	^Proxy-Connection:.*	Proxy-Connection:\ close
	rsprep	^Server:.* Server:\ Tux-2.0
	rsprep	^(Location:\ )([^:]*://[^/]*)(.*) \1\3
	rspdel  ^Connection:.*
	rspadd	Connection:\ close


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 :
  - assigner un cookie à chaque serveur
  - effectuer une répartition interne
  - insérer un cookie dans les réponses issues d'une répartition

Exemple :
-------
    listen application 0.0.0.0:80
	mode http
	cookie SERVERID insert indirect
	balance roundrobin
	server 192.168.1.1:80 cookie server01 check
	server 192.168.1.2:80 cookie server02 check

=======================
| 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 32768 > /proc/sys/net/ipv4/ip_queue_maxlen
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 --

-- fin --
