Bevor Sie eine Applikation ins Internet stellen, sollten Sie einige wichtige Sicherheitsvorkehrungen treffen. Dabei erleichtert Ihnen Fail2ban die Arbeit.
Von Freunden, Bekannten und Kollegen höre ich immer öfter, dass sie eigene Webseiten wie Nextcloud, Owncloud, Gitea, Seafile oder sonstige Applikationen in den eigenen vier Wänden betreiben. Solange die Dienste sich nur per VPN-Tunnel erreichen lassen, etwa per IPsec oder Wireguard, und nicht im Internet veröffentlicht sind, halten sich die Sorgen über die Sicherheit der eigenen Daten in Grenzen.
Möchten Sie Ihre eigene Webseite jedoch in den unendlichen Weiten des Internets zugänglich machen, sollten Sie einige Vorbereitungsmaßnahmen ergreifen. Dazu gehört neben einer sicheren Serverkonfiguration selbstverständlich TLS, idealerweise mit einem richtigen Zertifikat, sodass zumindest die Übertragung integer und vor Lauschern geschützt bleibt. Für einen dauerhaften Betrieb empfehle ich dringend, noch ein Stück weiterzugehen, um auf Anomalien reagieren zu können. Fail2ban [1] versetzt Betreiber von Webseiten in die Lage, Brute-Force-Attacken beispielsweise bei Logins nicht nur zu erkennen, sondern auch wirksam zu unterbinden.
Hinter dem Programm steckt ein hostbasiertes Intrusion Prevention System [2], das, wie der Name schon andeutet, bei fehlgeschlagenen Anmeldeversuchen eine Sperre verhängt. In Python geschrieben, kommt es auf allen Posix-kompatiblen Betriebssystemen zurecht. Auf dem fraglichen System muss dazu ein Paketfilter wie Iptables laufen. Die Idee von Fail2ban ist ebenso einfach wie genial: Ein Daemon überwacht dauerhaft bestimmte Protokolldateien, die Sie selbstverständlich vorab konfigurieren müssen, und löst das dynamische Erzeugen einer Firewall-Regel aus, sobald die Kriterien eines unterliegenden Regelwerks erfüllt sind.
Im Kontext von Fail2ban bezeichnet man eine solche Kombination aus Filtern und Aktionen als Jail. Prinzipiell lässt sich ein solches Jail für jede Software einrichten, die Logdateien schreibt. Die Protokolle müssen lediglich entsprechende Hinweise auf Passwortfehleingaben und die dazugehörige IP-Adresse enthalten. Mithilfe regulärer Ausdrücke lassen sich die Logs auswerten. Das heißt umgekehrt, dass Fail2ban ausschließlich Textdateien verarbeitet und keine Binärformate.
Als Beispiel soll im Folgenden der Protokolleintrag einer Webanwendung aus Listing 1 dienen. Den verwendeten Benutzernamen und die IP-Adresse bekommt man relativ einfach per Regex heraus, indem man mit Grep die Logdatei untersucht. Ein passender regulärer Ausdruck könnte aussehen wie in Listing 2.
Listing 1
Protokolleintrag
2024-09-03 02:34:12, Logon failed, Username: ABC, Peer: 81.82.83.84
Listing 2
Regex
^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}),Logon failed, Username: ([A-Za-z0-9]+),.*Peer:\s*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$
Allerdings erweist es sich als absolut nicht trivial, passende reguläre Ausdrücke zu schreiben. Wer hier nicht aufpasst, handelt sich schnell eine Injection-Lücke ein, zum Beispiel, wenn Angreifer Usernamen wie »root, Peer: 99.88.77.66« verwenden. Nutzen Sie im Regex zu großzügige Muster, etwa ».*«, also beliebige Zeichen in beliebiger Menge, könnte es einem Angreifer gelingen, über den Benutzernamen beliebige IP-Adressen auf Sperrlisten zu bringen. Testen Sie deswegen Ihre Regexe unbedingt sehr sorgfältig.
Etwas komplizierter
Wie kompliziert so ein Regex-Muster in der Praxis aussehen kann, zeigt Listing 3. Nextcloud beispielsweise verwendet JSON als Protokollformat. Je nach Art und Weise der Auswertung stellt sich das entweder als Fluch oder als Segen heraus. Für Elasticsearch, Logstash und Kibana, kurz den ELK-Stack, eignet sich JSON sicher optimal. Für Fail2ban hingegen verkompliziert das Format die Auswertung.
Listing 3
Filter-Konfiguration für Nextcloud
[Definition]
_groupsre = (?:(?:,?\s*"\w+":(?:"[^"]+"|\w+))*)
failregex = ^\{%(_groupsre)s,?\s*"remoteAddr":"<HOST>"%(_groupsre)s,?\s*"message":"Login failed:
^\{%(_groupsre)s,?\s*"remoteAddr":"<HOST>"%(_groupsre)s,?\s*"message":"Two-factor challenge failed:
^\{%(_groupsre)s,?\s*"remoteAddr":"<HOST>"%(_groupsre)s,?\s*"message":"Trusted domain error.
datepattern = ,?\s*"time"\s*:\s*"%%Y-%%m-%%d[T ]%%H:%%M:%%S(%%z)?"
Die Definition des Filters startet in Listing 3 mit einem entsprechenden Header, wie vom INI-Format gewohnt. In der zweiten Zeile folgt die Definition eines Patterns namens »_groupsre«, das letztlich ein Schlüssel-Wert-Paar definiert. Optional beginnt es mit einem Komma, um auch Listen von Schlüssel-Wert-Paaren damit verarbeiten zu können.
Deutlich interessanter sind die Zeilen 3 bis 5, in denen aus dem Log die Werte für »remoteAddr« über die Variable »<HOST>« gespeichert werden, falls in derselben Zeile noch Zeichenketten wie »Login failed«, »Two-factor challenge failed« oder »Trusted domain error« vorkommen.
Für die korrekte Auswertung der Logs ergänze ich in Zeile 6 noch ein JSON-kompatibles Muster für die Erkennung von Zeitstempeln. Das Gesamtwerk lege ich anschließend neben Dutzenden mitgelieferten Filterdefinitionen im Verzeichnis »/etc/fail2ban/filter.d/« in der Datei »nextcloud.conf« ab.
… und Action!
Wie eingangs erwähnt, machen solche Filter nur die eine Hälfte eines Jails aus. Den zweiten Teil bilden die dazugehörigen Actions. Hier stelle ich einerseits die Schwellwerte ein, ab denen der Bann greifen soll, und andererseits, ab welcher Karenzzeit er wieder aufgehoben werden kann. Außerdem gebe ich an, wie das Ganze funktionieren soll.
Listing 4 veranschaulicht, wie eine solche Action aussehen kann. Ich verwende Debian GNU/Linux, daher kommt als Paketfilter standardmäßig Nftables zum Einsatz. Tatsächlich genügt es, das »backend« auf »auto« einzustellen (zweite Zeile) und es zu aktivieren (dritte Zeile). Daraufhin muss ich lediglich noch definieren, welche Protokolle (»TCP«) und Ports (»80,443«) die Sperre umfassen soll und welcher Filter die Aktion auslöst. In diesem Fall ist das der Filter »nextcloud«, wie in Listing 3 eingerichtet. Der Filtername entspricht dabei dem Dateinamen ohne die Erweiterung ».conf«.
Listing 4
Jail-Konfiguration für Nextcloud
[nextcloud] backend = auto enabled = true port = 80,443 protocol = tcp filter = nextcloud maxretry = 3 findtime = 86400 bantime = 3600 logpath = /var/log/nextcloud/nextcloud.log
Interessant wird es ab Zeile 7: Hier hinterlege ich, dass bereits drei Fehlanmeldungen zu einem Bann von einer Stunde Dauer führen (Zeile 9). Den zeitlichen Rahmen, innerhalb dessen Fail2ban nach Fehlanmeldungen sucht, richtet der Code aus Zeile 8 ein. Die »findtime« bezeichnet die Zeitspanne, über die die Anzahl der Fehlanmeldungen aufsummiert wird. Das heißt summa summarum, dass eine einstündige (3600 Sekunden) Sperre per Firewall beginnt, wenn innerhalb eines Tags (86 400 Sekunden) drei Fehlanmeldungen vorliegen. Das entsprechende Logfile, das Fail2ban dazu auswertet, definiere ich in der letzten Zeile.
Bei »/var/log/nextcloud/« handelt es sich übrigens nicht um das Verzeichnis für diese Logdatei. Wohin Nextcloud loggen soll, legen Sie in der Datei »config/config.php« innerhalb Ihres Nextcloud-Verzeichnisses über den Parameter »logfile« fest.
Fail2ban steuern
Um die Arbeit mit Fail2ban zu erleichtern, gibt es neben dem Daemon noch einen Client. Über das Werkzeug Fail2ban-client lässt sich nicht nur der Server steuern, sondern auch das Paketfilter-Backend. Über den Client frage ich mithilfe des Kommandos »fail2ban-client status« ab, welche Jails momentan aktiv sind (Abbildung 1). Die derzeit gesperrten IP-Adressen lasse ich mir ebenfalls per Client ausgeben. Das funktioniert analog zur Statusanzeige über den Befehl »fail2ban-client banned« (Abbildung 2).
Wer IP-Adressen bannt, kommt früher oder später auch mit False Positives in Berührung, also mit ungerechtfertigten Sperren. Deshalb sollten Sie wissen, wie das Aufheben eines Banns vonstattengeht: Das Kommando »fail2ban-client unban IP« genügt, um eine zu Unrecht gebannte IP-Adresse flugs zu entsperren (Abbildung 3).
Eigenentwicklungen
Falls Sie jetzt Blut geleckt haben und selbst Filter für eigene Applikationen erzeugen möchten, rate ich Ihnen zu Fail2ban-regex. Das Tool greift Ihnen gehörig dabei unter die Arme, passende reguläre Ausdrücke auf Logs zu entwickeln und zu testen. Die Syntax ist simpel gehalten. Der Aufruf »fail2ban-regex Log Regex« wendet das Pattern »Regex« auf das Log »Log« an. Dabei kann wie in Abbildung 4 als Log auch eine einzelne Protokollzeile dienen.
Fazit
Als Bollwerk gegen unerwünschte Eindringlinge macht Fail2ban gerade in kleineren Umgebungen ohne umfangreiche IDS/IPS-Setups eine hervorragende Figur. Entsprechend gut geschriebene Filter fangen lästige Passwort-Cracker ab und geben ein gutes Gefühl. Dass Fail2ban sich durch seine Arbeitsweise, Logs auszuwerten, nahezu universell einsetzen lässt, qualifiziert es zu einem absolut unverzichtbaren Helfer – zumindest für mich. (csi)
Infos
- Fail2ban: https://github.com/fail2ban/fail2ban
- Developers Documentation: https://fail2ban.readthedocs.io/en/latest/index.html







