WAF mit HAProxy einrichten
Vollständige Anleitung, um HAProxy mit einer Web Application Firewall abzusichern, per coraza-spoa und dem OWASP Core Rule Set, ergänzt durch native ACL- und Stick-Table-Schutzmechanismen.
HAProxy ist der weltweit meistgenutzte quelloffene Load Balancer, für sich genommen jedoch keine Web Application Firewall. Quelloffenes HAProxy besitzt kein natives WAF-Modul; es inspiziert und verteilt Datenverkehr, terminiert TLS und setzt ACLs sowie Rate Limits durch, führt aber ab Werk keine signatur- oder regelbasierte Angriffserkennung auf Anwendungsebene aus.
Um quelloffenem HAProxy echte WAF-Fähigkeit zu geben, lagern Sie die Request-Inspektion über das Stream Processing Offload Protocol (SPOP) an eine externe Engine aus, mithilfe eines Stream Processing Offload Agent (SPOA). Die praktikabelste quelloffene Option ist coraza-spoa: Sie betreibt die OWASP-Coraza-Engine und das OWASP Core Rule Set (CRS) als SPOA, den HAProxy für jede Anfrage konsultiert. HAProxy Enterprise geht einen anderen Weg: eine integrierte, ML-gestützte WAF, die im selben Prozess läuft, ganz ohne separaten Agenten.
Diese Anleitung führt Sie durch beide Verteidigungsebenen. Zuerst härten Sie HAProxy mit nativen ACLs und Stick-Tables für Rate Limiting und grundlegende Filterung ab, anschließend bringen Sie coraza-spoa mit dem OWASP CRS für vollständigen Schutz auf Anwendungsebene in Stellung. Am Ende steht eine funktionierende, feinjustierbare WAF vor Ihren Backends, und das ohne Lizenzkosten.
Voraussetzungen
- HAProxy 2.6+ empfohlen (SPOE ist seit 1.7 verfügbar, betreiben Sie jedoch einen aktuellen LTS-Zweig)
- Root- oder sudo-Zugriff auf Ihren Server
- Ubuntu 22.04+, Debian 12+ oder RHEL/Rocky 9+ (andere Distributionen können abweichen)
- Eine funktionierende Backend-Anwendung, die bereits über HAProxy geproxyt wird
- Go 1.25+ und Git installiert (die go.mod von coraza-spoa erfordert eine aktuelle Go-Toolchain, um aus dem Quellcode zu bauen)
- Grundkenntnisse der HAProxy-Konfiguration (Frontends, Backends, ACLs)
Schritt-für-Schritt-Anleitung
Wählen Sie Ihren HAProxy-WAF-Ansatz
Quelloffenes HAProxy liefert kein WAF-Modul mit. Es gibt drei echte Optionen, um eine WAF zu ergänzen, und es lohnt sich, bewusst zu wählen, bevor Sie irgendetwas installieren:
- coraza-spoa (quelloffen, hier empfohlen): Betreibt die OWASP-Coraza-Engine und das OWASP Core Rule Set als Stream Processing Offload Agent. HAProxy sendet jede Anfrage über SPOP an den Agenten und handelt gemäß dem Urteil. Dieselben SecLang-Regeln wie bei ModSecurity.
- ModSecurity SPOA (quelloffen): Ein älterer SPOA, der die ModSecurity-Engine einbettet. Er funktioniert, wird aber weniger aktiv gepflegt als Coraza und ist für neue Bereitstellungen faktisch durch coraza-spoa abgelöst.
- HAProxy Enterprise WAF (kommerziell): Eine integrierte, ML-gestützte WAF, die im selben Prozess wie der Load Balancer läuft, mit optionalem OWASP-CRS-Kompatibilitätsmodus. Kein separater Agent, nahezu keine zusätzliche Latenz, erfordert aber eine Enterprise-Lizenz.
Unabhängig davon, welche WAF Sie wählen, übernehmen HAProxys native ACLs und Stick-Tables das Rate Limiting und die grobe Filterung. Diese Anleitung verwendet coraza-spoa, weil es kostenlos und quelloffen ist und dieselben CRS-Regeln wie ModSecurity ausführt.
Grundschutz mit ACLs und Stick-Tables ergänzen
Bevor Sie eine WAF-Engine hinzufügen, nutzen Sie HAProxys native Funktionen für Rate Limiting und grundlegende Request-Filterung. Stick-Tables verfolgen den Zustand pro Client; ACLs erlauben es Ihnen, auf Basis dieses Zustands zu blockieren oder zu drosseln. Fügen Sie dies Ihrem Frontend in /etc/haproxy/haproxy.cfg hinzu:
frontend web
mode http
bind :80
# Buffer the request body so the WAF can inspect POST and JSON payloads later
option http-buffer-request
# Track request rate per source IP over a 10s window
stick-table type ip size 100k expire 30s store http_req_rate(10s)
http-request track-sc0 src
# Deny clients making more than 100 requests per 10 seconds
http-request deny deny_status 429 if { sc_http_req_rate(0) gt 100 }
# Basic path-based filtering example
acl block_path path_beg /.git /.env /wp-login.php
http-request deny deny_status 403 if block_path
default_backend web-backend
Laden Sie neu und bestätigen Sie, dass die Konfiguration gültig ist:
sudo haproxy -c -f /etc/haproxy/haproxy.cfg
sudo systemctl reload haproxy
coraza-spoa installieren
coraza-spoa ist ein Go-Programm, das die Coraza-WAF-Engine betreibt und sie HAProxy über SPOP zur Verfügung stellt. Bauen Sie es aus dem Quellcode:
# Clone the SPOA
cd /opt
sudo git clone https://github.com/corazawaf/coraza-spoa.git
cd coraza-spoa
# Build the binary with Mage (there is no Makefile; requires Go 1.25+ per go.mod)
sudo go run mage.go build
# Install the compiled binary onto your PATH (it is written to build/)
sudo cp build/coraza-spoa /usr/local/bin/
Legen Sie ein Verzeichnis für die Agenten-Konfiguration und die Regeln an:
sudo mkdir -p /etc/coraza-spoa/rules
OWASP Core Rule Set herunterladen und konfigurieren
Coraza führt die standardmäßigen OWASP-CRS-Regeln aus, dieselben SecLang-Regeln, die auch ModSecurity verwendet. Holen Sie das CRS und seine Basiskonfiguration:
cd /etc/coraza-spoa/rules
sudo git clone https://github.com/coreruleset/coreruleset.git
cd coreruleset
# Create the CRS setup file from the example
sudo cp crs-setup.conf.example crs-setup.conf
Sie benötigen außerdem eine Coraza-Basiskonfiguration, die den Modus der Rule-Engine und die Behandlung des Request-Bodys festlegt. Erstellen Sie /etc/coraza-spoa/rules/coraza.conf mit den zentralen Direktiven:
# Start in detection mode until you have tuned false positives
SecRuleEngine DetectionOnly
SecRequestBodyAccess On
SecRequestBodyLimit 13107200
SecRequestBodyLimitAction Reject
SecResponseBodyAccess On
SecResponseBodyMimeType text/plain text/html text/xml application/json
SecAuditEngine RelevantOnly
SecAuditLogParts ABIJDEFHZ
SecAuditLog /var/log/coraza-spoa/audit.log
Den coraza-spoa-Agenten konfigurieren
Der Agent braucht seine eigene Konfiguration, die ihm mitteilt, welche Regeln er laden soll und wo er auf HAProxy lauscht. Erstellen Sie /etc/coraza-spoa/config.yaml:
# coraza-spoa agent configuration
bind: 127.0.0.1:9000
log_level: info
# Must match the name of an application defined in the list below
default_application: default
# applications is a YAML list; each entry is an object with a name
applications:
- name: default
directives: |
Include /etc/coraza-spoa/rules/coraza.conf
Include /etc/coraza-spoa/rules/coreruleset/crs-setup.conf
Include /etc/coraza-spoa/rules/coreruleset/rules/*.conf
# Enable response-phase inspection so the coraza-res message and the
# http-response deny rule actually run (defaults to false)
response_check: true
transaction_ttl_ms: 60000
log_level: info
log_file: /var/log/coraza-spoa/coraza.log
Legen Sie das Log-Verzeichnis an:
sudo mkdir -p /var/log/coraza-spoa
Testen Sie, dass der Agent startet und die Regeln fehlerfrei lädt:
sudo coraza-spoa -config /etc/coraza-spoa/config.yaml
Den HAProxy-SPOE-Filter konfigurieren
HAProxy kommuniziert mit dem Agenten über eine Konfigurationsdatei der Stream Processing Offload Engine (SPOE). Sie legt fest, welche Request-Daten an Coraza gesendet werden und in welche Variablen das Urteil zurückgeschrieben wird. Erstellen Sie /etc/haproxy/coraza.cfg:
[coraza]
spoe-agent coraza-agent
messages coraza-res
groups coraza-req
option var-prefix coraza
option set-on-error error
timeout hello 2s
timeout idle 2m
timeout processing 500ms
use-backend coraza-spoa
log global
spoe-message coraza-req
args app=var(txn.coraza.app) src-ip=src src-port=src_port dst-ip=dst dst-port=dst_port method=method path=path query=query version=req.ver headers=req.hdrs body=req.body exportRuleIDs=bool(false)
spoe-message coraza-res
args app=var(txn.coraza.app) id=var(txn.coraza.id) version=res.ver status=status headers=res.hdrs body=res.body exportRuleIDs=bool(false) detect-only=bool(false)
event on-http-response
spoe-group coraza-req
messages coraza-req
Die Anfrage wird über eine spoe-group gesendet, die das Frontend mit send-spoe-group auslöst; die Response-Nachricht wird automatisch beim Event on-http-response ausgelöst. Die Reihenfolge der Argumente entspricht exakt dem offiziellen coraza.cfg-Beispiel und muss genau in dieser Reihenfolge stehen.
Definieren Sie anschließend ein Backend, das auf den Agenten zeigt, damit HAProxy weiß, wo es ihn erreicht. Fügen Sie dies zu /etc/haproxy/haproxy.cfg hinzu:
backend coraza-spoa
mode tcp
option spop-check
server coraza 127.0.0.1:9000 check
Die WAF-Entscheidung in Ihr Frontend einbinden
Hängen Sie nun den SPOE-Filter an Ihr Frontend und handeln Sie gemäß dem Urteil, das Coraza zurückgibt. Aktualisieren Sie das Frontend aus Schritt 2 in /etc/haproxy/haproxy.cfg:
frontend web
mode http
bind :80
# Buffer the request body so Coraza can inspect POST and JSON payloads
option http-buffer-request
# Native rate limiting (from Step 2)
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 100 }
# Select which coraza-spoa application this frontend uses
http-request set-var(txn.coraza.app) str(default)
# Attach the Coraza WAF filter and send the request to the agent
filter spoe engine coraza config /etc/haproxy/coraza.cfg
http-request send-spoe-group coraza coraza-req
# Deny requests and responses Coraza flags as attacks
http-request deny deny_status 403 if { var(txn.coraza.action) -m str deny }
http-response deny deny_status 403 if { var(txn.coraza.action) -m str deny }
default_backend web-backend
Die Zeile option http-buffer-request ist nicht optional, wenn Ihnen Angriffe über den POST-Body wichtig sind. Ohne sie befüllt HAProxy das Sample req.body nicht zuverlässig, bevor die Anfrage an den Agenten gesendet wird, sodass SQLi- oder XSS-Payloads in Formular- oder JSON-Request-Bodys niemals an Coraza gesendet und ungeprüft durchgelassen werden, selbst wenn SecRequestBodyAccess auf On steht. Die Request-Body-Inspektion ist durch tune.bufsize (Standard 16 KB) begrenzt: HAProxy kann nur bis zu etwa einer bufsize puffern, sodass größere Bodys nicht vollständig an Coraza gesendet werden. Wenn Sie größere Formular- oder JSON-Bodys inspizieren müssen, erhöhen Sie tune.bufsize maßvoll (zum Beispiel auf 64 KB), statt es an einem mehrere Megabyte großen SecRequestBodyLimit auszurichten, was den Speicher pro Verbindung vervielfachen und ein Out-of-Memory-Risiko schaffen würde.
Validieren Sie die gesamte Konfiguration, bevor Sie neu laden:
sudo haproxy -c -f /etc/haproxy/haproxy.cfg
coraza-spoa als Dienst betreiben
Betreiben Sie den Agenten unter systemd, damit er beim Booten startet und bei Ausfall neu startet. Erstellen Sie /etc/systemd/system/coraza-spoa.service:
[Unit]
Description=Coraza SPOA WAF agent for HAProxy
After=network.target
[Service]
ExecStart=/usr/local/bin/coraza-spoa -config /etc/coraza-spoa/config.yaml
Restart=on-failure
User=haproxy
Group=haproxy
[Install]
WantedBy=multi-user.target
Aktivieren und starten Sie den Agenten und laden Sie dann HAProxy neu:
sudo systemctl daemon-reload
sudo systemctl enable --now coraza-spoa
sudo systemctl reload haproxy
# Confirm the agent is running and listening
sudo systemctl status coraza-spoa
Die WAF testen
Setzen Sie in /etc/coraza-spoa/rules/coraza.conf vorübergehend SecRuleEngine On, starten Sie den Agenten neu und senden Sie bekannte Angriffs-Payloads. Sie sollten mit einem 403 blockiert werden:
sudo systemctl restart coraza-spoa
# SQL injection attempt (should be blocked)
curl -I 'http://your-server.com/?id=1%20OR%201=1'
# XSS attempt (should be blocked)
curl -I 'http://your-server.com/?q=<script>alert(1)</script>'
# Request-body SQL injection attempt (should be blocked with http-buffer-request enabled)
curl -I -X POST -d 'id=1 OR 1=1' 'http://your-server.com/login'
# A normal request (should pass with 200)
curl -I 'http://your-server.com/'
Bestätigen Sie, dass die Blockierungen im Coraza-Audit-Log erscheinen:
sudo tail -f /var/log/coraza-spoa/audit.log
Sie sollten Einträge mit den passenden CRS-Regel-IDs und dem Anomalie-Score sehen, der das Deny ausgelöst hat.
Auf Fehlalarme feinjustieren
Betreiben Sie das System ein bis zwei Wochen im DetectionOnly-Modus und prüfen Sie das Audit-Log auf legitime Anfragen, die blockiert worden wären. Justieren Sie mit den standardmäßigen CRS-Mechanismen in einer eigenen Datei, die nach den Regeln geladen wird, zum Beispiel /etc/coraza-spoa/rules/custom-exclusions.conf:
# Disable a specific rule that misfires
SecRuleRemoveById 942100
# Disable a rule only for a specific path
SecRule REQUEST_URI "@beginsWith /api/upload" \
"id:1000,phase:1,pass,nolog,ctl:ruleRemoveById=920420"
Sie können außerdem das CRS-Paranoia-Level in crs-setup.conf senken, um Fehlalarme zu reduzieren (1 = wenigste Fehlalarme, 4 = strengste Einstellung):
SecAction "id:900000,phase:1,pass,t:none,nolog,\
setvar:tx.blocking_paranoia_level=1"
Sobald das Log sauber ist, setzen Sie SecRuleEngine On und starten den Agenten neu, um mit der Durchsetzung zu beginnen.
Fazit & Nächste Schritte
Ihre HAProxy-Instanz verfügt nun über gestaffelten Schutz: native ACLs und Stick-Tables für Rate Limiting und grobe Filterung, ergänzt durch eine vollwertige WAF auf Anwendungsebene per coraza-spoa mit dem OWASP Core Rule Set. Das verleiht quelloffenem HAProxy die WAF-Fähigkeit, die ihm nativ fehlt, und das ohne Lizenzkosten.
Nächste Schritte:
- Überwachen Sie
/var/log/coraza-spoa/audit.logauf blockierte Angriffe und Fehlalarme - Schalten Sie nach ein bis zwei Wochen im DetectionOnly-Modus SecRuleEngine auf On
- Richten Sie eine Log-Rotation für die Audit- und Agenten-Logs ein
- Leiten Sie die Coraza-Audit-Logs zur Korrelation an Ihr SIEM (ELK, Splunk, Grafana) weiter
- Planen Sie regelmäßige OWASP-CRS-Updates und testen Sie nach jedem Upgrade erneut
- Wenn Ihnen In-Process-Performance und ML-basierte Erkennung wichtiger sind als Regeltransparenz, prüfen Sie die integrierte WAF in HAProxy Enterprise als Alternative zum SPOA-Ansatz
Fehlerbehebung
HAProxy lädt neu, aber die WAF blockiert nie etwas
Prüfen Sie, ob der Agent läuft (systemctl status coraza-spoa) und ob SecRuleEngine auf On statt auf DetectionOnly steht. Im DetectionOnly-Modus protokolliert Coraza Treffer, setzt aber nie das Deny-Urteil, sodass HAProxy nichts hat, worauf es reagieren könnte.
Angriffe im Query-String werden blockiert, Angriffe im POST-Body jedoch nicht
Das bedeutet, dass Request-Bodys Coraza nicht erreichen. Fügen Sie option http-buffer-request zum Frontend hinzu, damit HAProxy den Body puffert und req.body befüllt, bevor die Anfrage an den Agenten gesendet wird. Ohne diese Option hat SecRequestBodyAccess On keine Daten zur Inspektion. Beachten Sie, dass die gepufferte Body-Größe durch tune.bufsize (Standard 16 KB) begrenzt ist; erhöhen Sie sie maßvoll, wenn Sie größere Bodys inspizieren müssen.
Die Variable txn.coraza.action wird nie gesetzt
Der var-prefix in /etc/haproxy/coraza.cfg muss mit dem Variablennamen in Ihrer http-request-deny-ACL übereinstimmen. Ist var-prefix gleich coraza, lautet die Variable txn.coraza.action. Eine Abweichung führt dazu, dass die ACL stillschweigend nie greift.
Nach Aktivierung des Blockierens wird sämtlicher Datenverkehr abgewiesen
Üblicherweise ein Sturm von Fehlalarmen durch ein nicht justiertes CRS auf hohem Paranoia-Level. Setzen Sie SecRuleEngine zurück auf DetectionOnly, senken Sie das Paranoia-Level auf 1 und prüfen Sie das Audit-Log auf die verursachenden Regel-IDs, bevor Sie erneut aktivieren.
HAProxy lässt bei Ausfall des Agenten durch (fail open)
Mit option set-on-error error lässt HAProxy Datenverkehr durch, wenn es den Agenten nicht erreicht. Das verhindert, dass ein Absturz des Agenten Ihre Website offline nimmt, bedeutet aber auch, dass ein unbemerkter Agentenausfall den Schutz deaktiviert. Alarmieren Sie auf den Zustand des coraza-spoa-Dienstes und auf die SPOE-Fehlerzähler in den HAProxy-Statistiken.
Der Agent startet mit einem Regel-Parse-Fehler nicht
Lesen Sie die Startausgabe; sie nennt die Datei und die Zeile der ersten nicht parsbaren Regel. Häufige Ursachen sind ein fehlender Include-Pfad, ein unvollständiger CRS-Checkout oder eine coraza-spoa-Version, deren Konfigurationsschlüssel von den in dieser Anleitung genannten abweichen.
Hohe Latenz nach Aktivierung der WAF
Jede Anfrage macht nun einen Roundtrip zum Agenten. Halten Sie coraza-spoa auf demselben Host wie HAProxy (127.0.0.1), verschärfen Sie das Processing-Timeout und ziehen Sie in Betracht, SecRequestBodyLimit zu senken oder die Inspektion des Response-Bodys zu reduzieren, falls Sie sie nicht benötigen.
Häufig gestellte Fragen
Enthält quelloffenes HAProxy eine integrierte WAF?
Nein. Quelloffenes HAProxy ist ein Load Balancer und Reverse Proxy; es besitzt kein natives WAF-Modul. Es bietet ACL-basierte Filterung und Rate Limiting per Stick-Table, aber keine Angriffserkennung auf Anwendungsebene. Für eine WAF lagern Sie die Inspektion entweder über SPOP an eine externe Engine aus (zum Beispiel coraza-spoa) oder nutzen das kommerzielle HAProxy Enterprise, das eine integrierte WAF besitzt.
Was ist ein SPOA und warum braucht HAProxy ihn für eine WAF?
SPOA steht für Stream Processing Offload Agent. Es ist ein externes Programm, mit dem HAProxy über das Stream Processing Offload Protocol (SPOP) kommuniziert, um Arbeit auszulagern, die es selbst nicht leisten kann. Da quelloffenes HAProxy keine WAF-Engine besitzt, betreibt ein SPOA wie coraza-spoa die WAF in einem separaten Prozess; HAProxy sendet jede Anfrage zur Inspektion an den Agenten und setzt das zurückgegebene Urteil durch.
Kann ich das OWASP Core Rule Set mit HAProxy betreiben?
Ja. coraza-spoa betreibt die OWASP-Coraza-Engine, die vollständig mit der ModSecurity-SecLang-Regelsprache und dem OWASP CRS kompatibel ist. Ihre CRS-Regeln und beliebige eigene SecLang-Regeln funktionieren genauso wie unter ModSecurity. HAProxy Enterprise bietet zudem einen optionalen CRS-Kompatibilitätsmodus, der CRS-Regeln durch seine ML-gestützte Engine ausführt.
Sollte ich coraza-spoa oder den ModSecurity SPOA verwenden?
Für neue Bereitstellungen ist coraza-spoa die bessere Wahl. Es betreibt die aktiv entwickelte OWASP-Coraza-Engine in reinem Go, nutzt dieselben CRS-Regeln und lässt sich einfacher bauen und containerisieren. Der ältere ModSecurity SPOA bettet die ModSecurity-Engine ein und ist faktisch abgelöst. Beachten Sie, dass coraza-spoa noch Preview-Status hat, validieren Sie also die Konfigurationsformate gegen die von Ihnen installierte Version.
Wie mache ich Rate Limiting in HAProxy ohne WAF?
Verwenden Sie Stick-Tables und ACLs, die native HAProxy-Funktionen sind. Definieren Sie eine Stick-Table, die eine Metrik wie http_req_rate pro Quell-IP verfolgt, erfassen Sie Verbindungen mit http-request track-sc0 src und weisen Sie dann Anfragen ab, die einen Schwellenwert überschreiten. Das läuft im selben Prozess ohne externen Agenten und ist Ihre erste Verteidigungsebene, die die tiefere Inspektion einer WAF ergänzt.
Fügt die WAF HAProxy Latenz hinzu?
Der SPOA-Ansatz fügt für jede Anfrage einen Roundtrip von HAProxy zum Agenten hinzu, sodass es gegenüber nativer Verarbeitung einen messbaren Mehraufwand gibt. Halten Sie coraza-spoa auf demselben Host (127.0.0.1) und justieren Sie das Processing-Timeout, um ihn zu minimieren. Die integrierte WAF von HAProxy Enterprise umgeht dies, indem sie im selben Prozess läuft, was ihr wesentlicher Performance-Vorteil gegenüber dem quelloffenen SPOA-Weg ist.
Was passiert, wenn der coraza-spoa-Agent abstürzt?
Das hängt von Ihrer SPOE-Fehlerbehandlung ab. Mit option set-on-error error lässt HAProxy bei nicht erreichbarem Agenten durch (fail open) und leitet Datenverkehr weiter, was Ihre Website online hält, aber während des Ausfalls den WAF-Schutz deaktiviert. Überwachen Sie deshalb den coraza-spoa-Dienst und die SPOE-Fehlerzähler von HAProxy und alarmieren Sie auf den Zustand des Agenten, damit ein stiller Ausfall Sie nicht ungeschützt lässt.
Ähnliche Anleitungen
ModSecurity mit NGINX installieren und konfigurieren
Vollständige Anleitung zur Bereitstellung von ModSecurity 3.x mit NGINX für kostenlosen, quelloffenen WAF-Schutz mit dem OWASP Core Rule Set.
Leitfaden für WAF-Sicherheits-Best-Practices
Wesentliche Best Practices zur Konfiguration und Pflege Ihrer Web Application Firewall für optimale Sicherheit.