In case you need a quick way of getting rid of spammers on your apache server, use this method as a temporary solution until you find a better one.
Please take not that it will evaluate EVERY SINGLE REQUEST through a Deny policy in Apache's mod_access - it does not halt your performance much if you're just running a site with few requests. But I only suggest this solution as a temporary one until you have a better integration. I am currently working on a Django middleware that will only check the client's IP address in case it is a POST request directed at a certain URL.
Since I've been told that Apache doesn't use /etc/hosts.deny, you should probably use this method instead, unless you have a webserver that actually uses hosts.deny. Another method would be to add the IP addresses to iptables, if you're running that -- this is probably more efficient than using Apache configuration policies.
This method creates a file that you can include in httpd.conf (to cover ALL your virtual hosts). The overall goal is to avoid .htaccess files since they are evaluated at runtime. Thus, you won't have to configure each site, and you save a but of CPU time.
Copy the script below to a file on your server, eg. /usr/sbin/stopforumspam.py. You might want to read it quickly as it will pretty much explain itself...
Make the file executable, eg. chmod +x /usr/sbin/stopforumspam.py
Add it to your crontab for automatic execution each night, eg. crontab -e and then insert the line 0 0 * * * /usr/sbin/stopforumspam.py && /etc/init.d/apache2 reload
By default, we will ban an entire class C subnet if more than 5 IP addresses are in this space. You can configure this behavior by giving stopforumspam.py a single argument, ie. /usr/sbin/stopforumspam.py 10 would mean that at least 10 IPs have to be within the class C subnet to qualify it for a ban.
By default stopforumspam.py creates /etc/apache2/stopforumspam.conf - you can change this by editing the script.
Add Include /etc/apache2/stopforumspam.conf to /etc/apache2/httpd.conf (Debian/Ubuntu).
Caution! Make sure that you DO NOT change the Order Deny,Allow option in VirtualHost directives or .htaccess files as this unblock the deny policies.
import re
import urllib
import zipfile
import sys
if len(sys.argv) > 1:
SUBNET_THRESHOLD = int(sys.argv[1])
else:
SUBNET_THRESHOLD = 5
DOWNLOAD_ZIP = "http://www.stopforumspam.com/downloads/listed_ip_7.zip"
ZIP_FILENAME = "listed_ip_7.txt"
HTTPD_CONFIG_INCLUDE = "/etc/apache2/stopforumspam.conf"
IP_MATCH = re.compile(r"^(\d+).(\d+).(\d+).(\d+)$")
filename, headers = urllib.urlretrieve(DOWNLOAD_ZIP)
z = zipfile.ZipFile(filename)
ips = z.read(ZIP_FILENAME)
ips = ips.split("\n")
ips = filter(lambda ip: IP_MATCH.match(ip), ips)
def get_ip_segments(ip):
segments_match = IP_MATCH.search(ip)
return [int(segments_match.group(i)) for i in range(1,5)]
def convert_ip_to_number(ip):
numeric_value = 0
ip_segs = get_ip_segments(ip)
for i in range(4):
numeric_value += ip_segs[i](255*(4-i))
return numeric_value
ips.sort(key=convert_ip_to_number)
subnets = {}
for ip in ips:
ip_segs = get_ip_segments(ip)
key = (ip_segs[0], ip_segs[1], ip_segs[2])
if not key in subnets.keys():
subnets[key] = [ip]
else:
subnets[key].append(ip)
final_list = []
for subnet, subnet_ips in subnets.items():
if len(subnet_ips) > SUBNET_THRESHOLD:
# Ban the whole subnet
final_list.append(".".join(map(str, subnet)) + ".0/24")
else:
final_list = final_list + subnet_ips
print ""
print "Lengh of original list: %d" % len(ips)
print "Lengh of final list: %d" % len(final_list)
apache_conf_file = file(HTTPD_CONFIG_INCLUDE, "w")
apache_conf_file.write("\n")
apache_conf_file.write(" Order Deny,Allow\n")
for entry in final_list:
apache_conf_file.write(" Deny from %s\n" % entry)
apache_conf_file.write("\n")
apache_conf_file.close()