====== Ubuntu - iptables - DNS query limiting ======
Analysis of DNS queries coming in was able to determine an average of 5 requests per second per IP.
This figure could be set as the limit for the firewall, however to prevent instances where a lot of valid requests come in a relatively short amount of time, it is better to not measure over a single second but an average over a longer period.
For example, as it's been determined that there is an average of 5 requests per second, then that would mean that over 10 seconds there would be an average of 50 requests.
To tighten the security even more, the firewall is also set to allow a maximum of 15 requests per second per IP.
#!/bin/bash
# This script limits the queries per second to 5/s
# with a burst rate of 15/s and does not require
# buffer space changes
# Requests per second
RQS="15"
# Requests per 10 seconds
RQH="50"
iptables --flush
iptables -A INPUT -p udp --dport 53 -m state --state NEW -m recent --set --name DNSQF --rsource
iptables -A INPUT -p udp --dport 53 -m state --state NEW -m recent --update --seconds 1 --hitcount ${RQS} --name DNSQF --rsource -j DROP
iptables -A INPUT -p udp --dport 53 -m state --state NEW -m recent --set --name DNSHF --rsource
iptables -A INPUT -p udp --dport 53 -m state --state NEW -m recent --update --seconds 7 --hitcount ${RQH} --name DNSHF --rsource -j DROP
----
===== Another method =====
The first rule resets the count and the timer.
The second rule ensures that excess packets are dropped.
iptables -I INPUT -p udp --dport 53 -i eth0 -m state --state NEW -m recent --set
iptables -I INPUT -p udp --dport 53 -i eth0 -m state --state NEW -m recent --update --seconds 15 --hitcount 30 -j DROP
----
===== Using String Matching =====
Firewall rules could be setup to protect against fake requests coming in.
Use an application such as **tcpdump** or **wireshark** to look at the individual incoming packets of fake requests. For example:
tcpdump -nvvxxi eth0 port 53
A snapshot of data coming in might show the following starting at offset 0x2C:
15: 01: 52.966446 IP (tos 0x0, ttl 179, id 14613, offset 0, flags [none], proto UDP (17), length 64)
184.154.66.179.16996> 176.9.26.150.53: [no cksum] 7490+ [1au] ANY? isc.org. ar:. OPT UDPsize = 4096 OK (36)
0x0000: 6c62 6dbc bb87 28c0 DA46 34a4 0800 4500
0x0010: 0040 3915 0000 B311 08ab b89a 42b3 b009
0x0020: 1a96 4264 0035 002c 0000 1d42 0100 0001
0x0030: 0001 0000 0000 0369 7363 7267 036f 0000
0x0040: ff00 0100 0029 1000 0000 8000 0000
Here is the cut from the above data from that data having the actual domain which can be found starting at offset 0x36 and it is isc.org in this case.
0100 0001
0x0030: 0001 0000 0000 0369 7363 7267 036f 0000
0x0040: ff00 0100 0029 1000 0000 8000 0000
Now lets ban that using string matching.
Since "." is a query for the root name servers, it has a very short query packet but a pretty long answer. The first rule drops requests for the root domain's name servers.
The second rule drops the specific source domain (in this example isc.org).
This removes the spaces, enclosed in two pipes and one has the right filter string.
iptables -t raw -I PREROUTING -p udp --destination-port 53 -m string --algo kmp --from 30 --hex-string "|010000010000000000000000020001|" -j DROP
iptables -t raw -I PREROUTING -p udp --destination-port 53 -m string --algo kmp --from 30 --hex-string "|0100000100000000000103697363036f72670000ff00010000291000000080000000|" -j DROP
See http://forum.slicehost.com/comments.php?DiscussionID=2970