[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