Home » Security » IP Abuse Detection for DreamHost

by 5 comments

Block IP Address .htaccessAlot 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

automatically generated .htaccess fileOnce 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

IP Abuse Detection from Apache Logs


# User-contributed script. Not sponsored by DreamHost.
# Script created 2008-01-16 by AskApache (

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

function exitt(){
 case $TERM in
  xterm*|vt*|ansi|rxvt|gnome*) echo -e "33]0;$USER@`hostname`: $HOME07" ;;

function ok_continue()
{ echo -en "n33[?25l33[30;42m[ Press any key to continue ]33[0mn" ;read -n 1 ans;echo -en "33[?25h"; }

function test_title()
{ echo -en "nn33[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 ;;

 echo -e "33[?25l";t=0; h=0; cat $TMPDI/$YD/logs/out.txt |
 while read a
 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" ;;
  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=" ";
[ $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
  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
  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"
  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"
  cd $HOME; exec sh $0 "$YD" "1"

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"
  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;;
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.133[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 SCRIPT07" ;;

if [ $# -lt 1 ]; then
 clear; title; [ -d $TMPDI ] || mkdir -m 755 $TMPDI
 cd ~/logs; DOMAINS=( `ls ~/logs/` ); cd $OLDPWD; menu ${DOMAINS[@]}
 mkdir -p -m 0755 $TMPDI/$YD/reports; mkdir -p -m 0755 $TMPDI/$YD/logs
 touch $TMPDI/$YD/logs/out.txt;
 REPORT=$(echo -en "$TMPDI/$YD/reports/`date +%mx%dx%y`.txt");

 if [ "$2" == "report" ]; then
  show_report "$1" "$2"
  error_abuse "$1" "$2"

exit 0

Read more on the DreamHost wiki page: Block IP Abuse.


Comments Welcome

AskApache is an FSF Contributing Member (with ThankGNU)

Information is freedom. Freedom is non-negotiable. So please feel free to modify, copy, republish, sell, or use anything on this site in any way at any time ;)

My Online Tools
Popular Articles

Hacking and Hackers

The use of "hacker" to mean "security breaker" is a confusion on the part of the mass media. We hackers refuse to recognize that meaning, and continue using the word to mean someone who loves to program, someone who enjoys playful cleverness, or the combination of the two.
-- Richard M. Stallman


It's very simple - you read the protocol and write the code. -Bill Joy

Except where otherwise noted, content on this site is licensed under a Creative Commons Attribution 3.0 License, just credit with a link.
This site is not supported or endorsed by The Apache Software Foundation (ASF). All software and documentation produced by The ASF is licensed. "Apache" is a trademark of The ASF. NCSA HTTPd.
UNIX ® is a registered Trademark of The Open Group. POSIX ® is a registered Trademark of The IEEE.

+Askapache | |

Site Map | Contact Webmaster | License and Disclaimer | Terms of Service | @Htaccess

↑ TOPMain