Cómo configurar PDNS replicado

Contenido

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.

schematic-768x561.png

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