Installation

Timing

A complete installation of woohoo pDNS GUI will require about 30 minutes to complete for an experienced admin.

This does not include installing woohoo pDNS itself.

Requirements

woohoo pDNS GUI is a Python 3 project, therefore you need Python 3 to run it.

Also, (access to) a pDNS database that ouptuts data in Passive DNS - Common Output Format is required. Currently only databases offering API token authentication are supported (not for example the ones _guiusing basic authentication). One option is to use woohoo pDNS.

The Flask webapp is served by Gunicorn. It is strongly suggested to have a reverse proxy (like Nginx, lighttpd, Apache, …) in front of it.

Overview

The installation will consist of the following steps:

  1. create a virtual environment (Python 3)

  2. install woohoo pDNS GUI and dependencies

  3. configure access to the pDNS database

  4. set up the configuration in the reverse proxy

  5. configure Gunicorn to serve the GUI

Installing

The virtual environment

Any way of virtualising the Python environment can be used to run woohoo pDNS GUI. For this guide we use Python’s integrated venv method.

Caution

woohoo pDNS has pinned it’s dependencies! This means that the exact version is noted in requirements.txt for all dependencies. This might have undesired side effects when installing in a non-empty environment where one of the packages woohoo pDNS depends on is already installed.

So, go ahead and choose a suitable home for your installation of woohoo pDNS GUI. For Linux/*BSD systems, something under /usr/local might make sense (e.g. /usr/local/opt/woohoo-pdns).

Once you have decided on the location and created a folder for woohoo pDNS GUI, create a new virtual environment like this:

$ python -m venv .pdns_gui

This will create a folder named .pdns_gui in the current directory and this folder will hold your virtual environment of the same name.

Note: on a Mac of mine, creating the virtual environment like this failed with an error like:

Error: Command '['/Users/<username>/tmp/.pdns/bin/python', '-Im', 'ensurepip', '--upgrade', '--default-pip']' returned non-zero exit status 1.

which can be fixed by following advice found on Stackoverflow:

$ python -m venv --without-pip .pdns
$ source .pdns_gui/bin/activate
$ curl https://bootstrap.pypa.io/get-pip.py | python
$ deactivate
$ source .pdns_gui/bin/activate

Install woohoo pDNS GUI and dependencies

Go ahead and activate the new environment if not already done (your shell prompt should change):

$ source .pdns_gui/bin/activate

You should now populate this new virtual environment with woohoo pDNS GUI and the required dependencies:

(.pdns_gui)$ pip install woohoo-pdns-gui

or install it from source:

(.pdns_gui)$ git clone https://gitlab.com/scherand/woohoo-pdns-gui
(.pdns_gui)$ cd woohoo-pdns-gui
(.pdns_gui)$ python setup.py install
(.pdns_gui)$ pip install -r requirements.txt

Configure access to the pDNS database

To properly run the woohoo pDNS GUI, you will have to provide a config file (python file, i.e. ending in .py) with the following information/format:

TIMEZONE="UTC"  # "Europe/Zurich" or "America/New_York"
WOOHOO_APPLICATION_ROOT = ""
SECRET_KEY = "snakeoil"
API_KEY = "MsfPfDqYMQGDc4nVcGTMS8UA"
API_ENTRYPOINT = "http://localhost:5000/api"

The values shown here are the default values that will be used if you do not provide a config file.

A list of all known timezones can be found via pytz.all_timezones.

You can use whatever you like for the SECRET_KEY; it is a Flask thing, see woohoo_pdns_gui.config.DefaultSettings.SECRET_KEY.

Use the WOOHOO_APPLICATION_ROOT variable if you are running woohoo pDNS from a subfolder of the vHost. If you have for example configured your reverse proxy to forward requests for www.example.com/pdns/ to gunicorn, you should set WOOHOO_APPLICATION_ROOT to /pdns. This is required that woohoo pDNS GUI can construct the correct hyperlinks.

Set up the configuration in the reverse proxy

Again, the exact steps depend on the reverse proxy software you use and the administrative processes around it. Assuming you have all the required permissions and want to use lighttpd, the configuration should look about as follows:

$HTTP["host"] =~ "^pdns.example.com$" {
    $HTTP["url"] =~ "^/" {
        proxy.server = ( "" => ( (
            "host" => "localhost",
            "port" => 5000
        ) ) )
    }
}

Configure Gunicorn to serve the GUI

The GUI is served by a Flask application (WSGI application) that lives in woohoo_pdns_gui.app.py and is served by Gunicorn. To fire it up, you can use many different ways. For example, a startup script.

Consider using a dedicated user for Gunicorn.

You must provide the name of a config file via an environment variable called WOOHOO_PDNS_GUI_SETTINGS. This should be the python config file mentioned earlier. If only a filename is specified, the file is expected to be in a folder called instance in the directory you are starting flask from. In general, the path to the config file is interpreted as relative to the mentioned instance folder.

The following outlines the FreeBSD rc.d script (/usr/local/etc/rc.d/pdns-api-gunicorn) I use for this purpose (inspired by a thread in the FreeBSD forums):

#! /bin/sh

# PROVIDE: pdns_gui_gunicorn
# REQUIRE: DAEMON
# KEYWORD: shutdown

#
# Add the following lines to /etc/rc.conf to enable the woohoo pDNS GUI:
#
#pdns_gui_gunicorn_enable="YES"

. /etc/rc.subr

name="pdns_gui_gunicorn"
rcvar="${name}_enable"
start_cmd="${name}_start"
stop_cmd="${name}_stop"
pidfile="/var/run/${name}.pid"
procname="daemon:"
gip="localhost"
gport="5000"

pdns_gui_gunicorn_start(){
    chdir /usr/local/opt/woohoo-pdns-gui
    . /root/.virtualenvs/pdns_gui/bin/activate
    LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 FLASK_ENV=production WOOHOO_PDNS_GUI_SETTINGS="pdns_gui_conf.py" daemon -r -S -P ${pidfile} -T pdns-gui-gunicorn -u root /root/.virtualenvs/pdns_gui/bin/gunicorn --workers 3 --bind ${gip}:${gport} "woohoo_pdns.gui:create_app()"
}

pdns_gui_gunicorn_stop(){
    if [ -f ${pidfile} ]; then
        echo -n "Stopping services: ${name}"
        # MUST send TERM signal (not e.g. INT) to work properly with '-P' switch
        # check daemon(8) for details
        kill -s TERM $(cat ${pidfile})
        if [ -f ${gsocket} ]; then
            rm -f ${gsocket}
        fi
        echo "."
    else
        echo "It appears ${name} is not running."
    fi
}

load_rc_config ${name}
# this sets the default 'enable' (to no)
: ${pdns_gui_gunicorn_enable:="no"}
run_rc_command "$1"