diff --git a/ansible-playbook.yml b/ansible-playbook.yml new file mode 100644 index 0000000..866441f --- /dev/null +++ b/ansible-playbook.yml @@ -0,0 +1,129 @@ +--- +- hosts: localhost + vars: + pdns_master_ip: 172.5.0.20 + pdns_slave_ip: 172.5.0.21 + +# for now, you need to manually create the docker network: +# docker network create --subnet 172.5.0.0/16 --gateway 172.5.0.1 pdns-net + + tasks: + - name: db + docker_container: + name: pdns-mariadb + image: mariadb:10.1 + pull: true + state: "{{ c_state | default('started') }}" + volumes: + - pdns-mariadb-volume:/var/lib/mysql:z + env: + MYSQL_ROOT_PASSWORD: my-secret-pw + networks: + - name: pdns-net + tags: + - db + + - name: phpmyadmin + docker_container: + name: pdns-phpmyadmin + image: phpmyadmin/phpmyadmin + pull: true + state: "{{ c_state | default('started') }}" + published_ports: + - '8888:80' + networks: + - name: pdns-net + tags: + - db + + - name: build pdns + docker_image: + name: my-pdns + state: "{{ i_state | default('present') }}" + force: true + path: ./pdns + tags: + - pdns + + - name: pdns master + docker_container: + name: pdns + image: my-pdns + state: "{{ c_state | default('started') }}" + hostname: ns1.example.com + networks: + - name: pdns-net + ipv4_address: '{{ pdns_master_ip }}' + etc_hosts: + ns1.example.com: '{{ pdns_master_ip }}' + ns2.example.com: '{{ pdns_slave_ip }}' + links: + - pdns-mariadb:mysql + env: + PDNS_master: yes + PDNS_api: yes + PDNS_api_key: secret + PDNS_webserver: yes + PDNS_webserver_address: 0.0.0.0 + PDNS_version_string: anonymous + PDNS_default_ttl: 1500 + PDNS_soa_minimum_ttl: 1200 + PDNS_default_soa_name: ns1.example.com + PDNS_default_soa_mail: hostmaster.example.com + PDNS_allow_axfr_ips: '{{ pdns_slave_ip }}' + PDNS_only_notify: '{{ pdns_slave_ip }}' + tags: + - pdns + + - name: pdns slave + docker_container: + name: pdns-slave + image: my-pdns + state: "{{ c_state | default('started') }}" + hostname: ns2.example.com + networks: + - name: pdns-net + ipv4_address: '{{ pdns_slave_ip }}' + etc_hosts: + ns1.example.com: '{{ pdns_master_ip }}' + ns2.example.com: '{{ pdns_slave_ip }}' + links: + - pdns-mariadb:mysql + env: + PDNS_gmysql_dbname: powerdnsslave + PDNS_slave: yes + PDNS_webserver: yes + PDNS_webserver_address: 0.0.0.0 + PDNS_version_string: anonymous + PDNS_disable_axfr: yes + PDNS_allow_notify_from: '{{ pdns_master_ip }}' + SUPERMASTER_IPS: '{{ pdns_master_ip }}' + tags: + - pdns + + - name: build pdns-admin + docker_image: + name: my-pdns-admin + state: "{{ i_state | default('present') }}" + force: true + path: ./pdns-admin + tags: + - pdns-admin + + - name: pdns-admin + docker_container: + name: pdns-admin + image: my-pdns-admin + state: "{{ c_state | default('started') }}" + tty: true + published_ports: + - '8889:80' + networks: + - name: pdns-net + links: + - pdns-mariadb:mysql + - pdns + volumes: + - pdns-admin-upload:/opt/powerdns-admin/upload + tags: + - pdns-admin diff --git a/pdns-admin/Dockerfile b/pdns-admin/Dockerfile new file mode 100644 index 0000000..84b42fd --- /dev/null +++ b/pdns-admin/Dockerfile @@ -0,0 +1,54 @@ +FROM fedora:24 +MAINTAINER "Peter Schiffer" + +RUN dnf -y --setopt=tsflags=nodocs install \ + tar \ + python-pip \ + python2-mysql \ + mariadb \ + gcc \ + redhat-rpm-config \ + python-devel \ + openldap-devel \ + nginx \ + uwsgi \ + uwsgi-plugin-python \ + && dnf clean all \ + && systemctl enable nginx \ + && systemctl enable uwsgi + +RUN mkdir -p /opt/powerdns-admin \ + && curl -sSL https://github.com/ngoduykhanh/PowerDNS-Admin/archive/master.tar.gz \ + | tar -xzC /opt/powerdns-admin --strip 1 \ + && chown -R root: /opt/powerdns-admin \ + && chown -R uwsgi: /opt/powerdns-admin/upload + +WORKDIR /opt/powerdns-admin + +RUN pip3 install envtpl \ + && pip install -r requirements.txt \ + && rm -rf ~/.cache/* + +STOPSIGNAL SIGRTMIN+3 +RUN echo 'ForwardToConsole=yes' >> /etc/systemd/journald.conf + +ENV container=docker \ + PDNS_ADMIN_LOGIN_TITLE="'PDNS'" \ + PDNS_ADMIN_TIMEOUT=10 \ + PDNS_ADMIN_LOG_LEVEL="'INFO'" \ + PDNS_ADMIN_BASIC_ENABLED=True \ + PDNS_ADMIN_SIGNUP_ENABLED=True \ + PDNS_ADMIN_RECORDS_ALLOW_EDIT="['SOA', 'NS', 'A', 'AAAA', 'CNAME', 'MX', 'TXT', 'SRV']" + +EXPOSE 80 + +VOLUME [ "/opt/powerdns-admin/upload" ] + +COPY pdns-admin.ini /etc/uwsgi.d/ +RUN chown uwsgi: /etc/uwsgi.d/pdns-admin.ini + +COPY pdns-nginx.conf /etc/nginx/nginx.conf +COPY config.py.tpl / +COPY docker-cmd.sh /init + +CMD [ "/init" ] diff --git a/pdns-admin/config.py.tpl b/pdns-admin/config.py.tpl new file mode 100644 index 0000000..47e8187 --- /dev/null +++ b/pdns-admin/config.py.tpl @@ -0,0 +1,14 @@ +import os +basedir = os.path.abspath(os.path.dirname(__file__)) + +{% for key, value in environment('PDNS_ADMIN_') %}{{ key }} = {{ value }} +{% endfor %} + +WTF_CSRF_ENABLED = True +BIND_ADDRESS = '0.0.0.0' +PORT = 9393 +LOG_FILE = '/dev/null' +UPLOAD_DIR = '/opt/powerdns-admin/upload' +SQLALCHEMY_DATABASE_URI = 'mysql://' + SQLA_DB_USER + ':' + SQLA_DB_PASSWORD + '@' + SQLA_DB_HOST + ':' + SQLA_DB_PORT + '/' + SQLA_DB_NAME +SQLALCHEMY_MIGRATE_REPO = os.path.join(basedir, 'db_repository') +SQLALCHEMY_TRACK_MODIFICATIONS = True diff --git a/pdns-admin/docker-cmd.sh b/pdns-admin/docker-cmd.sh new file mode 100755 index 0000000..7a69245 --- /dev/null +++ b/pdns-admin/docker-cmd.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +set -euo pipefail + +# Configure mysql env vars +: "${PDNS_ADMIN_SQLA_DB_HOST:='mysql'}" +: "${PDNS_ADMIN_SQLA_DB_PORT:='3306'}" +: "${PDNS_ADMIN_SQLA_DB_USER:='${MYSQL_ENV_MYSQL_USER:-root}'}" +if [ "${PDNS_ADMIN_SQLA_DB_USER}" = "'root'" ]; then + : "${PDNS_ADMIN_SQLA_DB_PASSWORD:='$MYSQL_ENV_MYSQL_ROOT_PASSWORD'}" +fi +: "${PDNS_ADMIN_SQLA_DB_PASSWORD:='${MYSQL_ENV_MYSQL_PASSWORD:-powerdnsadmin}'}" +: "${PDNS_ADMIN_SQLA_DB_NAME:='${MYSQL_ENV_MYSQL_DATABASE:-powerdnsadmin}'}" + +export PDNS_ADMIN_SQLA_DB_HOST PDNS_ADMIN_SQLA_DB_PORT PDNS_ADMIN_SQLA_DB_USER PDNS_ADMIN_SQLA_DB_PASSWORD PDNS_ADMIN_SQLA_DB_NAME + +# Configure pdns server env vars +: "${PDNS_ADMIN_PDNS_STATS_URL:='http://pdns:${PDNS_ENV_PDNS_webserver_port:-8081}/'}" +: "${PDNS_ADMIN_PDNS_API_KEY:='${PDNS_ENV_PDNS_api_key:-}'}" +: "${PDNS_ADMIN_PDNS_VERSION:='${PDNS_ENV_VERSION:-}'}" + +export PDNS_ADMIN_PDNS_STATS_URL PDNS_ADMIN_PDNS_API_KEY PDNS_ADMIN_PDNS_VERSION + +# Generate secret key +[ -f /root/secret-key ] || tr -dc _A-Z-a-z-0-9 < /dev/urandom | head -c 32 > /root/secret-key || true +PDNS_ADMIN_SECRET_KEY="'$(cat /root/secret-key)'" + +export PDNS_ADMIN_SECRET_KEY + +envtpl < /config.py.tpl > /opt/powerdns-admin/config.py + +# Initialize DB if needed +MYSQL_COMMAND="mysql -h ${PDNS_ADMIN_SQLA_DB_HOST//\'/} -P ${PDNS_ADMIN_SQLA_DB_PORT//\'/} -u ${PDNS_ADMIN_SQLA_DB_USER//\'/} -p${PDNS_ADMIN_SQLA_DB_PASSWORD//\'/}" + +until $MYSQL_COMMAND -e ';' ; do + >&2 echo 'MySQL is unavailable - sleeping' + sleep 1 +done + +$MYSQL_COMMAND -e "CREATE DATABASE IF NOT EXISTS ${PDNS_ADMIN_SQLA_DB_NAME//\'/}" + +MYSQL_CHECK_IF_HAS_TABLE="SELECT COUNT(DISTINCT table_name) FROM information_schema.columns WHERE table_schema = '${PDNS_ADMIN_SQLA_DB_NAME//\'/}';" +MYSQL_NUM_TABLE=$($MYSQL_COMMAND --batch --skip-column-names -e "$MYSQL_CHECK_IF_HAS_TABLE") +if [ "$MYSQL_NUM_TABLE" -eq 0 ]; then + python2 /opt/powerdns-admin/create_db.py +fi + +# python2 /opt/powerdns-admin/db_upgrade.py + +exec /usr/sbin/init diff --git a/pdns-admin/pdns-admin.ini b/pdns-admin/pdns-admin.ini new file mode 100644 index 0000000..1b01f4d --- /dev/null +++ b/pdns-admin/pdns-admin.ini @@ -0,0 +1,20 @@ +[uwsgi] +plugins = python + +uid=uwsgi +gid=uwsgi + +chdir = /opt/powerdns-admin +pythonpath = /opt/powerdns-admin + +mount = /=run.py +manage-script-name = true +callable = app + +vacuum = true +harakiri = 20 +post-buffering = 8192 +socket = localhost:9494 +pidfile = /run/uwsgi/%n.pid + +enable-threads = true diff --git a/pdns-admin/pdns-nginx.conf b/pdns-admin/pdns-nginx.conf new file mode 100644 index 0000000..79fc445 --- /dev/null +++ b/pdns-admin/pdns-nginx.conf @@ -0,0 +1,68 @@ +# For more information on configuration, see: +# * Official English Documentation: http://nginx.org/en/docs/ +# * Official Russian Documentation: http://nginx.org/ru/docs/ + +user nginx; +worker_processes auto; +error_log /var/log/nginx/error.log; +pid /run/nginx.pid; + +# Load dynamic modules. See /usr/share/nginx/README.fedora. +include /usr/share/nginx/modules/*.conf; + +events { + worker_connections 1024; +} + +http { + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + + include /etc/nginx/mime.types; + default_type application/octet-stream; + + # Load modular configuration files from the /etc/nginx/conf.d directory. + # See http://nginx.org/en/docs/ngx_core_module.html#include + # for more information. + include /etc/nginx/conf.d/*.conf; + + server { + listen 80 default_server; + listen [::]:80 default_server; + server_name _; + root /usr/share/nginx/html; + + # Load configuration files for the default server block. + include /etc/nginx/default.d/*.conf; + + location / { + try_files $uri @pdns_admin; + } + + location @pdns_admin { + include uwsgi_params; + uwsgi_pass localhost:9494; + } + + location /static/ { + alias /opt/powerdns-admin/app/static/; + } + + error_page 404 /404.html; + location = /40x.html { + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + } + } +} diff --git a/pdns/Dockerfile b/pdns/Dockerfile new file mode 100644 index 0000000..d8b124e --- /dev/null +++ b/pdns/Dockerfile @@ -0,0 +1,24 @@ +FROM fedora:24 +MAINTAINER "Peter Schiffer" + +RUN dnf -y --setopt=tsflags=nodocs install \ + pdns \ + pdns-backend-mysql \ + mariadb \ + && dnf clean all + +RUN pip3 install envtpl \ + && rm -rf ~/.cache/* + +ENV VERSION=4.0.1 \ + PDNS_guardian=yes \ + PDNS_setuid=pdns \ + PDNS_setgid=pdns \ + PDNS_launch=gmysql + +EXPOSE 53 53/udp + +COPY pdns.conf.tpl / +COPY docker-cmd.sh / + +CMD [ "/docker-cmd.sh" ] diff --git a/pdns/docker-cmd.sh b/pdns/docker-cmd.sh new file mode 100755 index 0000000..58e5f9a --- /dev/null +++ b/pdns/docker-cmd.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +set -euo pipefail + +# Configure mysql env vars +: "${PDNS_gmysql_host:=mysql}" +: "${PDNS_gmysql_port:=3306}" +: "${PDNS_gmysql_user:=${MYSQL_ENV_MYSQL_USER:-root}}" +if [ "${PDNS_gmysql_user}" = 'root' ]; then + : "${PDNS_gmysql_password:=$MYSQL_ENV_MYSQL_ROOT_PASSWORD}" +fi +: "${PDNS_gmysql_password:=${MYSQL_ENV_MYSQL_PASSWORD:-powerdns}}" +: "${PDNS_gmysql_dbname:=${MYSQL_ENV_MYSQL_DATABASE:-powerdns}}" + +export PDNS_gmysql_host PDNS_gmysql_port PDNS_gmysql_user PDNS_gmysql_password PDNS_gmysql_dbname + +# Create config file from template +envtpl < /pdns.conf.tpl > /etc/pdns/pdns.conf + +# Initialize DB if needed +MYSQL_COMMAND="mysql -h ${PDNS_gmysql_host} -P ${PDNS_gmysql_port} -u ${PDNS_gmysql_user} -p${PDNS_gmysql_password}" + +until $MYSQL_COMMAND -e ';' ; do + >&2 echo 'MySQL is unavailable - sleeping' + sleep 1 +done + +$MYSQL_COMMAND -e "CREATE DATABASE IF NOT EXISTS ${PDNS_gmysql_dbname}" + +MYSQL_CHECK_IF_HAS_TABLE="SELECT COUNT(DISTINCT table_name) FROM information_schema.columns WHERE table_schema = '${PDNS_gmysql_dbname}';" +MYSQL_NUM_TABLE=$($MYSQL_COMMAND --batch --skip-column-names -e "$MYSQL_CHECK_IF_HAS_TABLE") +if [ "$MYSQL_NUM_TABLE" -eq 0 ]; then + $MYSQL_COMMAND -D "$PDNS_gmysql_dbname" < /usr/share/doc/pdns/schema.mysql.sql +fi + +# Configure supermasters if needed +if [ "${SUPERMASTER_IPS:-}" ]; then + $MYSQL_COMMAND -D "$PDNS_gmysql_dbname" -e "TRUNCATE supermasters;" + MYSQL_INSERT_SUPERMASTERS='' + for i in $SUPERMASTER_IPS; do + MYSQL_INSERT_SUPERMASTERS="${MYSQL_INSERT_SUPERMASTERS} INSERT INTO supermasters VALUES('${i}', '${HOSTNAME}', 'admin');" + done + $MYSQL_COMMAND -D "$PDNS_gmysql_dbname" -e "$MYSQL_INSERT_SUPERMASTERS" +fi + +exec /usr/sbin/pdns_server diff --git a/pdns/pdns.conf.tpl b/pdns/pdns.conf.tpl new file mode 100644 index 0000000..8330f43 --- /dev/null +++ b/pdns/pdns.conf.tpl @@ -0,0 +1,2 @@ +{% for key, value in environment('PDNS_') %}{{ key|replace('_', '-') }}={{ value }} +{% endfor %}