3

I am using iptables' rate-limit module to prevent DoS attack (I know it cannot stop a full scale DDoS but at least it can help with smaller attacks).

In my rules I have something like:

/sbin/iptables -A INPUT -p TCP -m state --state NEW -d xx.xxx.xxx.xx --dport 80 -m limit --limit 20/minute --limit-burst 20 -j ACCEPT

It works well until last night when someone was hitting my port 80 non-stop. The connection was being dropped per the rule alright (as shown in the log). However, it also makes the server unavailable to other legitimate users as well.

I don't understand why it happened like that. I thought it would not affect any other users besides the one that is flooding the server.

Is it because iptables was overwhelmed?

Any feedback would be greatly appreciated.

Thank you!

TopQ
  • 93
  • 5

2 Answers2

2

It cuts off traffic for legitimate users as well because you haven't specified a source ip address to apply that rule to. If you changed the rule to have a source address, you would then be RateLimit'ing that specific address. As your rule stands now, that RateLimit is in effect for all addresses.

I have RateLimit setup to limit all IPs except those specified by certain rules. My configuration is as follows:

  • -I INPUT -m tcp -p tcp --dport 80 -d xx.xxx.xx.xx -j ACCEPT

  • -A INPUT -m tcp -p tcp --dport 80 -m hashlimit --hashlimit-upto 400/min --hashlimit-burst 500 --hashlimit-mode srcip --hashlimit-name ratelimit -j ACCEPT

  • -A INPUT -p tcp -m tcp --dport 80 -j RateLimit

I then have a bunch of rules such as

  • -A RateLimit -s xx.xxx.xx.x -j ACCEPT

This will allow communications on port 80, with a connection limit and burst configured, and limit by source ip (the --hashlimit-mode srcip ). IPs will be checked for existence in the RateLimit has ( --hashlimit-name ratelimit ).

Perhaps something like that is more what you are looking for?

Kendall
  • 1,063
  • 12
  • 25
  • DerfK's answer is probably a bit more helpful in this situation, as you likely don't know ahead of time which IPs you want (in)(ex)cluded from the RateLimit. However, I know exactly what I need to RateLimit, hence the different between my solution and DerfK's. – Kendall Sep 12 '11 at 18:49
  • Thanks!! Now I kind of understand the differences between limit, hashlimit and recent modules. I thought they served the same purposes. From reading your answer it sounds like both hashlimit and recent keep track of the origin IPs while limit doesn't. I am going to try recent module and hopefully my version of iptables has it... – TopQ Sep 12 '11 at 19:04
2

Your rule does not appear to specify any particular origin. After accepting 20 NEW connections in a minute, it stops accepting NEW connections.

You need to use the recent module in order for iptables to remember where the connections are coming from and blocking people that connect too fast from the same address. This takes two rules: one for iptables to "learn" the address, and then one for iptables to see how many times that address has hit the server in the specified time:

/sbin/iptables -A INPUT -p TCP -m state --state NEW -d xx.xxx.xxx.xx --dport 80 -m recent --set
/sbin/iptables -A INPUT -p TCP -m state --state NEW -d xx.xxx.xxx.xx --dport 80 -m recent --update --seconds 60 --hitcount 20 -j DROP

This article goes into more details.

DerfK
  • 19,493
  • 2
  • 38
  • 54
  • I was playing around with the Recent module and wrote a PHP script using cURL to hit my server 10 times in a loop. I was expecting the connection be dropped somewhere in the loop but they always go through all 10 iterations. Only until I re-run the PHP script again is the connection dropped by iptables. I guess it is because we are only tracking NEW connections? I tried to use NEW,ESTABLISHED and the result was the same. How do I block the connections in a loop in this case? Thanks. – TopQ Sep 13 '11 at 18:18
  • @TopQ Both your script and mine are trying to block connections after 20 connections in 60 seconds. Make your script loop past 20 and it should stop accepting connections. Note that you do NOT want to drop ESTABLISHED packets, since this would cut people off while they're downloading or uploading files or data. – DerfK Sep 13 '11 at 19:43
  • I actually use --seconds 20 --hitcount 3 so 10 iterations should exceed it. Thanks. – TopQ Sep 13 '11 at 20:29
  • @TopQ are you using php to execute the curl binary, or are you using the `curl_init()` etc functions, because the PHP curl module will keep the same connection open between requests unless you `curl_close()` between them or possibly use the CURLOPT_FORBID_REUSE option. – DerfK Sep 13 '11 at 23:15
  • I am using cURL functions curl_exec() in the loop and only call curl_close() at the end. It is very likely that it is using the same connection and that is the very reason for my original comment: I want to drop those requests on the same connection. Thank you. – TopQ Sep 14 '11 at 00:04
  • I don't think iptables is capable of that level of inspection, since it would have to understand when one request ends and another begins. This would be better controlled from the server. If you are using apache, consider mod_evasive. – DerfK Sep 14 '11 at 03:51
  • I see. Thanks! mod_evasive is flawed in the way it maintains the hash table. I was using it but it wasn't effective at all. I will accept your answer. Thanks again. – TopQ Sep 14 '11 at 15:45
  • I still have problems...maybe I should start a new post but here it is. I actually am serving different domains on different IPs (ip aliases) on one host. Not only do I need to keep track of the origin IPs, I also need to track the destination IPs as well. ipt_recent doesn't seem to track the destination IPs and lump all hits to all IPs together to one source IP. That is not what I want. Any other thoughts? Thanks. – TopQ Sep 15 '11 at 06:33
  • Yes, you should probably start a new question with exactly the problem you want to solve (and your environment, etc). Also, make clear that you're looking to control requests rather than just connections. Explain that you tried mod_evasive, and explain how you determined it didn't work for you. – DerfK Sep 15 '11 at 13:23
  • Actually I figured out a way by using --name so each destination IP has its own table to keep track of source IP. Thanks! – TopQ Sep 15 '11 at 17:54