IP Abuse Detection for DreamHost
Alot of people have asked me about the best way to block specific IP addresses that are attacking their servers. No easy answer for sure. Here's what I came up with.
Per-domain Error.log and Access.log checks
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.
Generates .htaccess blocking code
Once the tests have concluded (on my machine it takes maybe 5 minutes) you have the choice to view an automatically generated .htaccess file that you can copy into your real .htaccess file, full of the correct code to Block the bad IP's. You can tell them bye bye with a 403 Forbidden.
IP Abuse / .htaccess Blocking Script
#!/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.
« Updated robots.txt for WordPressAdvanced HTTP Redirection »
Comments