BLOG | NGINX

Dynamisches IP-Denylisting mit NGINX Plus und fail2ban

NGINX-Teil-von-F5-horiz-schwarz-Typ-RGB
Liam Crilly Miniaturbild
Liam Crilly
Veröffentlicht am 19. September 2017
Foto: Arnold Reinhold – Eigenes Werk, CC BY‑SA 3.0

Sie sind sich dessen vielleicht nicht bewusst, aber Ihre Website ist ständigen Bedrohungen ausgesetzt. Wenn WordPress ausgeführt wird, versuchen Bots, Sie mit Spam zu belästigen. Wenn eine Anmeldeseite vorhanden ist, besteht die Gefahr von Brute-Force-Angriffen auf Passwörter. Sie können auch Suchmaschinen-Spider als unerwünschte Besucher betrachten.

Es ist keine leichte Aufgabe, Ihre Site vor unerwünschten, verdächtigen und böswilligen Aktivitäten zu schützen. Web Application Firewalls wie NGINX App Protect und die bei unseren Partnern erhältlichen NGINX Plus Certified Modules sind wirksame Tools und müssen als Teil Ihres Sicherheits-Stacks betrachtet werden. Für die meisten Umgebungen gibt es nicht zu viel Sicherheit und ein mehrschichtiger Ansatz ist ausnahmslos der effektivste.

In diesem Blogbeitrag diskutieren wir die Verwendung von fail2ban als weitere Ebene des Websicherheits-Stacks. Fail2ban ist ein Intrusion Detection System (IDS), das Protokolldateien kontinuierlich auf verdächtige Aktivitäten überwacht und dann eine oder mehrere vorkonfigurierte Aktionen ausführt. Normalerweise überwacht fail2ban fehlgeschlagene Anmeldeversuche und blockiert (sperrt) dann die betreffende IP-Adresse für einen bestimmten Zeitraum. Dies ist eine einfache, aber wirksame Verteidigung gegen Brute-Force-Passwortangriffe.

In NGINX Plus R13 haben wir einen nativen Schlüssel-Wert-Speicher und eine neue NGINX Plus-API eingeführt. Dies ermöglicht eine Fülle dynamischer Konfigurationslösungen, bei denen die Konfiguration und das Verhalten von NGINX Plus von einem externen System gesteuert werden können. In diesem Blogbeitrag zeigen wir außerdem, wie mit fail2ban NGINX Plus automatisch neu konfiguriert werden kann, sodass Anfragen von IP-Adressen ignoriert werden, die mehrere fehlgeschlagene Authentifizierungsereignisse verursacht haben.

Editor – In NGINX Plus R16 und höher können Schlüssel-Wert-Speicher über alle NGINX Plus-Instanzen in einem Cluster hinweg synchronisiert werden. (Die Statusfreigabe in einem Cluster ist auch für andere NGINX Plus-Funktionen verfügbar.) Weitere Einzelheiten finden Sie in unserem Blog und im NGINX Plus Admin Guide .

Verwenden eines Schlüsselwertspeichers für die IP-Sperrliste

Der Schlüssel-Wert-Speicher von NGINX Plus ist ein nativer In-Memory-Speicher mit drei Hauptmerkmalen:

  1. Schlüssel-Wert-Paare werden als JSON-Objekte dargestellt
  2. Schlüssel-Wert-Paare werden vollständig über eine API verwaltet
  3. Die Werte stehen NGINX Plus als reguläre Konfigurationsvariablen zur Verfügung

Der Schlüssel-Wert-Speicher wird durch die Erstellung einer gemeinsam genutzten Speicherzone definiert, die mit der Direktive keyval_zone benannt ist. Dieser Schlüssel-Wert-Speicher kann dann mit einem ersten Wertesatz gefüllt werden, indem mit der HTTP- POST- Methode ein JSON-Objekt an die API gesendet wird. Die keyval- Direktive definiert dann, welche vorhandene Variable als Suchschlüssel verwendet wird (im Beispiel $remote_addr ) und den Namen einer neuen Variable ( $num_failures ), die aus dem entsprechenden Wert dieses Schlüssels ausgewertet wird.

Konfiguration und Verwaltung des Schlüsselwertspeichers von NGINX Plus

Die API wird aktiviert, indem ein Standortblock als NGINX Plus-API-Endpunkt festgelegt wird.


server {
listen 1111;
allow 127.0.0.1; # Nur Zugriff vom lokalen Host zulassen,
alles verweigern; # und Remotezugriff verhindern.

location /api {
api write=on; # Der NGINX Plus API-Endpunkt im Lese-/Schreibmodus
}
}
keyval_zone zone=denylist:1M;
keyval $remote_addr $num_failures zone=denylist;

server {
listen 80;

location / {
root /usr/share/nginx/html;
if ($num_failures) {
return 403;
}
}
}

vim: syntax=nginx

Bevor wir Schlüssel-Wert-Paare hinzufügen, gibt eine Anforderung des Inhalts des Denylist -Speichers ein leeres JSON-Objekt zurück.

$ curl http://localhost:1111/api/6/http/keyvals/denylist {}

Der Schlüssel-Wert-Speicher kann jetzt mithilfe der HTTP- POST- Methode (in Form des Arguments -d für den Curl -Befehl) mit einem anfänglichen Schlüssel-Wert-Paar gefüllt werden, um ein neues JSON-Objekt zu übermitteln. Mehrere Schlüssel-Wert-Paare können per POST in einen leeren Schlüssel-Wert-Speicher und danach einzeln übermittelt werden.

$ curl -iX POST -d '{"10.0.0.1":"1"}' http://localhost:1111/api/6/http/keyvals/denylist HTTP/1.1 201 Erstellt …

Ein Schlüssel-Wert-Paar wird entfernt, indem der Schlüssel mit dem Wert null gepatcht wird .

$ curl -iX PATCH -d '{"10.0.0.1":null}' http://localhost:1111/api/6/http/keyvals/denylist HTTP/1.1 204 Kein Inhalt ...

Alle Schlüssel-Wert-Paare können durch Senden der DELETE -Methode aus dem Schlüssel-Wert-Speicher entfernt werden.

$ curl -iX DELETE http://localhost:1111/api/6/http/keyvals/denylist HTTP/1.1 204 Kein Inhalt ...

Eine einfache Implementierung der IP-Denylist-Auflistung kann jetzt konfiguriert werden.


server {
listen 1111;
allow 127.0.0.1; # Nur Zugriff vom lokalen Host zulassen,
alles verweigern; # und Remotezugriff verhindern.

location /api {
api write=on; # Der NGINX Plus API-Endpunkt im Lese-/Schreibmodus
}
}
keyval_zone zone=denylist:1M;
keyval $remote_addr $num_failures zone=denylist;

server {
listen 80;

location / {
root /usr/share/nginx/html;
if ($num_failures) {
return 403;
}
}
}

vim: syntax=nginx

Dieser Konfigurationsausschnitt konfiguriert Port 80 als Webserver. Zeile 18 wertet die Variable $num_failures als den Werteteil des Schlüssel-Wert-Paares in der Denylist- Shared-Memory-Zone aus, der mit dem Schlüssel übereinstimmt, der der Variable $remote_addr (Client-IP-Adresse) entspricht. Dieser Prozess der Auswertung von $num_failures lässt sich sequenziell klarer ausdrücken:

  1. Übergeben Sie den Wert von $remote_addr an den Schlüssel-Wert-Speicher der Denylist
  2. Wenn $remote_addr eine exakte Übereinstimmung mit einem Schlüssel ist, dann den Wert dieses Schlüssel-Wert-Paares abrufen
  3. Gibt den Wert als $num_failures zurück

Wenn also die Client-IP-Adresse per POST an den Schlüssel-Wert-Speicher gesendet wurde, führen alle Anfragen zu einem HTTP 403Verbotener Fehler. Der Vorteil dieses Ansatzes (im Gegensatz zur Verwendung eines Kartenblocks für denselben Zweck) besteht darin, dass die IP-Sperrliste von einem externen System gesteuert werden kann.

Verwenden von fail2ban zum dynamischen Verwalten der IP-Sperrliste

Wie oben erwähnt, wird fail2ban häufig verwendet, um verdächtige und/oder böswillige Aktivitäten in Protokolldateien zu erkennen und dann Maßnahmen zum Schutz des Systems zu ergreifen. Die Standardaktion besteht darin, iptables so zu konfigurieren, dass alle Pakete gelöscht werden, die von der protokollierten IP-Adresse stammen. Dieser Ansatz blockiert zwar wirksam böswillige Akteure, bietet ernsthaften Benutzern jedoch ein sehr schlechtes Benutzererlebnis, insbesondere wenn sie ihr Kennwort vergessen haben. Für diese Benutzer scheint es, als sei die Website einfach nicht verfügbar.

Die folgende Konfiguration beschreibt eine Fail2Ban -Aktion , die die fehlerhafte IP-Adresse an den Schlüssel-Wert-Speicher der Denylist von NGINX Plus übermittelt. NGINX Plus zeigt dann eine hilfreichere Fehlerseite an und wendet gleichzeitig eine Anforderungsratenbegrenzung auf diese IP-Adresse an, sodass der Angriff effektiv neutralisiert wird, falls die Aktionen Teil eines Angriffs sind.

Wir gehen von einer Standardinstallation von fail2ban auf demselben Host wie NGINX Plus aus, wobei sich die gesamte Konfiguration im Verzeichnis /etc/fail2ban befindet.

Die folgende Fail2ban-Aktion verwendet die NGINX Plus-API, um „gesperrte“ IP-Adressen im Schlüssel-Wert-Speicher der Denylist auf die gleiche Weise hinzuzufügen und zu entfernen wie in unserem einfachen Beispiel oben. Wir platzieren die Datei nginx-plus-denylist.conf im Verzeichnis /etc/fail2ban/action.d .


[Definition]
actionban = curl -s -o /dev/null -d '{"":""}' http://localhost:1111/api/6/http/keyvals/denylist
actionunban = curl -s -o /dev/null -X PATCH -d '{"":null}' http://localhost:1111/api/6/http/keyvals/denylist

Das folgende fail2ban -Jail aktiviert den integrierten Filter, der fehlgeschlagene Anmeldeversuche mithilfe des HTTP Basic Authentication-Moduls von NGINX erkennt und die in nginx-plus-denylist.conf definierte Aktion anwendet. Viele weitere integrierte Filter sind verfügbar und können einfach erstellt werden, um unerwünschte Aktivitäten in NGINX-Zugriffsprotokollen zu erkennen.


[STANDARD]
Bannzeit = 120
Bannaktion = nginx-plus-denylist

[nginx-http-auth]
aktiviert = wahr

Der folgende NGINX Plus-Konfigurationsausschnitt wendet die HTTP-Basisauthentifizierung auf die Standardseite „Willkommen bei NGINX“ an. Wir platzieren die Datei password_site.conf im Verzeichnis /etc/nginx/conf.d .


server {
listen 1111;
allow 127.0.0.1; # Nur Zugriff vom lokalen Host zulassen,
alles verweigern; # und Remotezugriff verhindern.

location /api {
api write=on; # Der NGINX Plus API-Endpunkt im Lese-/Schreibmodus
}
}

keyval_zone zone=denylist:1M state=denylist.json;
keyval $remote_addr $num_failures zone=denylist;

limit_req_zone $binary_remote_addr zone=20permin:10M rate=20r/m;

server {
listen 80;
root /usr/share/nginx/html;

location / {
auth_basic „geschlossene Site“;
auth_basic_user_file users.htpasswd;

if ($num_failures) {
rewrite ^.* /banned.html;
}
}

Standort = /banned.html {
limit_req zone=20permin burst=100;
}
}

vim: syntax=nginx

Zeile 11 definiert die keyval_zone wie in denylist_keyval.conf oben, jedoch mit dem zusätzlichen Parameter state , der eine Datei angibt, in der der Status des Schlüssel-Wert-Speichers gespeichert wird und somit bestehen bleibt, wenn NGINX Plus gestoppt und neu gestartet wird. Der Schlüssel-Wert-Speicher bleibt auch nach regelmäßigen Neuladungen der Konfiguration bestehen, ohne dass eine Statusdatei angegeben werden muss. (Die Zeilen 1–10 werden nicht angezeigt, sind aber dieselben wie in denylist_keyval.conf .)

Zeile 14 erstellt eine gemeinsam genutzte Speicherzone namens 20permin und gibt eine maximale Anforderungsrate von 20 Anforderungen pro Minute für jede Client-IP-Adresse an. Dieses Ratenlimit wird dann angewendet, wenn eine IP-Adresse durch fail2ban zur Deny-Liste hinzugefügt wird (Zeile 30).

Der Serverblock definiert einen Webserver, der auf dem Standardport (80) lauscht, wobei alle Anfragen dem Standort / Block (Zeile 20) zugeordnet werden. Die Root- Direktive (Zeile 18) gibt an, wo sich unsere Webinhalte befinden. In den Zeilen 21 und 22 wird angegeben, dass für diese Site eine HTTP-Basisauthentifizierung erforderlich ist und dass sich die Datenbank mit den Passwörtern für autorisierte Benutzer in der Datei users.htpasswd befindet. Die Dokumentation zur Direktive „auth_basic_user_file“ beschreibt das Format dieser Datei. Beachten Sie, dass diese Konfiguration zu Testzwecken unverschlüsselt ist und jede Produktionswebsite, die die HTTP-Basisauthentifizierung verwendet, SSL/TLS zur Transportsicherheit verwenden sollte.

Wenn die Client-IP-Adresse auf die Sperrliste gesetzt wurde (Zeilen 24–26), wird statt der HTTP-403 Fehlercode, wir schreiben die Anfrage in /banned.html um, die dann im Standortblock verarbeitet wird, der genau mit dieser URI übereinstimmt (Zeile 29). Dies gibt eine statische Webseite zurück, die erklärt, dass die IP-Adresse des Benutzers aufgrund zu vieler Anmeldefehler gesperrt wurde. Außerdem wird eine restriktive Ratenbegrenzung (Zeile 30) angewendet, die einen böswilligen Client daran hindert, unnötig Systemressourcen zu verbrauchen.

Seite, die Benutzern angezeigt wird, wenn ihre IP-Adresse auf der Sperrliste steht

(Das HTML für diese Beispielwebseite ist als forbidden.html im GitHub-Repository für diesen Beitrag verfügbar.)

Wir können aufeinanderfolgende fehlgeschlagene Anmeldeversuche und die entsprechende Fail2ban-Aktivität wie folgt simulieren.

$ curl -i http://admin:passwort@www.example.com/HTTP/1.1 401 Nicht autorisiert ...
$ curl -i http://admin:admin@www.example.com/
HTTP/1.1 401 Nicht autorisiert ...
$ curl -i http://admin:pass1234@www.example.com/
HTTP/1.1 401 Nicht autorisiert ...
$ curl -i http://admin:letmein@www.example.com/
HTTP/1.1 401 Nicht autorisiert ...
$ curl -i http://admin:fred@www.example.com/
HTTP/1.1 401 Nicht autorisiert ...
$ curl http://admin:P@ssw0rd@www.example.com/
<!DOCTYPE html>
<html>
<Kopf>
<Titel>Verboten</title>
...
$ tail -f /var/log/fail2ban.log
2017-09-15 13:55:18,903 fail2ban.filter [28498]: INFO [nginx-http-auth] Gefunden 172.16.52.1 2017-09-15 13:55:28,836 fail2ban.filter [28498]: INFO [nginx-http-auth] Gefunden 172.16.52.1 2017-09-15 13:57:49,228 fail2ban.filter [28498]: INFO [nginx-http-auth] Gefunden 172.16.52.1 2017-09-15 13:57:50,286 fail2ban.filter [28498]: INFO [nginx-http-auth] Gefunden 172.16.52.1 2017-09-15 13:57:52,015 fail2ban.filter [28498]: INFO [nginx-http-auth] Gefunden 172.16.52.1 2017-09-15 13:57:52,088 fail2ban.actions [28498]: HINWEIS [nginx-http-auth] Ban 172.16.52.1 2017-09-15 13:59:52,379 fail2ban.actions [28498]: HINWEIS [nginx-http-auth] Unban 172.16.52.1

Diese dynamische IP-Sperrlistenlösung kann jetzt ohne weitere Konfigurationsänderungen ausgeführt werden. Fail2ban überwacht die NGINX-Protokolldateien und fügt gesperrte IP-Adressen über die API dem NGINX Plus-Schlüsselwertspeicher hinzu. Nach 120 Sekunden (der in jail.local konfigurierten Sperrzeit ) wird die betreffende IP-Adresse aus der Sperrliste entfernt, wiederum über die NGINX Plus-API, und Anmeldeversuche von dieser Adresse werden wieder akzeptiert.

Die NGINX Plus-API und der Schlüssel-Wert-Speicher machen diese Art der Integration möglich. Jetzt können erweiterte Konfigurationslösungen erreicht werden, ohne dass die Erstellung von NGINX Plus-Konfigurationsdateien und das Durchführen von Neuladevorgängen erforderlich ist. Wir würden gerne erfahren, wie Sie diese Funktionen zum Erstellen Ihrer eigenen dynamischen Konfigurationslösungen verwenden. Bitte fügen Sie unten einen Kommentar hinzu.

Um NGINX Plus auszuprobieren, starten Sie noch heute Ihre kostenlose 30-Tage-Testversion oder kontaktieren Sie uns, um Ihre Anwendungsfälle zu besprechen.


„Dieser Blogbeitrag kann auf Produkte verweisen, die nicht mehr verfügbar und/oder nicht mehr unterstützt werden. Die aktuellsten Informationen zu verfügbaren F5 NGINX-Produkten und -Lösungen finden Sie in unserer NGINX-Produktfamilie . NGINX ist jetzt Teil von F5. Alle vorherigen NGINX.com-Links werden auf ähnliche NGINX-Inhalte auf F5.com umgeleitet."