Firewall settings are great for preventing Denial of Service (DoS) attacks, however it may not always be your only solution. The day has finally arrived when I found this excellent module called mod_dosevasive (DoS Evasive) which keeps track of how many requests each client makes to your server within intervals. If a client is being forceful with your server and making too many requests, then it is more than likely not just a web browser but some automated process unleashed on your site to try and take it down.
This handy Apache module we have found takes care of these issues. Let's get started by setting it up.
Preparing for mod_dosevasive
The first step to prepare to install this module is determine your server configuration. Did you install Apache with Dynamic Shared Object support (DSO)? or is your server configured without it? If have DSO enabled, you can simply run the apxs command to import the module on the fly, otherwise you may have to recompile Apache to make this work.
Download and Modify mod_dosevasive
First, you need to download mod_dosevasive to your server. Do so by going to the official site:
Nuclear Elephant: DosEvasive
Download the source code to a directory of your choice and then unpack it:
wget 'http://www.nuclearelephant.com/projects/dosevasive/mod_dosevasive.1.8.tar.gz'
tar zxpf mod_dosevasive.1.8.tar.gz
Our next task is to determine if we want to use mod_dosevasive's email features. Later in this tutorial, we are going create an addon with PHP which allows us to do the mailing with a more verbose log of what is going on, and do the iptables firewall DROP rules, but but just in case, you need to edit the mod_dosevasive.c file and point it to the correct mailing application such as Sendmail.
In our mod_dosevasive.c file, we have altered the define MAILER line to reflect the path to Sendmail on our system:
#define MAILER "/usr/sbin/sendmail -t %s"
Now that we have the file prepared, we are ready to figure out how to get it into Apache.
Apache with DSO Support
If you're using an RPM version of Apache 1.3, then chances are your have DSO support enabled and this installation will be a breeze. First, locate the path to your apxs executable in the Apache bin directory and then run this command:
Watch for the output and if everything looks ok, the module is installed and you are clear to restart Apache. We'll add the configuration directives later in this tutorial, so you may skip the next section for now.
Apache without DSO
If your Apache is configured without DSO support (ie: not using --with-dso), then you must recompile Apache. Here's how to configure Apache with mod_dosevasive:
./configure \
--prefix=/www \
--add-module=/usr/local/src/mod_dosevasive/mod_dosevasive.c
make
make install
Note | You will definately want to include any other Apache modules in the configure line that you will need. Don't forget to add PHP! |
Now you are all set with Apache non-DSO and mod_dosevasive. Let's move on to the configuration files.
Basic Configuration mod_dosevasive
Our next task is to configure mod_dosevasive for your server. There's a good handful of directives you may use, but we'll cover the ones we need for this tutorial. You really should read the documentation on this module to understand the other directives that are available for you.
Apache 1.3 with DSO Support:
You need to add the module to the httpd.conf. Find the AddModule section and enter this line to the bottom of it:
All Apache 1.3 Configurations:
At the end of your httpd.conf file, add the following lines:
Normal Traffic Server:
DOSHashTableSize 3097
DOSPageCount 2
DOSSiteCount 50
DOSPageInterval 1
DOSSiteInterval 1
DOSBlockingPeriod 10
</IfModule>
High Traffic Server:
DOSHashTableSize 3097
DOSPageCount 4
DOSSiteCount 100
DOSPageInterval 1
DOSSiteInterval 1
DOSBlockingPeriod 10
</IfModule>
Let's break down only these configuration directives:
- DOSHashTableSize is the size of the hash table that is created for the IP addresses monitored.
- DOSPageCount is the number of pages allowed to be loaded for the DOSPageInterval setting. In our case, 2 pages per 1 second before the IP gets flagged.
- DOSSiteCount is the number of objects (ie: images, style sheets, javascripts, SSI, etc) allowed to be accessed in the DOSSiteInterval second. In our case, 50 objects per 1 second.
- DOSPageInterval is the number of seconds the intervals are set for DOSPageCount
- DOSSiteInterval is the number of seconds the intervals are set for DOSSiteCount
- DOSBlockingPeriod is the number of seconds the IP address will recieve the Error 403 (Forbidden) page when they have been flagged.
The next task is to tweak Apache while we are here. Change the following directives to these values:
The MaxRequestsPerChild directive which is normally set to zero (0) for unlimited needs to be set for proper cleanup:
Note | Read the documentation (README) again for this module to ensure you have any additional tweaks covered. |
Moving along, we can now build our custom PHP script that will do some magic for us. Let's discuss this on the next page.
mod_dosevasive does a wonderful job as it is. However, we believe in the Three Strike rule instead of just dropping someone from your server alltogether after one flagging by mod_dosevasive. Therefore, we have created a method by using PHP and MySQL to handle this.
From this point forward we assume that you have PHP abilities on the command line. You can check to be sure by running:php -v and if the command is found, PHP will print it's version on the screen. You are clear to proceed!
Our setup will do a couple of things here. First, if mod_dosevasive finds an offending user, it has the ability to call an external command, in this case, our PHP script. We can pass the offending IP address to the PHP script and process it into our database. This provides ammunition for us if we need to because we can also keep track of how many times an IP address has been flagged and we can also log what pages it was flagged for accessing by parsing our Apache log file for the relative information. Then, we can have PHP execute the iptables command when we find this user has been flagged three times. Let's move on and find out how.
Setting up the MySQL Database
Note | Before you begin, you will need to have access to MySQL and know your MySQL root password! |
First, login to MySQL and create a database, create a user and grant privelages to that database for the new user:
CREATE DATABASE blacklist;
use blacklist;
CREATE TABLE blacklist (
id int(5) NOT NULL auto_increment,
ip varchar(40) NOT NULL default '',
date datetime NOT NULL default '0000-00-00 00:00:00',
PRIMARY KEY (`id`)
) TYPE=innodb;
CREATE TABLE blacklist_logs (
id varchar(150) NOT NULL default '',
log_entry text NOT NULL,
ip_address varchar(40) NOT NULL default '',
date varchar(25) NOT NULL default '',
uri varchar(200) NOT NULL default '',
response varchar(100) NOT NULL default '',
size varchar(100) NOT NULL default '',
UNIQUE KEY id (`id`)
) TYPE=innodb;
GRANT ALL PRIVILEGES ON blacklist.* TO 'blacklist'@'localhost' IDENTIFIED BY 'somepassword';
exit
Now our database should be all set! We are ready to setup the PHP script.
The PHP Script
Our PHP script will perform a couple of operations:
- Receive the IP address from the command line
- Log the IP address, date and time into the blacklist table
- Retreive the information about this user from the log file and log normal files such as HTML, PHP and etc into our database.
- Count how many times the user has been flagged, if the count is 3 or more, then run the IPTABLES rule for dropping all access for this IP address.
- Sending an email notification to an administrator with a summary of the logfile access we have recorded.
Sound pretty cool? Let's move on! Create a PHP script somewhere on your hard drive. I prefer to locate it in /usr/local/etc and name it blacklist.php with the following contents:
<?php
$sql = mysql_connect('localhost', 'blacklist', 'somepassword');
mysql_select_db('blacklist', $sql);
$ip = $_SERVER['argv']['1'];
$sql = mysql_query("INSERT INTO blacklist (ip, date) VALUES ('$ip', now())");
$logs = `cat /path/to/logs/access.log | grep $ip`;
$arry = explode("n", $logs);
foreach($arry AS $line){
if(!eregi('gif', $line) && !eregi('jpg', $line) && !eregi('bmp', $line)
&& !eregi('png', $line) && !eregi('css', $line)
&& !eregi('js', $line)){
$larr = explode(" ", $line);
$ip_addr = $larr[0];
$date = str_replace("[", "", $larr[3]);
$date = mytime($date, 0);
$url = $larr[6];
$response = $larr[8];
$size = $larr[9];
$id = md5($ip.$date.$url);
mysql_query("INSERT INTO blacklist_logs (
id, log_entry, ip_address, date, uri, response, size)
VALUES ('$id', '$line', '$ip', '$date', '$url$
}
}
function mytime($str, $short = null){
$nstr = explode('/', $str);
$nstr2 = explode(":", $nstr['2']);
$m = $nstr['1']; $d = $nstr['0']; $y = $nstr2['0'];
$h = $nstr2['1']; $i = $nstr2['2']; $s = $nstr2['3'];
if($short){
return "$d-$m-$y";
} else {
return "$d-$m-$y | $h:$i:$s";
}
}
$ccount = mysql_result(mysql_query("SELECT COUNT(id) AS ccount FROM blacklist WHERE ip = '$ip'"),0);
if($ccount > 2){
$drop = "su - root -c '/sbin/iptables -I INPUT -s $ip -j DROP'";
// echo $drop;
`$drop`;
$subject = "$ip Banned from Server";
$to = "[email protected]";
$message = "The IP address: $ip has been banned from yoursite.comnn";
$message .= "Here is a summary of actions for this IP address:nn";
$sql = mysql_query("SELECT * FROM blacklist_logs WHERE ip_address = '$ip' ORDER BY id");
while($row = mysql_fetch_array($sql)){
$message .= $row['log_entry']."n";
echo $row['log_entry']."n";
}
$message .= "====================================================================";
$headers .= "From: Security<[email protected]>n";
$headers .= "Return-Path: <[email protected]>n"; //Return bouned mails to.
$headers .= "X-Priority: 1n"; //Message priority is set to HIGH
$headers .= "X-MSMail-Priority: Highn"; //Message Priority for Exchange Servers
$headers .= "X-Mailer: PHPn"; //IP address of who sent the mail.
mail($to, $subject, $message, $headers);
echo "$ip dropped and email sent.n";
}
?>
Note | Do not be a bafoon. Read each line of the previous PHP script and make sure the relevant information is correct for your server, usernames, email address and etc. We are not here to support you with PHP developing. |
Note | For the previous script to work properly, you must be using combined logging methods within Apache: CustomLog /path/to/access.log combined |
Now save this file out and you are ready to add another directive for mod_evasive on your Apache httpd.conf file:
Now, restart Apache and watch as the script kicks into action. If you use phpMyAdmin, you can browse the database and also tail your system log: tail -f /var/log/messages
When an IP address gets blocked, you SHOULD receive an email notifying you if you setup the PHP script properly.
Note | If you restart your firewall, or Flush iptables, all of the IP address will be flushed along with it. However, the next time they get flagged, they should automatically get dropped from your firewall. |