Blocking DNS Amplification attacks using IPtables and/or fail2ban

Updated 11 January 2019: Fixed syntax based on comments. Thank you!


If you are managing a Linux server, you’ve probably heard about DNS amplification attacks which make use of misconfigured DNS servers. DNS amplification is a DDoS technique which uses a large reply by DNS resolving the target. This is accomplished by spoofing the query with the source IP of the target victim to ask for a large DNS record, such as an ANY reply of the ROOT record or isc.org, which is most commonly found. The request itself is usually around 60-70 bytes, while the reply is as much as 2-3K. That’s why it’s called amplification. It will not only make your network participate in the attack, but it will also consume your bandwidth. More details can be found here.

Blocking these kinds of attacks can be tricky. However, there are some basic iptables rules that block most of it, using them in combination with fail2ban. As usual, your mileage might vary. The commands below were tested and executed on Ubuntu Server 16.04 LTS 64-bit.

Basically, it comes all down to adding these two IPtables rules:

iptables -A INPUT -p udp --dport 53 -m string --from 40 --algo bm --hex-string '|0000FF0001|' -m recent --set --name dnsanyquery 
iptables -A INPUT -p udp --dport 53 -m string --from 40 --algo bm --hex-string '|0000FF0001|' -m recent --name dnsanyquery --rcheck --seconds 60 --hitcount 3 -j DROP
iptables -A INPUT -p tcp --dport 53 -m string --from 52 --algo bm --hex-string '|0000FF0001|' -m recent --set --name dnsanyquery 
iptables -A INPUT -p tcp --dport 53 -m string --from 52 --algo bm --hex-string '|0000FF0001|' -m recent --name dnsanyquery --rcheck --seconds 60 --hitcount 3 -j DROP

Source: https://wiki.opennic.org/opennic/tier2security

The first iptables rule looks for the incoming udp packets on port 53 and searches the first 50 of packet for hex string “0000FF0001” (which is equivalent to an ANY query).
The second iptables rule drops the packet if the source ip and query type (in this case “ANY”) matches and occurred more than one time in the past second.

Make sure to save your iptables rules, using something like iptables-persistent, so that they stick when you reboot your server.

In case this approach doesn’t work for you, try using the following alternative, which makes use of Fail2ban instead of IPtables.

First edit the file /etc/fail2ban/jail.conf and add the following contents:

[iptables-dns]
enabled = true
ignoreip = 127.0.0.1
filter = iptables-dns
action = iptables-multiport [name=iptables-dns, port="53",
protocol=udp]
logpath = /var/log/iptables/dns_reqs.log
bantime = 86400
findtime = 120
maxretry = 1
[named-refused-udp]
enabled = true
[named-refused-tcp]
enabled = true

Next, create a new fail2ban filter by creating a new file called /etc/fail2ban/filter.d/iptables-dns.conf and adding the following contents to it:

[Definition]

failregex = fw-dns.*SRC=<HOST> DST
failregex = ^.* security: info: client #.*: query \(cache\)
'./(NS|A|AAAA|MX|CNAME)/IN' denied

ignoreregex =

After doing so, check using fail2ban-client status if you see the ‘iptables-dns’ jail listed. If fail2ban refuses to start, check your regex for typos using the following command:

fail2ban-regex /var/log/kern.log /etc/fail2ban/filter.d/iptables-dns.conf

That’s all folks! Any feedback or suggestions? Let me know in the comments!

Recommended further reading: A Realistic Approach and Mitigation Techniques for Amplifying DDOS Attack on DNS in Proceedings of 10th Global Engineering, Science and Technology Conference 2-3 January, 2015, BIAM Foundation, Dhaka, Bangladesh, ISBN: 978-1-922069-69-6 by Muhammad Yeasir Arafat, Muhammad Morshed Alam and Feroz Ahmed.

27 thoughts on “Blocking DNS Amplification attacks using IPtables and/or fail2ban

  1. A copy/paste of your regex results in:

    No ‘host’ group in ‘^.* security: info: client #.*: query \(cache\) ‘./(NS|A|AAAA|MX|CNAME)/IN’ denied’

    What’s the fix?

    1. Hi. The syntax should be correct as I’m currently using it on three different servers. Please double check the quotes, those might get messed up while copy-pasting. Thank you

  2. Using Ubuntu 16.04 TLS and Fail2Ban v0.10.1 i get the following message when i do the fail2ban-regex command as described in the post:

    Use failregex filter file : iptables-dns, basedir: /etc/fail2ban
    ERROR: No failure-id group in ‘^.* security: info: client #.*: query \(cache\) ‘./(NS|A|AAAA|MX|CNAME)/IN’ denied’

    Fail2ban does however start and show the jail as active, is this suppose to happen or do i need to change something?

      1. Hi Freek.

        I’m facing the same problem as Xeltor and darpachief comments.
        I’ve checked that iptables-extensions are in fact installed, loaded and working, as iptables doesn’t have any problem.

        The problem is fail2ban regex itself, something is wrong here:
        ^.* security: info: client #.*: query \(cache\) ‘./(NS|A|AAAA|MX|CNAME)/IN’ denied

        Can you please check if having problems too with this command on your servers?
        fail2ban-regex /var/log/kern.log /etc/fail2ban/filter.d/iptables-dns.conf

        Thank you!

  3. This statement is incorrect: searches the first 50 of packet for hex string “0000FF0001”

    It does not search the first 50. It searches beginning at offset 50.
    Furthermore it is not the offset of the packet but the offset beginning at the IP header (IP tables does not process the Ethernet header portion of the packet).

    https://www.centos.org/forums/viewtopic.php?t=62148

  4. Hello Freek,

    what about securing ipv6 ? do you have a guide on rate limiting etc for ipv6 as well? I am hosting a pi-hole VPS with ipv6 enabled and concerned that the above guide will only stop attacks on ipv4.

    1. Hi Yusuf,

      Sorry for my delayed reply.

      Great question! I haven’t looked into it, but perhaps substituting ‘iptables’ by ‘ip6tables’ might do the trick.
      Additionally, fail2ban also support IPv6 so that approach might work as well.

      Please let me know your findings!

      Kind regards,
      Freek

  5. I’m having the same issue as Arien and Xelter. Have checked carefully to ensure that nothing got messed up with the copy and paste. Would like to use your instructions to prevent my public pihole from being used for DNS amplification attacks. Running Ubuntu 18.04.1 and Fail2Ban 0.10.2

    Thanks for your great work!

    1. Hi Humble,

      Sorry for the delayed reply.

      I’ve updated the post with a fixed syntax based on the comments. Please let me know if that works for you.

      Kind regards,
      Freek.

  6. Here’s a transcript:

    > root@perky:/home/scott# fail2ban-regex /var/log/kern.log /etc/fail2ban/filter.d/iptables-dns.conf
    >
    > Running tests
    > =============
    >
    > Use failregex filter file : iptables-dns, basedir: /etc/fail2ban
    > ERROR: No failure-id group in ‘^.* security: info: client #.*: query \(cache\) ‘./(NS|A|AAAA|MX|CNAME)/IN’ denied’
    >

    1. Hi time2lose,

      I’ve updated the post with a fixed syntax based on the comments. Please let me know if that works for you.

      Kind regards,
      Freek

    1. Hi Jorge,

      I’ve updated the post with a fixed syntax based on the comments. Please let me know if that works for you.

      Kind regards,
      Freek

      1. Ok, Thabk you. I will test them.
        I can see you are not logging to /var/log/kern.log anymore, where do you configure logging to /var/log/iptables/dns_reqs.log ?

      2. Thanks for your reply Freek.

        What I meant is, Where do you configure logging to /var/log/iptables/dns_reqs.log?
        You use to log to /var/log/kern.log with the old iptables rule with -j LOG –log-prefix “fw-dns ” –log-level 7.
        Now I see you have a new “logpath” but I don’t where are you sending logs to this new file since the first iptables rule was deleted

      3. Logging is only needed for the fail2ban approach, because it monitors logfiles and acts upon them. The IPtables approach doesn’t need any logging, but you could if you wanted to, for example to monitor how often a rule is hit/triggered. Sorry for the confusion!

  7. Thank you for the updates Freek, however upon implementing the new notation Fail2Ban fails to start with the regex check telling me:

    >Running tests
    >=============
    >
    >Use failregex filter file : iptables-dns, basedir: /etc/fail2ban
    >Wrong config file: While reading from ‘/etc/fail2ban/filter.d/iptables-dns.conf’ [line 4]: option ‘failregex’ in section ‘Definition’ already exists
    >ERROR: failed to load filter /etc/fail2ban/filter.d/iptables-dns.conf

    Thank so much for helping us get to the bottom of this. I really want to send these DDOS bots packing!

  8. Using Debian 9 with fail2ban v0.9.6

    # fail2ban-regex /var/log/kern.log /etc/fail2ban/filter.d/iptables-dns.conf

    Running tests
    =============

    Use failregex filter file : iptables-dns, basedir: /etc/fail2ban
    Traceback (most recent call last):
    File “/usr/bin/fail2ban-regex”, line 34, in
    exec_command_line()
    File “/usr/lib/python3/dist-packages/fail2ban/client/fail2banregex.py”, line 599, in exec_command_line
    if not fail2banRegex.start(opts, args):
    File “/usr/lib/python3/dist-packages/fail2ban/client/fail2banregex.py”, line 499, in start
    if not self.readRegex(cmd_regex, ‘fail’):
    File “/usr/lib/python3/dist-packages/fail2ban/client/fail2banregex.py”, line 280, in readRegex
    if not reader.read():
    File “/usr/lib/python3/dist-packages/fail2ban/client/configreader.py”, line 274, in read
    return ConfigReader.read(self, self._file)
    File “/usr/lib/python3/dist-packages/fail2ban/client/configreader.py”, line 89, in read
    ret = self._cfg.read(name)
    File “/usr/lib/python3/dist-packages/fail2ban/client/configreader.py”, line 185, in read
    config_files_read = SafeConfigParserWithIncludes.read(self, config_files)
    File “/usr/lib/python3/dist-packages/fail2ban/client/configparserinc.py”, line 206, in read
    fileNamesFull += self._getIncludes(filenames)
    File “/usr/lib/python3/dist-packages/fail2ban/client/configparserinc.py”, line 151, in _getIncludes
    fileNamesFull += self.__getIncludesUncached(filename, seen)
    File “/usr/lib/python3/dist-packages/fail2ban/client/configparserinc.py”, line 168, in __getIncludesUncached
    parser, i = self._getSharedSCPWI(resource)
    File “/usr/lib/python3/dist-packages/fail2ban/client/configparserinc.py”, line 130, in _getSharedSCPWI
    i = cfg.read(filename, get_includes=False)
    File “/usr/lib/python3/dist-packages/fail2ban/client/configparserinc.py”, line 251, in read
    return SafeConfigParser.read(self, fileNamesFull, encoding=’utf-8′)
    File “/usr/lib/python3.5/configparser.py”, line 696, in read
    self._read(fp, filename)
    File “/usr/lib/python3.5/configparser.py”, line 1089, in _read
    fpname, lineno)
    configparser.DuplicateOptionError: While reading from ‘/etc/fail2ban/filter.d/iptables-dns.conf’ [line 4]: option ‘failregex’ in section ‘Definition’ already exists

    Starting fail2ban in debug mode:
    # fail2ban-client -x start
    ERROR Failed during configuration: While reading from ‘/etc/fail2ban/filter.d/iptables-dns.conf’ [line 4]: option ‘failregex’ in section ‘Definition’ already exists

Leave a Reply

Your email address will not be published. Required fields are marked *