Installing PowerDNS

PowerDNS Overview

PowerDNS is an open source DNS server written in C++. A DNS server is a server that contains a database of public IP addresses and their associated domain names. Its purpose is to resolve, or translate, those common names to IP addresses as requested. PowerDNS runs on most Linux and all other Unix derivatives. The software comes with the PowerDNS-Admin web interface, simplifying the management of your DNS zones.

Requirements

Installing PowerDNS

The goal of this guide is to have PowerDNS configured with a MariaDB/MySQL backend with SQL replication to sync information between secondary servers.

Installing and Configuring MariaDB (All Servers)

PowerDNS needs a database to store DNS zones. MariaDB is being used to store the information, alternatively a zone file configuration is possible.

1 . Start by installing MariaDB on your install as described here.

2 . Once the database server is installed, connect to it and create a database called powerdns:

mysql -u root -p
CREATE DATABASE powerdns;

3 . Create a user for PowerDNS and assign privileges to the powerdns database:

GRANT ALL ON powerdns.* TO 'powerdns'@'localhost' \
IDENTIFIED BY '<SECRET_PASSWORD>';

4 . Update the settings by flushing the privileges:

FLUSH PRIVILEGES;

5 . Switch to the powerdns database to create the required tables:

USE powerdns;

6 . Create the required tables:

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

Useful tip: You can verify that the tables have been created, by running show tables;.

7 . Disconnect from the MariaDB server:

QUIT;

Disabling systemd-resolve

Ubuntu Bionic Beaver comes with systemd-resolve preinstalled, which needs to be disabled as it binds to port 53 which will conflict with the ports required for PowerDNS.

1 . Disable the resolvd service by running the following commands:

systemctl disable systemd-resolved
systemctl stop systemd-resolved

2 . Remove the symlinked resolv.conf file:

ls -lh /etc/resolv.conf
lrwxrwxrwx 1 root root 24 Dec  3 12:23 /etc/resolv.conf -> /lib/systemd/resolv.conf

3 . Create a new resolv.conf file containing your preferred DNS resolver (for example 9.9.9.9):

echo "nameserver 9.9.9.9" > /etc/resolv.conf

Installing PowerDNS Server

1 . Add the official PowerDNS repository to apt:

echo "deb [arch=amd64] http://repo.powerdns.com/ubuntu bionic-auth-41 main" > /etc/apt/sources.list.d/pdns.list

2 . Import the PowerDNS GPG key:

curl https://repo.powerdns.com/FD380FBB-pub.asc | apt-key add -

3 . Update apt and install the PowerDNS package (pdns-server) and MySQL backend (pdns-backend-mysql):

apt update
apt install pdns-server pdns-backend-mysql -y

4 . Edit the file /etc/powerdns/pdns.d/pdns.local.gmysql.conf with your SQL credentials, created previously so it looks like the following example:

# SQL Configuration (MariaDB)
# Launch gmysql backend
launch+=gmysql
# gmysql parameters
gmysql-host=localhost
gmysql-port=3306
gmysql-dbname=powerdns
gmysql-user=powerdns
gmysql-password=<SECRET_PASSWORD>
gmysql-dnssec=yes

5 . Make a backup of the current configuration file:

mv /etc/powerdns/pdns.conf /etc/powerdns/pdns.conf-orig

6 . Create a new /etc/powerdns/pdns.conf file as following:

# PowerDNS configuration file
# Replace ns1.example.com with your primary nameserver's hostname
default-soa-name=ns1.mydomain.com
include-dir=/etc/powerdns/pdns.d
launch=
security-poll-suffix=
setgid=pdns
setuid=pdns

7a . On the primary server only include the following lines:

api=yes
# Replace <RANDOM_KEY> with a randomly generated key for API access
api-key=<RANDOM_KEY>
master=yes
slave=no

7b . On the secondary server only include the following lines:

master=no
slave=yes

8 . Restart the pdns service:

systemctl restart pdns

9 . Verify that the PowerDNS server responds correctly:

# dig @127.0.0.1

; <<>> DiG 9.11.3-1ubuntu1.3-Ubuntu <<>> @127.0.0.1
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: REFUSED, id: 36221
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1680
;; QUESTION SECTION:
;.				IN	NS

;; Query time: 1 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Mon Dec 03 12:53:57 UTC 2018
;; MSG SIZE  rcvd: 28

Configuring MySQL Replication

The MySQL replication setup is a very important part allows near instant DNS updates across all linked nameservers. This replication method, has some benefits over the other replication mode of PowerDNS, called supermaster setup, which can be unstable sometimes and does not remove deleted zones across every server automatically.

Configuring the Primary Server

The following steps have to be done on the primary server only.

1 . Edit the MySQL configuration file (/etc/mysql/mariadb.conf.d/50-server.cnf) as follows:

bind-address = 0.0.0.0

server-id = 1
log_bin = /var/log/mysql/mysql-bin.log
expire_logs_days = 10
max_binlog_size = 100M
binlog_do_db = pdns

2 . Restart MariaDB:

systemctl restart mysql

3 . Connect to MariaDB and create a user for the secondary server:

mysql -u root -p
GRANT REPLICATION SLAVE ON *.* TO 'pdns-slave'@'<SECONDARY_SERVER_IP>' IDENTIFIED BY '<SECRET_PASSWORD>';
FLUSH PRIVILEGES;

4 . Run show master status; from the MariaDB console:

+------------------+----------+--------------+------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000001 |      634 | powerdns     |                  |
+------------------+----------+--------------+------------------+
1 row in set (0.01 sec)

Note the File (mysql-bin.000001) and Position (634) numbers, as they will be required in a later step.

Configuring the Secondary Server

The following steps have to be done on the secondary server only.

1 . Edit the MySQL configuration file (/etc/mysql/mariadb.conf.d/50-server.cnf) as follows:

server-id=2
relay-log=slave-relay-bin
relay-log-index=slave-relay-bin.index
replicate-do-db=powerdns

Important: The server-id has variable needs to be unique for each server, if you want to configure more than one secondary server increase it accordingly, for example: server-id=2, server-id=3, etc.

2 . Restart MariaDB:

systemctl restart mysql

3 . Log into the MariaDB console:

mysql -u root -p

4 . Run the following command to link the secondary to the primary server:

change master to
master_host='<PRIMARY_SERVER_IP>',
master_user='pdns-secondary',
master_password='<SECRET_PASSWORD>', master_log_file='mysql-bin.000001', master_log_pos=634;
start slave;

Important: The values mysql-bin.000001 and 634 correspond to the values you noted down in a previous step.

5 . Check the secondary server status to see if everything worked:

show slave status;

Installing PowerDNS-Admin

PowerDNS-Admin is an advanced web interface that simplifies the management of DNS zones. PowerDNS-Admin will be installed on the primary server.

1 . Connect to MariaDB and create a new database and user for PowerDNS-Admin:

mysql -u root -p
CREATE DATABASE pdnsadmin CHARACTER SET utf8 COLLATE utf8_general_ci;
GRANT ALL PRIVILEGES ON pdnsadmin.* TO 'pdnsadminuser'@'%' IDENTIFIED BY '<SECRET_PASSWORD>';
FLUSH PRIVILEGES;
quit;

2 . Install Python 3 and the packages required for building python libraries:

apt install python3-dev libmysqlclient-dev python-mysqldb libsasl2-dev libffi-dev libldap2-dev libssl-dev libxml2-dev libxslt1-dev libxmlsec1-dev pkg-config virtualenv -y

3 . Download and install the yarn GPG key, add the repository to apt and install yarn to build asset files:

curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list
apt update && apt install yarn -y

4 . Download and extract PowerDNS-Admin, then create a virtualenv:

wget https://github.com/ngoduykhanh/PowerDNS-Admin/archive/master.tar.gz
mkdir /opt/web
tar -C /opt/web -xvf master.tar.gz
mv /opt/web/PowerDNS-Admin-master /opt/web/powerdns-admin
cd /opt/web/powerdns-admin
virtualenv -p python3 flask

5 . Activate the Python 3 environment and install the required libraries:

. ./flask/bin/activate
pip install -r requirements.txt
pip install python-dotenv

6 . Create a configuration file from the template shipped with the tool:

cp config_template.py config.py

7 . Edit the config.py file and enter the database credentials. Once edited the file should look like the following example:

SQLA_DB_USER = 'pdnsadminuser'
SQLA_DB_PASSWORD = '<SECRET_PASSWORD>'
SQLA_DB_HOST = '127.0.0.1'
SQLA_DB_NAME = 'pdnsadmin'
SQLALCHEMY_TRACK_MODIFICATIONS = True

8 . Once the file is edited, create the database scheme by running the following commands:

export FLASK_APP=app/__init__.py
flask db upgrade

9 . Run db migrate:

flask db migrate -m "Init DB"

10 . Generate the asset files with yarn and build the application:

yarn install --pure-lockfile
flask assets build

11 . Test if your application is working:

./run.py

An output like the following will appear. Press CTRL+C to exit the application:

* Serving Flask app "app" (lazy loading)
* Environment: production
  WARNING: Do not use the development server in a production environment.
  Use a production WSGI server instead.
* Debug mode: on
[INFO]  * Running on http://127.0.0.1:9191/ (Press CTRL+C to quit)
[INFO]  * Restarting with stat
[WARNING]  * Debugger is active!
[INFO]  * Debugger PIN: 104-611-896

12 . To manage PowerDNS-Admin with systemd, create a new service file called /etc/systemd/system/powerdns-admin.service:

[Unit]
Description=PowerDNS-Admin
After=network.target

[Service]
User=root
Group=root
WorkingDirectory=/opt/web/powerdns-admin
ExecStart=/opt/web/powerdns-admin/flask/bin/gunicorn --workers 2 --bind unix:/opt/web/powerdns-admin/powerdns-admin.sock app:app

[Install]
WantedBy=multi-user.target

13 . Reload systemd, start the powerdns-admin service and enable it to be started automatically during system boot:

systemctl daemon-reload
systemctl start powerdns-admin
systemctl enable powerdns-admin

14 . Verify that the service is running with the following command: systemctl status powerdns-admin

The output should look like as follows:

● powerdns-admin.service - PowerDNS-Admin
   Loaded: loaded (/etc/systemd/system/powerdns-admin.service; enabled; vendor preset: enabled)
   Active: active (running) since Mon 2018-12-03 14:05:12 UTC; 6min ago
 Main PID: 8986 (gunicorn)
    Tasks: 3 (limit: 2295)
   CGroup: /system.slice/powerdns-admin.service
           ├─8986 /opt/web/powerdns-admin/flask/bin/python3 /opt/web/powerdns-admin/flask/bin/gunicorn --workers 2 --bind unix:/opt/web/po
           ├─9002 /opt/web/powerdns-admin/flask/bin/python3 /opt/web/powerdns-admin/flask/bin/gunicorn --workers 2 --bind unix:/opt/web/po
           └─9003 /opt/web/powerdns-admin/flask/bin/python3 /opt/web/powerdns-admin/flask/bin/gunicorn --workers 2 --bind unix:/opt/web/po

Installing Nginx Proxy

A Nginx proxy is being used to access the PowerDNS-Admin interface from a web browser.

1 . Install Nginx:

apt install nginx -y

2 . Create a configuration file called /etc/nginx/conf.d/powerdns-admin.conf:

server {
  listen *:80;
  server_name               <YOUR_DOMAIN_NAME>;

  index                     index.html index.htm index.php;
  root                      /opt/web/powerdns-admin;
  access_log                /var/log/nginx/powerdns-admin.local.access.log combined;
  error_log                 /var/log/nginx/powerdns-admin.local.error.log;

  client_max_body_size              10m;
  client_body_buffer_size           128k;
  proxy_redirect                    off;
  proxy_connect_timeout             90;
  proxy_send_timeout                90;
  proxy_read_timeout                90;
  proxy_buffers                     32 4k;
  proxy_buffer_size                 8k;
  proxy_set_header                  Host $host;
  proxy_set_header                  X-Real-IP $remote_addr;
  proxy_set_header                  X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_headers_hash_bucket_size    64;

  location ~ ^/static/  {
    include  /etc/nginx/mime.types;
    root /opt/web/powerdns-admin/app;

    location ~*  \.(jpg|jpeg|png|gif)$ {
      expires 365d;
    }

    location ~* ^.+.(css|js)$ {
      expires 7d;
    }
  }

  location / {
    proxy_pass            http://unix:/opt/web/powerdns-admin/powerdns-admin.sock;
    proxy_read_timeout    120;
    proxy_connect_timeout 120;
    proxy_redirect        off;
  }

}

3 . Verify the syntax of the configuration file and reload nginx if everything is fine:

nginx -t
systemctl reload nginx

4 . PowerDNS-Admin is accessible at http://<YOUR_DOMAIN_NAME> now:

PowerDNS Admin

Installing a Let's Encrypt Certificate

Let’s Encrypt certbot will be used to automatically obtain and manage the SSL certificate for PowerDNS-Admin. It takes also care about the required modifications in Nginx configuration files:

1 . Add the required repositories and install the tool via apt:

add-apt-repository ppa:certbot/certbot
apt update
apt install software-properties-common  python-certbot-nginx -y

2 . Request a certificate for the server:

certbot --nginx

When asked enter your email address and agree to the terms of Let’s Encrypt.

3 . The connection to PowerDNS-Admin is now secured by SSL.

Creating a First User and Linking the API

1 . Open a web browser and enter the address of your server: https://<YOUR_DOMAIN_NAME>/register. Fill in all required information to create your first admin user.

2 . Once logged into the interface you will be asked to complete the information about the API. Fill in the following information:

  • PDNS API URL Your PowerDNS API URL (in this case http://127.0.0.1:8081/ as PowerDNS-Admin is running on the primary server).
  • PDNS API KEY Your PowerDNS API key (configured during the setup of the primary server).
  • PDNS VERSION Your PowerDNS version number (the latest version at the time of edition of this tutorial is: 4.1.5).

3 . Once the infomation is provided, the dashboard will be available:

PowerDNS Admin

After declaring the new DNS servers with your registrar, you will be able to manage your domains from the PowerDNS Admin panel. For more information, you may refer to the official documentation.

Discover a New Cloud Experience

Deploy SSD Cloud Servers in seconds.