So begrenzen Sie die Häufigkeit von Anforderungen in HAProxy: Schritt-für-Schritt-Anleitung


Der Autor erklärt, wie die Geschwindigkeitsbegrenzung (Ratenbegrenzung) für HAProxy- Abfragen mit bestimmten IP-Adressen implementiert wird . Das Mail.ru Cloud Solutions- Team hat seinen Artikel übersetzt - wir hoffen, dass Sie damit nicht so viel Zeit und Mühe darauf verwenden müssen, wie Sie es aufwenden mussten.

Tatsache ist, dass dies eine der beliebtesten Methoden zum Schutz eines Servers vor DoS-Angriffen ist, es jedoch schwierig ist, im Internet klare Anweisungen zur spezifischen Konfiguration zu finden. Durch Versuch und Irrtum zwang der Autor HAProxy, die Häufigkeit von Anforderungen in einer Liste von IP-Adressen zu begrenzen, die in Echtzeit aktualisiert wird.

Für die Konfiguration von HAProxy sind keine Vorkenntnisse erforderlich, da alle erforderlichen Schritte im Folgenden beschrieben werden.

Open Source und kostenlos HAProxy ist ein leicht zugänglicher Load Balancer und Proxy-Server. In den letzten Jahren ist es sehr beliebt geworden, weil es eine hohe Leistung mit einem Minimum an Ressourcen bietet. Im Gegensatz zu alternativen Programmen bietet die gemeinnützige Version von HAProxy Community Edition genügend Funktionen für einen zuverlässigen Lastausgleich.

Dieses Programm ist zunächst ziemlich schwer herauszufinden. Sie verfügt jedoch über eine sehr sorgfältige und detaillierte technische Dokumentation . Der Autor sagt, dass dies die detaillierteste Dokumentation unter allen Open-Source-Programmen ist, die er jemals verwendet hat.
Hier ist eine Schritt-für-Schritt-Anleitung.

Konfigurieren eines Load Balancers


Um Zeit zu sparen und sich nicht von der Einrichtung der Infrastruktur ablenken zu lassen, nehmen Sie die Docker- und Docker Compose- Images und starten Sie die Hauptkomponenten schnell.

Die erste Aufgabe besteht darin, die Arbeitsinstanz des HAProxy-Load-Balancers mit mehreren Apache-Backend-Servern zu erhöhen.

Klonen Sie das Repository


$ git clone git@github.com:stargazer/haproxy-ratelimiter.git
$ cd haproxy-ratelimiter

Sie können Installationsparameter anzeigen Dockerfileund docker-compose.ymlanzeigen. Ihre Diskussion würde den Rahmen dieses Artikels sprengen. Lassen Sie uns daher auf die Tatsache eingehen, dass sie eine funktionierende HAProxy-Instanz namens loadbalancerzwei Backend-Server api01und erstellt haben api02. Um HAProxy zu konfigurieren, verwenden wir zunächst die Datei haproxy-basic.cfgund wechseln dann zu haproxy-ratelimiting.cfg.

Der Einfachheit halber wurde die Konfigurationsdatei haproxy-basic.cfgauf das Nötigste reduziert und von Überschüssen befreit. Schauen wir es uns an:

defaults
mode http
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms

frontend proxy
bind *:80

use_backend api

backend api
balance roundrobin

server api01 api01:80
server api02 api02:80

In diesem Abschnitt wird festgelegt frontend proxy, dass HAProxy Port 80 überwacht und alle Anforderungen an den Serverpool apiim Backend weiterleitet .

KATEGORIE backend apigibt an Backend - Pool apimit zwei Back - End - Servern api01und api02und die entsprechenden Adressen. Der Server für die Bearbeitung jeder eingehenden Anforderung wird vom Lastausgleichsalgorithmus ausgewählt roundrobin, dh die beiden verfügbaren Server werden nacheinander verwendet.

Lassen Sie uns alle drei unserer Container starten


$ sudo docker-compose up

Jetzt haben wir einen Container loadbalancer, der Anforderungen an zwei Backends api01und Server umleitet api02. Wir werden eine Antwort von einem von ihnen erhalten, wenn wir in die Adressleiste eingeben http://localhost/.

Es ist interessant, die Seite mehrmals zu aktualisieren und die Protokolle anzuzeigen docker-compose.

api01_1 | 192.168.192.3 - - [08 / Jan / 2019: 11: 38: 09 +0000] "GET / HTTP / 1.1" 200 45
api02_1 | 192.168.192.3 - - [08 / Jan / 2019: 11: 38: 10 +0000] "GET / HTTP / 1.1" 304 -
api01_1 | 192.168.192.3 - - [08 / Jan / 2019: 11: 38: 10 +0000] "GET / HTTP / 1.1" 304 -
api02_1 | 192.168.192.3 - - [08 / Jan / 2019: 11: 38: 11 +0000] "GET / HTTP / 1.1" 304 -
api01_1 | 192.168.192.3 - - [08 / Jan / 2019: 11: 38: 11 +0000] "GET / HTTP / 1.1" 304 -
api02_1 | 192.168.192.3 - - [08 / Jan / 2019: 11: 38: 11 +0000] "GET / HTTP / 1.1" 304 -

Wie Sie sehen können, verarbeiten zwei Server apinacheinander Anforderungen.

Wir haben jetzt eine HAProxy-Instanz mit einer sehr einfachen Lastausgleichskonfiguration und wir haben eine Vorstellung davon, wie es funktioniert.

Fügen Sie ein Limit für die Anzahl der Anforderungen hinzu


Um die Anzahl der Anforderungen an den Load Balancer zu begrenzen, müssen Sie die Konfigurationsdatei in der HAProxy-Instanz ändern. Stellen Sie sicher, dass der Container loadbalancerdie Konfigurationsdatei verwendet haproxy-ratelimiter.cfg.

Ändern Sie einfach die Docker-Datei, um die Konfigurationsdatei zu ersetzen.

FROM haproxy:1.7
COPY haproxy-ratelimiter.cfg /usr/local/etc/haproxy/haproxy.cfg

Grenzen setzen


Alle Einstellungen werden in der Konfigurationsdatei registriert haproxy-ratelimiter.cfg. Lass es uns sorgfältig studieren.

defaults
mode http
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms

frontend proxy
bind *:80

# ACL function declarations
acl is_abuse src_http_req_rate(Abuse) ge 10
acl inc_abuse_cnt src_inc_gpc0(Abuse) gt 0
acl abuse_cnt src_get_gpc0(Abuse) gt 0

# Rules
tcp-request connection track-sc0 src table Abuse
tcp-request connection reject if abuse_cnt
http-request deny if abuse_cnt
http-request deny if is_abuse inc_abuse_cnt

use_backend api
backend api
balance roundrobin

server api01 api01:80
server api02 api02:80

backend Abuse
stick-table type ip size 100K expire 30m store gpc0,http_req_rate(10s)

HAProxy bietet eine Reihe von Grundelementen auf niedriger Ebene, die mehr Flexibilität bieten und für eine Vielzahl von Anwendungsfällen geeignet sind. Seine Zähler erinnern mich oft an das akkumulative Register (Addierer) in der CPU. Sie speichern Zwischenergebnisse, verwenden unterschiedliche Semantiken als Eingabe, aber am Ende sind sie nur Zahlen. Um alles gut zu verstehen, ist es sinnvoll, am Ende der Konfigurationsdatei zu beginnen.

Tabelle Abuse


backend Abuse
stick-table type ip size 100K expire 30m store gpc0,http_req_rate(10s)


Hier haben wir ein Dummy-Backend namens Abuse("Missbrauch") eingerichtet. Fiktiv, weil es nur für Stick-Tabellen verwendet wird, auf die sich der Rest der Konfiguration mit Namen beziehen kann Abuse. Eine Stick-Tabelle ist eine im Prozessspeicher gespeicherte Tabelle, in der Sie für jeden Datensatz die Lebensdauer bestimmen können.

Unsere Tabelle hat folgende Eigenschaften:

  • type ip: Anfragen werden in der Tabelle nach IP-Adresse als Schlüssel gespeichert. Anfragen von derselben IP-Adresse beziehen sich daher auf denselben Datensatz. Dies bedeutet im Wesentlichen, dass wir IP-Adressen und zugehörige Daten verfolgen.
  • size 100K: Die Tabelle enthält maximal 100.000 Datensätze.
  • expire 30m: Die Aufbewahrungsdauer für Datensätze beträgt 30 Minuten Inaktivität.
  • store gpc0,http_req_rate(10s): Der Zähler gpc0und die Anzahl der IP-Adressanforderungen für die letzten 10 Sekunden werden mit Einträgen gespeichert . Mithilfe der Hilfe können gpc0wir verfolgen, wie oft die IP-Adresse bei Missbrauch bemerkt wird. Tatsächlich bedeutet ein positiver Zählerwert, dass die IP-Adresse bereits als verdächtig markiert ist. Nennen wir diesen Zähler abuse indicator.

Im Allgemeinen Abusekönnen Sie anhand der Tabelle verfolgen, ob die IP-Adresse als verdächtig markiert ist und wie häufig Anfragen von dieser Adresse aktuell sind. Daher haben wir eine Historie von Aufzeichnungen sowie Echtzeitinformationen.

Fahren wir nun mit dem Abschnitt fort frontend proxyund sehen, was dort neu ist.

ACL-Funktionen und -Regeln


frontend proxy
bind *:80

# ACL function declarations
acl is_abuse src_http_req_rate(Abuse) ge 10
acl inc_abuse_cnt src_inc_gpc0(Abuse) gt 0
acl abuse_cnt src_get_gpc0(Abuse) gt 0

# Rules
tcp-request connection track-sc0 src table Abuse
tcp-request connection reject if abuse_cnt
http-request deny if abuse_cnt
http-request deny if is_abuse inc_abuse_cnt

use_backend api

Zugriffssteuerungslisten (Access Control Lists, ACLs) sind Funktionsdeklarationen, die nur aufgerufen werden, wenn die Regel festgelegt ist.

Schauen wir uns alle drei ACL-Einträge genauer an. Beachten Sie, dass alle explizit auf eine Tabelle verweisen Abuse, in der IP-Adressen als Schlüssel verwendet werden. Daher gilt jede Funktion für die Anforderungs-IP-Adresse:

  • acl is_abuse src_http_req_rate(Abuse) ge 10: Die Funktion wird is_abusezurückgegeben, Truewenn die aktuelle Anforderungsfrequenz größer oder gleich zehn ist.
  • acl inc_abuse_cnt src_inc_gpc0(Abuse) gt 0: Die Funktion wird inc_abuse_cntzurückgegeben, Truewenn der Inkrementwert gpc0größer als Null ist. Da der Anfangswert gpc0Null ist, gibt diese Funktion immer zurück True. Mit anderen Worten, es erhöht den Wert abuse indicatorund signalisiert im Wesentlichen Missbrauch von dieser IP-Adresse.
  • acl abuse_cnt src_get_gpc0(Abuse) gt 0: Die Funktion wird abuse_cntzurückgegeben, Truewenn der Wert gpc0größer als Null ist. Mit anderen Worten, er sagt, wenn diese IP-Adresse bereits bei Missbrauch entdeckt wurde.

Wie bereits erwähnt, sind ACLs einfach Deklarationen, dh Funktionsdeklarationen. Sie gelten nicht für eingehende Anforderungen, bis eine Regel ausgelöst wird.

Es ist sinnvoll, die im selben Abschnitt definierten Regeln zu betrachten frontend. Die Regeln werden nacheinander auf jede eingehende Anforderung angewendet - und führen die Funktionen über die soeben definierte ACL aus.

Mal sehen, was jede Regel tut:

  • tcp-request connection track-sc0 src table Abuse: Fügt der Tabelle eine Abfrage hinzu Abuse. Da der Schlüssel die IP-Adresse in der Tabelle ist, wird diese Regel zur Liste der IP-Adressen hinzugefügt.
  • tcp-request connection reject if abuse_cnt: TCP-, IP- , abuse. , TCP- IP-.
  • http-request deny if abuse_cnt: , IP- . IP-, abuse.
  • http-request deny if is_abuse inc_abuse_cnt: , is_abuse inc_abuse_cnt True. , , IP- , IP- .

Im Wesentlichen führen wir zwei Arten von Überprüfungen ein: in Echtzeit und in der Blacklist aus dem Abfrageverlauf. Die zweite Regel lehnt alle neuen TCP-Verbindungen ab, wenn die IP-Adresse bei Missbrauch festgestellt wurde. Die dritte Regel verbietet den Dienst von HTTP-Anforderungen für eine IP-Adresse aus der schwarzen Liste, unabhängig von der aktuellen Häufigkeit von Anforderungen von dieser Adresse. Die vierte Regel stellt sicher, dass HTTP-Anforderungen von einer IP-Adresse sofort abgelehnt werden, sobald der Schwellenwert für die Anforderungshäufigkeit überschritten wurde. Daher funktioniert die zweite Regel hauptsächlich bei neuen TCP-Verbindungen, die dritte und vierte bei bereits eingerichteten Verbindungen, wobei die erste eine historische Prüfung und die zweite eine Echtzeitprüfung ist.

Probieren wir den Filter in Aktion aus!


Jetzt können wir unsere Container wieder zusammenbauen und starten.

$ sudo docker-compose down
$ sudo docker-compose build
$ sudo docker-compose up

Der Load Balancer sollte vor den beiden Backend-Servern gestartet werden.

Leiten wir unseren Browser an http://localhost/. Wenn wir die Seite ein Dutzend Mal schnell aktualisieren, überschreiten wir den Schwellenwert von zehn Anfragen in einem Intervall von zehn Sekunden - und unsere Anfragen werden abgelehnt. Wenn wir die Seite weiterhin aktualisieren, werden neue Anforderungen sofort abgelehnt - noch bevor die TCP-Verbindung hergestellt wurde.

Fragen


Warum sind maximal zehn Anfragen pro zehn Sekunden zulässig?


Die Tabelle Abusebestimmt http_req_rate(10s), dh die Häufigkeit von Anforderungen wird in einem Fenster von zehn Sekunden gemessen. Eine Funktion is_abuseaus der ACL wird Truemit einer Anforderungsrate von ≥ 10 innerhalb des angegebenen Intervalls zurückgegeben. Missbrauch wird daher als Häufigkeit von Anfragen von zehn oder mehr Anfragen in zehn Sekunden angesehen.

In diesem Artikel haben wir beispielsweise beschlossen, eine Untergrenze festzulegen, um die Überprüfung der Funktion des Begrenzers zu vereinfachen.

Was ist der Unterschied zwischen den Verbindungsregeln für http-Anfragen und TCP-Anfragen?


Aus der Dokumentation :

http-request: Die http-request-Anweisung definiert eine Reihe von Regeln, die auf der Netzwerkebene 7 gelten. [OSI-Modell]

Aus der Dokumentation :
TCP-Anforderungsverbindung: Ausführen einer Aktion für eine eingehende Verbindung in Abhängigkeit von einer Bedingung auf der Netzwerkschicht 4

HTTP-, TCP-?


Stellen Sie sich vor, HTTP-Anforderungen an den Server senden mehrere TCP-Verbindungen von derselben IP-Adresse. Die Häufigkeit von HTTP-Anforderungen überschreitet schnell die Schwellenwerte. Dann tritt die vierte Regel in Kraft, die Anforderungen verwirft und die IP-Adresse auf die schwarze Liste setzt.

Jetzt ist es durchaus möglich, dass HTTP-Verbindungen von derselben IP-Adresse offen bleiben (siehe dauerhafte HTTP-Verbindung ) und die Häufigkeit von HTTP-Anforderungen unter den Schwellenwert gefallen ist. Die dritte Regel garantiert das fortgesetzte Blockieren von HTTP-Anforderungen, da diese abuse indicatorauf dieser IP ausgelöst werden.

Angenommen, nach einigen Minuten versucht dieselbe IP, TCP-Verbindungen herzustellen. Sie werden sofort gelöscht, da die zweite Regel gilt: Sie sieht die gekennzeichnete IP-Adresse und trennt die Verbindung sofort.

Fazit


Zunächst kann es schwierig sein, die Begrenzung der Verarbeitungsgeschwindigkeit von Anforderungen mit HAProxy zu verstehen. Um alles richtig zu machen, benötigen Sie ein ziemlich "niedriges" Denken und eine Reihe nicht intuitiver Aktionen. Die Dokumentation in diesem Teil ist wahrscheinlich zu technisch und leidet unter dem Fehlen grundlegender Beispiele. Wir hoffen, dass dieser Leitfaden das Manko ausgleicht und allen, die diesen Weg einschlagen möchten, die Richtung zeigt.

Was noch zu lesen :

  1. Wie fehlertolerante Architektur in der Mail.ru Cloud Solutions-Plattform implementiert wird .
  2. Top 10 Kubernetes Tricks und Tipps .
  3. Unser Telegrammkanal zur digitalen Transformation .

All Articles