Aller au contenu
English
Compte : non connecté, synchronisation possible Mon apprentissage
Selectionner le thème
English

Protéger et observer HAProxy : ACL, stick-tables, en-têtes, journaux et Prometheus

Par mis à jour le

Améliorer cette page

Protéger HAProxy consiste à décrire en ACL ce qu'il faut refuser, à compter les requêtes de chaque client dans une stick-table pour limiter les abus, et à ajouter des en-têtes de sécurité ; l'observer consiste à produire des journaux structurés et à exposer ses métriques à Prometheus sur une interface qui n'est pas publique. Cette leçon est la suite de la publication en HTTPS avec HAProxy : elle reprend les gestes de protection et d'observation de la formation, dans la syntaxe de HAProxy. Les exemples valent pour HAProxy 3.4.7, installé depuis haproxy.debian.net sur Debian 13.

La leçon va de la règle qui refuse à la métrique qui mesure.

  • Écrire une ACL et l'appliquer à une règle.
  • Exiger une authentification sur une route et refuser des méthodes.
  • Limiter le débit par client avec une stick-table.
  • Ajouter des en-têtes de sécurité aux réponses.
  • Exposer l'exporteur Prometheus sur une interface restreinte.
  • Situer l'API d'exécution et ses risques.

Cette leçon suppose que vous publiez déjà un service avec HAProxy.

Une ACL nomme une condition, et une règle http-request l'utilise pour agir. Une ACL teste un élément de la requête : un chemin, une méthode, une adresse source, un en-tête. La règle associée refuse, redirige ou modifie quand la condition est vraie.

frontend https_in
acl chemin_admin path_beg /admin
acl reseau_interne src 10.0.0.0/8
http-request deny deny_status 403 if chemin_admin !reseau_interne

Cette règle répond 403 à tout accès à /admin qui ne vient pas du réseau interne. Les ACL se lisent comme des phrases : nommez-les pour ce qu'elles testent, et gardez en tête qu'une adresse source n'est fiable que si HAProxy est le premier relais ; derrière un autre proxy, c'est l'adresse du relais que vous verrez.

Comment exiger un mot de passe et refuser des méthodes ?

Section intitulée « Comment exiger un mot de passe et refuser des méthodes ? »

Une route sensible s'authentifie avec une userlist et http-request auth, et une méthode inutile se refuse par une ACL sur method. La userlist déclare les comptes avec un mot de passe haché, jamais en clair. La règle auth répond 401 tant que la condition http_auth n'est pas satisfaite.

userlist admins
user ops password $5$Xk3pQ9aB$dCdsGkSjDndrErPudW5LAbbgWQkf5eMHpYsiK0QLek9
frontend https_in
acl chemin_admin path_beg /admin
http-request auth realm Administration if chemin_admin !{ http_auth(admins) }
acl methode_ecriture method PUT DELETE
http-request deny deny_status 405 if methode_ecriture

Le hachage de l'exemple vient de openssl passwd -5 et correspond au mot de passe Exemple-Ops-2026 : générez le vôtre. Un refus ne se prouve qu'en montrant ce qui passe encore : curl -i sans identifiants sur /admin doit rendre 401, la même requête avec --user doit atteindre le backend, un DELETE doit rendre 405 et un GET doit toujours aboutir. Une configuration qui refuserait tout satisferait les trois premiers contrôles en ayant simplement supprimé le service.

Une stick-table garde en mémoire un compteur par clé, par exemple par adresse source, et une règle refuse les clés qui dépassent un seuil. Il faut trois éléments : déclarer la table et ce qu'elle compte, suivre chaque requête avec track-sc, puis refuser quand le compteur dépasse la limite.

frontend https_in
stick-table type ip size 100k expire 30s store http_req_rate(10s)
http-request track-sc0 src
http-request deny deny_status 429 if { sc_http_req_rate(0) gt 50 }

La table compte ici le nombre de requêtes par adresse sur une fenêtre de dix secondes, et la règle répond 429 au-delà de cinquante. Le seuil et la fenêtre sont à choisir selon l'application : trop bas, ils bloquent des utilisateurs légitimes ; trop haut, ils ne protègent de rien. Comme pour toute limitation par adresse, ce que vous comptez doit correspondre au client réel.

http_req_rate(10s) est une moyenne glissante, pas un compteur qui repart de zéro à intervalle fixe. Le manuel 3.4 écrit qu'elle rapporte « the average HTTP request rate over that period ». Après une rafale, la cadence redescend progressivement, et le client n'est servi à nouveau qu'une fois repassée sous le seuil. Cette décroissance est lente : sur une fenêtre de dix secondes et un seuil de 20, une rafale de 25 requêtes laissait encore une requête de contrôle sur deux refusée treize secondes après la dernière. Une bonne limite relâche donc d'elle-même, sans bannir, mais plus tard qu'on ne le croit. Enfin, la clé ne doit pas être une valeur que le client envoie : un compteur clavé sur un en-tête est remis à zéro par celui qu'il devait limiter, d'où le choix de src.

Ajoutez des en-têtes de réponse qui réduisent la surface d'attaque côté navigateur, avec http-response set-header. Ils disent au navigateur de ne pas deviner le type d'un contenu, de ne pas laisser afficher la page dans un cadre, de ne pas transmettre l'adresse d'origine et de n'utiliser que HTTPS.

http-response set-header X-Content-Type-Options nosniff
http-response set-header X-Frame-Options DENY
http-response set-header Referrer-Policy no-referrer
http-response set-header Content-Security-Policy "default-src 'self'; frame-ancestors 'none'"
http-response set-header Strict-Transport-Security "max-age=31536000"

Vérifiez avec curl -I les en-têtes réellement reçus, pas ceux que vous croyez avoir configurés : une règle placée dans le mauvais bloc, ou une réponse d'erreur produite par HAProxy lui-même, peut ne pas les porter. Le contrôle fiable porte sur une réponse venue du backend, pas sur une page d'erreur.

HAProxy peut formater ses journaux en JSON, à condition que le frontend ait une destination de journaux. Un format structuré se filtre et s'agrège sans analyse de texte. Sans directive log sur le frontend ou héritée de la section global, haproxy -c signale que le format est ignoré : c'est le premier réflexe de diagnostic si les lignes n'apparaissent pas.

defaults
mode http
log global
log-format "%{+json}o %(client_ip)ci %(status)ST %(bytes)B %(duration)Ta %(backend)b %(server)s"

Chaque champ porte son nom, ce qui permet à un outil comme Loki ou Elasticsearch de les indexer sans règle d'extraction.

HAProxy sert lui-même ses métriques au format Prometheus : l'exportateur est compilé d'office dans le paquet 3.4.7 de haproxy.debian.net, donc il n'y a ni compilation à faire ni exportateur séparé à installer. Il s'active par un service interne, use-service prometheus-exporter, sur un frontend dédié qu'il faut restreindre. Les métriques révèlent la topologie des backends et leur état : elles n'ont pas à être joignables depuis Internet.

frontend metriques
bind 127.0.0.1:8404
http-request use-service prometheus-exporter if { path /metrics }
no log

Liez ce frontend à l'adresse que Prometheus atteint réellement, jamais à toutes les interfaces. N'y activez pas stats enable : cela publierait la page de statistiques sans authentification, et cette page ne montre plus la version depuis la 3.4 (stats show-version est désactivé par défaut) : lisez la version avec haproxy -v.

Vérifiez d'abord le point de métriques avec curl http://127.0.0.1:8404/metrics. La sortie doit contenir haproxy_frontend_status et haproxy_server_status, ce dernier portant la santé de chaque serveur de backend. Côté Prometheus, un scrape réussi se voit par une cible à l'état up (page des cibles, ou l'API /api/v1/targets).

L'API d'exécution est une socket de commande qui permet de lire l'état de HAProxy et de le modifier à chaud, sans rechargement. On y interroge l'état des serveurs, on désactive un serveur pour une maintenance, on consulte le contenu d'une stick-table. Elle ne se publie jamais en réseau : une socket Unix aux droits restreints, ou une écoute locale, suffit, parce que quiconque l'atteint peut changer le comportement du proxy. Le paquet Debian en pose une, /run/haproxy/admin.sock, en mode 660 et de niveau admin.

Le niveau du socket décide de ce qu'on peut défaire. Un socket de niveau operator exécute déjà set table et clear table : il peut donc effacer un blocage posé par une stick-table, par exemple avec echo "clear table https_in key 192.0.2.10" | socat stdio /run/haproxy/admin.sock. Réservez ces niveaux aux comptes qui doivent lever un blocage, et traitez l'accès à la socket comme un droit d'administration.

Mettre en pratique : protéger puis observer avec HAProxy

Section intitulée « Mettre en pratique : protéger puis observer avec HAProxy »

Deux labs se jouent dans l'ordre : le premier protège le frontend, le second observe avec Prometheus. Chacun se valide par un contrôle automatisé.

Le premier lab part d'un service publié par HAProxy dont la route /admin est servie à qui la demande, qui accepte un nombre illimité de requêtes par client, qui transmet DELETE et PUT au backend et dont les réponses ne portent aucun en-tête de sécurité. Il vous demande d'ajouter les en-têtes de sécurité, d'exiger des identifiants sur /admin, de refuser en 429 plus de 20 requêtes par tranche de 10 secondes et par adresse source, et de refuser DELETE et PUT. La validation joue dans les deux sens : chaque refus est vérifié avec la requête qui doit encore passer, et le service doit redevenir disponible une fois la fenêtre écoulée.

Le second lab part d'un service déjà publié par HAProxy, avec une instance Prometheus prête à le surveiller. Le point de métriques ne renvoie aucune métrique, et les deux interfaces de supervision, ce point de métriques et l'interface de Prometheus, sont joignables depuis d'autres machines. Il vous demande de configurer l'exporteur Prometheus, de limiter les deux interfaces à l'hôte et de faire remonter à Prometheus une cible up ainsi que les métriques de santé du backend, sans interrompre le service public.

Protéger HAProxy, c'est refuser par ACL et limiter par stick-table ; l'observer, c'est des journaux structurés et des métriques sur une interface fermée.

  • Une ACL nomme une condition, http-request deny agit.
  • userlist et http-request auth exigent des identifiants.
  • Une stick-table compte par clé, track-sc suit, un seuil refuse ; le débit est une moyenne glissante qui redescend.
  • http-response set-header ajoute les en-têtes de sécurité.
  • Le journal JSON exige une destination de journaux.
  • Les métriques et l'API d'exécution restent sur l'hôte.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

8 questions
8 min.
70% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

Ce projet vous est utile ?

Les guides, formations, quiz et labs restent gratuits. Si vous les utilisez régulièrement, vous pouvez contribuer à leur maintenance et à leur développement.

Devenir membre de soutien (nouvel onglet) Soutenir une fois (nouvel onglet) En savoir plus

Suivez aussi le projet sur LinkedIn