PowerDNS-Admin

PowerDNS-Admin is a web-based administration interface for PowerDNS.

Unlike other older front-end applications for PowerDNS, who often write directly to the PowerDNS database, it uses the PowerDNS application programming interface introduced in PowerDNS 3.x and 4.x.

Prerequisites

  • A working PowerDNS installation;

  • MariaDB database server for storage of zone meta-data, users and their roles;

  • Git to get the source code;

  • Python language development environment;

  • Development libraries for MariaDB, SASL, LDAP and SSL;

  • NodeJS, and Yarn

  • uWSGI Python application web-server;

  • Nginx web-server as a reverse proxy to the web application;

Database

Create a random password for the database user (32 characters recommended):

$ pwgen --secure 32 1
********************************

Independently of PowerDNS storing its data in a MariaDB database, PowerDNS- Admin uses its own database to store users, access rights and various other information about the managed domains (zones).

To create the database used by the PowerDNS-Admin application start an interactive session with root privileges on your database server:

$ mysql -u root -p
Enter password: ********

While using your MariaDB root password to login.

mysql> CREATE DATABASE powerdnsadmin;
mysql> GRANT ALL PRIVILEGES ON powerdnsadmin.* TO 'powerdnsadmin'@'localhost'
        IDENTIFIED BY '********************************';
mysql> FLUSH PRIVILEGES;
mysql> quit

While using the random database user password created earlier.

Installation

Software Libraries

Install required software packages:

$ sudo apt install -y build-essential pkg-config curl git \
    python3-dev python3-venv \
    libmariadb-dev libldap2-dev libsasl2-dev libpq-dev

Install NodeJs

Node.js is a cross-platform, open-source server environment that can run on Windows, Linux, Unix, macOS, and more. Node.js is a back-end JavaScript runtime environment, runs on the V8 JavaScript engine, and executes JavaScript code outside a web browser.

$ curl -sL https://deb.nodesource.com/setup_14.x | sudo bash -
$ sudo apt install -y nodejs

Install Yarn

Yarn is one of the main JavaScript package managers, developed in 2016 by Sebastian McKenzie of Meta for the Node.js JavaScript runtime environment. An alternative to the npm package manager, Yarn was created as a collaboration of Facebook (now Meta), Exponent (now Expo.dev), Google, and Tilde (the company behind Ember.js) to solve consistency, security, and performance problems with large codebases.

$ curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor \
    | sudo tee /usr/share/keyrings/yarnkey.gpg >/dev/null
$ echo "deb [signed-by=/usr/share/keyrings/yarnkey.gpg] https://dl.yarnpkg.com/debian stable main" \
    | sudo tee /etc/apt/sources.list.d/yarn.list
$ sudo apt update && sudo apt install -y yarn

Source Code

Start a shell for the user running the webserver:

$ sudo -u www-data -Hs

Set the version number:

$ PDNS_ADMIN_VERSION=0.4.1

Download the package from GitHub:

wget -O /tmp/PowerDNS-Admin-v${PDNS_ADMIN_VERSION}.tar.gz \
    https://github.com/PowerDNS-Admin/PowerDNS-Admin/archive/refs/tags/v${PDNS_ADMIN_VERSION}.tar.gz

Unpack and and install it:

$ cd /var/www/example.net/admin
$ tar -xzf /tmp/PowerDNS-Admin-v${PDNS_ADMIN_VERSION}.tar.gz

Create a new virtual Python environment and activate it:

www-data$ cd ./PowerDNS-Admin-${PDNS_ADMIN_VERSION}
www-data$ python3 -mvenv ./venv
www-data$ source ./venv/bin/activate

You should now see that your are working in a virtual environment by looking at your system command prompt:

(venv) www-data$

Install the required Python libraries:

(venv) www-data$ echo "cython < 3.0" > constraints.txt
(venv) www-data$ PIP_CONSTRAINT=constraints.txt pip install --upgrade pip
(venv) www-data$ PIP_CONSTRAINT=constraints.txt pip install setuptools wheel pkgconfig
(venv) www-data$ PIP_CONSTRAINT=constraints.txt pip install -r requirements.txt

Configuration

A random Flask secret app key (32 characters recommended):

$ pwgen --secure 32 1
********************************

Copy the configuration and edit it:

(venv) www-data$ cp ./configs/development.py ./configs/production.py

/var/www/example.net/admin/PowerDNS-Admin-0.4.1/configs/production.py:

Basic Settings

  • Enable CSRF (Cross-Site Request Forgery) protection for the HTML forms. Default is True.

  • Set a secret key to sign the users browser cookies for user session protection.

  • The address and port where the applications web-interface listens for incoming requests.

1### BASIC APP CONFIG
2SALT = '$2b$12$yLUMTIfl21FKJQpTkRQXCu'
3SECRET_KEY = '********************************'
4BIND_ADDRESS = '0.0.0.0'
5PORT = 9191
6SERVER_EXTERNAL_SSL = None

Database Connection

1### DATABASE CONFIG
2SQLA_DB_USER = 'pdnsadminuser'
3SQLA_DB_PASSWORD = '********************************'
4SQLA_DB_HOST = 'localhost'
5SQLA_DB_NAME = 'powerdnsadmin'
6SQLALCHEMY_TRACK_MODIFICATIONS = True

While using the random database user password created earlier.

 1SQLALCHEMY_DATABASE_URI = 'mysql://{}:{}@{}/{}'.format(
 2   urllib.parse.quote_plus(SQLA_DB_USER),
 3   urllib.parse.quote_plus(SQLA_DB_PASSWORD),
 4   SQLA_DB_HOST,
 5   SQLA_DB_NAME
 6)
 7
 8### DATABASE - PostgreSQL
 9## Don't forget to uncomment the import in the top
10#SQLALCHEMY_DATABASE_URI = 'postgres://{}:{}@{}/{}'.format(
11#    urllib.parse.quote_plus(SQLA_DB_USER),
12#    urllib.parse.quote_plus(SQLA_DB_PASSWORD),
13#    SQLA_DB_HOST,
14#    SQLA_DB_NAME
15#)

Disable SAML Authentication

Since we don’t have or use LDAP, we disable it by commenting out the relevant entries.

1# SAML Authnetication
2SAML_ENABLED = False

Activate Configuration

(venv) www-data$ export FLASK_CONF=../configs/production.py

Initialize Database

Let it create the database records:

(venv) www-data$ ./create_db.py

Upgrade Database

Do the DB migration:

(venv) www-data$ export FLASK_APP=powerdnsadmin/__init__.py
(venv) www-data$ flask db upgrade

Generate Asset Files

(venv) www-data$ yarn install --pure-lockfile
(venv) www-data$ flask assets build

First Run

Let it start for a first time to create the administrative user:

(venv) www-data$ ./run.py

Point your browser to http://192.0.2.41:9393 to create your first user account, which automatically will have administrative privileges.

Log-out with your browser and stop the server by pressing CTRL-C in you session.

Leave the virtual environment:

(venv) www-data$ deactivate

SystemD Configuration

Service

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

[Service]
PIDFile=/run/powerdns-admin/pid
User=www-data
Group=www-data
WorkingDirectory=/var/www/example.net/admin/PowerDNS-Admin-0.4.1
ExecStartPre=+mkdir -p /run/powerdns-admin/
ExecStartPre=+chown www-data:www-data -R /run/powerdns-admin/
ExecStart=/var/www/example.net/admin/PowerDNS-Admin/venv/bin/gunicorn \
            --pythonpath /var/www/example.net/admin/PowerDNS-Admin-git/venv/lib/python3.10/site-packages \
            --pid /run/powerdns-admin/pid \
            --bind unix:/run/powerdns-admin/socket 'powerdnsadmin:create_app()'
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target

Socket

[Unit]
Description=PowerDNS-Admin socket

[Socket]
ListenStream=/run/powerdns-admin/socket

[Install]
WantedBy=sockets.target

Activate

$ sudo systemctl daemon-reload
$ sudo systemctl powerdns-admin.socket enable
$ sudo systemctl powerdns-admin.socket start

Nginx Configuration

Create the file /etc/nginx/webapps/powerdns-admin.conf as follows:

# **********************************************************************
# PowerDNS-Admin-GUI
# **********************************************************************


index index.html index.htm index.php;
root /var/www/example.net/admin/PowerDNS-Admin;

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/example.net/admin/PowerDNS-Admin/powerdnsadmin;
    location ~*  \.(jpg|jpeg|png|gif)$ { expires 365d; }
    location ~* ^.+.(css|js)$ { expires 7d; }
}

location ~ ^/upload/  {
    include mime.types;
    root /var/www/example.net/admin/PowerDNS-Admin/powerdnsadmin;
    location ~* \.(jpg|jpeg|png|gif)$ { expires 365d; }
    location ~* ^.+.(css|js)$ { expires 7d; }
}

location / {
    proxy_pass http://unix:/run/powerdns-admin/socket;
    proxy_read_timeout 120;
    proxy_connect_timeout 120;
    proxy_redirect off;
}

# -*- mode: nginx; indent-tabs-mode: nil; tab-width: 4; -*-

Include the file in the appropriate Nginx server definition:

# PowerDNS-Admin
include     webapps/powerdns-admin.conf;

Test the and reload the Nginx configuration:

$ sudo service nginx conftest && sudo service nginx reload

App Configuration

PowerDNS API Server Connection

This is how the web application connects to the API interface of the PowerDNS server.

Settings should match with your REST-API server configuration of PowerDNS.

References