DNSSEC on the PowerDNS Authoritative Server

PowerDNS is a Dutch company (now part of Open-Xchange), which has developed three DNS software packages in the last twenty years: * PowerDNS Authoritative Server * PowerDNS Recursor * dnsdist (a load balancer)

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; a macOS version is also available from Homebrew. 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.

DNSSEC

It was the implementation of DNSSEC (from version 3.0) 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 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 the Authoritative Server and the 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 DNSSEC features of the Authoritative Server. It is the DNSSEC support features in particular that make PowerDNS attractive to many operators. We therefore take you through basic installation of the Authoritative Server on a system running CentOS/RHEL. The Bind connector is also considered. Configuration of the Recursor to work as a validating resolver is considered in a separate article.

Contents

Part I Installation and configuration

Part II Management

Part III DNSSEC-specific options and commands

Part I Installation and configuration

1.1 Ready-to-use packages

At the time of writing (autumn 2019), the current version of the PowerDNS Authoritative Server is 4.2.0. The 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 and CentOS/RHEL). DNSSEC is supported by the Authoritative Server from version 3.0. When support was introduced, the signing of domains on other authoritative servers (e.g. BIND named, possibly in combination with OpenDNSSEC) was quite cumbersome. By contrast, PowerDNS adopted a flick-the-switch approach from the start. Operators could therefore sign all their domains with a single command. The DNSSEC configuration, the re-signing of the DNS records and the key management were then all handled automatically by the server. Because PowerDNS saved operators the trouble of contending with the complexity of DNSSEC, it benefited from the growing demand for the security extension and soon accounted for a high proportion of signed domains. The discussion presented below is based on PowerDNS Authoritative Server version 4.1.13 on CentOS 7.6. The documentation for the Authoritative Server is available here.

1.2 Installation

Installation of the Authoritative Server begins with configuration of the EPEL and PowerDNS Authoritative Server repositories, plus installation of the yum-plugin-priorities package:

  yum install epel-release yum-plugin-priorities &&
  curl -o /etc/yum.repos.d/powerdns-auth-41.repo
      https://repo.powerdns.com/repo-files/centos-auth-41.repo

Followed by the pdns and pdns-tools packages:

  yum install pdns pdns-tools

Although the PowerDNS software is also available directly from the EPEL repository for CentOS/RHEL, this article is based on the version from the powerdns-auth-41 repository. The latest versions of Fedora (29, 30 and 31, the last of which is still under development) are currently distributed with Authoritative Server versions 4.1.11 and 4.2.0 by means of updates. Alternatively, the packages can be downloaded directly from Kees Monshouwer's PDNS repository.

1.3 Backends

The Authoritative Server software includes numerous backends: connectors for various sources where the server can retrieve (and usually write) its zone information. The available backends, as well as the additional packages that you need to install for each, are listed in the table below.

The Oracle backend is not supplied with CentOS/RHEL. To avoid rights violations, you need to compilere it yourself. However, the backend should no longer be used anyway, because the backends for Oracle, MyDNS and OpenDBX have been removed from the software from version 4.3. Most operators are likely to set up the Authoritative Server with a MySQL/MariaDB backend, though.

1.4 Modes of Operation

The Authoritative Server supports various Modes of Operation:

  • Native replication (default): no notifications or zone transfers are performed; all updates utilise the replication mechanisms of the supporting databases (as with the Infoblox appliances within a Grid). According to the makers of PowerDNS, the mechanism has been shown to work for MySQL/MariaDB and Oracle, even over poor intercontinental connections.

  • Master: notifications are sent out to slaves, which can then request zone updates.

  • Supermaster: when a server sends a notification, a receiving server responds by automatically configuring itself as a (super)slave for that particular domain.

  • Incoming zone transfers can be filtered using a Lua script for each zone, which is then run for each resource record.

For native replication (the default), therefore, you don't need to do anything except set up unidirectional database replication from master to slaves. Furthermore, all backends can subsequently be configured as read-only (except those connected to a user/management frontend; typically an administrative hidden master).

1.5 MariaDB

The set-up discussed in this article uses MariaDB, the successor to (fork) provided by MySQL following Sun's takeover by Oracle. Most readers will probably have similar set-ups. Many of the major operators that switched to the PowerDNS Authoritative Server did so because of the DNSSEC support. The basic installation is therefore described in full below.

Our first step is to install the MariaDB software and the MySQL/MariaDB backend:

  yum install mariadb mariadb-server pdns-backend-mysql

Next, the MariaDB server is (permanently) enabled and started:

  [root@localhost ~]# systemctl enable mariadb
  Created symlink from /etc/systemd/system/multi-user.target.wants/mariadb.service
      to /usr/lib/systemd/system/mariadb.service.
  [root@localhost ~]# systemctl start mariadb

  [root@localhost ~]# systemctl status mariadb
  ● mariadb.service - MariaDB database server
    Loaded: loaded (/usr/lib/systemd/system/mariadb.service; enabled; vendor preset: disabled)
    Active: active (running) since Mon 2019-02-04 15:31:09 CET; 1min 17s ago
 Main PID: 30431 (mysqld_safe)
    CGroup: /system.slice/mariadb.service
            ├─30431 /bin/sh /usr/bin/mysqld_safe --basedir=/usr
            └─30592 /usr/libexec/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib64/mysql/plugin --log...

  Feb 04 15:31:07 localhost.localdomain mariadb-prepare-db-dir[30215]: MySQL manual for more instructions.
  Feb 04 15:31:07 localhost.localdomain mariadb-prepare-db-dir[30215]: Please report any problems at http://mariadb.org/jira
  Feb 04 15:31:07 localhost.localdomain mariadb-prepare-db-dir[30215]: The latest information about MariaDB is available...g/.
  Feb 04 15:31:07 localhost.localdomain mariadb-prepare-db-dir[30215]: You can find additional information about the MyS...at:
  Feb 04 15:31:07 localhost.localdomain mariadb-prepare-db-dir[30215]: http://dev.mysql.com
  Feb 04 15:31:07 localhost.localdomain mariadb-prepare-db-dir[30215]: Consider joining MariaDB's strong and vibrant com...ty:
  Feb 04 15:31:07 localhost.localdomain mariadb-prepare-db-dir[30215]: https://mariadb.org/get-involved/
  Feb 04 15:31:07 localhost.localdomain mysqld_safe[30431]: 190204 15:31:07 mysqld_safe Logging to '/var/log/mariadb/ma...og'.
  Feb 04 15:31:07 localhost.localdomain mysqld_safe[30431]: 190204 15:31:07 mysqld_safe Starting mysqld daemon with dat...ysql
  Feb 04 15:31:09 localhost.localdomain systemd[1]: Started MariaDB database server.
  Hint: Some lines were ellipsized, use -l to show in full.

Having done that, we can initialise a fresh installation of MariaDB as follows:

[root@localhost ~]# mysql_secure_installation

  NOTE: RUNNING ALL PARTS OF THIS SCRIPT IS RECOMMENDED FOR ALL MariaDB
        SERVERS IN PRODUCTION USE!  PLEASE READ EACH STEP CAREFULLY!

  In order to log into MariaDB to secure it, we'll need the current
  password for the root user.  If you've just installed MariaDB, and
  you haven't set the root password yet, the password will be blank,
  so you should just press enter here.

  Enter current password for root (enter for none): 
  OK, successfully used password, moving on...

  Setting the root password ensures that nobody can log into the MariaDB
  root user without the proper authorisation.

  Set root password? [Y/n] y
  New password: ************
  Re-enter new password: ************
  Password updated successfully!
  Reloading privilege tables..
   ... Success!

  By default, a MariaDB installation has an anonymous user, allowing anyone
  to log into MariaDB without having to have a user account created for
  them.  This is intended only for testing, and to make the installation
  go a bit smoother.  You should remove them before moving into a
  production environment.

  Remove anonymous users? [Y/n] y
   ... Success!

  Normally, root should only be allowed to connect from 'localhost'.  This
  ensures that someone cannot guess at the root password from the network.

  Disallow root login remotely? [Y/n] y
   ... Success!

  By default, MariaDB comes with a database named 'test' that anyone can
  access.  This is also intended only for testing, and should be removed
  before moving into a production environment.

  Remove test database and access to it? [Y/n] y
   - Dropping test database...
   ... Success!
   - Removing privileges on test database...
   ... Success!

  Reloading the privilege tables will ensure that all changes made so far
  will take effect immediately.

  Reload privilege tables now? [Y/n] y
   ... Success!

  Cleaning up...

  All done!  If you've completed all of the above steps, your MariaDB
  installation should now be secure.

  Thanks for using MariaDB!

1.6 Database user and definition

The following step involves using the MariaDB monitor to create a user account ('pdns') and the database itself (here also 'pdns') for the Authoritative Server:

  [root@localhost ~]# mysql -u root -p
  Enter password: ************
  Welcome to the MariaDB monitor.  Commands end with ; or \g.
  Your MariaDB connection id is 10
  Server version: 5.5.60-MariaDB MariaDB Server

  Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

  Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

  MariaDB [(none)]> CREATE USER 'pdns'@'localhost' IDENTIFIED BY '************';
  Query OK, 0 rows affected (0.00 sec)

  MariaDB [(none)]> GRANT DELETE, INSERT, SELECT, UPDATE ON pdns.*
      TO 'pdns'@'localhost';
  Query OK, 0 rows affected (0.00 sec)

  MariaDB [(none)]> FLUSH PRIVILEGES;
  Query OK, 0 rows affected (0.00 sec)

  MariaDB [(none)]> CREATE DATABASE pdns;
  Query OK, 1 row affected (0.00 sec)

  MariaDB [(none)]> USE pdns;
  Database changed
  MariaDB [pdns]>

The dedicated database tables for the Authoritative Server can then be created. For that, we have adopted the definitions published on the PowerDNS website [1]:

CREATE TABLE domains (
  id                    INT AUTO_INCREMENT,
  name                  VARCHAR(255) NOT NULL,
  master                VARCHAR(128) DEFAULT NULL,
  last_check            INT DEFAULT NULL,
  type                  VARCHAR(6) NOT NULL,
  notified_serial       INT DEFAULT NULL,
  account               VARCHAR(40) CHARACTER SET 'utf8' DEFAULT NULL,
  PRIMARY KEY (id)
) Engine=InnoDB CHARACTER SET 'latin1';

CREATE UNIQUE INDEX name_index ON domains(name);


CREATE TABLE records (
  id                    BIGINT AUTO_INCREMENT,
  domain_id             INT DEFAULT NULL,
  name                  VARCHAR(255) DEFAULT NULL,
  type                  VARCHAR(10) DEFAULT NULL,
  content               VARCHAR(64000) DEFAULT NULL,
  ttl                   INT DEFAULT NULL,
  prio                  INT DEFAULT NULL,
  change_date           INT DEFAULT NULL,
  disabled              TINYINT(1) DEFAULT 0,
  ordername             VARCHAR(255) BINARY DEFAULT NULL,
  auth                  TINYINT(1) DEFAULT 1,
  PRIMARY KEY (id)
) Engine=InnoDB CHARACTER SET 'latin1';

CREATE INDEX nametype_index ON records(name,type);
CREATE INDEX domain_id ON records(domain_id);
CREATE INDEX ordername ON records (ordername);


CREATE TABLE supermasters (
  ip                    VARCHAR(64) NOT NULL,
  nameserver            VARCHAR(255) NOT NULL,
  account               VARCHAR(40) CHARACTER SET 'utf8' NOT NULL,
  PRIMARY KEY (ip, nameserver)
) Engine=InnoDB CHARACTER SET 'latin1';


CREATE TABLE comments (
  id                    INT AUTO_INCREMENT,
  domain_id             INT NOT NULL,
  name                  VARCHAR(255) NOT NULL,
  type                  VARCHAR(10) NOT NULL,
  modified_at           INT NOT NULL,
  account               VARCHAR(40) CHARACTER SET 'utf8' DEFAULT NULL,
  comment               TEXT CHARACTER SET 'utf8' NOT NULL,
  PRIMARY KEY (id)
) Engine=InnoDB CHARACTER SET 'latin1';

CREATE INDEX comments_name_type_idx ON comments (name, type);
CREATE INDEX comments_order_idx ON comments (domain_id, modified_at);


CREATE TABLE domainmetadata (
  id                    INT AUTO_INCREMENT,
  domain_id             INT NOT NULL,
  kind                  VARCHAR(32),
  content               TEXT,
  PRIMARY KEY (id)
) Engine=InnoDB CHARACTER SET 'latin1';

CREATE INDEX domainmetadata_idx ON domainmetadata (domain_id, kind);


CREATE TABLE cryptokeys (
  id                    INT AUTO_INCREMENT,
  domain_id             INT NOT NULL,
  flags                 INT NOT NULL,
  active                BOOL,
  content               TEXT,
  PRIMARY KEY(id)
) Engine=InnoDB CHARACTER SET 'latin1';

CREATE INDEX domainidindex ON cryptokeys(domain_id);


CREATE TABLE tsigkeys (
  id                    INT AUTO_INCREMENT,
  name                  VARCHAR(255),
  algorithm             VARCHAR(50),
  secret                VARCHAR(255),
  PRIMARY KEY (id)
) Engine=InnoDB CHARACTER SET 'latin1';

CREATE UNIQUE INDEX namealgoindex ON tsigkeys(name, algorithm);

1.7 Configuration of the PowerDNS Authoritative Server

Having set up the MariaDB server and our 'pdns' database, we can move on to configuring the Authoritative Server itself. The first step is to add the following 'launch' command for the MySQL/MariaDB backend to the configuration file '/etc/pdns/pdns.conf':

  launch=gmysql
  gmysql-host=127.0.0.1
  gmysql-port=3306
  gmysql-user=pdns
  gmysql-password=************
  gmysql-dbname=pdns
  gmysql-dnssec=yes

Note the final statement, which enables DNSSEC support. Everything else (timing, re-signing, key management, etc) is then dealt with automatically. Our newly configured 'gmysql' backend will be loaded when the Authoritative Server is (re)started. Note that, midway through the configuration template, there is a stray 'launch=' statement. Therefore, if you get the response "Unable to launch, no backends configured for querying", it's a good idea to start by checking that your configuration file is clean. Other initial configuration options include the IP addresses that the Authoritative Server is to monitor for incoming DNS queries and the address that the server itself is to use to initiate outbound connections:

local-address=192.0.2.3 local-ipv6=2001:db8::2:3 query-local-address=192.0.2.3 query-local-address6=2001:db8::2:3

In addition, all Modes of Operation except for native mode (default) need to be explicitly enabled in the configuration file:

master=yes of: slave=yes of: slave=yes, in combinatie met: superslave=yes

1.8 Generic backends

From version 4.0.0, PowerDNS provides generic backends for MySQL/MariaDB, ODBC, Oracle, PostgreSQL and SQLite3. The generic backends contain general SQL commands, which you can easily modify using the configuration file. So, for example, you can tailor the backend database interface to your own set-up and database schema. The command for viewing all the SQL queries built into the generic MySQL/MariaDB backend is as follows:

  pdns_server --no-config --launch=gmysql --config

The list of options is too long to reproduce in full. However, by way of example, here is the query used to add a new domain to the database:

#################################
# gmysql-insert-zone-query	
#
# gmysql-insert-zone-query=insert into domains
#   (type,name,master,account,last_check,notified_serial)
#   values(?,?,?,?,NULL,NULL)

Details of the generic interface are provided here. The linked page describes how to add records to the 'domain' and 'records' tables and how to compile the administrative 'supermasters' table for various server configurations (native/Slave/Superslave/Master). New slave domains are retrieved and activated within 60 seconds ('slave-cycle-interval'). Thereafter, the TTL of the SOA record determines the timing of the next check. On a Master server, the 'slave-cycle-interval' determines the timing of NOTIFY message transmission.

1.9 Starting the PowerDNS Authoritative Server

If we now start the Authoritative Server, it has to connect to the previously configured database via the 'gmysql' backend:

[root@localhost ~]# systemctl enable pdns.service
Created symlink from /etc/systemd/system/multi-user.target.wants/pdns.service
    to /usr/lib/systemd/system/pdns.service.

[root@localhost ~]# systemctl start pdns.service

[root@localhost pdns]# systemctl status pdns.service
● pdns.service - PowerDNS Authoritative Server
   Loaded: loaded (/usr/lib/systemd/system/pdns.service; enabled; vendor preset: disabled)
   Active: active (running) since Fri 2019-08-16 17:07:06 CEST; 4s ago
     Docs: man:pdns_server(1)
           man:pdns_control(1)
           https://doc.powerdns.com
 Main PID: 3678 (pdns_server)
    Tasks: 8
   CGroup: /system.slice/pdns.service
           └─3678 /usr/sbin/pdns_server --guardian=no --daemon=no --disable-syslog --log-timestamp=no --write-pid=no

Aug 16 17:07:03 localhost.localdomain pdns_server[3678]: TCP server bound to 0.0.0.0:53
Aug 16 17:07:04 localhost.localdomain pdns_server[3678]: TCPv6 server bound to [::]:53
Aug 16 17:07:04 localhost.localdomain pdns_server[3678]: PowerDNS Authoritative Server 4.1.13 (C) 2001-2018 PowerDNS.COM BV
Aug 16 17:07:04 localhost.localdomain pdns_server[3678]: Using 64-bits mode. Built using gcc 4.8.5 20150623 (Red Hat ...81d.
Aug 16 17:07:04 localhost.localdomain pdns_server[3678]: PowerDNS comes with ABSOLUTELY NO WARRANTY. This is free sof...n 2.
Aug 16 17:07:06 localhost.localdomain pdns_server[3678]: Polled security status of version 4.1.13 at startup, no know...: OK
Aug 16 17:07:06 localhost.localdomain pdns_server[3678]: Creating backend connection for TCP
Aug 16 17:07:06 localhost.localdomain systemd[1]: Started PowerDNS Authoritative Server.
Aug 16 17:07:06 localhost.localdomain pdns_server[3678]: About to create 3 backend threads for UDP
Aug 16 17:07:06 localhost.localdomain pdns_server[3678]: Done launching threads, ready to distribute questions
Hint: Some lines were ellipsized, use -l to show in full.

As you will see from the following, the Authoritative Server then monitors UDP and TCP on all network interfaces for port number 53, using both IPv4 and IPv6:

[root@localhost ~]# netstat -lntup | grep pdns
tcp     0    0 0.0.0.0:53    0.0.0.0:*    LISTEN    3757/pdns_server
tcp6    0    0 :::53         :::*         LISTEN    3757/pdns_server
udp     0    0 0.0.0.0:53    0.0.0.0:*              3757/pdns_server
udp6    0    0 :::53         :::*                   3757/pdns_server

Part II Management

2.1 Database interface

With the Authoritative Server, the database usually functions as an interface between the DNS server infrastructure and the administrative environment. Domains and associated records are created and modified in the database, then the information is automatically retrieved by the Authoritative Server for publication and, where relevant, distribution to slaves. Considering the complexity of the DNS, the database schema for the Authoritative Server (see above) is surprisingly simple. The 'records' table has two fields specifically for DNSSEC:

  • 'auth': this field must be 1/true for all DNS records for which the zone in question is authoritative, except NS and glue records for delegated zones

  • 'ordername': this field must be completed if NSEC/NSEC3 is used, with the value depending on the record type and the NSEC/NSEC3 mode used

However, we advise using the management port, the web interface or the web API, all of which are described below. That will be more straightforward, less error-sensitive and easier to maintain.

In the event of an inward zone transfer or use of the command 'pdnsutil rectify-zone', the two fields are automatically completed by the Authoritative Server. The default installation of PowerDNS uses NSEC. However, we recommend enabling NSEC3 by adding a NSEC3PARAM record, so as to protect against name walking:

  pdnsutil set-nsec3 example.nl '1 0 1 -'

2.2 Management port

The Authoritative Server offers a management port on TCP port 53000 and on the Unix socket '/var/run/pdns.controlsocket'. The two interfaces offer exactly the same functionality for server-oriented management tasks (server configuration, statistics and the initiation of DNS transfers and notifications). In order to open the management port for localhost and local networks, you need to add the following statements to the configuration file:

  tcp-control-address=127.0.0.1
  tcp-control-port=53000
  tcp-control-range=127.0.0.0/8, 10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12,
      ::1/128, fe80::/10
  tcp-control-secret=************

The location of the Unix socket is configured as follows:

  socket-dir=/var/run/

For direct access to the interface, you can use the command 'telnet localhost 53000'. After entering the password, you can only give a single command before the connection is broken. Normally, however, you will access the port using the 'pdns_control' command. The general syntax is as follows:

pdns_control <command> <arguments>

Visit the man page or use the command 'pdns_control --help'. Other commands are used to immediately forward the output received via the management port. See, for example, the command 'pdns_control help':

[root@localhost ~]# pdns_control help
ccounts                          get cache statistics
current-config                   retrieve the current configuration
list-zones [master|slave|native] show list of zones
notify <domain>                  queue a notification
notify-host <domain> <host>      notify host for specific domain
purge [<record>]                 purge entries from packet cache
qtypes                           get QType statistics
quit                             quit daemon
rediscover                       discover any new zones
reload                           reload all zones
remotes                          get top remotes
respsizes                        get histogram of response sizes
retrieve <domain>                retrieve slave domain
rping                            ping instance
set <var> <value>                set config variables
show <statistic>                 show a specific statistic or * to get a list
token-login <module> <slot> <pin> Login to a PKCS#11 token
uptime                           get instance uptime
version                          get instance version

For the avoidance of doubt, it is worth clarifying that the help is not obtained from the pdns_control program itself, but from a dump of the 'help' command sent via the control socket to the server. The same thing can be done remotely via the network interface, by entering the following command:

  pdns_control --remote-address=192.0.2.3 --remote-port=53000
      --secret=************ help

2.3 Web interface

In addition to the text-oriented management port described, the Authoritative Server features a built-in web server. The procedure for activating the web server and opening it for localhost and local networks (in our example, on port 8081) is as follows:

  webserver=yes
  webserver-address=127.0.0.1
  webserver-port=8081
  webserver-allow-from=127.0.0.0/8, 10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12,
    ::1/128, fe80::/10
  webserver-password=************
  webserver-print-arguments=no
  webserver-loglevel=normal  [vanaf versie 4.2.0]

As you'll see on your monitor (after opening port 8081 in your firewall), and as illustrated in the screenshot below, the web server provides only a port for (performance) monitoring of your server.

2.4 Web API

The Authoritative Server does, however, offer a fully featured interface in the form of a web API based on REST and JSON according to the OpenAPI Specification (Swagger). The interface uses the same web server as the web interface, but in addition to statistics it has functions for editing zone information, metadata and DNSSEC key material. The web API is enabled as follows:

api=yes api-key=************

In this article, it isn't practicable to illustrate how to program with the Authoritative Server's web API, but the availability of the web API can easily be tested like so:

curl -v -H 'X-API-Key: ************' http://127.0.0.1:8081/api/v1/servers/localhost

[offerman@vegas DNSSEC2]$ curl -v -H 'X-API-Key: ************'
    http://192.0.2.3:8081/api/v1/servers/localhost
*   Trying 192.0.2.3...
* TCP_NODELAY set
* Connected to 192.0.2.3 (192.0.2.3) port 8081 (#0)
> GET /api/v1/servers/localhost HTTP/1.1
> Host: 192.0.2.3:8081
> User-Agent: curl/7.53.1
> Accept: */*
> X-API-Key: ************
> 
< HTTP/1.1 200 OK
< Access-Control-Allow-Origin: *
< Connection: close
< Content-Length: 248
< Content-Security-Policy: default-src 'self'; style-src 'self' 'unsafe-inline'
< Content-Type: application/json
< Server: PowerDNS/4.1.13
< X-Content-Type-Options: nosniff
< X-Frame-Options: deny
< X-Permitted-Cross-Domain-Policies: none
< X-Xss-Protection: 1; mode=block
<
* Closing connection 0

For a detailed description of the web API, refer to the PowerDNS documentation or the machine-readable Swagger interface specification here.

2.5 Bind backend

Before considering the Authoritative Server's the DNSSEC-specific functionality, it is useful to take a look at the Bind backend. After all, flick-the-switch DNSSEC support was the main reason why many operators migrated their DNS infrastructures to PowerDNS [1, 2]. The Bind backend allows you to load a (limited) 'named.conf' configuration file and associated zone files. Because data is retrieved from a static file set, rather than a dynamic database, the Authoritative Server can use the connector only to load zone information (read-only backend, except for slave zones). On the CentOS/RHEL platform, the Bind backend is a standard component of the Authoritative Server and does not therefore need to be installed separately. In the set-up described here, the connector is used in combination with the MySQL/MariaDB connector. The set-up is configured as follows:

 launch=bind

  bind-config=/etc/pdns/named.conf
  bind-check-interval=600

That requires two simple configuration files 'named.conf' and 'db.example.nl' to be created and placed in the directory '/etc/pdns/'. The Bind backend supports only a handful of the normal options in 'named.conf':

  • options

    • directory

    • also-notify

  • zone

    • file

    • type

    • masters

    • also-notify

Here are the contents of the 'named.conf' file that we used to initiate the DNS server for our domain example.nl:

  options {
      directory "/etc/pdns";
      };
  zone "example.nl" {
      type master;
      file "db.example.nl";
      };

From the log file, we can then see that the relevant zone has been successfully loaded:

Sep 15 18:02:47 localhost pdns_server: [bindbackend] Parsing 1 domain(s), will report when done
Sep 15 18:02:47 localhost pdns_server: [bindbackend] Done parsing domains, 0 rejected, 1 new, 0 removed

2.6 Combined backends

For more complex migrations, multiple backends can be combined by listing them in the 'launch' statement:

 launch=gmysql,bind

When responding to incoming DNS queries, the Authoritative Server will then query the various backends in the order that they are listed. If you want to use multiple backends of the same type (typically multiple servers), you can name them separately:

 launch=gmysql,gmysql:server2,bind

In the example, we are using both the primary MySQL/MariaDB server and a second database server. You then configure the second server by including the server name in the configuration options, as follows:

  gmysql-server2-host=192.0.2.14
  gmysql-server2-port=3306
  gmysql-server2-user=pdns
  gmysql-server2-password=************
  gmysql-server2-dbname=pdns
  gmysql-server2-dnssec=yes

It is important to note, however, that the Bind backend cannot be included in the 'launch' statement more than once. Any additional zone files therefore have to be added to the existing 'named.conf' configuration. The makers of PowerDNS advise against using combined backends unless you have a good reason for doing so. Their rationale is that multiple backend use is not a clearly defined option, so things may not work as expected following future updates.

2.7 More options for the Bind backend

Other Bind-specific options that can be included in the Authoritative Server configuration file:

  • bind-dnssec-db: A file for recording DNSSEC meta-information; required if the server in question functions as a slave for a zone that is already signed (elsewhere)

  • bind-check-interval: This option is used mainly when the information in the zone files is subject to frequent change; however, it also enables you to easily set up a combination with OpenDNSSEC, where zone files are automatically signed and recorded for automatic retrieval by the Authoritative Server

In addition, a hybrid Bind mode is available for Masters. In hybrid mode, the zone information is obtained from the Bind backend, but signing and key management are handled by the Authoritative Server. The DNSSEC-related records are saved in another backend, typically SQLite3 (embedded). The set-up therefore follows naturally from the combination of backends described above, where the various backends are queried in order when incoming queries are processed. As with combined backends, the makers of PowerDNS advise against using the hybrid mode unless you have a good reason for doing so.

2.8 Migration to PowerDNS

The PowerDNS documentation suggests various fairly straightforward ways of migrating old DNS information to the Authoritative Server:

  • By first creating all existing domains on the Authoritative Server as slaves, then switching them to master/native mode on the Authoritative Server following the zone transfer

  • By converting the zone files themselves to (generic) SQL using the 'zone2sql' tool

  • By using the 'pdnsutil load-zone' command to read the zone files directly

Another option is the 'pdnsutil b2b-migrate' command, which you can use to migrate a zone from one backend to another within the Authoritative Server itself. The command also enables you to use the Authoritative Server as a tool for performing a wide variety of backend/platform conversions on your zone files.

Deel III DNSSEC-specific options and commands

The third and final part of this article looks at the DNSSEC-specific options and commands not considered above.

3.1 Cryptographic algorithms

We'll start by going over the configuration options for the cryptographic algorithms:

PowerDNS provides a list of supported algorithms, but the actual availability of the listed algorithms depends on the options specified when compiling the software. A list can be retrieved using the 'pdnsutil list-algorithms' command:

  [root@localhost ~]# pdnsutil list-algorithms
  DNSKEY algorithms supported by this installation of PowerDNS:
  5 - RSASHA1
  7 - RSASHA1-NSEC3-SHA1
  8 - RSASHA256
  10 - RSASHA512
  13 - ECDSAP256SHA256
  14 - ECDSAP384SHA384
  15 - ED25519

Therefore, although the Authoritative Server supports various other cryptographic algorithms for DNSSEC [1, 2], we advise (continued) use of ECDSA256 [3, 4]. If you'd like to know more about the differences between the cryptographic algorithms, take a look at the dedicated section of our 'DNSSEC – FAQ' page.

3.2 Random generator

When deciding which cryptographic algorithm to use, it is important to bear in mind that DSA-based algorithms are particularly susceptible to the effects of a poorly initialised random generator. They are liable to provide insecure key pairs if the generator is not well initialised. For a production system, therefore, the following option should always be used.

  entropy-source=/dev/random

Completely different random generators can be selected using the 'rng' option:

  rng=openssl

For both security and scalability reasons, it is best to use a bump-in-the-wire architecture based on OpenDNSSEC and an HSM (Hardware Security Module) for a larger DNS infrastructure. The installation and configuration of OpenDNSSEC is considered in a separate article (in Dutch only). The Authoritative Server itself currently offers experimental support for PKCS#11.

3.3 More DNSSEC-specific options

The remaining DNSSEC-specific configuration options are as follows:

direct-dnskey=no

If this option is enabled, the DNSKEY-, CDS- and CDNSKEY records are also loaded from the zone information

dnssec-key-cache-ttl=30

The number of seconds that DNSSEC keys retrieved from the database are retained in the cache

max-nsec3-iterations=0

Maximum number of NSEC3 iterations; nowadays you always set this to 0

max-signature-cache-entries

Maximum number of signatures that can be retained in the cache

signing-threads=3

Number of threads that the server can use for signing domains

3.4 The 'pdnsutil' command

In this subsection, we consider the 'pdnsutil' command: the command that, as an operator, you are likely to make most use of for managing (signed) zones, whether directly (via the command line) or indirectly (in scripts). The 'pdnsutil' command acts on the database directly, implying that you can easily give commands for execution via the network. For command execution, the program uses the local PDNS configuration file. The 'pdnsutil' command used to be called 'pdnssec', which is reflected in the associated functionality. The DNSSEC-specific functions supported by the command are summarised below. In the following subsection, we'll illustrate the use of the various commands for signing the zone example.nl.

activate-zone-key < zone > < key-id > Activate the < key-id > key for < zone >
deactivate-zone-key < zone > < key -id> Deactivate the < key-id > key for < zone >
add-zone-key < zone > KSK/ZSK [active/inactive] < keybits > < algorithm > Generate a new key pair for < zone > and use 'active' to sign the zone immediately
create-bind-db < file > Create the (embedded) DNSSEC database (SQLite3) for the hybrid Bind mode
(Don not forget to specify this file with the bind-dnssec-db configuration option)
disable-dnssec < zone > Disable DNSSEC for < zone >
export-zone-dnskey < zone > < key-id > Export (dump) DNSKEY and DS records for key < key-id > in < zone >
export-zone-ds < zone > Export (dump) all KSK DS records in < zone >
(After exporting the records, they can be uploaded to the operator of the parent domain for publication)
export-zone-key < zone > < key-id > Export (dump) all key information (including the private key) for < key-id > in < zone >
generate-zone-key KSK/ZSK [algorithm] [keybits] Generate (and dump) a new key pair (default cryptographic algorithm: ECDSA256)
import-zone-key < zone > < file > KSK/ZSK Import a key pair from < file > in < zone >
list-keys [zone] List all key pairs (for < zone >)
rectify-zone < zone > Recreate the auth and ordername database fields for < zone >
rectify-all-zones Recreate the 'auth' and 'ordername' database fields for all zones
remove-zone-key < zone > < key-id > Delete the key < key-id > from < zone >
secure-zone < zone > Sign < zone > (using the default settings)
secure-all-zones [increase-serial] Sign all zones (and increase the serial number)
set-nsec3 < zone > ['< hash-algorithm > < flags > < iterations > < salt >'] [narrow] Give the NSEC3 settings for < zone >;
the parameter string contains the information to be included in the NSEC3PARAM record;
the
unset-nsec3 < zone > Convert an NSEC3 setting for < zone > back to NSEC
set-presigned < zone > Use the provided in-zone) DNSSEC records for < zone >
unset-presigned < zone > Stop using the in-zone DNSSEC records for < zone >
show-zone < zone > Display all DNSSEC-related settings for < zone >
set-publish-cds < zone > [digestalgos] Publish the CDS records for < zone >;
[digestalgos] contains a list of hash algorithms for (C)DS records (default: 2: SHA-256)
unset-publish-cds < zone > Deactivate publication of CDS records for < zone >
set-publish-cdnskey < zone > Publish the CDNSKEY records for < zone >
unset-publish-cdnskey < zone > Deactivate publication of CDNSKEY records for < zone >

For information about the remaining functions of the 'pdnsutil' command, see the man man page.

3.5 Signing a zone

Having taken all the steps described to install, configure and get to know the PowerDNS Authoritative Server, we are ready to actually sign our zone example.nl. The following procedure assumes an installation as described above, where the zone is loaded using the Bind backend. Because the Bind backend is read-only, we'll create a hybrid Bind configuration, where the zone information is obtained from the Bind backend, but signing and key management are handled by the Authoritative Server. The signatures (RRSIG records) and the key material for the domain are stored in an (embedded) SQLite3 database. The first step is to create the database (if it doesn't already exist). We can then generate the KSK and ZSK pairs for signing the zone example.nl. The entire process is set out below, complete with a number of commands for displaying the current status.

[root@localhost ~]# pdnsutil create-bind-db /etc/pdns/bind-db.sqlite3

[root@localhost ~]# pdnsutil add-zone-key example.nl KSK inactive ecdsa256
Sep 19 17:50:40 [bindbackend] Done parsing domains, 0 rejected, 1 new, 0 removed
Added a KSK with algorithm = 13, active=0
1

[root@localhost ~]# pdnsutil add-zone-key example.nl ZSK inactive ecdsa256
Sep 19 17:51:07 [bindbackend] Done parsing domains, 0 rejected, 1 new, 0 removed
Added a ZSK with algorithm = 13, active=0
2

[root@localhost ~]# pdnsutil list-keys example.nl
Sep 19 17:52:31 [bindbackend] Done parsing domains, 0 rejected, 1 new, 0 removed
Zone          Type    Size    Algorithm          ID    Location     Keytag
--------------------------------------------------------------------------
example.nl    ZSK     256     ECDSAP256SHA256    2     cryptokeys    7300
example.nl    KSK     256     ECDSAP256SHA256    1     cryptokeys    4293

[root@localhost ~]# pdnsutil set-nsec3 example.nl '1 0 0 -'
NSEC3 set, please secure and rectify your zone.

[root@localhost ~]# pdnsutil set-publish-cdnskey example.nl
[root@localhost ~]# pdnsutil set-publish-cds example.nl

[root@localhost ~]# pdnsutil activate-zone-key example.nl 1
[root@localhost ~]# pdnsutil activate-zone-key example.nl 2

[root@localhost ~]# pdnsutil check-zone example.nl
Sep 19 17:53:14 [bindbackend] Done parsing domains, 0 rejected, 1 new, 0 removed
Checked 19 records of 'example.nl', 0 errors, 0 warnings.

[root@localhost ~]# pdnsutil show-zone example.nl
Sep 19 18:01:16 [bindbackend] Done parsing domains, 0 rejected, 1 new, 0 removed
This is a Master zone
Last SOA serial number we notified: 0 != 2017101300 (serial in the database)
Metadata items: 
	NSEC3PARAM	1 0 0 -
	PUBLISH-CDNSKEY	1
	PUBLISH-CDS	1,2
Zone has hashed NSEC3 semantics, configuration: 1 0 0 -
keys: 
ID = 2 (ZSK), flags = 256, tag = 7300, algo = 13, bits = 256    Active ( ECDSAP256SHA256 )
ID = 1 (KSK), flags = 257, tag = 4293, algo = 13, bits = 256    Active ( ECDSAP256SHA256 )
KSK DNSKEY = example.nl. IN DNSKEY 257 3 13 y5JN0NxY8vYMyKDSR3D+6lJDJmQUAp1SsCRaMSOPSAMGvslhBOy4GXQxeMpxmhvM4aFdHAm09TtGwirSBS/ULw==
    ; ( ECDSAP256SHA256 )
DS = example.nl. IN DS 4293 13 1 78776d2a17958d3bac818b26172477119f4c2213
    ; ( SHA1 digest )
DS = example.nl. IN DS 4293 13 2
    a31facbfd1dff2003b91bb4348bd923dc9dabab3987b70371af1e6fc8eb7eda2
    ; ( SHA256 digest )
DS = example.nl. IN DS 4293 13 4 3bd91b55b497a11a27efbb63bd97180b3946186257f6f02b3ce780d7c837fb419206ba70891c3fd45a4a3071f9e83172
    ; ( SHA-384 digest )

[root@localhost ~]# pdnsutil export-zone-dnskey example.nl 1
example.nl IN DNSKEY 257 3 13 y5JN0NxY8vYMyKDSR3D+6lJDJmQUAp1SsCRaMSOPSAMGvslhBOy4GXQxeMpxmhvM4aFdHAm09TtGwirSBS/ULw==

[root@localhost ~]# pdnsutil export-zone-ds example.nl 
Sep 19 18:07:06 [bindbackend] Done parsing domains, 0 rejected, 1 new, 0 removed
example.nl. IN DS 4293 13 1
    78776d2a17958d3bac818b26172477119f4c2213 ; ( SHA1 digest )
example.nl. IN DS 4293 13 2
    a31facbfd1dff2003b91bb4348bd923dc9dabab3987b70371af1e6fc8eb7eda2
    ; ( SHA256 digest )
example.nl. IN DS 4293 13 4 3bd91b55b497a11a27efbb63bd97180b3946186257f6f02b3ce780d7c837fb419206ba70891c3fd45a4a3071f9e83172
    ; ( SHA-384 digest )

Exactly what key information is required to form a cryptographic link (in the chain of trust) with the parent domain depends on the registry. In our role as operator of the .nl top-level domain, we ask our registrars to provide DNSKEY records, not DS records. By generating DS records ourselves from the DNSKEY records provided, we can control which hash algorithm is used. If you go for a hybrid set-up like the one illustrated above, don't forget to add the location of the SQLite3 database to the Authoritative Server configuration:

  bind-dnssec-db=/etc/pdns/bind-db.sqlite3

3.6 Signing everything at once

If you want to sign an existing zone in one go, on the basis of the defaults, you can do the whole job with just two commands:

  pdnsutil secure-zone example.nl

  pdnsutil rectify-zone example.nl

It's also possible to do the same for all domains at once:

  pdnsutil secure-all-zones

  pdnsutil rectify-all-zones

If you compare the simplicity of that procedure with the way DNSSEC is configured in other DNS server software, it's easy to see why the PowerDNS Authoritative Server has proved so popular with operators looking to introduce DNSSEC (where the .nl zone is concerned, often motivated by SIDN's incentive scheme for registrars).

3.7 Dynamic Lua records

Finally, it is worth taking a look at the Authoritative Server's built-in Lua engine. 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. Using the built-in Lua facility (from version 4.2), you can make DNS server responses dependent on current conditions. That's done by incorporating Lua-code strings in your DNS records, causing the server response to be determined dynamically when a query about a given record is received. By way of illustration, here is a simple example:

  www    IN    LUA    A    "pickclosest({'192.0.2.14','198.51.100.14'})"

The effect of that code string is that the response includes details of the nearest server to the enquirer. To that end, the GeoIP backend is used to ascertain the smallest (physical) distance between the enquirer and a host. In order to use dynamic Lua records, you have to enable the relevant configuration option as follows:

  enable-lua-records=yes

Lua records can be enabled for an individual zone like so:

  ENABLE-LUA-RECORDS = 1

There's another setting, which causes the scripts to be assigned a shared context (per thread) for execution, rather than being handled separately. That option is activated as follows:

  enable-lua-records=shared

Because the contents of the records are determined dynamically, Lua records cannot be combined with pre-signed zones. Consequently, the Authoritative Server signs DNS responses exclusively on-the-fly, implying that the private key has to be on the public servers as well. The intention is for the dynamic Lua records to be standardised, so that in due course this feature can also be implemented by other DNS server software. For more information about dynamic Lua records, use this link.

3.8 In conclusion

Because the PowerDNS Authoritative Server is quite extensive, it can take a while to get to know it. However, the software's architecture is well designed: the DNS server itself functions as a central hub, with a variety of ports arranged around it. Backend connectors provide interfaces with the supporting databases. Transfers to other Authoritative Servers are easily performed by means of database replication. As well as the direct database interface, there is a web interface and an API, plus a control socket with associated command-line tools. Standout features of the PowerDNS Authoritative Server include flick-the-switch support for DNSSEC (note that this article includes no information about timing, re-signing or key management), dynamic Lua records and the ability to convert zone information from one format to another by transferring it between the relevant backends. More information about the PowerDNS Authoritative Server is available here [1, 2, 3].