IPTables has to be one of the tools that I use the most on my day to day work. The default firewall tool chain on Linux has a lot of options to filter pretty much any traffic you wish.
In this article we will show you how to block DNS requests (domain names + request types) via IPTables. Enjoy!
DNS is binary protocol, where the domain name and the record type (A, AAAA, MX) are all encoded into the DNS query, and response, packets. If you were trying to filter www.example.com, you would not see the whole domain name in the packets.
What you would see is:
Where the domain name is broken down into each sub section (www, example and com) with the sub section length before it.
Simple, right? The string "www" has 3 characters, so its length is written as |03|.
That’s how the specs specify how the qname should be used in the protocol. To give another example, this is how test.mysite.defragged.com would appear:
Test has 4 bytes in length, followed by mysite with 6 bytes in length, etc.
Make note that the numeric values are hexadecimal values, not decimals. For example, if a text string had 10 characters it would be presented as 0a and 100 characters would be represented as 64.
An easy way to verify the hexadecimal value is to use a decimal to hexadecimal converter.
With this basic knowledge we can block DNS requests via iptables by leveraging the hex-string module. DNS requests use port 53/UDP by default, so if we want to block www.example.com, we would do:
/sbin/iptables -I INPUT -p udp --dport 53 -m string --hex-string "|03|www|07|example|03|com|" --algo bm -j DROP
See what we did?
We passed the string to the hex-string module, broke down into multiple sections: length, sub domain, length, sub domain, etc. A powerful feature of hex-string is that it automatically converts normal strings into hex internally, making our life a lot easier when writing rules (we didn’t have to convert www into hex, for example). However, if you look at your IPTables rule lists, you would see them all encoded into hex:
# iptables -nvL 0 0 DROP udp -- * * 0.0.0.0/0 0.0.0.0/0 udp dpt:53 STRING match "|03777777076578616d706c6503636f6d|" ALGO name bm TO 65535
In addition to the domain name, if you want to restrict by a specific DNS record, you can add them after the domain name (000001 for A requests, 000006 for SOA requests, 0000ff for ANY requests, etc). So to block A records for www.example.com you would do:
/sbin/iptables -I INPUT -p udp --dport 53 -m string --hex-string "|03|www|07|example|03|com|000001|" --algo bm -j DROP
I recommend commenting those rules so you can easily find them later when running iptables -nvL (remember that it will convert to hex).