vielmehr, der default Teil
[DEFAULT]
bantime.increment = true
bantime.rndtime = 30m
bantime.maxtime = 60d
bantime.factor = 2
bantime.formula = ban.Time * math.exp(float(ban.Count+1)*banFactor)/math.exp(1*banFactor)
bantime.overalljails = true
usedns = no
ignoreip = 127.0.0.1 server-ip meine-ip
jail.conf
- Standard-Einstellungen erklärtIn der jail.conf
von Fail2ban gibt es eine Sektion [DEFAULT]
, die allgemeine Einstellungen definiert, die für alle Jails gelten, sofern sie nicht spezifisch überschrieben werden. Hier sind einige der wichtigsten Konfigurationsoptionen:
Diese Einstellung aktiviert die inkrementelle Sperrzeit. Wenn ein Client wiederholt gegen die Regeln verstößt, wird die Sperrzeit jedes Mal erhöht.
Diese Option fügt eine zufällige Zeitspanne zur Sperrzeit hinzu, um zu verhindern, dass Angreifer die genaue Dauer der Sperre vorhersagen können. In diesem Fall beträgt die zufällige Zeitspanne bis zu 30 Minuten.
Dies ist die maximale Dauer, für die eine IP gesperrt werden kann. Hier ist die Obergrenze auf 60 Tage festgelegt.
Dieser Faktor bestimmt, wie stark die Sperrzeit bei wiederholten Verstößen ansteigt. Mit einem Faktor von 2
verdoppelt sich die Sperrzeit jedes Mal.
Die Formel zur Berechnung der Sperrzeit lautet:
ban.Time * math.exp(float(ban.Count+1)banFactor)/math.exp(1banFactor)
Diese komplexe Berechnung sorgt dafür, dass die Sperrzeit exponentiell ansteigt, je öfter ein Angreifer versucht, gegen die Regeln zu verstoßen.
Wenn diese Einstellung aktiviert ist, werden Sperrzeiten global über alle Jails hinweg summiert.
Das bedeutet, dass ein Angreifer, der in verschiedenen Jails auffällig wird, schneller längere Sperrzeiten ansammelt.
Mit dieser Einstellung wird festgelegt, ob DNS-Abfragen verwendet werden, um die IP-Adressen in Hostnamen aufzulösen. Mit no
werden DNS-Abfragen deaktiviert, was die Erkennung beschleunigt und die nervigen WARNING Determined IP using DNS Lookup:
Meldungen verhindert.
Hier werden IP-Adressen aufgelistet, die von den Sperrmaßnahmen ausgenommen sind. Standardmäßig ist die lokale Adresse 127.0.0.1
und möglicherweise auch die IP-Adresse des Servers und die eigene IP (server-ip
, meine-ip
) eingetragen. Diese IPs werden niemals gesperrt. Schlau ist, wenn man seine eigene IP auch aktualisiert.
(💡 Vielleicht wäre das auch eine Aufgabe für ein Script 💡)
Diese Einstellungen bieten eine flexible und zugleich robuste Konfiguration für die Verwaltung von Sperrzeiten und der DNS-Nutzung in Fail2ban.
Durch die Verwendung von inkrementellen Sperrzeiten und der Möglichkeit, bestimmte IPs auszuschließen, kann Fail2ban effizient auf wiederholte Angriffe reagieren, während legitime Benutzer ungestört bleiben.
Zum Realtime Check habe ich das Script remaining_ban_time geschrieben.
Am Beispiel Wordpress
versuche ich zu beschreiben, wie ich einen Filter baue und teste.
Zuerst mache ich mir einen Ausschnitt des Logfiles, weil das meisst viel zu groß ist und man den Wald vor lauter Bäumen nicht sieht.
grep wp-login /var/log/apache2/other_vhosts_access.log > /var/log/apache2/other_vhosts_access.log-trunc
Das Log /var/log/apache2/other_vhosts_access.log-trunc
betrachte ich dann mit lnav. Man kann jedoch auch ein beliebiges Log-Viewer-Tool verwenden.
Da ich kein Regex-Experte bin, taste ich mich langsam an die Regex heran.
Zum Testen nutze ich fail2ban-regex
:
fail2ban-regex /var/log/apache2/other_vhosts_access.log-trunc /etc/fail2ban/filter.d/wordpress.conf --print-all-missed | lnav
Die Vorteile sind:
Ich teste mit der Original filter.d/wordpress.conf
, um Copy&Paste-Fehler zu vermeiden und kann Feinabstimmungen vornehmen, ohne ständig fail2ban-client reload auszuführen.
Da der Testdatensatz unverändert ist, sind Erfolg und Misserfolg sehr deutlich zu erkennen.
Update zu Wordpress jails:
Es gibt auch plugins für wordpress, die einem die Arbeit erheblich erleichtern und extra Tokens ins weblog einbauen.
Zum Beispiel wp-fail2ban
In dem plugin sind 3 Beispiel Jails drin, die einem die Arbeit abnehmen, selber was zu basteln.
[wordpress-hard]
enabled = true
port = http,https
filter = wordpress-hard
logpath = /var/log/apache2/other_vhosts_access.log
bantime = 1h
maxretry = 1
filter.d/custom-postgresql.conf
[Definition]
failregex = ^<HOST>.+FATAL: password authentication failed for user.+$
^<HOST>.+FATAL: no pg_hba.conf entry for host .+$
ignoreregex = duration:#
jail.local:
[custom-postgresql]
port =5432
logtimezone = UTC+0200
logpath = /var/log/postgresql/postgresql-13-main.log
maxretry = 3
enabled = true
filter.d/wordpress.conf
[Definition]
failregex = ^.* <HOST> - - \[.*\] "(POST) /.*wp-login\.php.* HTTP/1\.[01]" (200|301|302|404)
ignoreregex =
jail.local:
[wordpress]
enabled = true
port = http,https
logpath = /var/log/apache2/other_vhosts_access.log
filter = wordpress
maxretry = 6
findtime = 600
bantime = 3600
filter.d/wp-json.conf
[Definition]
failregex = ^.* <HOST> - - \[.*\] "(POST|GET) /wp-json/.* HTTP/1\.[01]" (200|301|302|404)
ignoreregex = ^.* <HOST> - - \[.*\] "GET /wp-json/real-queue.* HTTP/1\.[01]" (200|301|302|404)
^.* <HOST> - - \[.*\] "GET /wp-json/wp/.* HTTP/1\.[01]" (200|301|302|404)
jail.local:
[wp-json]
enabled = true
port = http,https
logpath = /var/log/apache2/other_vhosts_access.log
filter = wp-json
maxretry = 6
findtime = 200
bantime = 600
Diese Regex ist darauf ausgelegt, eine Vielzahl von SQL-Injection-Mustern in den Logdateien zu erkennen, indem sie spezifische SQL-Keywords und Muster sucht, die häufig bei Angriffen verwendet werden. Sie ist darauf ausgelegt, sowohl kodierte als auch nicht-kodierte SQL-Injection-Versuche zu erfassen, indem sie die URL-Parameter und SQL-spezifische Schlüsselwörter überprüft. [1]
[Definition]
failregex = ^<HOST> - - \[.*\] "GET /.*(\?|&)(option|view|id|Itemid|url_id|entry_id|ivVT|.*)=.*(SELECT|UNION|CONCAT|AND\s*1=1|OR\s*1=1|INFORMATION_SCHEMA|xp_cmdshell|FLOOR\(RAND\(\)|'%%27|%%22|--).*HTTP/.*"$
# Erkennt jetzt auch diese exit dingsbumse
#
ignoreregex =
jail.local
[sql-injection]
enabled = true
port = http,https
filter = sql-injection
logpath = /var/log/apache2/other_vhosts_access.log
maxretry = 1
bantime = 1d
findtime = 12h
filter.d/apache-label
Spezialfall für einen confluence Kunden
Scheinbar ein Bot Netz macht massenhaft Zugriffe auf Seiten, die normalerweise nicht direkt aufgerufen werden.
[Definition].
failregex = ^.* <HOST> - - \[.*\] "GET /label/.* HTTP/1\.[01]" (200|301|302|404)
# work in progress
jail.local:
[apache-label]
enabled = true
port = http,https
filter = apache-label
logpath = /var/log/apache2/other_vhosts_access.log
maxretry = 1
bantime = 3600
Es geht um die bots, die versuche .git .env u.ä. zu finden, also definitiv keinn echter userzugriff
[Definition]
failregex = ^.* <HOST> - - \[.*\] "GET /(\.git|\.env)/.* HTTP/[0-9\.]+"
ignoreregex =
jail.local
[bot-dev]
enabled = true
filter = bot-dev
logpath = /var/log/apache2/other_vhosts_access.log
port = http,https
bantime = 1d
findtime = 3h
maxretry = 1
Einträge vorzeitig löschen
fail2ban-client -i status wordpress
Status for the jail: wordpress
|- Filter
| |- Currently failed: 6
| |- Total failed: 18
| `- File list: /var/log/apache2/other_vhosts_access.log
`- Actions
|- Currently banned: 2
|- Total banned: 2
`- Banned IP list: 167.99.56.219 91.8.87.65
Fail2Ban v1.0.2 reads log file that contains password failure report
and bans the corresponding IP addresses using firewall rules.
fail2ban> set wordpress unbanip 167.99.56.219
1
fail2ban> set wordpress unbanip 91.8.87.65
1
fail2ban> quit
fail2ban-client set wp-json unbanip IP
Oder
fail2ban-client unban <IP> ... <IP>
hebt die Sperre von
fail2ban-client reload --unban apache-label
Ergebnis:
fail2ban-client status apache-label|grep -A2 Actions
`- Actions
|- Currently banned: 0
|- Total banned: 2157
Möglichkeit, IPs manuell zur Sperrliste in Fail2Ban hinzufügen, die nach eines bestimmten Zeitraums (aber länger als die übliche Zeit) wieder freigegeben werden.
Dazu brauche ich ein minimales "notfall" Jail
jail.local:
[notfall]
enabled = true
bantime = 10h # <== Üblich sind 3600 = 1 Stunde, hier 10 Stunden
Und damit fail2ban zufrieden ist, auch einen
filter.d/notfall.conf
[Definition] # <== Muss drin sein
Anwendung:
CLI Command | Zweck |
---|---|
fail2ban-client reload |
Jail aktivieren |
fail2ban-client set notfall banip IP |
IP sperren |
fail2ban-client status notfall |
Kontrolle |
fail2ban-client set notfall unbanip IP |
IP entsperren |
fail2ban-client status notfall |
Kontrolle |
recidive
[recidive]
enabled = true
bantime = 5w
findtime = 1d
Ich habe das im Folgenden beschriebenen Phänomen etliche Zeit mit Untersuchungen, Tests und Experimente gemacht, bis ich das Problem eingrenzen und sehr einfach beseitigen konnte.
Es gab sehr nervige Meldungen in /var/log/fail2ban.log
2024-08-17 08:55:06,407 fail2ban.filter [502678]: INFO [apache-wplogin2] Found de:443 - 2024-08-17 08:55:06
2024-08-17 08:55:06,408 fail2ban.observer [502678]: INFO [apache-wplogin2] Found de:443, bad - 2024-08-17 08:55:06, 1 # -> 2.0
2024-08-17 08:55:06,851 fail2ban.actions [502678]: WARNING [apache-wplogin2] de:443 already banned
2024-08-17 08:55:25,842 fail2ban.filter [502678]: INFO [apache-wplogin2] Found de:443 - 2024-08-17 08:55:25
2024-08-17 08:55:25,842 fail2ban.observer [502678]: INFO [apache-wplogin2] Found de:443, bad - 2024-08-17 08:55:25, 1 # -> 2.0
2024-08-17 08:56:32,090 fail2ban.actions [502678]: NOTICE [apache-wplogin2] Unban de:443
2024-08-17 08:56:53,588 fail2ban.transmitter [502678]: ERROR Command ['set', 'apache-wplogin2', 'unbanip', 'de:443'] has failed. Received UnknownJailException('apache-wplogin2')
2024-08-17 08:56:57,684 fail2ban.transmitter [502678]: ERROR Command ['set', 'apache-wplogin2', 'unbanip', 'de:443'] has failed. Received UnknownJailException('apache-wplogin2')
2024-08-17 16:57:49,482 fail2ban.ipdns [502678]: WARNING Unable to find a corresponding IP address for de:443: [Errno -2] Name or service not known
2024-08-17 16:59:29,301 fail2ban.transmitter [502678]: ERROR Command ['set', 'apache-wplogin2', 'unbanip', 'de:443'] has failed. Received UnknownJailException('apache-wplogin2')
2024-08-17 17:00:01,993 fail2ban.filter [502678]: INFO [apache-wplogin2] Found de:443 - 2024-08-17 16:57:03
2024-08-17 17:00:02,015 fail2ban.observer [502678]: INFO [apache-wplogin2] Found de:443, bad - 2024-08-17 16:57:03, 1 # -> 2.0
2024-08-17 17:00:18,590 fail2ban.ipdns [502678]: WARNING Unable to find a corresponding IP address for de:443: [Errno -2] Name or service not known
2024-08-17 17:01:01,443 fail2ban.transmitter [502678]: ERROR Command ['set', 'apache-wplogin2', 'unbanip', 'de:443'] has failed. Received UnknownJailException('apache-wplogin2')
Was ich auch versuchte, ich kam zu keiner Lösung.
de:443
trat sehr oft auf, diese PseudoIP brachte mich um den Schlaf.
Selbstredend war in den Apache Logs und anderswo 0 Vorkommen einer solchen de:443
PseudoIP vorhanden.
Mir fiel auf, dass apache-wplogin2
und apache-wplogin
beide aktiv waren.
[apache-wplogin]
enabled = true
filter = apache-wplogin
action = iptables-multiport[name=apache-wplogin, port='http,https', protocol=tcp]
port = http,https
logpath = /var/log/apache2/other_vhosts_access.log
maxretry = 3
findtime = 10m
bantime = 2h
[apache-wplogin2]
enabled = true
filter = apache-wplogin2
port = http,https
logpath = /var/log/apache2/other_vhosts_access.log
maxretry = 3
findtime = 10m
bantime = 2h
Okay, sind halt 2 Jails, na und?
Der Schlüssel findet sich in den regex der Filter:
cat /etc/fail2ban/filter.d/apache-wplogin.conf
[Definition]
failregex = ^[\w\.-]+:\d{1,5} <HOST> - - \[.*\] "POST \/wp-login\.php
ignoreregex =
cat /etc/fail2ban/filter.d/apache-wplogin2.conf
[Definition]
failregex = <HOST>.*] "POST /wp-login.php
ignoreregex =
Meine Vermutung war nun, dass die beiden Jails mit praktisch gleichen matches sich gegenseitig stören und dass es dadurch zu diesen Phantom IPs "de:443" kommt.
Getestet habe ich das, indem ich apache-wplogin2
kurzer Hand ein paar Stunden auf
enabled = false gesetzt habe.
In dem Zeitraum war das Phänomen verschwunden, eine Gegenkontrolle durch wiedereinschalten von apache-wplogin2
erbrachte sofort das False IP Phänomen zurück.
Fazit:Öfter mal auch auf solche blöden Dinge achten und keine nahezu idententische Regex in Jails verwenden!
Was ist mit der IP w.x.y.z los?
in .bashrc
search_fail2ban() {
local ip="$1"
if [[ -z "$ip" ]]; then
echo "Usage: search_fail2ban <IP_ADDRESS>"
return 1
fi
egrep "$(date +%Y-%m-%d).*${ip}" /var/log/fail2ban.log
}
mit source ~/.bashrc
laden
und freuen
search_fail2ban IP-Adresse
Noch ein Alias:
WHEN_RELOAD_F2B
Zeigt die letzten config reloads, nützlich, wenn man vergessen hat, wann man die letzten Änderungen an Jails gemacht hat.
alias WHEN_RELOAD_F2B='echo "fail2ban-client reload:"&&zgrep "Reload finished." /var/log/fail2ban.log*|cut -d: -f2-3|sort'
Fußnoten
ChatGPT war so freundlich und hat die Regex erklärt:
1. ^<HOST> - - \[.*\] "GET /.*(\?|&)(option|view|id|Itemid|url_id|entry_id|ivVT|.*)=.*
^<HOST>: Beginnt mit der IP-Adresse des Hosts. <HOST> ist ein Platzhalter, den fail2ban durch die tatsächliche IP-Adresse ersetzt.
- - \[.*\]: Ein fester Textteil, der dem Format des Logeintrags entspricht. - - sind zwei Bindestriche, die normalerweise den Authentifizierungs- oder Authentifizierungsteil darstellen. \[.*\] steht für die Zeitstempel-Klammern.
"GET /.*(\?|&): Das Log-Ereignis beginnt mit der HTTP GET-Anfrage. /.* steht für den Pfad, der beliebige Zeichen enthalten kann, und (\?|&) stellt sicher, dass ein Fragezeichen ? oder ein kaufmännisches Und-Zeichen & vorhanden ist, um Parameter in der URL zu erkennen.
(option|view|id|Itemid|url_id|entry_id|ivVT|.*)=.*: Dies erkennt Parameter in der URL. Die Parameter option, view, id, Itemid, url_id, entry_id, ivVT und alle anderen Parameter (.*) werden berücksichtigt. Der Ausdruck =.* stellt sicher, dass dem Parameterwert folgt.
2. (SELECT|UNION|CONCAT|AND\s*1=1|OR\s*1=1|INFORMATION_SCHEMA|xp_cmdshell|FLOOR\(RAND\(\)|'%%27|%%22|--).*
SELECT|UNION|CONCAT: Diese Begriffe sind typische SQL-Befehle oder -Funktionen, die in SQL-Injection-Angriffen verwendet werden.
AND\s*1=1|OR\s*1=1: Diese Ausdrücke sind typische SQL-Injection-Techniken, um immer wahre Bedingungen zu erzeugen. \s* steht für beliebig viele Leerzeichen (einschließlich keiner).
INFORMATION_SCHEMA|xp_cmdshell: Diese Begriffe beziehen sich auf spezielle SQL-Features oder -Funktionen, die oft in SQL-Injection-Angriffen verwendet werden.
FLOOR\(RAND\(\): Ein SQL-Befehl, der in bestimmten Angriffen vorkommen kann, um zufällige Werte zu generieren.
'%%27|%%22|--: Dies sind URL-kodierte Zeichen, die für SQL-Injection verwendet werden. %%27 entspricht ' (Single Quote), %%22 entspricht " (Double Quote), und -- ist ein SQL-Kommentar.
3. HTTP/.*"$
HTTP/.*": Dies bestätigt das Ende der HTTP-Anfrage und stellt sicher, dass die Regex bis zum Ende der Logzeile reicht. Das .* steht für beliebige Zeichen nach dem HTTP-Protokoll, bis zum Ende des Logeintrags.
$: Der End-String-Zeichen stellt sicher, dass die Regex am Ende der Zeile endet.