Dès qu'on connecte un serveur public à
l'Internet, on risque des ennuis. L'un de
ceux-ci est l'écroulement du serveur sous la charge, que celle-ci soit
provoquée par une attaque délibérée ou tout
simplement et banalement par un trafic excessif par rapport à ce que
peut gérer le serveur (l'effet Slashdot). La solution la
plus efficace est souvent de servir plutôt des
pages statiques (dans la grande majorité des cas, les pages
dynamiques sont inutiles et n'ont été utilisées que parce que la
web agency pouvait facturer
davantage avec du dynamique). Avec les pages statiques, seule la
capacité du réseau est un facteur limitant. Mais, si le serveur Web
donne accès à des applications et que ce sont les ressources
consommées par celles-ci qui sont le facteur limitant ?
Il existe plein de solutions à ce problème. On peut par exemple
configurer son pare-feu pour limiter le nombre
d'accès (voir mon article sur la
limitation de HTTP avec Netfilter). À l'autre extrémité, il y a
aussi la solution, si les pages si demandées sont générées par une
application, de mettre la limitation de trafic dans le code de
l'application (d'ailleurs, si un de mes lecteurs a des bonnes
solutions pour Mason, ça m'intéresse ; en
Django, il y a la
solution de Frédéric De Zorzi). On peut aussi mettre
devant le serveur un relais dont l'une des tâches sera de limiter le
trafic (comme par exemple Varnish).
Mais, dans cet article, je ne vais parler que des solutions
fournies par Apache. Il y a plein de modules
Apache qui permettent de limiter le trafic. Il est difficile de
trouver des comparaisons entre eux, donc beaucoup d'administrateurs
systèmes en prennent un au hasard. Ils n'ont pas les mêmes fonctions
et surtout pas le même niveau de qualité (plusieurs semblent des
projets abandonnés). Certains modules sont très
sommaires (ce qui ne veut pas dire inutiles) comme mod_limitipconn,
qui ne permet que de limiter le nombre de connexions simultanées faites
par chaque adresse IP. Ou comme mod_ratelimit
qui ne permet de contrôler que le débit (le nombre d'octets envoyés par
seconde). Même chose apparemment pour mod_bwshare. D'autres
sont plus riches comme mod_cband qui peut
également limiter le nombre de requêtes par seconde, ou le nombre de
connexions simultanées (comme mod_limitipconn).
Mais les deux modules les plus souvent cités pour faire de la
limitation de trafic sont bien plus riches : mod_evasive et
mod_security.
Commençons par mod_evasive. Une configuration typique ressemble à
(avec mes commentaires) :
Compliquée ? Il faut dire que la documentation officielle est
serrée... Ces instructions limitent les visiteurs à trois requêtes
pour une page (vingt pour le site entier) pendant une période d'une
minute. On
trouve la documentation
dans le fichier README de la
distribution (sur une Debian, c'est dans
/usr/share/doc/libapache2-mod-evasive/README.gz). On
peut aussi consulter, à propos des
différentes variables « Installation et configuration du mod Evasive. ».
La plus grosse limitation de mod_evasive est qu'il est seulement
global. Pas moyen de le faire par virtual host ou
par répertoire. Certes, on peut mettre les directives dans un site ou
un répertoire (Apache ne proteste pas, le module se charge) mais elles
sont alors
ignorées : si on met les directives mod_evasive dans <VirtualHost>,
elles s'appliquent à tous les virtual hosts.
À lire le source, cela semble normal (il s'attache à des évenements
globaux). Et relativement logique, un outil anti-DoS doit aller vite
et ne pas faire trop de traitement. Mais ce n'est pas pratique du tout
pour un limiteur de trafic.
Pire, si un des vhosts est un serveur pour Subversion, ça
cafouille horriblement car mod_evasive interfère avec
l'authentification (premier svn co accepté, le
second fait des 401 Unauthorized).
(Sur les spécificités de mod_evasive pour une
Debian, voir « Prevent
DOS attacks on apache webserver for DEBIAN linux with
mod_evasive ».)
Le second module Apache très souvent cité pour ce genre de travail
est mod_security. Il est bien plus générique et peut servir à beaucoup
d'autres choses. Notamment, il peut s'appliquer à seulement un
vhost ou seulement une partie du site. Mais il n'est pas trivial à configurer : sa
configuration se fait par des directives successives, évaluées en
séquence (comme avec le bon vieux
sendmail.cf ou, pour
prendre un exemple Apache, mod_rewrite). Il
est donc indispensable de lire la doc officielle.
Un exemple de configuration pour limiter le trafic (avec mes
commentaires) :
Dommage, le code de retour HTTP normal pour un excès de requêtes (le
429 du ) n'est pas reconnu par
mod_security. Autrement, cela fonctionne bien et les excès sont
journalisés :
[Mon May 07 12:21:30 2012] [error] [client 192.0.2.44] ModSecurity: Access denied with code 403 (phase 1). Operator GT matched 2 at IP:pagecount. [file "/etc/apache2/mods-enabled/mod-security.conf"] [line "19"] [msg "Too many requests"] [hostname "www.example.net"] [uri "/apps/foobar"] [unique_id "T6eiKsCGB-gAAHSOXEwAAABS"]
Sur l'usage de mod_security pour la limitation de trafic, on peut
aussi consulter « rate
limiting with mod_security », « How
to limit connections per IP based on domain +
string » et « Apache mod_security Setup Help? ».
Aussi bien avec mod_evasive qu'avec mod_security, la limitation est
par adresse IP source. Autant dire qu'en IPv6,
elle va être triviale à contourner. Je ne connais pas de moyen, avec
ces deux modules, de mettre tout un préfixe (dont on spécifierait la
longueur) dans la même règle.
Comme toujours avec les outils anti-méchants, rappelez-vous que ce
qui marche lors d'un test avec wget, ou en
laboratoire avec cent adresses IP d'attaque, va peut-être se comporter
très différemment avec 20 000 zombies fous se
lançant contre votre serveur. Gardez l'esprit ouvert et soyez prêt à
changer de technique.
Connaissant le fanatisme des utilisateurs de
nginx, nul doute que plusieurs d'entre eux
m'écriront pour me dire que leur serveur HTTP est
vachement meilleur et dispose de meilleurs mécanismes de limitation de
trafic. Je leur laisse de la place ici pour insérer les URL
de leur choix.
Merci à Patrice Bouvard et Éric van der Vlist pour leur relecture.