En este artículo quiero contaros, paso a paso, cómo he montado un servidor DNS
basado en PowerDNS que podrás utilizar, por ejemplo, como servidor DNS para un
cluster de Kubernetes on-premise con external-dns.
Pero primero…
PowerDNS es un servidor de DNS autoritativo y/o recursivo desarrollado bajo
licencia GPL que puede emplear una gran variedad de backends como origen de los
records DNS. Entre ellos destacan:
- Bases de datos relacionales
- Ficheros de zonas BIND
- LDAP
Otra de sus funcionalidades que lo hacen especialmente interesante es que
dispone de una API de control y monitoreo razonablemente bien documentada
mediante la cual podremos manipular las zonas que deseemos empleando
herramientas externas, scripts de nuestras propias automatizaciones, kubernetes
(external-dns), etc..
Para estas pruebas he empleado un entorno muy básico que consta de dos VMs
Debian 11 completamente limpias.

Todos los pasos de esta guía pretenden ser una guia de instalación manual. No
recomendaría una instalación manual salvo que estéis aprendiendo cómo funciona
el entorno DNS que queréis montar o si lo vais a utilizar como entorno de
prueba. En cualquier otro caso recomendaria automatizar el proceso de
instalación y configuración, teniendo cuidado también con la securización del
entorno. En breve subiré otro artículo en el que configuro el mismo entorno con
Ansible.
Advertencia
Podéis seguir este artículo con un entorno basado en
otras distros. A priori no deberíais tener ningún tipo de problema con distros
derivadas de Debian y gestor de paquetes apt, pero si empleáis distros basadas
en RHEL y rpm tened en cuenta que es posible que necesitéis hacer algún retoque
a las dependencias que utilizo.
Todos los pasos indicados en este apartado son para ambos hosts, tanto el master
como la réplica.
Para este entorno vamos a simplificar la instalación, por lo que instalaremos la
base de datos en el propio servidor de DNS, aunque idealmente deberían de ser
hosts completamente distintos.
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
|
Necesitamos modificar la configuración de MariaDB para que los eventos de
modificación de la base de datos se den solo si se altera una fila. Puedes
encontrar más información acerca de cómo y qué es el
binlog aquí.
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
|
Instalamos PDNS desde sus repos en lugar desde los repos de Ubuntu para poder
instalar la versión deseada, 4.5 en este caso.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # Add PDNS PGP keys and packet repsitories
sudo vim /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
|
Creamos la base de datos para PDNS, el usuario asociado y sus permisos y
cargamos el esquema con las tablas.
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
|
Una vez preparados ambos nodos con MariaDB y PDNS comenzamos a configurar el
master. Para ello, en primer lugar, configuraremos la replicación de MariaDB
modificando el fichero de configuración de MariaDB las siguiente lineas.
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
|
A continuación, creamos el usuario con el que se realizará la replicación y
aprovecharemos para obtener la posición del binlog que utilizaremos
posteriormente en la réplica para configurar allí el punto de partida de la
replicación.
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 | |
+------------------+----------+--------------+------------------+
|
Finalmente, basta configurar PDNS Authoritative y Recursor con los siguientes
cambios en sus ficheros de configuración. No es necesario borrar el fichero,
simplemente asegúrate de que los parámetros aquí expuestos tengan el valor
indicado.
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
|
La configuración anterior tiene dos detalles que es conveniente recalcar:
- local-port=5300: Las peticiones DNS que llegan a los servidores (tanto
master como réplicas) las atenderán los recursor, no los authoritative
servers, por lo que serán los primeros los que escuchen en el puerto
estandard DNS (53). El authoritative server de PDNS en este entorno es solo
consultado por el propio recursor de su misma instancia si llega una
petición para la zona de la que son dueños. Es por ello que cualquier puerto
libre nos sirve, 5300 en este caso.
- api=yes y webserver=yes: El origen de este artículo vino dado por la
necesidad de tener un servidor de DNS con el que fuese fácil integrar el
módulo de external-dns de
kubernetes. Con PDNS, por medio de la API es un proceso muy sencillo, en el
que solo hace falta la IP del servidor y la API Key. Si no vas a hacer uso
de estas funcionalidades puedes no incluir estos parámetros.
Para el recursor la configuración es más sencilla y queda de la siguiente forma.
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
|
Ya en el nodo de la réplica cambiamos la configuración de MariaDB para
incrementar el server ID e indicar la base de datos (binlog realmente) a
replicar.
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
|
Para que comience la replicación es necesario iniciar el proceso desde la shell
de MariaDB tal y como se muestra a continuación. Es en este paso donde tenemos
que indicar el valor de posición del binlog que anotamos previamente al
configurar el master.
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
|
Tras el paso anterior ya tenemos las bases de datos con la replicación
configurada y funcionando. Ya solo falta configurar PDNS en la réplica con el
siguiente fichero para el Authoritative server esclavo.
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.7 # 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
|
Y para finalizar la configuración de nuestro entorno, esta sería la
configuración del recursor en la réplica.
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.127 # 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
|
En este punto ya tenemos nuestros servidores PDNS configurados, por lo que
podemos probar que el entorno funciona utilizando la utilidad pdnsutil para
crear y editar la zona DNS del authoritative server, y con *nslookup *verificar
que resuelve las entradas que le indicamos en ambos nodos. Para ello, desde el
nodo master:
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 our 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
|