Prevent DOS with iptables

Of course there are several types of DOS attacks , in this post I will demonstrating the use if iptables to limit the traffic on port 80.
The goal is to keep your web server “responsive” to legitimate traffic, but to throttle back on excessive (potential DOS) traffic.
In this demonstration iptables is configured :

  1. The default policy is ACCEPT (to prevent lockout in the event of flushing the rules with iptables -F).
  2. “Legitimate” traffic is then allowed. In this example I am allowing traffic only on port 80.
  3. All other traffic is then blocked at the end of the INPUT chain (the final rule in the INPUT chain is to DROP all traffic).


The rules I will demonstrate are as follows:

First rule : Limit NEW traffic on port 80

sudo iptables -A INPUT -p tcp –dport 80 -m state –state NEW -m limit –limit 50/minute –limit-burst 200 -j ACCEPT

Lets break that rule down into intelligible chunks.
-p tcp –dport 80 => Specifies traffic on port 80 (Normally Apache, but as you can see here I am using nginx).
-m state NEW => This rule applies to NEW connections.
-m limit –limit 50/minute –limit-burst 200 -j ACCEPT =>This is the essence of preventing DOS.

  • “–limit-burst” is a bit confusing, but in a nutshell 200 new connections (packets really) are allowed before the limit of 50 NEW connections (packets) per minute is applied.

For a more technical review of this rule, see this netfilet page. Scroll down to a bit to the “limit” section.

Second rule – Limit established traffic

This rule applies to RELATED and ESTABLISHED all traffic on all ports, but is very liberal (and thus should not affect traffic on port 22 or DNS).
If you understood the above rule, you should understand this one as well.

sudo iptables -A INPUT -m state –state RELATED,ESTABLISHED -m limit –limit 50/second –limit-burst 50 -j ACCEPT

In summary, 50 ESTABLISHED (and/or RELATED) connections (packets really) are allowed before the limit of 50 ESTABLISHED (and/or RELATED) connections (packets) per second is applied.
Do not let that rule fool you, although it seems very open, it does put some limits on your connections.
Test it for yourself, try using the first rule with and without the second rule.

Full set of rules

After the above commands, here is the complete set of rules I am testing:

iptables-save
# Generated by iptables-save v1.4.4 on --
*nat
:PREROUTING ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT
# Completed on --
# Generated by iptables-save --
*mangle
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
COMMIT
# Completed on --
# Generated by iptables-save v1.4.4 on --
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state RELATED,ESTABLISHED -m limit --limit 50/sec \
--limit-burst 50 -j ACCEPT
-A INPUT -p icmp -m limit --limit 1/sec -j ACCEPT
-A INPUT -p tcp -m tcp --dport 80 -m state --state NEW -m limit --limit 50/min \
--limit-burst 200 -j ACCEPT
-A INPUT -j LOG
-A INPUT -j DROP
-A FORWARD -j DROP
-A OUTPUT -o lo -j ACCEPT
COMMIT
# Completed on --

This rule set is for demonstration only and is NOT a complete set of rules for a web server. Do no use this rule set unmodified on a production server.

Testing the rule set

Human interaction

Open Firefox, point it to your web page. The web page should load nice and fast.
Hit F5 repetitively, load the page as fast as you can. Your web site should remain nice and responsive.
So far, so good, we want our site to remain responsive.

Simulated DOS

Actual DOS attacks are many times faster then humans, here I will use ab.
See this link or the Apache documentation for information of ab.

Baseline, without the above 2 rules

ab -n 100 -c 10 http://bodhi's_test_server.com/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking bodhi's_test_server.com (be patient).....done
Server Software:        nginx
Server Hostname:       bodhi's_test_server.com
Server Port:            80
Document Path:          /
Document Length:        59786 bytes
Concurrency Level:      10
Time taken for tests:   13.174 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Total transferred:      6002700 bytes
HTML transferred:       5978600 bytes
Requests per second:    7.59 [#/sec] (mean)
Time per request:       1317.369 [ms] (mean)
Time per request:       131.737 [ms] (mean, across all concurrent requests)
Transfer rate:          444.98 [Kbytes/sec] received
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:      122  129   2.2    128     134
Processing:  1151 1182  19.1   1177    1260
Waiting:      125  132   8.2    128     170
Total:       1280 1310  19.3   1305    1390
Percentage of the requests served within a certain time (ms)
  50%   1305
  66%   1313
  75%   1316
  80%   1321
  90%   1328
  95%   1354
  98%   1386
  99%   1390
 100%   1390 (longest request)

Notice:
Requests per second: 7.59 [#/sec] .
Total time for requests: 13 seconds .
(Data) Transfer rate: 444.98 [Kbytes/sec] .

With the above rules

First attempt:

ab -n 100 -c 10 http://bodhi's_test_server.com/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking bodhi's_test_server.com (be patient)...
apr_poll: The timeout specified has expired (70007)
Total of 99 requests completed

Oh no ! timed out, LOL
Second attempt (I reduced the number of requests to 90):

ab -n 90 -c 10 http://bodhi's_test_server.com/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking bodhi's_test_server.com (be patient).....done
Server Software:        nginx
Server Hostname:       bodhi's_test_server.com
Server Port:            80
Document Path:          /
Document Length:        59786 bytes
Concurrency Level:      10
Time taken for tests:   69.684 seconds
Complete requests:      90
Failed requests:        0
Write errors:           0
Total transferred:      5402430 bytes
HTML transferred:       5380740 bytes
Requests per second:    1.29 [#/sec] (mean)
Time per request:       7742.658 [ms] (mean)
Time per request:       774.266 [ms] (mean, across all concurrent requests)
Transfer rate:          75.71 [Kbytes/sec] received
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:      123  128   4.3    127     155
Processing:  1036 6269 10081.4   1921   51059
Waiting:      125 1240 5908.7    128   49656
Total:       1159 6396 10081.1   2044   51186
Percentage of the requests served within a certain time (ms)
  50%   2044
  66%   2981
  75%   5478
  80%   7047
  90%  20358
  95%  27356
  98%  48218
  99%  51186
 100%  51186 (longest request)

Notice :
Requests per second: 1.29 [#/sec] (mean)
Total time for requests: 69 seconds.
(Data) Transfer rate: 75.71 [Kbytes/sec] [Kbytes/sec].

For those unfamiliar with ab, that is a “minor” DOS

For comparison, here is what ab can do to the server (iptables was flushed [disabled]):

ab -n 1000 -c 100 http://bodhi's_test_server.com/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking bodhi's_test_server.com (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests
Server Software:        nginx
Server Hostname:       bodhi's_test_server.com
Server Port:               80
Document Path:          /
Document Length:        58708 bytes
Concurrency Level:      100
Time taken for tests:   59.324 seconds
Complete requests:      1000
Failed requests:        945
   (Connect: 0, Receive: 0, Length: 945, Exceptions: 0)
Write errors:           0
Total transferred:      59190450 bytes
HTML transferred:       58945935 bytes
Requests per second:    16.86 [#/sec] (mean)
Time per request:       5932.368 [ms] (mean)
Time per request:       59.324 [ms] (mean, across all concurrent requests)
Transfer rate:          974.37 [Kbytes/sec] received
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:      127  908 817.9    788    8016
Processing:   735 4779 1805.2   4368   15707
Waiting:      128  981 827.2    811   12143
Total:       1058 5687 1880.8   5269   17450
Percentage of the requests served within a certain time (ms)
  50%   5269
  66%   5899
  75%   6340
  80%   6863
  90%   8078
  95%   9001
  98%  10937
  99%  11730
 100%  17450 (longest request)

Notice :
Requests per second: 16.86 [#/sec]
Total time for requests: 69 seconds.
(Data) Transfer rate: 974.37 [Kbytes/sec] .
As you can see, the server has no problem dishing out 974.37 [Kbytes/sec] !!!

Closing remarks

Hopefully you now understand this “simple” example limiting a DOS on port 80.
With these rules your web site remains responsive to human interaction in firefox. Go ahead, hit F5 (refresh the page) as fast as you can, see if you can get your web page to slow down =) .
The difference is that as with a DOS attack, ab is hitting the server faster then you can with F5 , so your site is responsive to “normal” activity, but blocks DOS.
Obviously this is but one example and there are several types of DOS attacks. The goal is to demonstrate the use of iptables using a few “simple” rules.
You task is to take this knowledge and apply it to you own server.