BOTs drosseln / blocken per iptables

greystone

Active Member
Hallo zusammen,

heute hat mich der ClaudeBot genervt. Der prügelt doch tatsächlich mit 1700 verschiedenen AWS IPs auf einen meiner Server ein, um den zu indizieren. Es gibt wie immer viele Wege zum Ziel.

Ich habe mich heute entschieden das per iptables zu drosseln, mit ips, die ich aus dem apache log file extrahiere. Das ist recht generisch und funktioniert so gut wie überall. Hier ist das Script, falls das jemand gebrauchen kann (block-bot.sh):

Code:
#!/bin/bash

declare -rx PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin
declare -rx LC_ALL=C

# --- configure your environment here

declare -rx botname="claudebot"                         # <----- add your specific botname(s) (regex) here
declare -rx logfile=/var/log/apache2/your-access.log    # <----- add your apache2 access-log-file-path here
declare -rx conn_limit="1/sec"                          # <----- how many bot connections do you allow?

# --- configuration section end

get_bot_ips() {
        grep -Ei "$botname" "$logfile" \
               | gawk 'ips[$1]!=1 {ips[$1]=1;print $1}'
}

setup_chain() {

        local chain=$1

        {
                iptables -F $chain
                iptables -X $chain
                iptables -N $chain

        } &>/dev/null

}

eexit() {

        echo "Fatal Error: $1"
        echo "aborting"
        echo
        exit 1

}

mylog() {

        echo "$(date) : $1"

}

main() {

        echo
        which gawk      &>/dev/null || eexit "gawk missing. please install"
        which iptables  &>/dev/null || eexit "iptables missing. please install"
        [ -r "$logfile" ]           || eexit "apache logfile $logfile not readable. please check variable \$logfile"

        mylog "doing iptables base setup for $botname rate limiting..."
        iptables -D INPUT -j _MYBOT &>/dev/null

        local ip

        setup_chain _MYBOT
        setup_chain _RATE_LIMIT

        iptables -A _RATE_LIMIT -p tcp -m tcp --dport 80  -m conntrack --ctstate NEW -m limit --limit $conn_limit -j ACCEPT
        iptables -A _RATE_LIMIT -p tcp -m tcp --dport 443 -m conntrack --ctstate NEW -m limit --limit $conn_limit -j ACCEPT
        iptables -A _RATE_LIMIT -p tcp -m tcp --dport 80  -m conntrack --ctstate NEW -j DROP
        iptables -A _RATE_LIMIT -p tcp -m tcp --dport 443 -m conntrack --ctstate NEW -j DROP

        mylog "adding bot ips..."

        while read ip ; do

                ((count=count+1))
                iptables -A _MYBOT -s $ip -j _RATE_LIMIT

        done < <(get_bot_ips)
        iptables -A _MYBOT -j RETURN
        iptables -I INPUT 1 -j _MYBOT
        mylog "added $count rules / ips for Bot-Pattern '$botname'"
        mylog "bot rate limiting finished for Bot-Pattern '$botname'"
        echo

}

main
 
Last edited:
Das Skript ist simpel und wohl durchaus effektiv, allerdings hast du das Rad neu erfunden.
Die gängigste Variante wäre fail2ban welches es bereits eine generische "apache-badbots.conf" kennt, welche man erweitern kann.

(Hoffentlich konstruktive) Anmerkungen zum Skript:
- Einige Variablen winn $conn_limit oder $ip werden unquoted eingesetzt
- "for x in y" _kann_ zu MAX_ARG_STRLEN führen, ich würde folgendes Konstrukt empfehlen: while read line; do <ZEUG>; done < <( get_bot_ips )
- kontinuierliche Überwachung oder mehrfaches Ausführen kann durch "iptables -A" die Firewall mit Duplikatas füllen
 
Einige Variablen winn $conn_limit oder $ip werden unquoted eingesetzt
Quoting wird überbewertet. Ich könnte auch locker noch 100 Zeilen Validierung dran hängen. Habe ich hier keinen Bock drauf. Das tut schon ausreichend. Ich hab's ohnehin schon deutlich aufgehübscht, bevor ich das hier ins Forum geschrieben habe.
- "for x in y" _kann_ zu MAX_ARG_STRLEN führen, ich würde folgendes Konstrukt empfehlen: while read line; do <ZEUG>; done < <( get_bot_ips )
Ja. Das stimmt! Das muss noch.
- kontinuierliche Überwachung oder mehrfaches Ausführen kann durch "iptables -A" die Firewall mit Duplikatas füllen
Das nicht. Das Script löscht die Regeln am Anfang jedes Aufrufes. Das Script ist mit Augenmerk auf Idempotenz geschrieben.

Bzgl. f2b: TIMTOWTDI

Ansonsten wollte ich auch nicht blocken sondern nur drosseln. Geht mit f2b natürlich auch irgendwie...
 
Last edited:
Ich habe mir gerade auch nochmal die Zahlen der Bot-Requests angeschaut (letzte Woche, runterskaliert auf 24 Stunden):

Code:
ClaudeBot                          227800
serpstatbot                          3923
BLEXBot                              2431
AhrefsBot                            1982
SeekportBot                          1119
DotBot                                950
MJ12bot                               914
YandexBot                             747
bingbot                               456
Googlebot                             412
PetalBot                              304
SemrushBot                             72
zoominfobot                            71
Amazonbot                              39
Applebot                               34
MetaJobBot                             32
aiHitBot                               18
DuckDuckBot                            12
BitSightBot                            12
Pinterestbot                            9
MojeekBot                               6
laboraybot                              5
SeznamBot                               4
startmebot                              3
ImagesiftBot                            3
Twitterbot                              2
wpbot                                   2
DataForSeoBot                           2
SeobilityBot                            2
Linkbot                                 2
KStandBot                               2
DomainStatsBot                          2
VelenPublicWebCrawler                   1
SuperBot                                1
SafeDNSBot                              1
GPTBot                                  1
 
Last edited:
Hallo,
Ich habe mir gerade auch nochmal die Zahlen der Bot-Requests angeschaut (letzte Woche, runterskaliert auf 24 Stunden):

Code:
ClaudeBot                          227800
...
GPTBot                                  1
Ich setze auch apache-badbots ein; in der Liste fehlen mir noch ein paar meiner Lieblinge:

- Go-http-client (ist bei den Chinesen der absolute Renner!)
- Java/1 und /2 in allen Variationen
- Bytespider

Im Moment ist bei mir iptables mehr als 800 Zeilen lang ...
Da muß die KI eben ohne meinen Content auskommen :)

Grüße!
 
Echt 800 Zeilen..
Was steht denn da alles drin...?
:whistle:
Reiche Ernte fahren wie gesagt apache-badbots und wplogin ein;
und ich drope die Clients gleich für einen ganzen Monat.
Ab und zu kürze ich besonders intensiv genutzte IP-Bereiche:

Chain f2b-apache-badbots
pkts bytes target prot opt in out source destination
356 21320 DROP 0 -- * * 52.224.0.0/11 0.0.0.0/0

So sind es jetzt nur noch 650 Zeilen.
 
Hallo tomcat, wäre es möglich deine failregex in fail2ban mal zu sehen. Gerne auch als PN. Danke voraus. :)
 
Hallo tomcat, wäre es möglich deine failregex in fail2ban mal zu sehen. Gerne auch als PN. Danke voraus. :)
Hallo,
da ist kein großes Geheimnis dabei.

Bei apache-badbots.conf
failregex = ^<HOST> -.*"(GET|POST|HEAD).*HTTP.*".*(?:%(badbots)s|%(badbotscustom)s).*$
... da war meiner dunklen Erinnerung nach ein kleiner Tippfehler drin.

Dann noch badbotscustom um die entsprechenden Quälgeister erweitern:
badbotscustom = EmailCollector|WebEMailExtrac|TrackBack/1\.02|sogou music spider|(?:Mozilla/\d+\.\d+ )?Jorgee|Go-http-client/1\.1|Java/1|Java/2|claudebot|gptbot|Bytespider|python-requests|ALittle Client|facebookexternalhit

Bei wplogin.conf ...
failregex = <HOST>.*(GET|HEAD).*(login\.php|xmlrpc\.php|phpmyadmin).* 404

... habe ich Spaßes halber phpmyadmin mit eingetragen; das ist so zu sagen mein Honeypot für Arme:
*Mein* phpmyadmin habe ich umbenannt nach xyz_phpmyadmin; so werden alle, die weiterhin mit phpmyadmin zugreifen für einen Monat ausgesperrt.
Ja, das ist Security through Obscurity; aber ich halte mit so 99,9% aller Loginversuche vom Hals, und ich betreibe auch keine Whistle-Blower Plattform für US-Kriegsverbrechen:)

Grüße!
 
Hallo, vielen Dank tomcat.

Hierzu nochmal eine Frage in die Runde.

Meine "failregex" ist noch etwas umfangreicher. Obwohl die Jail für Wordpress so konfiguriert ist das max. 1 Versuch möglich ist gelingt es offensichtlich mehrere Requests zu senden bevor fail2ban die entsprechende IP sperrt, wie das u.s. Beispiel.

Code:
2024-08-11 17:31:06    Error    xx.xx.xx.xx    403    GET //xmlrpc.php?rsd HTTP/1.1        Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36    1.01 K    SSL/TLS-Zugriff für Apache
2024-08-11 17:31:10    Error    xx.xx.xx.xx    403    GET //?author=1 HTTP/1.1        Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36    1.01 K    SSL/TLS-Zugriff für Apache
2024-08-11 17:31:10    Error    xx.xx.xx.xx    403    GET //?author=2 HTTP/1.1        Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36    1.01 K    SSL/TLS-Zugriff für Apache
2024-08-11 17:31:11    Error    xx.xx.xx.xx    401    GET //wp-json/wp/v2/users/ HTTP/1.1        Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36    1.36 K    SSL/TLS-Zugriff für Apache
2024-08-11 17:31:11    Error    xx.xx.xx.xx    403    GET //wp-json/oembed/1.0/embed?url=https://www.example.de/ HTTP/1.1        Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36    1.01 K    SSL/TLS-Zugriff für Apache
2024-08-11 17:31:12    Error    xx.xx.xx.xx    403    POST //xmlrpc.php HTTP/1.1

Hier die aktuelle failregex:

Code:
failregex = ^<HOST>.* "(GET|POST|HEAD) /\?author=[0-9]+
            ^<HOST>.* "(GET|POST|HEAD) /xmlrpc.php\?rsd
            ^<HOST>.* "(GET|POST|HEAD) /xmlrpc.php
            ^<HOST>.* "(GET|POST|HEAD) /wp-includes/wlwmanifest.xml
            ^<HOST>.* "(GET|POST|HEAD) /wp-json/wp/v2/users/
            ^<HOST>.* "(GET|POST|HEAD) /wp-json/oembed/1.0/embed
            ^<HOST>.* "(GET|POST|HEAD) /wp-admin/install.php
            ^<HOST>.* "(GET|POST|HEAD) /wp-admin/setup-config.php
            ^<HOST>.* "(GET|POST|HEAD) /wp-config.php
            ^<HOST>.* "(GET|POST|HEAD) /wp-login.php
            ^<HOST>.* "(GET|POST|HEAD) /timthumb.php
            ^<HOST>.* "(GET|POST|HEAD) /phpmyadmin
            ^<HOST>.* "(GET|POST|HEAD) /phpMyAdmin
            ^<HOST>.* "(GET|POST|HEAD) /PMA
            ^<HOST>.* "(GET|POST|HEAD) /mysqladmin
            ^<HOST>.* "(GET|POST|HEAD) /backup
            ^<HOST>.* "(GET|POST|HEAD) /backups
            ^<HOST>.* "(GET|POST|HEAD) /.zip
            ^<HOST>.* "(GET|POST|HEAD) /.sql
            ^<HOST>.* "(GET|POST|HEAD) /wordpress

Wo könnte das Problem liegen . Danke wieder voraus.
 
Last edited:
Hierzu nochmal eine Frage in die Runde.

Meine "failregex" ist noch etwas umfangreicher. Obwohl die Jail für Wordpress so konfiguriert ist das max. 1 Versuch möglich ist gelingt es offensichtlich mehrere Requests zu senden bevor fail2ban die entsprechende IP sperrt, wie das u.s. Beispiel.

Code:
2024-08-11 17:31:06    Error    xx.xx.xx.xx    403    GET //xmlrpc.php?rsd HTTP/1.1        Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36    1.01 K    SSL/TLS-Zugriff für Apache
2024-08-11 17:31:10    Error    xx.xx.xx.xx    403    GET //?author=1 HTTP/1.1        Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36    1.01 K    SSL/TLS-Zugriff für Apache
2024-08-11 17:31:10    Error    xx.xx.xx.xx    403    GET //?author=2 HTTP/1.1        Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36    1.01 K    SSL/TLS-Zugriff für Apache
2024-08-11 17:31:11    Error    xx.xx.xx.xx    401    GET //wp-json/wp/v2/users/ HTTP/1.1        Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36    1.36 K    SSL/TLS-Zugriff für Apache
2024-08-11 17:31:11    Error    xx.xx.xx.xx    403    GET //wp-json/oembed/1.0/embed?url=https://www.example.de/ HTTP/1.1        Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36    1.01 K    SSL/TLS-Zugriff für Apache
2024-08-11 17:31:12    Error    xx.xx.xx.xx    403    POST //xmlrpc.php HTTP/1.1

Fail2ban verrichtet seine Arbeit ja nicht in 0-Zeit; daher kann es sein, dass kurz nacheinander eintreffende Requests schon mal durchrutschen (wobei 6 Sekunden doch etwas üppig sind).
Mein Glaskugel sagt noch: Prüfe doch mal, wieviel CPU der fail2ban-server Prozess so frisst; wenn man nämlich Regeln gar zu kompliziert und/oder die zu durchsuchenden log-files gar zu groß sind, man sich auf diese Art (wie ein selbst-DOS) ins eigene Knie schießt.

Grüße!
 
Vielen Dank. :)

CPU-Auslastung von 0,3 bis 0,7 % im Normalbetrieb und Spitzenwerte von bis zu 2 % für den fail2ban-server Prozess.

Das müsste im Rahmen sein.
 
Hallo tomcat8, kurze Frage noch. Führst Du die von fail2ban gesammelten IP,s auch einer Blacklist, also dauerhaften Sperre zu? Danke voraus.
 
Back
Top