Infrastructure-as-Code Series: DNS (Part 1)

Friday, May 17, 2019 13:30 · 853 words · 5 minutes read terraform devops iac series

Infrastructure-as-Code Series

  1. Getting Started
  2. Project Structure
  3. DNS (Part 1)
  4. DNS (Part 2)

In my previous blog post “Infrastructure-as-Code Series: Project Structure” I have described how to setup Terraform on your machine and how to structure your Infrastructure-as-Code project.

What is DNS?

“DNS” in Cloudflare: The Domain Name Systems (DNS) is the phonebook of the Internet. Humans access information online through domain names, like or Web browsers interact through Internet Protocol (IP) addresses. DN S translates domain names to IP addresses so browsers can load Internet resources. (read more)

What is PowerDNS?

PowerDNS is a DNS server, there are two PowerDNS nameserver products:

  • Authoritative Server - will answer questions about domains it knows about, but will not go out on the net to resolve queries about other domains. When the Authoritative Server answers a question, it comes out of the database, and can be trusted as being authoritative. There is no way to pollute the cache or to confuse the daemon.
  • Recursor - by default has no knowledge of domains itself, but will always consult other authoritative servers to answer questions given to it.


Below I will demonstrate how to install and configure PowerDNS server, which can used as DNS server for your infrastructure. I will use Ubuntu 18.04 as environment.


PowerDNS uses different storage mechanism to store all of the DNS records related data. I will use the MariaDB as a backend for this setup.

apt update
apt install -y mariadb-server



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

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

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

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

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

Now that you have MariaDb installed and configured, you need to create the powerdns database, its schema and the pdns user, which will be used by the PowerDNS server.

Connect to the MariaDb server using the root user

mysql -u root -p

… create the powerdns and pdns db user

-- Creating the powerdns database

-- Granting permissions on the powerdns schema to the <power_admin> user
GRANT ALL ON powerdns.* TO '<power_admin>'@'localhost' IDENTIFIED BY '<power_admin_password>';
GRANT ALL ON powerdns.* TO '<power_admin>'@'localhost.localdomain' IDENTIFIED BY '<power_admin_password>';


… migrate the powerdns schema

mysql -u <power_admin> -p < schema.mysql.sql


#!/usr/bin/env bash
apt update

# Install PowerDNS Authoritive Server
apt install -y pdns-recursor

# Upon executing this command you will see an error saying: Failed to start PowerDNS Authoritative Server.
# This happens because the service tries to use port 53, which already in use by the pdns-recursor service.
# Later on during the configuration phase, we will change this setting.
apt install -y pdns-server

systemd[1]: pdns.service: Main process exited, code=exited, status=1/FAILURE
systemd[1]: pdns.service: Failed with result 'exit-code'.
systemd[1]: Failed to start PowerDNS Authoritative Server.

# Install generic MySQL backend for PowerDNS
# Choose the option for manual configuration of the driver.
apt-get install -y pdns-backend-mysql

rm /etc/powerdns/pdns.d/*

# Disable the stock Ubuntu systemd resolved service:
sudo systemctl disable systemd-resolved
sudo systemctl stop systemd-resolved

N.B. Upon installing the pdns-server you will see an error saying: Failed to start PowerDNS Authoritative Server. This happens because the service tries to use port 53, which already in use by the pdns-recursor service. Later on during the configuration phase, we will change this setting.

N.B. It is important to stop and disable the systemd-resolved service of Ubuntu.

Add the MySQL backend configuration settings under the pdns.local.gmysql.conf file.

vi /etc/powerdns/pdns.d/pdns.local.gmysql.conf

Change the local-port setting of the pdns service to avoid clashes with pdns-recoursor service configuration:

sed -i "s|# local-port=.*|local-port=5300|" /etc/powerdns/pdns.conf
sed -i "s|# config-dir=.*|config-dir=/etc/powerdns|" /etc/powerdns/pdns.conf
sed -i "s|# daemon=.*|daemon=yes|" /etc/powerdns/pdns.conf
sed -i "s|# guardian=.*|guardian=yes|" /etc/powerdns/pdns.conf
sed -i "s|# master=.*|master=yes|" /etc/powerdns/pdns.conf
sed -i "s|# max-tcp-connections=.*|max-tcp-connections=20|" /etc/powerdns/pdns.conf

service pdns restart

Deploy startup script

Since this server will be used as part of data center and it should be configured dynamically, deploy the following script under /root/

#!/usr/bin/env bash
LOCAL_IP=`ifconfig | grep 'inet addr:'| grep -v '' | grep 'addr:10.' | cut -d: -f2 | awk '{ print $1}'`
PUBLIC_IP=`curl -s | sed -e 's/.*Current IP Address: //' -e 's/<.*$//'`

ARPA_ADDR=`echo $LOCAL_IP | awk 'BEGIN{FS="."}{print $2"."$1""}'`
NETWORK=`echo $LOCAL_IP | awk 'BEGIN{FS="."}{print $1"."$2".0.0/16"}'`

# patching the pdns recursor configuration file
sed -i "s|allow-from=.*|allow-from=$NETWORK|" /etc/powerdns/recursor.conf
sed -i "s|local-address=.*|local-address=$LOCAL_IP|" /etc/powerdns/recursor.conf
sed -i "s|forward-zones=.*|forward-zones=$DOMAIN_NAME=,$ARPA_ADDR=|" /etc/powerdns/recursor.conf

# patching the pdns server configuration file
sed -i "s|# api=.*|api=yes|" /etc/powerdns/pdns.conf
sed -i "s|# api-key=.*|api-key=$API_KEY|" /etc/powerdns/pdns.conf
sed -i "s|# webserver=.*|webserver=yes|" /etc/powerdns/pdns.conf
sed -i "s|# webserver-address=.*|webserver-address=$PUBLIC_IP|" /etc/powerdns/pdns.conf
sed -i "s|# webserver-port=.*|webserver-port=$SERVER_PORT|" /etc/powerdns/pdns.conf

pdnsutil create-zone $DOMAIN_NAME ns1.$DOMAIN_NAME
pdnsutil create-zone $ARPA_ADDR

Verify the setup

Note: Setup is tested against PowerDNS Administrative Server 4.1.1

pdnsutil create-zone test-zone-1
Creating empty zone 'test-zone-1'

pdnsutil list-zone test-zone-1
test-zone-1	3600	IN	SOA	a.misconfigured.powerdns.server hostmaster.test-zone-1 1 10800 3600 604800 3600

In the next blog post I will describe how to provision DNS setup for your infrastructure.

The complete demo application, described in this blog post, can be found here.
comments powered by Disqus