Hero Image

PowerDNS (Auth)

Authoritive Nameservers

I have the following going for me:

All are running Ubuntu 18.10 (cosmic) with the following software from the default repositories:

  • PowerDNS 4.1 Authoritive Nameserver
  • MariaDB Server 10.3

Using a SQL-backend makes us able to enable the HTTP API, which opens up a whole new world of managing your domains.
However, I will be mainly managing and editing zones with pdnsutil. You should really familiarise yourself with it, as it's really powerful and your one-stop tool for everything.

Installation of software (all nodes)

First, you should install the database-server we're going to use.

MariaDB/MySQL-server

Do not use SQLite for this! I repeat: Do NOT use SQLite for this. It often causes problems on production systems!

MariaDB 10.1 and higher also come with Galera Cluster integrated. You should, however, not use it if you don't have an odd amount of nodes of at least 3, set up in specific ways, etc. Additionally, if you're having a world-wide cluster of them like me, latency will already not be great.. and needing a VPN (suggestion: tinc), will add even more latency. I've used it in the past and it worked, but it had a lot of caveats/problems. You're better off not using it.

MariaDB is a drop-in replacement for MySQL, and with the way MySQL is going, it's being ditched by other distributions as well (like Debian).
You can also install MySQL here, it won't change anything for the rest of the article here. You should set a password for the (MySQL-)root user here when asked:

apt install mariadb-server

Now, start by generating a password:

< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c${1:-32};echo;

Paste it somewhere in a text file or something for now, you'll need it a few times later.

Open a privileged MySQL-prompt (this file contains credentials used by Ubuntu's system scripts, for easy and uniform access):

mysql --defaults-extra-file=/etc/mysql/debian.cnf

Set up the PowerDNS-account in MySQL with it:

GRANT ALL PRIVILEGES ON powerdns.* TO 'powerdns'@'localhost' IDENTIFIED BY 'password_you_created_earlier';
CREATE DATABASE powerdns;
USE powerdns;

Next, fill the database with the proper tables as explained on https://doc.powerdns.com/authoritative/backends/generic-mysql.html (recommended). If you're in a hurry, and starting with a fresh database, you can use my local copy (powerdns.sql) (fastest):

wget -O- https://finalx.nl/powerdns.sql | mysql --defaults-extra-file=/etc/mysql/debian.cnf powerdns

Or, alternatively copy/paste from here:

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 UNSIGNED 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);

ALTER TABLE records ADD CONSTRAINT `records_domain_id_ibfk` FOREIGN KEY (`domain_id`) REFERENCES `domains` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE comments ADD CONSTRAINT `comments_domain_id_ibfk` FOREIGN KEY (`domain_id`) REFERENCES `domains` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE domainmetadata ADD CONSTRAINT `domainmetadata_domain_id_ibfk` FOREIGN KEY (`domain_id`) REFERENCES `domains` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE cryptokeys ADD CONSTRAINT `cryptokeys_domain_id_ibfk` FOREIGN KEY (`domain_id`) REFERENCES `domains` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;

PowerDNS 4.1 Authoritive Nameserver

Next, install PowerDNS and its MySQL-backend:

apt install pdns-server pdns-backend-mysql

The initial configuration for Ubuntu packages is located in /etc/powerdns. We will not be using the BIND-backend, and we shouldn't be touching /etc/powerdns/pdns.conf. Put your own configuration in /etc/powerdns/pdns.d/ instead. They give us some example configuration files by default, so remove them: Configuration

rm /etc/powerdns/pdns.d/*

Create a new configuration-file (/etc/powerdns/pdns.d/pdns.local.conf) on each node, containing your MySQL-credentials for your PowerDNS-database:

launch=gmysql
gmysql-socket=/var/run/mysqld/mysqld.sock
gmysql-dbname=powerdns
gmysql-user=powerdns
gmysql-group=client
gmysql-password=<password_you_created_earlier>
gmysql-dnssec=yes
gmysql-innodb-read-committed=yes
gmysql-timeout=10

You should obviously replace <password_you_created_earlier> with the actual password that you created earlier. We will be expanding this configuration-file in the next article(s), but for now, we're done.

Please note that we are not using replication here. Each slave is stand-alone in cases of outage and to keep complexity low.

Further configuration

Configuration of the nodes is split up in different articles. Check out the following articles:

  1. Setting up the hidden master further.