This shell script analyzes a specific domains apache logs, both error and access, and then finds the IP address that hit your server the most. Then it checks for a a reverse DNS for each of the IP addresses, if it doesn't have a reverse DNS, AND it results in a positive to one of the 3 other tests its added to a bad IP list.
#!/bin/sh # User-contributed script. Not sponsored by DreamHost. # Script created 2008-01-16 by AskApache (www.askapache.com) ### SHELL OPTIONS set +o noclobber # allowed to clobber files set +o noglob # globbing on set +o xtrace # change to - to enable tracing set +o verbose # change to - to enable verbose debugging set -e # abort on first error # directory where log files, reports, and generated .htaccess files will be saved TMPDI="$HOME/ip_abuse" function exitt(){ case $TERM in xterm*|vt*|ansi|rxvt|gnome*) echo -e " 33]0;$USER@`hostname`: $HOME 07" ;; esac } function ok_continue() { echo -en "n 33[?25l 33[30;42m[ Press any key to continue ] 33[0mn" ;read -n 1 ans;echo -en " 33[?25h"; } function test_title() { echo -en "nn 33[0;32mn>>>"; echo -e " 33[1;37m $1 33[0m n"; echo -e "nn[ ${1} ]n" >> $REPORT; } function error_abuse(){ clear; title case "$2" in connlimit|1*) test_title "CONCURRENT CONNECTION TEST"; future=2 echo -e "Shows IP's making more than 20 requests concurrently.nn" cat $TMPDI/$YD/logs/e* |grep 'concurrent connection limit'|awk -F ']' '{print $3}' | awk '{print $2}'|sort|uniq -c |sort -nr|sed 's/^ *//'|egrep "[0-9]{2}+ " > $TMPDI/$YD/logs/out.txt ;; access|2*) test_title "TOP 50 IP TEST"; future=3 echo -e "Displays the top 50 unique IP addresses that access your site." echo -e "If they don't have a reverse DNS maybe they should be blocked.nn" cat $TMPDI/$YD/logs/a* |awk '{print $1|"sort|uniq -dc|sort -nr"}' |egrep "[0-9]{3}+ "| awk '{print $1,$2}' > $TMPDI/$YD/logs/out.txt ;; internal|3*) test_title "INTERNAL RECURSION TEST"; future="report" echo -e "Shows the IP's that triggered an Internal Recursion Error," echo -e "meaning that their is a looping problem on your server.n" cat $TMPDI/$YD/logs/e* |grep 'LimitInternalRecursion'|awk -F ']' '{print $3}'|awk '{print $2}' | sort|uniq -c|sort -nr|sed 's/^ *//'|egrep "[0-9]{2}+ " > $TMPDI/$YD/logs/out.txt ;; esac echo -e " 33[?25l";t=0; h=0; cat $TMPDI/$YD/logs/out.txt | while read a do if [ $t -lt 50 ];then n=`echo "$a"|awk '{print $1}'`; ip=`echo "$a"|awk '{print $2}'` host=`nice -n 19 host -qQ -s 1 "${ip}" 2>&1|tr 'n' 't'|awk '{print $2}'`; case "$host" in does) echo -en " 33[0;33m"; echo -e "$ip" >> $REPORT host=${host/does/!!!POSSIBLE-ABUSE!!!}; (( t++ )); (( h++ )); ips=" $ip$ips" ;; not) echo -en " 33[0;33m"; echo -e "$ip" >> $REPORT host=${host/not/!!!POSSIBLE-ABUSE!!!}; (( t++ )); (( h++ )); ips=" $ip$ips" ;; PTR) echo -en " 33[0;33m"; echo -e "$ip" >> $REPORT host=${host/PTR/!!!POSSIBLE-ABUSE!!!}; (( t++ )); (( h++ )); ips=" $ip$ips" ;; .) echo -en " 33[0;33m"; echo -e "$ip" >> $REPORT host=${host/./!!!POSSIBLE-ABUSE!!!}; (( t++ )); (( h++ )); ips=" $ip$ips" ;; esac echo -en " ${n}t${ip}t${host} 33[0mn" if [ $h -gt 3 ]; then echo -e "Deny from$ips" >> $TMPDI/$YD/.htaccess; h=0; ips=" "; fi fi done [ $h -gt 0 ] && echo -e "Deny from$ips" >> $TMPDI/$YD/.htaccess ok_continue; exec sh $0 "$1" "$future" } function menu(){ PS3="`echo -e ' 33[0;36m'`Please Select a Domain To Test: `echo -e ' 33[0m'`"; echo -ne " 33[0m" select v do YD="$v" mkdir -p -m 0755 $TMPDI/$YD/reports mkdir -p -m 0755 $TMPDI/$YD/logs echo "" > $TMPDI/$YD/logs/out.txt echo "" > $TMPDI/$YD/.htaccess echo -e "## IP-ABUSE-LOOKUPnOrder Allow,DenynAllow from All" > $TMPDI/$YD/.htaccess ELOG="$TMPDI/$YD/logs/error.log" ALOG="$TMPDI/$YD/logs/access.log" REPORT=$(echo -en "$TMPDI/$YD/reports/`date +%mx%dx%y`.txt"); echo "" > $REPORT; echo -e "GENERATED REPORT FOR $YD n`date`n" >> $REPORT clear; title; cd ~/logs/$YD/http if [ ! -f $ELOG ]; then test_title "Creating Error log" `nice -n 19 gunzip -dc e*.gz | split -b 1m -a 4 -d - $TMPDI/$YD/logs/e.`; wait cp error.log $TMPDI/$YD/logs; echo -e " 33[0;31m [ DONE ]nn" fi if [ ! -f $ALOG ]; then test_title "Creating access log" `nice -n 19 gunzip -dc a*.gz | split -b 5m -a 4 -d - $TMPDI/$YD/logs/a.`; wait cp access.log $TMPDI/$YD/logs; echo -e " 33[0;31m [ DONE ]nn" fi cd $HOME; exec sh $0 "$YD" "1" done } function show_report(){ clear; title; PS3="`echo -e ' 33[0;36m'`Please select a course of action: `echo -e ' 33[0m'`"; echo -ne " 33[0m" select v in "View Report" "View .htaccess" "Quit" do case "$v" in *Report) clear; title; test_title "VIEWING $REPORT"; cat $REPORT ;; *htaccess) clear; title; test_title "VIEWING $TMPDI/$YD/.htaccess"; cat $TMPDI/$YD/.htaccess ;; Quit) break;; esac done exit 0 } function title(){ # pretty sweet! echo -e " 33[1;30m __________________________________________________________________________ " echo -e "| 33[1;32m ___ __ __ __ 33[1;30m|" echo -e "| 33[1;32m / _ _______ ___ ___ _ / // /__ ___ / /_ 33[1;30m|" echo -e '| 33[1;32m / // / __/ -_) _ `/ ` / _ / _ (_- __/ 33[1;30m|' echo -e "| 33[0;32m /____/_/ __/_,_/_/_/_/_//_/___/___/__/ 33[1;30m|" echo -e "| |" echo -e "| 33[1;37mDREAMHOST IP ABUSE DETECTION SCRIPT VERSION 0.1 33[1;30m |"; echo -e " 33[1;30m __________________________________________________________________________ 33[0mnn" } # catch non-kill exit to reset term / ncurses trap exitt EXIT # set window title if client is capable case $TERM in xterm*|vt*|ansi|rxvt|gnome*) echo -e " 33]0;DREAMHOST IP ABUSE DETECTION SCRIPT 07" ;; esac if [ $# -lt 1 ]; then clear; title; [ -d $TMPDI ] || mkdir -m 755 $TMPDI cd ~/logs; DOMAINS=( `ls ~/logs/` ); cd $OLDPWD; menu ${DOMAINS[@]} else YD="${1}" mkdir -p -m 0755 $TMPDI/$YD/reports; mkdir -p -m 0755 $TMPDI/$YD/logs touch $TMPDI/$YD/logs/out.txt; ELOG="$TMPDI/$YD/logs/error.log" ALOG="$TMPDI/$YD/logs/access.log" REPORT=$(echo -en "$TMPDI/$YD/reports/`date +%mx%dx%y`.txt"); if [ "$2" == "report" ]; then show_report "$1" "$2" else error_abuse "$1" "$2" fi fi exit 0
Read more on the DreamHost wiki page: Block IP Abuse.