Instalación de PowerDNS

Configuración Inicial

Primero debemos instalar algunas dependencias y configurar los repositorios de PowerDNS. Además necesitaremos crear un directorio para guardar nuestros credenciales y archivos generados.

# Instale las dependencias básicas
apt-get install software-properties-common gnupg2 lsb-release curl -y

# Obtenemos la clave pública del Repositorio de PowerDNS, puede estar desactualizado
# Master Branch
wget -O- https://repo.powerdns.com/CBC8B383-pub.asc | gpg --dearmor > /etc/apt/trusted.gpg.d/pdns_master.gpg
# Stable Branch
wget -O- https://repo.powerdns.com/FD380FBB-pub.asc | gpg --dearmor > /etc/apt/trusted.gpg.d/pdns_stable.gpg

# Establecemos variables para la configuración del repo.
systemReleaseVersion=$(lsb_release -cs)
osLabel=$(lsb_release -sa 2>/dev/null|head -n 1|tr '[:upper:]' '[:lower:]')

# Creamos un directorio para la instalación
mkdir -p /opt/pdns_install
workpath="/opt/pdns_install"

Añadir los Repositorios de Fuentes

Una vez realizamos la configuración inicial podemos configurar los Repositorios de PowerDNS.

Si solo desea instalar el Servidor de PowerDNS Autoritativo, puede usar un solo repositorio, pero se recomienda añadir todos por si acaso.

# Repositorio de PowerDNS Autoritativo
repo_pdnsAuth="deb [arch=amd64] http://repo.powerdns.com/${osLabel} ${systemReleaseVersion}-auth-master main"
echo $repo_pdnsAuth > "/etc/apt/sources.list.d/pdns-auth.list"

# Repositorio de PowerDNS Recursor
repo_pdnsRec="deb [arch=amd64] http://repo.powerdns.com/${osLabel} ${systemReleaseVersion}-rec-master main"
echo $repo_pdnsRec > "/etc/apt/sources.list.d/pdns-rec.list"

# Repositorio de PowerDNS DNS Dist
repo_dnsdist="deb [arch=amd64] http://repo.powerdns.com/${osLabel} ${systemReleaseVersion}-dnsdist-master main"
echo $repo_dnsdist > "/etc/apt/sources.list.d/dnsdist.list"

echo \
"Package: pdns-*
Pin: origin repo.powerdns.com
Pin-Priority: 600" > /etc/apt/preferences.d/pdns

Instalar el Backend de PowerDNS

Antes de instalar PowerDNS necesitará elegir un backend (Postgres o MariaDB). Recomiendo PostgreSQL pero MariaDB también es buena elección.

MariaDB / MySQL

# Añadir la clave pública de MariaDB
wget -O- https://mariadb.org/mariadb_release_signing_key.asc | gpg --dearmor > /etc/apt/trusted.gpg.d/mariadb.gpg

# Añadir el Repositorio de MaríaDB
echo "# MariaDB Repository List" > "/etc/apt/sources.list.d/mariadb.list"
echo "deb [arch=amd64,arm64,ppc64el,s390x] https://mirrors.ukfast.co.uk/sites/mariadb/repo/10.5/${osLabel} ${systemReleaseVersion} main" >> "/etc/apt/sources.list.d/mariadb.list"
echo "deb-src https://mirrors.ukfast.co.uk/sites/mariadb/repo/10.5/${osLabel} ${systemReleaseVersion} main" >> "/etc/apt/sources.list.d/mariadb.list"

# Actualizar las listas de paquetes:
apt update

# Instalar MariaDB
apt install mariadb-server

Postgresql (PGSQL)

# Añadir la clave pública de PostgreSQL
wget -O- https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor > /etc/apt/trusted.gpg.d/postgresql.gpg

# Añadir el Repositorio de PostgreSQL
echo "deb http://apt.postgresql.org/pub/repos/apt ${systemReleaseVersion}-pgdg main" > /etc/apt/sources.list.d/pgdg.list

# Actualizar las listas de paquetes:
apt-get update

# Instalar la última versión de PostgreSQL.
# Si quiere una versión específica utilice 'postgresql-12' o similar en lugar de 'postgresql':
apt-get -y install postgresql

# Habilitar el Servicio de PostgreSQL
systemctl enable postgresql

# Encender el Servicio de PostgreSQL
systemctl start postgresql

No olvide configurar su conexión de local unix socket en el archivo pg_hba.conf para utilizar md5 o scram-sha-256 en lugar de peer.

Generar la Base de Datos

Una vez haya instalado el DBMS necesitará generar credenciales para PowerDNS y crear su esquema de BBDD.

pdns_db="pdns"
pdns_db_user="pdnsadmin"
pdns_pwd="$(tr -dc A-Za-z0-9 </dev/urandom | head -c 13 ; echo '')"
pdnsadmin_salt="$(tr -dc A-Za-z0-9 </dev/urandom | head -c 32 ; echo '')"
pdns_apikey="$(tr -dc A-Za-z0-9 </dev/urandom | head -c 32 ; echo '')"

echo "pdns_db=$pdns_db" > "$workpath/db_credentials"
echo "pdns_db_user=$pdns_db_user" >> "$workpath/db_credentials"
echo "pdns_pwd=$pdns_pwd" >> "$workpath/db_credentials"
echo "pdnsadmin_salt=$pdnsadmin_salt" >> "$workpath/db_credentials"
echo "pdns_apikey=$pdns_apikey" >> "$workpath/db_credentials"
echo "workpath=$workpath" >> "$workpath/db_credentials"
echo 'systemReleaseVersion=$(lsb_release -cs)' >> "$workpath/db_credentials"
echo 'osLabel=$(lsb_release -sa 2>/dev/null|head -n 1|tr '[:upper:]' '[:lower:]')' >> "$workpath/db_credentials"
chown root:root "$workpath/db_credentials"
chmod 640 "$workpath/db_credentials"
cd "$workpath"

Una vez generados los credenciales podemos continuar y crear la Base de Datos.

MariaDB / MySQL

# MySQL/MariaDB - Establecemos esta variable para más tarde
db_type="mysql"
echo "db_type=$db_type" >> "$workpath/db_credentials"

echo \
"-- PowerDNS MySQL/MariaDB Create DB File
CREATE DATABASE pdns;
GRANT ALL ON pdns.* TO pdnsadmin@localhost IDENTIFIED BY '$pdns_pwd';
FLUSH PRIVILEGES;" > "$workpath/pdns-createdb-my.sql"
mariadb -u root < "$workpath/pdns-createdb-my.sql"

Postgresql (PGSQL)

# PostgreSQL - Establecemos esta variable para más tarde
db_type="pgsql"
echo "db_type=$db_type" >> "$workpath/db_credentials"

echo \
"-- PowerDNS PGSQL Create DB File
CREATE USER $pdns_db_user WITH ENCRYPTED PASSWORD '$pdns_pwd';
CREATE DATABASE $pdns_db OWNER $pdns_db_user;
GRANT ALL PRIVILEGES ON DATABASE $pdns_db TO $pdns_db_user;" > "$workpath/pdns-createdb-pg.sql"
sudo -u postgres psql < "$workpath/pdns-createdb-pg.sql"

Instalar PowerDNS/DNSDist

# Actualizar las listas de paquetes:
apt update

# Para MariaDB
apt-get install pdns-backend-mysql -y
# Para PostgreSQL
apt-get install pdns-backend-pgsql -y

# Servidor de PowerDNS Authoritative
apt-get install pdns-server -y

# Servidor de PowerDNS Recursor
apt install pdns-recursor

# Balanceador de DNS Dist
apt install dnsdist

# Des-habilitar systemd-resolved
systemctl disable systemd-resolved
systemctl stop systemd-resolved

Poblar la Base de Datos

Una vez haya instalado el Servidor de PowerDNS necesitará poblar su base de datos con el esquema que se provee en el backend.

db_config_path="/etc/powerdns/pdns.d"

# Define el archivo de configuración basado en el tipo de BBDD
db_config_file="$db_config_path/g$db_type.conf"

if [[ $db_type == "mysql" ]]; then
    mariadb -u pdnsadmin -p $pdns_db < "/usr/share/pdns-backend-$db_type/schema/schema.$db_type.sql"
else
    current_pgsql_ver=$(psql -V|awk -F " " '{print $NF}'|awk -F "." '{print $1}')
    export PGPASSWORD="$pdns_pwd"
    psql -U $pdns_db_user -h 127.0.0.1 -d $pdns_db < "/usr/share/pdns-backend-$db_type/schema/schema.$db_type.sql"
    unset PGPASSWORD
fi

# Copia el archivo de configuración de ejemplo
cp /usr/share/doc/pdns-backend-$db_type/examples/g$db_type.conf /etc/powerdns/pdns.d/

# Modifica los valores en su archivo de configuración de PowerDNS 
# con sus credenciales generados
sed -i "s/\(^g$db_type-dbname=\).*/\1$pdns_db/" "$db_config_file"
sed -i "s/\(^g$db_type-host=\).*/\1127.0.0.1/" "$db_config_file"
if [[ $db_type == "mysql" ]]; then
    sed -i "s/\(^g$db_type-port=\).*/\13306/" "$db_config_file"
else
    sed -i "s/\(^g$db_type-port=\).*/\15432/" "$db_config_file"
fi
sed -i "s/\(^g$db_type-user=\).*/\1$pdns_db_user/" "$db_config_file"
sed -i "s/\(^g$db_type-password=\).*/\1$pdns_pwd/" "$db_config_file"
chmod 640 "$db_config_file"
chown root:pdns "$db_config_file"

Configurar PowerDNS (Autoritativo)

Antes de encender el servicio debe configurar algunos parámetros básicos:

# Establezca su IP Local
local-address=127.0.0.1, $EXTERNAL_SERVER_IP

# Si solo utiliza PowerDNS Autoritativo
local-port=53

# Puerto de PDNS Auth con DNSDist
local-port=5300

Configurar PowerDNS (Recursor)

Una vez haya instalado el servicio de PowerDNS Recursor puede continuar y añadir zonas de delegación. Usualmente debería añadirlas solamente si son zonas que usted posee y deben ser resueltas por el servidor Autoritativo de PowerDNS Local.

Para hacerlo edite el archivo /etc/powerdns/recursor.conf.

Puede añadir zonas con el siguiente formato:

# Primer Zona Delegada
forward-zones=example.com=127.0.0.1:5300
# Zona Delegada N
forward-zones+=example.com=127.0.0.1:5300

Configurar la Clave de API

A continuación configurará su clave de API para que el Front-end FLASK pueda comunicarse con el Back-end de PowerDNS.

# Establezca la Clave de API con la que generó en la instalación 
# y habilite la API
sed -i "s/.*api=\(yes\|no\)+$/api=yes/" "/etc/powerdns/pdns.conf"
sed -i "s/\(.*api-key=\).*/\1$pdns_apikey/" "/etc/powerdns/pdns.conf"
sed -i "s/.*webserver=\(yes\|no\)+$/webserver=yes/" "/etc/powerdns/pdns.conf"

# Detenga y encienda el servicio
systemctl stop pdns
systemctl start pdns

Añada el Puerto del Webserver API y el CIDR a permitir.

# Solo se permite el acceso a la API desde estas subredes
webserver-allow-from=127.0.0.1,$LAN_CIDR

# El puerto en el cual escucha la API
webserver-port=8081

Instalar el Front-end / UI de Admin

Instalar Dependencias

Una vez ha instalado el backend, instalaremos el Front-end.

# Instalar las Dependencias de PowerDNS Admin
apt-get install nginx \
python3-dev \
python3-venv \
libsasl2-dev \
libldap2-dev \
libssl-dev \
libxml2-dev \
libxslt1-dev \
libxmlsec1-dev \
libffi-dev \
pkg-config \
apt-transport-https \
virtualenv \
build-essential \
libmariadb-dev \
git \
libpq-dev \
python3-flask -y

Luego de instalar las dependencias requeridas también deberá instalar NodeJS y YARN.

# NodeJS
curl -sL https://deb.nodesource.com/setup_20.x | bash -
apt-get update -y
apt-get install nodejs -y

# YARN
wget -O- https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor > /etc/apt/trusted.gpg.d/yarnpkg.gpg
echo "deb https://dl.yarnpkg.com/debian/ stable main" > "/etc/apt/sources.list.d/yarn.list"
apt-get update -y
apt-get install yarn -y

Ahora podremos clonar el repositorio git de PowerDNS admin y compilarlo.

# Clonar el Repositorio
git clone https://github.com/ngoduykhanh/PowerDNS-Admin.git /var/www/html/pdns
# O
git clone https://github.com/PowerDNS-Admin/PowerDNS-Admin.git /var/www/html/pdns

# Vaya al directorio debajo y active el virtual environment
cd "/var/www/html/pdns/"
virtualenv -p python3.9 flask
source "./flask/bin/activate"

pip install --upgrade pip

# There's a bug with PyYAML 5.4 and Cython 3.0
sed -i "s/PyYAML==5.4/PyYAML==6.0/g" requirements.txt

# Instalar los requerimientos
pip install -r requirements.txt

# Desactivar el virtualenv
deactivate

Una vez hecho esto podemos establecer todas las variables de entorno para el Front-end.

prod_config="/var/www/html/pdns/configs/production.py"
cp "/var/www/html/pdns/configs/development.py" $prod_config

# Establecer Filesystem_sessions
sed -i "s/\(^FILESYSTEM_SESSIONS_ENABLED = \).*/\1True/" $prod_config

# Establecer la SALT en su parámetro correspondiente
sed -i "s/\(^SALT = \).*/\1\'$pdnsadmin_salt\'/" $prod_config
# Establecer APIKEY en su parámetro correspondiente
sed -i "s/\(^SECRET_KEY = \).*/\1\'$pdns_apikey\'/" $prod_config
# Establecer MySQL/MariaDB/PGSQL Password en su parámetro correspondiente
sed -i "s/\(^SQLA_DB_PASSWORD = \).*/\1\'$pdns_pwd\'/" $prod_config
# Establecer DB Name
sed -i "s/\(^SQLA_DB_NAME = \).*/\1\'$pdns_db\'/" $prod_config
# Establecer DB User
sed -i "s/\(^SQLA_DB_USER = \).*/\1\'$pdns_db_user\'/" $prod_config

# Si está utilizando PostgreSQL debe añadir el siguiente requerimiento:
if [[ $db_type != "mysql" ]]; then
	source "./flask/bin/activate"
	pip install psycopg2
	deactivate
fi

sed -i "s/#import urllib.parse/import urllib.parse/g" $prod_config

# Insertar el PUERTO luego de SQLA_DB_USER
if [[ $db_type == "mysql" ]]; then
    db_port="3306"
else
    db_port="5432"
fi

if ! [[ $(grep "SQLA_DB_PORT" $prod_config) ]]; then
    sed -i "/^SQLA_DB_USER.*/a SQLA_DB_PORT = $db_port" $prod_config
else
    sed -i "s/\(^SQLA_DB_PORT = \).*/\1$db_port/" $prod_config
fi
# Insert DB URI
cat >> $prod_config <<EOF
SQLALCHEMY_DATABASE_URI = '$db_type://{}:{}@{}/{}'.format(
    urllib.parse.quote_plus(SQLA_DB_USER),
    urllib.parse.quote_plus(SQLA_DB_PASSWORD),
    SQLA_DB_HOST,
    SQLA_DB_NAME
)
EOF

# Comentar la DATABASE URI de SQLITE Pre-determinada
sed -i "s/\(^SQLALCHEMY_DATABASE_URI = 'sqlite.*\)/#\1/" $prod_config

Compilar el Front-end / UI de Admin

Una vez las dependencias y requerimientos están instalados podemos proceder a compilar y configurar el Front-end.

cd "/var/www/html/pdns/"
source "./flask/bin/activate"

export FLASK_APP=powerdnsadmin/__init__.py
export FLASK_CONF=../configs/production.py
flask db upgrade
yarn install --pure-lockfile
flask assets build
deactivate

Configurar los Servicios del Front-end

Copie los siguientes archivos a su Directorio NGINX o creelos con los siguientes bloques de texto.

/etc/systemd/system/pdnsadmin.service

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

[Service]
PIDFile=/run/pdnsadmin/pid
User=pdns
Group=pdns
Environment="FLASK_CONF=../configs/production.py"
WorkingDirectory=/var/www/html/pdns
ExecStart=/var/www/html/pdns/flask/bin/gunicorn --pid /run/pdnsadmin/pid --bind unix:/run/pdnsadmin/socket 'powerdnsadmin:create_app()'
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target

/etc/systemd/system/pdnsadmin.socket

[Unit]
Description=PowerDNS-Admin socket

[Socket]
ListenStream=/run/pdnsadmin/socket

[Install]
WantedBy=sockets.target

/etc/nginx/sites-enabled/powerdns-admin.conf

server {
    listen  *:80;
    server_name               pdnsadmin.example.com;

    index                     index.html index.htm index.php;
    root                      /var/www/html/pdns;
    access_log                /var/log/nginx/pdnsadmin_access.log combined;
    error_log                 /var/log/nginx/pdnsadmin_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 /var/www/html/pdns/powerdnsadmin;

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

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

    location / {
        proxy_pass            http://unix:/run/pdnsadmin/socket;
        proxy_read_timeout    120;
        proxy_connect_timeout 120;
        proxy_redirect        off;
    }
}
chown -R pdns:www-data "/var/www/html/pdns"
nginx -t && systemctl restart nginx

Luego de copiar y crear su entrada en NGINX debe habilitar los servicios para que PowerDNS-Admin se encienda automáticamente.

echo "d /run/pdnsadmin 0755 pdns pdns -" >> "/etc/tmpfiles.d/pdnsadmin.conf"
mkdir "/run/pdnsadmin/"
chown -R pdns: "/run/pdnsadmin/"
chown -R pdns: "/var/www/html/pdns/powerdnsadmin/"

systemctl daemon-reload

systemctl enable --now pdnsadmin.service pdnsadmin.socket

Sus credenciales de instalación para la BBDD están guardados en /opt/pdns_install/db_credentials

Para ver el estado de PowerDNS Admin puede realizar el comando:

systemctl status pdnsadmin.service pdnsadmin.socket

Configurar DNSDist

Finalmente, si desea utilizar DNSDist para responder pedidos Autoritativos y Recursivos selectivamente en base al CIDR, deberá hacer los siguientes cambios:

-- dnsdist configuration file, an example can be found in /usr/share/doc/dnsdist/examples/

setLocal('{EXTERNAL_SERVER_IP}:53')
--addLocal('{ANOTHER_IP}:53')
setACL({'0.0.0.0/0', '::/0'}) -- Allow all IPs access

newServer({address='127.0.0.1:5300', pool='auth'})
newServer({address='127.0.0.1:5301', pool='recursor'})

recursive_ips = newNMG()
recursive_ips:addMask('127.0.0.0/8')
recursive_ips:addMask('{LAN_CIDR}') -- These network masks are the ones from allow-recursion in the Authoritative Server
addAction(NetmaskGroupRule(recursive_ips), PoolAction('recursor'))
addAction(AllRule(), PoolAction('auth'))

-- disable security status polling via DNS
setSecurityPollSuffix("")

En esencia esto filtrará sus Hosts de la LAN de los pedidos externos.

Borrar las variables

Finalmente una vez concluida la instalación querrá despejar las variables de la memoria.

unset systemReleaseVersion
unset osLabel
unset repo_pdnsAuth
unset repo_pdnsRec
unset repo_dnsdist
unset pdns_pwd
unset pdnsadmin_salt
unset pdns_apikey
unset workpath