PowerDNS Blog

An introduction to PowerDNS Recursor 3.7.0 | PowerDNS Blog

Written by Bert Hubert | Jan 22, 2015 5:00:00 AM

Today we released the first Release Candidate (RC) of PowerDNS Recursor 3.7.0. And although we do document (almost) all of our features, documentation is not the same as effectively informing our users of what our software can do.

In this blog, I’ll be highlighting some of the things that are new in 3.7.0, or which arrived recently in the 3.6 series.

Binding to 0.0.0.0 and ::

For a long time, PowerDNS Recursor did not support binding to the ‘any’ addresses, because Unix & POSIX semantics mean that a socket bound to such an any address has no control over which IP address it answers from. So for example, if your server is on 192.168.1.2 and 192.168.1.3, and question comes in on a socket bound to 0.0.0.0, the answer might go out to either the .2 or the .3 address – no matter what address the question arrived on!

Binding to 0.0.0.0 (and ::, of course) is very useful for load balancing purposes, so as of 3.7.0, we use some specific calls to implement ‘sendfromto()’ so we can force the source address to be correct. Just bind to 0.0.0.0 or :: and the Recursor will do the right thing.

Live graphs

The PowerDNS Recursor has supported our HTTP-based RESTful JSON API for a while now, although we have not publicized this sufficiently while we stabilized the API. In 3.7.0, this API was enhanced to enable it to power a rather impressive (even if we say so ourselves) live display of traffic, updating every second. It looks like this:

For now, development of this live website is proceeding faster than the PowerDNS release schedule, which means you can find the HTML for this page (which is independent of PowerDNS itself) on GitHub, where you can also find instructions on how to enable this live display in the stock version of Recursor 3.7.0.

Graphing as a service

We’ve explained this at length in a previous post, but with our built-in Carbon/Graphite support, it is a breeze to get pretty graphs of your own recursor, or, if you have problems to send your statistics our way. By having a live view on your system, we can quickly diagnose issues. Recommended!

Enhanced Lua scripting

For the last few months, the world of DNS has been about one thing only: attacks. Amplification attacks, reflection attacks and ‘infinite recursion queries’. Our vision is that PowerDNS should be as robust as possible by generally being fast and smart, and therefore be resilient against attacks. Sadly, some attacks can be rather nasty, and can’t be fended off with generic solutions.

PowerDNS has extensive support for Lua scripting, and in 3.7.0 we added a number of features intended to serve as the basis of “scripts against the attack of the day”.

Next to the existing hooks (preresolve, which intercepts a packet after parsing, postresolve which can edit a packet after the answer is in, and nxdomain and noerror which are called for those two results), we’ve added two new ones: ipfilter and preoutquery.

‘ipfilter’ hooks in before client packet parsing even starts, and allows you to block a packet before it has used a lot of CPU cycles. This is a great place to (dynamically) stop attacks.

‘preoutquery’ is a new kind of hook. It does not fire based on user (client) traffic, but before a remote nameserver is consulted. Frequently, attack traffic is for loads of random domain names, but in the end only a few (fixed) IP addresses are the target of the attack. If you know these addresses, you can trigger on them in ‘preoutquery’, and drop the query quickly before it used more CPU and network. But even better, once you know the client IP address that is performing the attack on you, you can block it with ipfilter!

Here is a sample script that does just that:

lethalgroup=iputils.newnmgroup()
lethalgroup:add("192.121.121.0/24") -- touch these nameservers and you die

blockset=iputils.newipset() -- which client IP addresses we block

function preoutquery(remoteip, domain, qtype)
--  print("pdns wants to ask "..remoteip:tostring().." about "..domain.." "..qtype.." on behalf of requestor "..getlocaladdress())
    if(lethalgroup:match(remoteip))
    then
--      print("We matched the group "..lethalgroup:tostring().."!", "killing query dead & adding requestor "..getlocaladdress().." to block list")
        blockset[iputils.newca(getlocaladdress())]=1
        return -3,{} --   -3 means 'kill'
    end
    return -1,{}         --   -1 means 'no opinion'
end


local delcount=0

function ipfilter(remoteip)
    delcount=delcount+1

    if((delcount % 10000)==0)
    then
--      print("Clearing blockset!")
        blockset=iputils.newipset()  -- clear it
    end

    if(blockset[remoteip] ~= nil) then
        return 1         -- block!
    end
    return -1                -- no opinion
end

This script blocks an attacker for the next 10000 queries if they ever ‘touched’ a namserver known to be involved in malicious traffic.

The ‘ipset’ used above is fast enough that a 10 million long list of blocked IP addresses doesn’t noticeably affect performance!

Improved ringbuffers

To investigate traffic, ringbuffers of the last x-thousand (by default 10000) queries or errors are available. Some sample output:

# rec_control top-queries
Over last 10000 entries:
4.04% e3191.dscc.akamaiedge.net.|A
4.04% e3191.dscc.akamaiedge.net.|AAAA
1.50% pool.ntp.org.|A
1.44% us-courier.push-apple.com.akadns.net.|A
1.25% twitter.com.|A
1.24% twitter.com.|AAAA
0.42% www.google.com.|A
(...)
77.40% rest

When under attack, typically a lot of random traffic comes in which makes it harder to see what is going on. For this purpose, we’ve added the ‘pub’ filters, which group queries via the Mozilla Public Suffix list:

# rec_control top-pub-queries
Over last 10000 entries:
5.26% google.com.|A
5.21% google.com.|AAAA
4.38% akamaiedge.net.|AAAA
4.08% akamaiedge.net.|A
2.35% gstatic.com.|A

Other interesting rings:

  • top-servfail-queries, listing the top queries leading to servfail responses. (top-pub-servfail-queries grouped)
  • top-largeanswer-remotes, listing top clients receiving large answers (as typically used in reflection attacks)
  • top-remotes, listing clients causing most queries
  • top-servfail-remotes, listing clients causing most servfail responses

Finally

We hope you enjoy these new features, and we’d love to hear your thoughts on if they do the job, or how they could be better!