DNSSEC validation with the PowerDNS Recursor
Accessible resolver with Lua expansion options
PowerDNS is een Nederlands bedrijf (tegenwoordig onderdeel van Open-Exchange) dat over de afgelopen 20 jaar drie software-pakketten voor DNS heeft ontwikkeld:
PowerDNS is a Dutch company (now part of Open-Exchange), which has developed three DNS software packages in the last twenty years:
Written in C++, the software is scalable and fast. The code runs on all Unix-type systems, and the latest versions are available in ready-to-use form for most Linux and BSD distributions.
All three packages are published as open-source software under the GPLv2 licence. The company generates its income from consultancy, support and customisation activities. The user therefore has all the benefits of the classic open-source business model: open software that you can use 'for free' if you have the time and expertise, an active community of users and developers, and a commercial service provider to fall back on if the need arises. Alternatively, there is the PowerDNS Platform, a software suite based on the three software products.
It was the implementation of DNSSEC that opened the way for PowerDNS to make its big breakthrough: almost all major internet service providers that wanted to bulk-sign their domains did so using the PowerDNS Authoritative Server. DNSSEC validation wasn't added to Recursor until version 4.0. SIDN provided (financial) support to make that extension possible.
One particularly interesting feature is the Lua engine incorporated into both Authoritative Server and Recursor. Lua is an easy-to-learn script language that makes internal data structures readily accessible to operators. The use of Lua is considered later.
In this article, we look at the configuration of PowerDNS Recursor as a validating resolver on CentOS/RHEL. PowerDNS Authoritative Server is considered in a separate article.
The latest version of PowerDNS Recursor is 4.1.8. There have been no further releases of PowerDNS Authoritative Server since 4.1.5, but version 4.2.0 is in the pipeline (currently at the alpha stage).
The PowerDNS software is available to download as a ready-to-use package from the repo site. Packages are available for all the most widely used Linux distributions (Debian, Raspbian, Ubuntu, CentOS/RHEL and SLES).
Bear in mind that PowerDNS Recursor has supported DNSSEC only since version 4.0. Moreover, the first implementation wasn't entirely successful, due to the development team's efforts to create a tolerant validator. We therefore recommend using version 4.1 or higher.
The discussion presented below is based on PowerDNS Recursor version 4.1.8 on CentOS 7.6. The documentation for Recursor is available here.
Installation of PowerDNS Recursor begins with configuration of the EPEL and PDNS 4.1 repositories, plus installation of the yum-plugin-priorities package:
yum install epel-release yum-plugin-priorities && curl -o /etc/yum.repos.d/powerdns-rec-41.repo https://repo.powerdns.com/repo-files/centos-rec-41.repo
Next, the pdns-recursor package itself is installed:
yum install pdns-recursor
Although the latest version (4.1.8) is also available directly from the EPEL repository for CentOS/RHEL, this article is based on the version from the powerdns-rec-41 repository. The latest versions of Fedora (28, 29 and 30, the last of which is still under development) are distributed with Recursor version 4.1.8 included by means of updates.
The philosophy of the PowerDNS software builders is that, wherever possible, the complexities should be hidden from the user (operator) and that as much as possible should be automated. In line with that philosophy, the configuration in the '/etc/pdns-recursor/recursor.conf' file is considerably more limited and therefore <q>easier</q> than the configuration of Unbound. What's more, the settings in the file are confined to what may be described as typical server matters: daemon/process, access, logging, stats, caching and the network. The file includes hardly any DNSSEC/protocol-specific settings. As a result, configuration for a straightforward resolver can easily be done by a system/network operator without any specific DNSSEC expertise.
With the default configuration distributed with RHEL (where the only setting is setuid/setgid), all you have to do is open the recursor for (recursive) queries from addresses other than localhost, e.g. as follows:
local-address=192.0.2.5, 2001:db8::2:5 allow-from=192.0.2.0/24, 2001:db8::/64
|off||Disabled: DO/AD flags in queries are ignored; the DO flag is never set in outgoing (recursive) queries.|
|process-no-validate (default)||The DO flag is set in outgoing queries, but incoming DNSSEC records are not validated.|
|process||Validation is performed only if requested by the client by means of a DO/AD flag.|
|log-fail||Validation is always performed (and logged!), whether requested by the client or not.
It is therefore possible to see the proportion of domain names for which validation is requested (using an AD flag) and what proportion would generate SERVFAIL responses, before you set up your clients to request validation from the resolver.
|validate||Validation is always performed, and the result is always a SERVFAIL if a domain name can't be validated; an AD flag is returned to the client only if requested.
This level of validation enables the server-level protection of clients that have only a stub resolver or don't support DNSSEC at all.
With the default option 'dnssec=process-no-validate' enabled, Recursor does not perform validation, so the AD flag is never set in responses. However, if requested by the client by means of a DO flag, the DNSSEC-specific record types (RRSIG, DNSKEY, DS) are provided with each response. In that case, Recursor also always requests the records in question (recursively), so that the information can be retained in its cache.
It's advisable to use the default setting if you allow your end users to do their own DNSSEC validation. Then transmission of the AD flag does not depend on the security of the last mile. See this hands-on article for details of how to configure the validating Unbound resolver (in combination with DNSSEC-Trigger) on an end user's PC.
Clients that have only a stub resolver never do their own validation. Consequently, the default option is secure only if communication between resolver and client is via an internal/closed network.
Bogus domain names
The second DNSSEC-specific option in the '/etc/pdns-recursor/recursor.conf' file is 'dnssec-log-bogus'. This option enables the logging of all bogus domain names encountered by Recursor, even if the 'dnssec' option (the first option discussed above) is not set to 'process', 'log-fail' or 'validate'.
The ability to do that was very useful some years ago, when bogus domains were common due to mismatches between DNSKEYs and the key material registered with the parent domain (in the DS record). However, that problem has since been eradicated.
Administration of PowerDNS Recursor — including start-up — is via systemd:
[root@localhost ~]# systemctl status pdns-recursor.service ● pdns-recursor.service - PowerDNS Recursor Loaded: loaded (/usr/lib/systemd/system/pdns-recursor.service; disabled; vendor preset: disabled) Active: inactive (dead) Docs: man:pdns_recursor(1) man:rec_control(1) https://doc.powerdns.com [root@localhost ~]# systemctl enable pdns-recursor.service Created symlink from /etc/systemd/system/multi-user.target.wants/pdns-recursor.service to /usr/lib/systemd/system/pdns-recursor.service. [root@localhost ~]# systemctl start pdns-recursor.service [root@localhost ~]# systemctl status pdns-recursor.service ● pdns-recursor.service - PowerDNS Recursor Loaded: loaded (/usr/lib/systemd/system/pdns-recursor.service; enabled; vendor preset: disabled) Active: active (running) since Wed 2019-01-02 18:17:03 CET; 12s ago Docs: man:pdns_recursor(1) man:rec_control(1) https://doc.powerdns.com Main PID: 26998 (pdns_recursor) Tasks: 5 CGroup: /system.slice/pdns-recursor.service └─26998 /usr/sbin/pdns_recursor --daemon=no --write-pid=no --disable-syslog --log-timestamp=no Jan 02 18:17:03 localhost.localdomain pdns_recursor: Listening for TCP queries on 127.0.0.1:53 Jan 02 18:17:03 localhost.localdomain pdns_recursor: Set effective group id to 981 Jan 02 18:17:03 localhost.localdomain pdns_recursor: Set effective user id to 987 Jan 02 18:17:03 localhost.localdomain pdns_recursor: Launching 3 threads Jan 02 18:17:03 localhost.localdomain pdns_recursor: Done priming cache with root hints Jan 02 18:17:03 localhost.localdomain systemd: Started PowerDNS Recursor. Jan 02 18:17:03 localhost.localdomain pdns_recursor: Done priming cache with root hints Jan 02 18:17:03 localhost.localdomain pdns_recursor: Done priming cache with root hints Jan 02 18:17:03 localhost.localdomain pdns_recursor: Done priming cache with root hints Jan 02 18:17:03 localhost.localdomain pdns_recursor: Enabled 'epoll' multiplexer
Root KSK rollover
Correct installation of the current DNSSEC trust anchors warrants particular attention at the present time. The reason being that the KSK pair for the root zone has recently been rolled over. In other words, ICANN, the root zone's administrator, has replaced the previous (very first) cryptographic key pair at the base of the entire DNSSEC infrastructure (KSK-2010) with a new key pair (KSK-2017).
Originally, the rollover was supposed to take place a year earlier. However, shortly before the scheduled rollover date, ICANN postponed it, due to fears that many internet users would encounter problems if the rollover went ahead on the planned day. The rollover proper was rescheduled for 11 October 2018, exactly a year after the original date.
PowerDNS Recursor is not able to save (dynamic) trust anchors itself. The software initially has only the hard-coded trust anchors available to it. In other words, Recursor does not support RFC 5011, a protocol for automatically fetching trust anchors from the root servers and installing them.
Consequently, new trust anchors have to be added manually, unless the distribution maintainer has added them for you. Since the DNSSEC implementation in versions of PowerDNS Recursor older than 4.1.0 should no longer be used, the information presented here relates to the most recent releases only.
From version 4.0.5, the new trust anchor for KSK-2017 is built into the software. That can be verified using the following command:
[root@localhost ~]# rec_control get-tas Configured Trust Anchors: . 19036 8 2 49aac11d7b6f6446702e54a1607371607a1a41855200fd2ce1cdde32f24e8fb5 20326 8 2 e06d44b80b8f1d39a95c0b0d7c65d08458e880409bbc683457104237c7f8ec8d
The response shows that two trust anchors are indeed installed: with key IDs 19036 (KSK-2010) and 20326 (KSK-2017), respectively. The old trust anchor is still included because, strictly speaking, the root KSK rollover process is not yet entirely complete. One final step remains: deletion of the old trust anchor for KSK-2010 from the resolvers. That is scheduled to happen in the first quarter of 2019. Until the old trust anchor has been deleted, system operators shouldn't think of the rollover as done and dusted.
More trust anchors
If you need to add (further) trust anchors for whatever reason, you can do so on-the-fly using this command:
rec_control add-ta . 20326 8 2 \ E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D
However, to ensure that the new trust anchor is still available after any subsequent resolver restart, you need to add an 'addDS' command to the Lua script. The Lua command itself is then as follows:
addDS('.', "20326 8 2 E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D")
From version 4.2.0, the 'addDS' command has been replaced by 'addTA'. Details are available here and here. The DNSSEC-specific commands available in Recursor's Lua interface are considered later in this article. #link
Negative trust anchors
A negative trust anchor is used where you want to disable DNSSEC validation for a particular domain temporarily or indefinitely. You might want to do that if, for example, the domain's DNSSEC configuration is faulty and your users are complaining that the domain is unreachable (because it has correctly been blocked by the validating resolver).
To create an exception for the domain example.nl, the command would be as follows:
[root@localhost ~]# rec_control add-nta example.nl 'resolving issues' Added Negative Trust Anchor for example.nl with reason 'resolving issues'
A list of active negative trust anchors can be called up as follows:
[root@localhost ~]# rec_control get-ntas Configured Negative Trust Anchors: example.nl resolving issues
To delete the listed trust anchors, the command is:
[root@localhost ~]# rec_control clear-nta example.nl Removed Negative Trust Anchors for example.nl
Alternatively, a negative trust anchor can be permanently added to the configuration using the following Lua command:
addNTA('example.nl', "resolving issues")
Lua has its design flaws, but as an embedded engine it is very useful for providing users with a run-time scripting portal within your software. It enables developers to quickly and easily add new interfaces to their C-code.
The built-in Lua-facility (from version 4.0) makes PowerDNS Recursor particularly attractive if you want the ability to adjust your configuration dynamically: you can load, replace and unload scripts without interrupting the resolver's operational activities. The set-up has the significant limitation that only one script can be active at any given time: when you load a new script, any existing script's configuration is replaced.
Lua scripts are loaded (i.e. changed) and unloaded using the following two commands:
rec_control reload-lua-script <script_file> rec_control unload-lua-script
Lua start-up script
There is also a completely separate Lua start-up script, which is loaded when Recursor is started up. The start-up script is specified using the configuration option 'lua-config-file'. A suitable location for the file is '/etc/recursor-conf.lua'. The option has no default value; if you don't define one, no Lua configuration is loaded.
It's important to be aware that the Lua start-up script is loaded only once (when you start Recursor), whereas the Lua run-time script referred to above is run every time a query is handled.
If you want the Lua start-up script to run again, you can use the following command:
In de Lua-interface van de Recursor is het hele DNSQuestion object met al zijn velden beschikbaar gemaakt. Hetzelfde geldt voor de DNSHeader en de EDNSOptionView. Je kunt domeinnamen, IP-adressen en netmasks aanmaken, om die vervolgens te bevragen en te bewerken.
Daarnaast zijn er functies voor het opvragen van statistieken en het wegschrijven van log-berichten. En er is de mogelijkheid om hooks te installeren op diverse plaatsen in het proces van query naar response.
Een paar specifieke functies die we hier nog willen noemen:
addSortList: vergelijkbaar met het sortlist statement van BIND named, voor het sorteren van de records in een RRset-antwoord,bijvoorbeeld als 'load balancing'-strategie op A/AAA records
ondersteuning voor het RPZ-protocol (Response Policy Zone) om voor specifieke domeinnamen andere antwoorden te retourneren,typisch om domeinen die bekend staan als kwaadaardig geautomatiseerd te omzeilen
Voor wie liever met een JSON/REST API werkt is er de mogelijkheid om een webserver op te starten als onderdeel van de Recursor.
Via Recursor's Lua interface, the entire DNSQuestion object can be accessed, including all its fields. You can also access the DNSHeader and EDNSOptionView. It's possible to create domain names, IP addresses en netmasks, which can then be queried and edited.
The following functions warrant attention in this context:
addSortList: similar to the sort list statement in BIND named, for sorting the records in an RRset response, e.g. as a 'load balancing' strategy on A/AAA records
Anyone who prefers to work with a JSON/REST API can start up a web server within Recursor.
Finally, the 'rec_control' and Lua commands with specific relevance for DNSSEC are listed in the table below.
|add-ta||addTA||Add a trust anchor|
|clear-ta||clearTA||Delete a trust anchor|
|get-tas||Call up a list of current negative trust anchors|
|add-nta||addDS/addNTA||Add a negative trust anchor|
|clear-nta||clearDS/clearNTA||Delete a negative trust anchor|
|get-ntas||Call up a list of current negative trust anchors|
|reload-lua-config||Re-load the Lua start-up script|
|reload-lua-script||Re-load the Lua run-time script|
|unload-lua-script||Unload the Lua run-time script|
From version 4.2, the Lua commands addDS and clearDS have been replaced by addNTA and clearNTA, respectively. Version 4.2 also introduces a completely new command: readTrustAnchorsFromFile, which can be used to load multiple DNSKEY/DS records from a file (in Bind 'zone file' format).
PowerDNS Recursor is an accessible resolver that system/network operators can configure themselves, without needing specialist DNSSEC expertise. Its main difference from the Unbound resolver is that the settings in the configuration file are restricted to familiar server-related matters, whereas the configuration of Unbound involves a long list of options regarding both server-related and DNSSEC/protocol-specific matters. If you want to do more with PowerDNS Recursor, you can load separate start-up/run-time scripts for the built-in Lua engine.
Like Unbound's default configuration, the out-of-the-box configuration with the CentOS distribution (on which this article is based) is suitable for immediate use once the port lists have been edited.