In this post I’d like to give you a simple step-by-step guide on how to setup a
replicated PDNS server that can be used, for example, as on-premise Kubernetes
DNS server with external-dns.
But first…
What is PowerDNS
PowerDNS is an authoritative/recursive DNS, server developed under GPL, that can
a wide range of backends to store DNS zones:
- Relational databases
- BIND zone files
- LDAP
One useful functionality of PDNS is the control and monitoring web API, that is
quite well documented, and that enables full control over zones manipulation
with external tools and automations, kubernetes external-dns, etc.
Environment
The environment is quite simple as is only composed by two freshly installed
Debian 11 virtual machines.
Setup
The installation here described is completely manual. I don’t recommend this way
of installing and configuring PDNS unless you are practicing with a test
environment or are trying to have a first contact with this kind of setups. For
real applications the process should be automated and hardening measures should
be applied to enforce security.
Warning
This article can be followed using any Linux
distribution. At first glance there shouldn’t be any issues by using a Debian
based distribution that uses apt-packages, despite of the fact that repository
URLs change, but keep in mind that if you use a RHEL based distribution more
adjustments could be needed.
Pre-requirements
All the steps of this section should be applied to both nodes, the master and
the slave.
To simplify the setup we will install the DB alongside PDNS, but, keep in mind
that the is recommended to have both services on separate machines.
1
2
3
4
5
6
7
8
| sudo apt-get install software-properties-common dirmngr apt-transport-https -y
sudo apt-key adv --fetch-keys 'https://mariadb.org/mariadb_release_signing_key.asc'
sudo add-apt-repository 'deb [arch=amd64,arm64,ppc64el] http://ams2.mirrors.digitalocean.com/mariadb/repo/10.5/debian bullseye main'
sudo apt update
sudo apt install mariadb-server -y
# Just secure the root user and remove samnple data
sudo mysql_secure_installation
|
We will need to modify MariaDB’s configuration to change how it generate
replication events to the one that only takes into account a ROW change. You can
checkout the official documentation on how binlog
works here.
1
2
3
4
5
6
7
8
9
| sudo nano /etc/mysql/mariadb.conf.d/50-server.cnf
#Add this line under the [mysqld] tag
binlog-format=ROW
# Restart the DB to apply changes
sudo systemctl restart mariadb
# Enable on-boot start of the DB
sudo systemctl enable mariadb
|
Create the PDNS database, user, privileges and load the schema.
1
2
3
4
5
6
7
| mysql -u root -p
MariaDB [(none)]> show variables like 'binlog_format'; # Asegurate de que haya cambiado a row
MariaDB [(none)]> create database powerdns;
MariaDB [(none)]> GRANT ALL PRIVILEGES ON powerdns.* TO 'powerdns'@'localhost' IDENTIFIED BY 'yourdatabasepassword';
MariaDB [(none)]> exit;
curl https://raw.githubusercontent.com/PowerDNS/pdns/rel/auth-4.5.x/modules/gmysqlbackend/schema.mysql.sql | mysql --user=root --password=<ROOT-PASSWORD from mysql_secure-installation> --database=powerdns
|
We will install PDNS from its repos instead of the Debian ones to be able to
install a specific version, 4.5 in this example, instead of the distribution
selected one.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # Add PDNS PGP keys and packet repsitories
sudo nano /etc/apt/sources.list.d/pdns.list
deb [arch=amd64] http://repo.powerdns.com/ubuntu focal-auth-45 main
deb [arch=amd64] http://repo.powerdns.com/ubuntu focal-rec-45 main
sudo nano /etc/apt/preferences.d/pdns
Package: pdns-*
Pin: origin repo.powerdns.com
Pin-Priority: 600
curl https://repo.powerdns.com/FD380FBB-pub.asc | sudo apt-key add -
# Install PDNS Authoritative Server, Recursor and MySQL/MariaDB connector
sudo apt-get update && sudo apt-get install pdns-server pdns-recursor pdns-backend-mysql -y
|
Master node preparation
Now that both server already have the common configuration and packages
installed it’s time to start configuring the master node. To do so, we start
configuring replication at MariaDB master by modifying its configuration file
with the following changes.
1
2
3
4
5
6
7
8
9
10
11
| sudo nano /etc/mysql/mariadb.conf.d/50-server.cnf
# Add/modify the following lines
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 = powerdns
# Restart MariaDB to reload config
sudo systemctl restart mariadb
|
Next, we create the user used by the database for replication and,, at the same
time, we will write down the position of the binlog that we will use later to
configure replication in the slave node, as that value will tell the DB in which
position of the binlog it should start to replicate.
1
2
3
4
5
6
7
8
9
10
11
12
13
| mysql -u root -p
# Create replication user and give permissions
MariaDB [(none)]> GRANT REPLICATION SLAVE ON *.* TO 'pdns-secondary'@'<SECONDARY_SERVER_IP>' IDENTIFIED BY '<SECRET_PASSWORD>';
MariaDB [(none)]> FLUSH PRIVILEGES;
MariaDB [(none)]> show master status;
## Demo output of the status command. Just write down the position value
+------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000001 | 634 | powerdns | |
+------------------+----------+--------------+------------------+
|
Finally, we only need to configure PDNS Authoritative and Recursor with the
following changes in their configuration files. There’s no need to remove the
entire file, just ensure that the parameters proposed here contain the expected
values.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| sudo nano /etc/powerdns/pdns.conf
# If you plan to use the API then enable it and webserver too
api=yes
api-key=<SECRET-API-KEY>
webserver=yes
# The IP of the interface where PDNS web server will listen
webserver-address=192.168.1.6 # Master IP address
# Explicit allow request to API/web server from this IPs
webserver-allow-from=192.168.1.0/24
# In which which port will we listen to DNS requests
local-port=5300
# This is the master node
master=yes
slave=no
# Database parameters
launch=gmysql
gmysql-host=127.0.0.1
gmysql-user=powerdns
gmysql-password=<POWERDNS-MARIDB-PASSWORD>
gmysql-dbname=powerdns
# Reload PDNS configuration
sudo systemctl restart pdns.service
|
There are two points in the previous configuration that are worth mentioning:
- local-port=5300: DNS request that arrive to servers (both, master and slave)
are handled by the recursor, not the authoritative server, so that’s why
recursors are the ones that listen on the standard DNS port 53.
Authoritative servers will be requested only from the recursors on the same
instance if a DNS request for their owned zone arrives. Saying that, any
free port is valid to be assigned to the authoritative server, 5300 in this
case.
- api=yes and webserver=yes: This post is the product of the need of owning a
DNS server that can be easily integrated with
kubernetes external-dns.
With PDNS API the process is straightforward and only the IP of the server
and the API key are needed. If you are not planning to use the API or the
monitoring portal feel free to comment out those options.
Recursor configuration is far easier and can be summarized as the following.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| sudo nano /etc/powerdns/recursor.conf
# IP of the interface used to listen DNS requests
local-address=192.168.1.6
# Forward requests of *.subdomain.example.com to the Authoritative server
forward-zones=subdomain.example.com=127.0.0.1:5300
# Rest of queries goes to google DNSs
forward-zones-recurse=.=8.8.8.8
local-port=53
dnssec=off
# Reload PDNS recursor configuration
sudo systemctl restart pdns-recursor.service
|
Slave preparation
In the slave node the first step is to change MariaDB config to increment the
server identifier and the binglog and database used for replication.
1
2
3
4
5
6
7
8
| server-id=2
bind-address = 0.0.0.0
relay-log=slave-relay-bin
relay-log-index=slave-relay-bin.index
replicate-do-db=powerdns
# Restart MariaDB to reload config
sudo systemctl restart mariadb
|
To start the replication process we need to command an explicit start in MariaDB
shell. At this point we should tell MariaDB the binlog position that we wrote
down previously when we configured the master node.
1
2
3
4
5
6
7
| mysql -u root -p
MariaDB [(none)]> 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=<POSITION>;
MariaDB [(none)]> start slave;
# If all is OK this will show "Waiting for master to send event"
MariaDB [(none)]> show slave status
|
Previous step gave us a fully configured database that replicates. All that
remains is to configure the slave PDNS with the following file for the
Authoritative server.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| sudo nano /etc/powerdns/pdns.conf
# If you plan to use the API then enable it and webserver too
api=yes
api-key=<SECRET-API-KEY>
webserver=yes
# The IP of the interface where PDNS web server will listen
webserver-address=192.168.1.6 # Slave IP address
# Explicit allow request to API/web server from this IPs
webserver-allow-from=192.168.1.0/24
# In which which port will we listen to DNS requests
local-port=5300
# This is the master node
master=no # This change is important is the slave
slave=yes # This change is important is the slave
# Database parameters
launch=gmysql
gmysql-host=127.0.0.1
gmysql-user=powerdns
gmysql-password=<POWERDNS-MARIDB-PASSWORD>
gmysql-dbname=powerdns
# Reload PDNS configuration
sudo systemctl restart pdns.service
|
To wind up the configuration process we can configure the slave recursor with
the following configuration.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| sudo nano /etc/powerdns/recursor.conf
# IP of the interface used to listen DNS requests
local-address=192.168.1.6 # Slave IP address
# Forward requests of *.subdomain.example.com to the Authoritative server
forward-zones=subdomain.example.com=127.0.0.1:5300
# Rest of queries goes to google DNSs
forward-zones-recurse=.=8.8.8.8
local-port=53
dnssec=off
# Reload PDNS recursor configuration
sudo systemctl restart pdns-recursor.service
|
Testing
At this point we have both PDNS server up and running so it is time to check
that all is working as expected with the help of the pdnsutil tool to create
and edit our DNS zone in the authoritative server. With nslookup we will
verify that the DNS entries that we will create are properly resolving. IN the
master node:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| # Create our sample zone (this zone is the one that we delegate to out authoritative server)
sudo pdnsutil create-zone subdomain.example.com
# Add the NSs server entries
sudo pdnsutil add-record subdomain.example.com @ NS ns1.subdomain.example.com
sudo pdnsutil add-record subdomain.example.com @ NS ns2.subdomain.example.com
# Provide de A records for the NSs
sudo pdnsutil add-record subdomain.example.com ns1 A 192.168.1.6
sudo pdnsutil add-record subdomain.example.com ns2 A 192.168.1.7
# Verify that the zone seems to be as expected
sudo pdnsutil list-zone subdomain.example.com
# Verify no errors in the zone
sudo pdnsutil check-all-zones
# The nslookup to the slave should answer with the A record of ourselfs if replication is OK
nslookup ns1.subdomain.example.com 192.168.1.7
Server: 192.168.1.7
Address: 192.168.1.7#53
Non-authoritative answer:
Name: ns1.subdomain.example.com
Address: 192.168.1.6
|