Plesk: Fail2Ban Jails für Nginx

Plesk liefert Nginx und Fail2Ban als optionale Module aus – aber keine Fail2Ban-Jails für Nginx. In diesem Beitrag lernen Sie wie man Nginx Logdateien in Fail2Ban berücksichtigt, und ein paare darauf abgestimmte Kniffe in WordPress.

Warum Nginx alleine nicht reicht

Wenn man ausschließlich Nginx als Webserver für WordPress (oder andere Websites) verwendet, dann ergibt sich folgendes Sicherheitsproblem: Viele Plugins kennen Nginx (noch) nicht (wie WP Cerber); andere berücksichtigen Nginx und schreiben angepasste Nginx-Regeln in eine Datei (wie iThemes Security), z.B. um angreifende IPs zu sperren. Diese Anweisungen greifen aber erst, wenn Nginx neu gestartet wird. So müsste man Nginx regelmäßig (z.B. alle 30 Minuten) neu starten – was aber dem Server / der Website bis zu 30 Minuten lang schlechter geschützt lassen würde. Das Sicherheitsplugin würde zwar die Angriffe immer wieder abfangen, aber die Last auf dem Server bliebe bei einem DoS / DDoS Angriff hoch, weil Webserver und WordPress (Plugins) jede Anfrage weiterhin abarbeiten müssten. Dieses Problem ist mit Apache weniger akut, da die aktualisierten .htaccess Dateien sofort von Apache gelesen und umgesetzt werden – WordPress wird nicht mehr erreicht (Apache aber schon).

Es gibt aber einen besseren Weg – egal welchen Webserver verwendet wird: Fail2Ban. Man braucht dafür Jails, die die Webserver-Zugriffsprotokolle (access & error logs) auswerten und angreifende IPs dann direkt in der Firewall des Betriebssystems (iptables) aussperren. Das ist viel besser, weil unerwünschte Anfragen nicht einmal mehr zum Webserver durchkommen, der Server erfährt während eines Angriffs deutlich weniger Last.

Plesk bietet Nginx und Fail2Ban als wählbare Module – letzteres mit Apache Jails aber ohne Nginx Jails – standardmäßig an; mein Vorschlag konsequenterweise auch Nginx Jails auszuliefern hat leider wenig Zuspruch gefunden.

Ich habe mich nach sorgfältiger Prüfung für WP Cerber als Sicherheits- und Anti-Spam Plugin entschieden. Eine Option des Plugins ist es, das Login (wp-login.php) zu verstecken, und direkte Aufrufe von wp-login.php sofort zu sperren. Dies ist in den von mir erstellten Jails berücksichtigt – was aber für alle Plugins gilt, die wp-login.php auf ähnlicher Weise verstecken (wie z.B. “WPS Hide Login”). Achtung: iThemes Security versteckt wp-login.php nicht bei seinem “Hide Backend” Feature, sondern fügt einen Parameter hinzu, d.h. iThemes Security kann in meiner Vorgehensweise nicht in dieser Hinsicht verwendet werden.

WordPress Login-Versuche mit falschen Daten

WP Cerber beschränkt die Anzahl ungültiger Versuche, z.B. auf 3 Versuche (konfigurierbar). Bis dahin kommt eine entsprechende Meldung für den Benutzer mit einem 403 Code (Forbidden). Nach dem letzten Fehlversuch kommt eine entsprechende Meldung (“Sie dürfen sich nicht einloggen”), ebenfalls mit Code 403. Weitere Versuche wiederholen die Ablehnung und teilen die übrige Zeit des Lockouts mit, ebenfalls mit Code 403. Aus dem Webserver-Log kann man den Sachverhalt also ersehen. WP Cerber bietet die Option, eine Custom-Datei für solche Meldungen zu benutzen – was aber unnötig ist, weil wir die Nginx-Zugriffsprotokolle ohnehin überwachen müssen.

Access.log: “POST /<hidden>/ HTTP/2.0” 403
Error.log: Kein Eintrag

Ohne WP Cerber liefert WordPress ein 200 Code bei Login-Fehlerversuchen und ein 302 Code bei erfolgreichem Login. Der Plesk-Filter “plesk-wordpress.conf” (für Apache) prüft diese Codes bei POST-Anfragen nach wp-login.php, was nicht greift wenn das Login versteckt wurde. Wenn das Login mittels WP Cerber versteckt wurde, setzt Cerber den Status 404 Not Found für Anfragen nach wp-login.php.

Access.log: “GET /wp-login.php HTTP/2.0” 404
Error.log: Kein Eintrag.

Dies soll abgefangen werden und SOFORT zu einem Ban (Firewall-Eintrag) führen.

Nginx Protokolldateien

Hier in der Annahme, dass eigene Nginx-Konfig von HTTP auf HTTPS umlenkt:

proxy_access_log

Auch wenn wie heutzutage üblich ausschließlich SSL angeboten wird, stehen hierin bei einer gut besuchten / bekannten Website einige Einträge – meist Abfragen von Bots nach robots.txt oder (seltener) Site Verification Dateien oder Favicons. Aber auch Links von „facebookexternalhit“ – kümmert sich FaceBook nicht um SSL? Ferner “/wp-cron.php?doing_wp_cron=…”. Schließlich auch manche Anfragen nach „richtigen“ Seiten.

Sämtliche Anfragen werden mit HTTP Code 301 quittiert (Moved Permanantly) – die Weiterleitung nach HTTPS. Die Überwachung dieser Datei bei einer SSL-Website scheint unnötig, schadet aber nicht (außer der Beanspruchung von Server-Leistung).

proxy_access_ssl_log

/var/www/vhosts/system/{domain}/logs/proxy_access_ssl_log

Zeilenformat (untenstehend als ein Feld pro Zeile):
Remote-IP

Remote-User
[Lokale-Datum-Uhrzeit]
“Anfrage”
HTTP-Status-Code-Antwort
Body-Bytes-gesendet
“HTTP-Referer”
“HTTP-User-Agent”

Für Fail2Ban relevante Zeilenarten:

  • Code 404 Not Found
    Alle Anfragen nach Dateien ohne Endung, die nicht im Dateisystem vorhanden sind, gehen zu WordPress /index.php. Alle anderen Anfragen nach Dateien, die nicht vorliegen, werden direkt von Nginx mit 404 quittiert und erreichen WordPress nicht. Kann WordPress bei Anfragen ohne Endung nichts damit anfangen (kann nicht als Permalink / Special Slug erkannt werden) sendet WordPress eine 301-Weiterleitung zur Startseite. Sonderfall: Anfragen nach wp-login.php, wenn das Login durch WP Cerber versteckt ist (special slug) – in dem Fall kommt ein 404 von WP Cerber. Eine IP, die innerhalb eines kurzen Zeitraums zu viele 404-Antworten verursacht, wird ausgesperrt. Ich empfehle, sonstige 404 nur dann protokollieren zu lassen, wenn man vor hat, den Code im Access Log auch zu prüfen. Ansonsten gibt es für dieselbe Anfrage im Error Log den “not found”, den man suchen kann.
  • Code 403 Forbidden
    WP Cerber setzt diesen Code, wenn er eine Anfrage für verdächtig hält – z.B. eine Login-Anfrage mit falschen Daten oder von einer bereits eingeloggten IP. Diese 403 Code kann mehrere Gründe haben und hängt stark davon ab, wie WP Cerber konfiguriert ist. Dieser Code kann auch von anderen Plugins bei fehlender Authorisierung gesetzt werden. Eine IP, die innerhalb eines kurzen Zeitraums einige wenige 403-Antworten verursacht, muss ausgesperrt werden. Meine eigene Nginx-Config setzt auch etliche Sicherheitsregeln mit “return 403”.
  • Code 400 Bad Request (Server hält Anfrage für fehlerhaft formuliert)
    Code 401 Unauthorised (von Webserver-Authentication)
    Standardantwort des Webservers wenn Authentifizierung nötig – kein Fehler.
    Hier muss man sich auf die Prüfungen des error-logs verlassen.
    Code 402 Payment required (nur selten benutzt)
    Code 405 Method not allowed (HTTP Anfrage-Typ ist nicht erlaubt)
    Code 406 Not acceptable (Accept-Headers des Browsers können nicht bedient werden)
    Code 411 Length Required
    Code 412 Precondition Failed
    Code 413 Request Entity Too Large
    Code 414 Request-URI Too Long
    Code 415 Unsupported Media Type
    Code 416 Requested Range Not Satisfiable
    Code 417 Expectation Failed
    Weitere Codes bis 431 (und alleinstehend 451) kommen in der Praxis kaum vor.
    Läuft eine Website an sich rund, so würde ich alle dieser 4xx Codes (die nicht 403 oder 404 sind) pauschal zusammenzählen und nach ein paarmal Auftreten die Client-IP sperren.
  • Fazit:
    Code 404 mit „wp-login.php“ extra behandeln und scharf einstellen.
    Sonstige Code 404‘s behandeln wir nicht, oder optional im Error Log, nicht scharf eingestellt.
    Codes 400, 402, 403, 405 usw. können zusammen geprüft und scharf darauf reagiert werden.

proxy_error_log

Hier stehen (in der Praxis):

  • directory index of … is forbidden (erscheint als 403 im access_log)
  • access forbidden by rule (erscheint als 403 im access_log**)
  • password mismatch / was not found in (erscheint evtl. als 401 im access_log)
  • No such file or directory / is not found (erscheint evtl. als 404 im access_log)
  • FastCGI sent in stderr (PHP-Fehler – i.a. kein Problem des Besuchers)

Bei Nextcloud sah ich gelegentlich auch “upstream timed out”, aber das darf man nicht bzgl. Fail2Ban auswerten.

** Aber nicht bei Anfragen ohne vhost / mit unbekanntem vhost. Daher muss dies abgefangen werden.

Fazit: proxy_access_ssl_log muss überwacht werden für die 4xx Codes, die meisten davon haben kein Pendant im Error Log. Ein Custom-Log von WP Cerber bringt nichts, weil wir den access_log wegen anderer 4xx-Fehler ohnehin überwachen müssen. Die Überwachung von proxy_error_log ist für HTTP-Auth (password mismatch / was not found) praktisch unumgänglich; dort kann optional auch nach “No such file or directory / is not found” gefahndet werden, aber erst wenn man sich vergewissert hat dass keine Website auf dem Server viele 404-Fehler selbst verursacht.

Fail2Ban Filters

So wie von Plesk konfiguriert (bzw. standardmäßig?) sind die access logs von Apache und Nginx gleich aufgebaut. Daher könnte man ggf. Filter-Ausdrücke 1:1 von den vorhandenen Apache-Jails übernehmen. Allerdings: die von Plesk bereitgestellten Jails für Apache und WordPress sind nur wenige und dazu ziemlich mau. Filterdateien sieht man an der Plesk-GUI nicht, nur deren Namen.

Sie befinden sich unter: /etc/fail2ban/filter.d

Anmerkungen zu Fail2Ban

  • Plesk liefert zum Zeitpunkt des Schreibens Fail2Ban v0.10.3.fix1 aus. Aktuell auf GitHub ist inzwischen Fail2Ban 0.11.2.
  • Es gibt anscheinend keine aktuelle Dokumentation. Das Online-Wiki wird seit 2015 bei Fail2Ban 0.9.3 nicht mehr gepflegt, auf GitHub findet man nur Issues, aber keine Doku.
  • IPv6 Handling und Überwachung des Systemd-Journals kamen danach und sind somit de facto nicht oder nur spärlich / schwer zu finden dokumentiert.
  • Auf Plesk-Debian Servern schreibt Nginx keine Einträge zum Systemd-Journal.
  • Mit man 5 jail.conf am Server CLI erhält man eine etwas jüngere Dokumentation, die z.B. prefregex erklärt.
    Fail2Ban erweitert mit eigenen Konstrukten und Aliasen die ohnehin etwas sonderbare Regex-Grammatik von Python (in der Fail2Ban geschrieben ist). Das Endergebnis ist für PHP- und JS-Regex-Hacker eine echte Herausforderung.
  • Früher war das Regex für timestamp-Erkennung fest in Fail2Ban integriert und nicht einmal dokumentiert. In späteren Versionen ist es wohl optional möglich ein Regex für die Timestamp-Parsing mit der Anweisung „datepattern“ anzugeben.
  • Aus einem Forumsbeitrag vom aktuellen Maintainer von Fail2Ban: Also note that fail2ban cuts out the date-time matching datepattern from the string before search the match for failregex.
  • Das (standardmäßig vorhandene) recidive Jail ist eine spannende Einrichtung, wobei Fail2Ban seine eigene Logdatei überwacht. Darin findet er IPs, die von verschiedenen anderen Filtern protokolliert wurden. Dies ist sehr nützlich um Angreifer zu erkennen, die z.B. mal über Postfix, mal über SSH und auch mal beim Webserver erwischt wurden, aber dort die jeweilige Schwelle (maxretrys) nicht bzw. noch nicht überschritten haben. In der Praxis ist dies die Jail, die am meisten zuschlägt. In Plesk Standardkonfiguration ist recidive ungewöhnlich streng eingestellt – Standard-Suchzeit, nur 3 Wiederholungen, und dann eine Sperre für 7 Tage. Die Praxis gibt Plesk recht – ich hatte noch nie ein Problem mit diesem Jail.

Welche Jails liefert Plesk aus für Apache, WordPress?

  • apache-auth.conf
    HTTP Authorisierungsfehler. Filter von Fail2Ban v.0.10 – sie dürfte gut funktionieren.
  • apache-badbots.conf
    Sucht nach „Bad Bots“ im User Agent Feld – mit einer Hitliste aus 2015. Das dürfte jetzt eher wenig bringen.
  • plesk-wordpress.conf
    Sucht nach gescheiterten Login-Versuchen nach WordPress-Manier. Wird gar nichts bringen wenn ein Sicherheits-Plugin wp-login.php versteckt hat.

Tja, und das war es schon – ziemlich mau.

Welche Jails sind sinnvoll für Nginx?

Entsprechend meinen obigen Recherchen:

  • nginx-404-wp-login (access logs)
    Anfragen mit „wp-login.php“ abfangen und den Host sofort sperren.
    Achtung: Dieses Jail deaktivieren bis bei ALLEN WordPress-Instanzen am Server das wp-login.php versteckt ist – sonst kann man sich gar nicht mehr einloggen.
    In der Praxis erweist sich diese Jail als die, die am häufigsten als einzelne Jail zuschlägt.
    Wichtig: Auch bei Websites die nicht auf WordPress basieren, solche Abfragen gleich in Nginx-Config abfangen – denn viele Angreifer wissen nicht, dass eine Website nicht mit WordPress gemacht wurde, und probieren es einfach.
  • nginx-403-wordpress (access logs)
    Hier werden Zugriffe, die in der Nginx-Config oder durch ein Sicherheitsplugin mit 403 Forbidden quittiert wurden, ebenfalls extra abgefangen. Auch scharf einstellen – sperren nach 2 Versuchen.
  • nginx-4xx-general (access logs)
    Die meisten 4xx Fehlercodes abfangen und den Host nach wenigen Versuchen sperren:
    400|402|403|405|406|406|411|412|413|414|415|416|417|444
  • nginx-wordpress-errlog (error logs)
    Schlägt an bei “access forbidden by rule” in Verbindung mit mehrerer gängigen WordPress-Angriffsvektoren, die dementsprechend in der Nginx-Config mit einer 403 quittiert werden sollen. Scharf einstellen, z.B. maxretry=2.
  • nginx-forbidden-by-rule (error logs)
    Fängt alle “access forbidden by rule” Meldungen ab. Daher weniger scharf einstellen, z.B. maxretry=5.
  • nginx-http-auth (error logs)
    Fehlversuche sich per HTTP-Auth zu authentifizieren abfangen und den Host nach wenigen Versuchen sperren.

Übrigens: Die manuell eingespielten Jails werden von Plesk gefunden und sind in seiner GUI verwaltbar.

Welche Logdateien sollen überwacht werden?

Nur die, die wirklich wichtig sind. Sonst habe ich beobachtet dass in der Nacht, wo “logrotate” läuft und allzu viele Logdateien umschaufelt, die von Fail2Ban überwacht werden, die CPU-Last richtung Decke schießt und sich erst über Stunden wieder beruhigt.

  • Bei Verwendung von SSL überall, die nicht-SSL Logs nicht überwachen – sie enthalten nur 301 Weiterleitungen.
  • Mit dem Befehl “ls -l /var/www/vhosts/system/*/logs/proxy_access_ssl_log” nachschauen ob es alte Domains / Subdomains gibt die nicht mehr aktiv betrieben werden und in den Zustand “Disabled” versetzt werden können. Anschließend deren alten Logdateien löschen, so dass Fail2Ban sich gar nicht mit Brachholz befassen muss.

Filter und Jails von mir erwerben

Ich hatte mit diesem Thema mehrere, streckenweise frustrierende, Manntage Arbeit. Oben habe ich genug verraten, dass Sie einen fliegenden Start haben, falls Sie die Filter und Jails selber schreiben möchten. Alternativ können Sie mir per E-Mail um die Zusendung meiner praxiserprobten Jails gegen eine Gebühr von 50€ zzgl. Mwst. bitten. Sie erhalten 7 Dateien zum Einspielen, sowie eine kurze Installationsanleitung und eine Liste von Webadressen mit weiteren relevanten Informationen.

Für eine zusätzliche Gebühr kann ich auch meine erprobten Nginx-Konfigurationsdateien bereitstellen, die mit den Jails Hand in Hand arbeiten.

Noch keine Kommentare

zu Plesk: Fail2Ban Jails für Nginx

Schreibe einen Kommentar

Bitte alle Felder ausfüllen. Deine IP-Adresse wird anonymisiert gespeichert. Fantasienamen und E-Mail Adressen (wie "donald@duck.org") werden akzeptiert.