[dnsdist] rules for flood protection?

Chris lists+pdns at gbe0.com
Sun Apr 19 05:34:59 UTC 2020


Hi,

On 18/4/20 11:26 pm, Marco Marzo via dnsdist wrote:
 > I haven't found much "done" around to protect against popular attacks.

Which attacks? I presume you are only talking about attacks at the DNS 
protocol level, but for starters:

* Filter traffic destined to the externally accessible IP's bound to 
dnsdist as far upstream as possible; if you are on the receiving end of 
a DDoS attack it's better to have the traffic dropped outright before 
even making it to the server especially for unused services. Low ports 
can be blocked with stateless ACL's on network hardware usually at line 
rate. Higher ports may require a stateful firewall though but very often 
I see attacks that are only destined to low ports (<1024).

* Make sure that your method of HA/load balancing will not be killed by 
a DDoS attack. For this reason I prefer to use ECMP to distribute the 
traffic to the IP's used for DNS services. If you are using a load 
balancer make sure that it can handle a large amount of 
sessions/connections (until the attack may be blocked by other methods). 
No point having rules in dnsdist to help block attacks if the traffic 
never makes it there in the first place.

* If using iptables on the dnsdist servers, skip stateful processing for 
DNS traffic.

* Make use of SO_REUSEPORT if the dnsdist server has the resources to 
handle large amounts of traffic. Send some traffic floods to the server 
to benchmark the difference with different numbers of listeners to find 
the optimum number for your server. You may also find that you will get 
better performance by running multiple dnsdist instances, each with 
multiple listeners (they can listen to the same IP('s) using 
SO_REUSEPORT as well).

* Use the dnsdist packet cache. Also perform benchmarking to find the 
optimum settings for your setup (number of shards).

* Craft any rules configured with dnsdist carefully. Adding rules does 
have a significant impact. As an example you may have 3 rules each with 
a regex - if you can combine those rules into a single regex it may be a 
bit more complex from a management point of view but you should find the 
additional capacity is worth it.

* Use rules to outright block traffic before processing other rules. As 
an example, if you don't require AXFR/IXFR/Notify/Update support 
(running a recursor? running authoritative that use other methods to 
replicate the zones such as MySQL replication?) that can be blocked 
outright either by dropping or returning a REFUSED response. A rule to 
do this may look like:

addAction(OrRule({QTypeRule(DNSQType.AXFR), QTypeRule(DNSQType.IXFR), 
OpcodeRule(DNSOpcode.Notify), OpcodeRule(DNSOpcode.Update)}), 
RCodeAction(DNSRCode.REFUSED))

If you do need support for those query types and the IP's sending those 
requests are known, add a nmg and filter based on that nmg so that all 
other sources are blocked.

* Make use of dynamic blocks, as an example:

local dbr = dynBlockRulesGroup()

--- Create a dynamic block rule for overall queries/second allowed
dbr:setQueryRate(
   ---- The number of queries/second to rate limit at
   150,
   ---- Set the measurement period over the last 10 seconds
   10,
   ---- Log the action to syslog
   "Exceeded query rate limit",
   ---- Add the block for 120 seconds
   120
)

--- Create a dynamic block rule to block queries that have resulted in a 
NXDOMAIN response
dbr:setRCodeRate(
   ---- Match the NXDOMAIN response
   DNSRCode.NXDOMAIN,
   ---- The number of queries/second to rate limit at
   75,
   ---- Set the measurement period over the last 10 seconds
   10,
   ---- Log the action to syslog
   "Exceeded NXD response rate",
   ---- Add the block for 120 seconds
   120
)

--- No dynamic block rule configured for SERVFAIL queries/second from hosts

--- Create a dynamic block rule to block inbound queries/second from 
hosts of the ANY type
dbr:setQTypeRate(
   ---- Match the ANY query type
   DNSQType.ANY,
   ---- The number of queries/second to rate limit at
   50,
   ---- Set the measurement period over the last 10 seconds
   10,
   ---- Log the action to syslog
   "Exceeded ANY rate",
   ---- Add the block for 120 seconds
   120
)

--- These IP ranges will never be blocked
dbr:excludeRange({
   ---- RFC1918 allocations
   "10.0.0.0/8",
   "172.16.0.0/12",
   "192.168.0.0/16"
})

--- Apply the dynamic block rules
function maintenance()
   dbr:apply()
end

* Add rules to manage requests of the ANY type.

* In addition to the dynamic blocks, use rules to limit the number of 
queries that may be sent from a source. You can whitelist your own 
prefixes to ensure that they do not get triggered. A rule can be added 
to force clients sending a certain range of QPS to TCP and then if 
exceeding that limit drop.

That's just a few things that are important in my mind, of course there 
are general recommendations like making sure the number of servers 
actually running dnsdist is sized appropriately, the number of backends 
is sized appropriately and so on.

Thanks


More information about the dnsdist mailing list