Stop DoS attacks with an IP tables rule?

I was wondering if I could prevent small (D)DoS attacks with a simple IP tables rule?

By small I mean that they are flooding my web server with about 400+ requests from one or two IP addresses. I can drop the IP addresses after I notice that they have started hitting my web server, but it normally takes a few minutes for IP tables to kick in against that IP, and start dropping it completely so that it doesn't impact that web server.

I drop the IP with the following command:

iptables -I INPUT -s "IP HERE" -j DROP

And then obviously save it:

/etc/init.d/iptables save

I normally find out the attacking IP address(es) with the following command:

netstat -plan|grep :80|awk '{print $5}'|cut -d: -f 1|sort|uniq -c|sort -n

The issue with doing it that way is that I have to be there, and it requires me to act after the fact. Is there an IP tables rule that I could use to drop an IP address right after it hits 150 connections? That way I don't have to worry about it overwhelming the web server, and I also don't have to be there at the time to block it.

By the way, I'm using Apache on CentOS if that matters.

Thank you for your time.


Solution 1:

For something that offers flexibility, look into the recent (and limit) module(s). Recent will keep a track of the number of connections made by an IP, over a given time frame, and can be used to trigger a specific rule. The recent module is (relatively) CPU heavy - but when compared to loading a dynamic page, can be quite acceptable.

-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p tcp -m multiport --dports 80,443 -m recent --update --seconds 3600 --name BANNED --rsource -j DROP
-A INPUT -p tcp -m multiport --dports 80,443 -m state --state NEW -j ATTK_CHECK

-A ATTACKED -m limit --limit 5/min -j LOG --log-prefix "IPTABLES (Rule ATTACKED): " --log-level 7
-A ATTACKED -m recent --set --name BANNED --rsource -j DROP
-A ATTK_CHECK -m recent --set --name ATTK –-rsource
-A ATTK_CHECK -m recent --update --seconds 600 --hitcount 150 --name ATTK --rsource -j ATTACKED
-A ATTK_CHECK -m recent --update --seconds 60 --hitcount 50 --name ATTK --rsource -j ATTACKED
-A ATTK_CHECK -j ACCEPT

Essentially, the above amounts to:

  • Accept connections that have been established already (i.e. they have passed our rules)
  • Drop all connections on ports 80 and 443 (SSL) from banned IPs - if they keep trying to connect, extend the time - wait an hour (without any connection attempts) before unblocking them.
  • Check new connections against our rules:
    • Consider more than 150 connections in 10 minutes an attack
    • Consider more than 50 connections in 1 minute an attack
    • Log the attack (but not more than 5 entries per minute - don't want to flood our logs); and block the connection

For a more detailed explanation, and a tiered version of the above, which I use, see this article

Keep in mind that ipTables will not help with network congestion at all, and is fairly ineffective against any distributed attacks - its advantage is in security and reducing the load on your server.

Solution 2:

You can use iptables to block an abuser like this, but you have to be careful because iptables is very CPU-intensive. Every packet is checked against every rule, so adding rules can quickly cause the server to run out of cycles. I had a client once that would add a new rule every time they wanted to block someone, and then one day they had slightly higher than normal traffic and the system fell over. Tables can reduce the number of rules looked at, but you can't just throw a lot of rules in iptables.

Another alternative is black-hole routing:

ip route add blackhole $IP_HERE
ip route flush cache

The routing table is hashed (with a hashtable), whereas iptables rules have to be looked at sequentially, which is much less efficient. This way, you can have hundreds of thousands of routes without a problem, where thousands of iptables rules would become problematic to run through for every request.

As far as your second question, you can use the hashlimit module to create an automatic blacklist for abusers. See my post about preventing a DDoS attack for more details, but the short form is:

iptables -A INPUT -p tcp --dport 80 -m hashlimit --hashlimit-upto 50/min \
    --hashlimit-burst 500 --hashlimit-mode srcip --hashlimit-name http -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j DROP