Adjustment to work with Python3

This commit is contained in:
Khanh Ngo 2018-03-30 13:49:35 +07:00
parent b6ed658cbd
commit b5b3b77acb
15 changed files with 254 additions and 159 deletions

1
.gitignore vendored
View file

@ -23,6 +23,7 @@ nosetests.xml
flask flask
config.py config.py
logfile.log logfile.log
log.txt
db_repository/* db_repository/*
upload/avatar/* upload/avatar/*

17
Dockerfile Normal file
View file

@ -0,0 +1,17 @@
FROM ubuntu:latest
MAINTAINER Khanh Ngo "ngokhanhit@gmail.com"
ARG ENVIRONMENT=development
ENV ENVIRONMENT=${ENVIRONMENT}
WORKDIR /powerdns-admin
RUN apt-get update -y
RUN apt-get install -y python3-pip python3-dev libmysqlclient-dev supervisor
RUN apt-get install -y libsasl2-dev libldap2-dev libssl-dev
COPY ./requirements.txt /powerdns-admin/requirements.txt
RUN pip3 install -r requirements.txt
ADD ./supervisord.conf /etc/supervisord.conf
ADD . /powerdns-admin/
COPY ./configs/${ENVIRONMENT}.py /powerdns-admin/config.py

View file

@ -2,20 +2,21 @@ import re
import sys import sys
import json import json
import requests import requests
import urlparse
import hashlib import hashlib
from app import app from app import app
from distutils.version import StrictVersion from distutils.version import StrictVersion
from urllib.parse import urlparse
if 'TIMEOUT' in app.config.keys(): if 'TIMEOUT' in app.config.keys():
TIMEOUT = app.config['TIMEOUT'] TIMEOUT = app.config['TIMEOUT']
else: else:
TIMEOUT = 10 TIMEOUT = 10
def auth_from_url(url): def auth_from_url(url):
auth = None auth = None
parsed_url = urlparse.urlparse(url).netloc parsed_url = urlparse(url).netloc
if '@' in parsed_url: if '@' in parsed_url:
auth = parsed_url.split('@')[0].split(':') auth = parsed_url.split('@')[0].split(':')
auth = requests.auth.HTTPBasicAuth(auth[0], auth[1]) auth = requests.auth.HTTPBasicAuth(auth[0], auth[1])
@ -55,7 +56,7 @@ def fetch_remote(remote_url, method='GET', data=None, accept=None, params=None,
if r.status_code not in (200, 400, 422): if r.status_code not in (200, 400, 422):
r.raise_for_status() r.raise_for_status()
except Exception as e: except Exception as e:
raise RuntimeError("While fetching " + remote_url + ": " + str(e)), None, sys.exc_info()[2] raise RuntimeError('Error while fetching {0}'.format(remote_url)) from e
return r return r
@ -72,16 +73,16 @@ def fetch_json(remote_url, method='GET', data=None, params=None, headers=None):
try: try:
assert('json' in r.headers['content-type']) assert('json' in r.headers['content-type'])
except Exception as e: except Exception as e:
raise Exception("While fetching " + remote_url + ": " + str(e)), None, sys.exc_info()[2] raise RuntimeError('Error while fetching {0}'.format(remote_url)) from e
# don't use r.json here, as it will read from r.text, which will trigger # don't use r.json here, as it will read from r.text, which will trigger
# content encoding auto-detection in almost all cases, WHICH IS EXTREMELY # content encoding auto-detection in almost all cases, WHICH IS EXTREMELY
# SLOOOOOOOOOOOOOOOOOOOOOOW. just don't. # SLOOOOOOOOOOOOOOOOOOOOOOW. just don't.
data = None data = None
try: try:
data = json.loads(r.content) data = json.loads(r.content.decode('utf-8'))
except UnicodeDecodeError: except Exception as e:
data = json.loads(r.content, 'iso-8859-1') raise RuntimeError('Error while loading JSON data from {0}'.format(remote_url)) from e
return data return data
@ -92,6 +93,7 @@ def display_record_name(data):
else: else:
return record_name.replace('.'+domain_name, '') return record_name.replace('.'+domain_name, '')
def display_master_name(data): def display_master_name(data):
""" """
input data: "[u'127.0.0.1', u'8.8.8.8']" input data: "[u'127.0.0.1', u'8.8.8.8']"
@ -99,6 +101,7 @@ def display_master_name(data):
matches = re.findall(r'\'(.+?)\'', data) matches = re.findall(r'\'(.+?)\'', data)
return ", ".join(matches) return ", ".join(matches)
def display_time(amount, units='s', remove_seconds=True): def display_time(amount, units='s', remove_seconds=True):
""" """
Convert timestamp to normal time format Convert timestamp to normal time format
@ -139,6 +142,7 @@ def display_time(amount, units='s', remove_seconds=True):
return final_string return final_string
def pdns_api_extended_uri(version): def pdns_api_extended_uri(version):
""" """
Check the pdns version Check the pdns version
@ -148,14 +152,10 @@ def pdns_api_extended_uri(version):
else: else:
return "" return ""
def email_to_gravatar_url(email, size=100):
def email_to_gravatar_url(email="", size=100):
""" """
AD doesn't necessarily have email AD doesn't necessarily have email
""" """
hash_string = hashlib.md5(email.encode('utf-8')).hexdigest()
if not email: return "https://s.gravatar.com/avatar/{0}?s={1}".format(hash_string, size)
email=""
hash_string = hashlib.md5(email).hexdigest()
return "https://s.gravatar.com/avatar/%s?s=%s" % (hash_string, size)

View file

@ -3,7 +3,6 @@ import ldap
import time import time
import base64 import base64
import bcrypt import bcrypt
import urlparse
import itertools import itertools
import traceback import traceback
import pyotp import pyotp
@ -11,13 +10,14 @@ import re
import dns.reversename import dns.reversename
from datetime import datetime from datetime import datetime
from urllib.parse import urljoin
from distutils.util import strtobool from distutils.util import strtobool
from distutils.version import StrictVersion from distutils.version import StrictVersion
from flask_login import AnonymousUserMixin from flask_login import AnonymousUserMixin
from app import app, db from app import app, db
from lib import utils from app.lib import utils
from lib.log import logger from app.lib.log import logger
logging = logger('MODEL', app.config['LOG_LEVEL'], app.config['LOG_FILE']).config() logging = logger('MODEL', app.config['LOG_LEVEL'], app.config['LOG_FILE']).config()
if 'LDAP_TYPE' in app.config.keys(): if 'LDAP_TYPE' in app.config.keys():
@ -160,7 +160,7 @@ class User(db.Model):
result_set.append(result_data) result_set.append(result_data)
return result_set return result_set
except ldap.LDAPError, e: except ldap.LDAPError as e:
logging.error(e) logging.error(e)
raise raise
@ -269,6 +269,7 @@ class User(db.Model):
self.role_id = Role.query.filter_by(name='User').first().id self.role_id = Role.query.filter_by(name='User').first().id
if User.query.count() == 0: if User.query.count() == 0:
self.role_id = Role.query.filter_by(name='Administrator').first().id self.role_id = Role.query.filter_by(name='Administrator').first().id
self.password = self.get_hashed_password(self.plain_text_password) self.password = self.get_hashed_password(self.plain_text_password)
db.session.add(self) db.session.add(self)
@ -451,8 +452,8 @@ class Domain(db.Model):
self.settings.append(DomainSetting(setting=setting, value=value)) self.settings.append(DomainSetting(setting=setting, value=value))
db.session.commit() db.session.commit()
return True return True
except Exception, e: except Exception as e:
logging.error('Can not create setting %s for domain %s. %s' % (setting, self.name, str(e))) logging.error('Can not create setting %s for domain %s. %s' % (setting, self.name, e))
return False return False
def get_domains(self): def get_domains(self):
@ -476,7 +477,7 @@ class Domain(db.Model):
""" """
headers = {} headers = {}
headers['X-API-Key'] = PDNS_API_KEY headers['X-API-Key'] = PDNS_API_KEY
jdata = utils.fetch_json(urlparse.urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones'), headers=headers) jdata = utils.fetch_json(urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones'), headers=headers)
return jdata return jdata
def get_id_by_name(self, name): def get_id_by_name(self, name):
@ -500,7 +501,7 @@ class Domain(db.Model):
headers = {} headers = {}
headers['X-API-Key'] = PDNS_API_KEY headers['X-API-Key'] = PDNS_API_KEY
try: try:
jdata = utils.fetch_json(urlparse.urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones'), headers=headers) jdata = utils.fetch_json(urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones'), headers=headers)
list_jdomain = [d['name'].rstrip('.') for d in jdata] list_jdomain = [d['name'].rstrip('.') for d in jdata]
try: try:
# domains should remove from db since it doesn't exist in powerdns anymore # domains should remove from db since it doesn't exist in powerdns anymore
@ -564,8 +565,8 @@ class Domain(db.Model):
except: except:
db.session.rollback() db.session.rollback()
return {'status': 'ok', 'msg': 'Domain table has been updated successfully'} return {'status': 'ok', 'msg': 'Domain table has been updated successfully'}
except Exception, e: except Exception as e:
logging.error('Can not update domain table.' + str(e)) logging.error('Can not update domain table. Error: {0}'.format(e))
return {'status': 'error', 'msg': 'Can not update domain table'} return {'status': 'error', 'msg': 'Can not update domain table'}
def add(self, domain_name, domain_type, soa_edit_api, domain_ns=[], domain_master_ips=[]): def add(self, domain_name, domain_type, soa_edit_api, domain_ns=[], domain_master_ips=[]):
@ -596,17 +597,17 @@ class Domain(db.Model):
} }
try: try:
jdata = utils.fetch_json(urlparse.urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones'), headers=headers, method='POST', data=post_data) jdata = utils.fetch_json(urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones'), headers=headers, method='POST', data=post_data)
if 'error' in jdata.keys(): if 'error' in jdata.keys():
logging.error(jdata['error']) logging.error(jdata['error'])
return {'status': 'error', 'msg': jdata['error']} return {'status': 'error', 'msg': jdata['error']}
else: else:
logging.info('Added domain %s successfully' % domain_name) logging.info('Added domain %s successfully' % domain_name)
return {'status': 'ok', 'msg': 'Added domain successfully'} return {'status': 'ok', 'msg': 'Added domain successfully'}
except Exception, e: except Exception as e:
print traceback.format_exc() traceback.print_exc()
logging.error('Cannot add domain %s' % domain_name) logging.error('Cannot add domain %s' % domain_name)
logging.debug(str(e)) logging.debug(e)
return {'status': 'error', 'msg': 'Cannot add this domain.'} return {'status': 'error', 'msg': 'Cannot add this domain.'}
def create_reverse_domain(self, domain_name, domain_reverse_name): def create_reverse_domain(self, domain_name, domain_reverse_name):
@ -671,13 +672,13 @@ class Domain(db.Model):
headers = {} headers = {}
headers['X-API-Key'] = PDNS_API_KEY headers['X-API-Key'] = PDNS_API_KEY
try: try:
jdata = utils.fetch_json(urlparse.urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s' % domain_name), headers=headers, method='DELETE') jdata = utils.fetch_json(urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s' % domain_name), headers=headers, method='DELETE')
logging.info('Delete domain %s successfully' % domain_name) logging.info('Delete domain %s successfully' % domain_name)
return {'status': 'ok', 'msg': 'Delete domain successfully'} return {'status': 'ok', 'msg': 'Delete domain successfully'}
except Exception, e: except Exception as e:
print traceback.format_exc() traceback.print_exc()
logging.error('Cannot delete domain %s' % domain_name) logging.error('Cannot delete domain %s' % domain_name)
logging.debug(str(e)) logging.debug(e)
return {'status': 'error', 'msg': 'Cannot delete domain'} return {'status': 'error', 'msg': 'Cannot delete domain'}
def get_user(self): def get_user(self):
@ -730,7 +731,7 @@ class Domain(db.Model):
headers = {} headers = {}
headers['X-API-Key'] = PDNS_API_KEY headers['X-API-Key'] = PDNS_API_KEY
try: try:
jdata = utils.fetch_json(urlparse.urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s/axfr-retrieve' % domain), headers=headers, method='PUT') jdata = utils.fetch_json(urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s/axfr-retrieve' % domain), headers=headers, method='PUT')
return {'status': 'ok', 'msg': 'Update from Master successfully'} return {'status': 'ok', 'msg': 'Update from Master successfully'}
except: except:
return {'status': 'error', 'msg': 'There was something wrong, please contact administrator'} return {'status': 'error', 'msg': 'There was something wrong, please contact administrator'}
@ -746,7 +747,7 @@ class Domain(db.Model):
headers = {} headers = {}
headers['X-API-Key'] = PDNS_API_KEY headers['X-API-Key'] = PDNS_API_KEY
try: try:
jdata = utils.fetch_json(urlparse.urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s/cryptokeys' % domain.name), headers=headers, method='GET') jdata = utils.fetch_json(urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s/cryptokeys' % domain.name), headers=headers, method='GET')
if 'error' in jdata: if 'error' in jdata:
return {'status': 'error', 'msg': 'DNSSEC is not enabled for this domain'} return {'status': 'error', 'msg': 'DNSSEC is not enabled for this domain'}
else: else:
@ -791,7 +792,7 @@ class Record(object):
headers = {} headers = {}
headers['X-API-Key'] = PDNS_API_KEY headers['X-API-Key'] = PDNS_API_KEY
try: try:
jdata = utils.fetch_json(urlparse.urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s' % domain), headers=headers) jdata = utils.fetch_json(urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s' % domain), headers=headers)
except: except:
logging.error("Cannot fetch domain's record data from remote powerdns api") logging.error("Cannot fetch domain's record data from remote powerdns api")
return False return False
@ -865,11 +866,11 @@ class Record(object):
} }
try: try:
jdata = utils.fetch_json(urlparse.urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s' % domain), headers=headers, method='PATCH', data=data) jdata = utils.fetch_json(urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s' % domain), headers=headers, method='PATCH', data=data)
logging.debug(jdata) logging.debug(jdata)
return {'status': 'ok', 'msg': 'Record was added successfully'} return {'status': 'ok', 'msg': 'Record was added successfully'}
except Exception, e: except Exception as e:
logging.error("Cannot add record %s/%s/%s to domain %s. DETAIL: %s" % (self.name, self.type, self.data, domain, str(e))) logging.error("Cannot add record %s/%s/%s to domain %s. DETAIL: %s" % (self.name, self.type, self.data, domain, e))
return {'status': 'error', 'msg': 'There was something wrong, please contact administrator'} return {'status': 'error', 'msg': 'There was something wrong, please contact administrator'}
@ -1043,8 +1044,8 @@ class Record(object):
try: try:
headers = {} headers = {}
headers['X-API-Key'] = PDNS_API_KEY headers['X-API-Key'] = PDNS_API_KEY
jdata1 = utils.fetch_json(urlparse.urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s' % domain), headers=headers, method='PATCH', data=postdata_for_delete) jdata1 = utils.fetch_json(urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s' % domain), headers=headers, method='PATCH', data=postdata_for_delete)
jdata2 = utils.fetch_json(urlparse.urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s' % domain), headers=headers, method='PATCH', data=postdata_for_new) jdata2 = utils.fetch_json(urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s' % domain), headers=headers, method='PATCH', data=postdata_for_new)
if 'error' in jdata2.keys(): if 'error' in jdata2.keys():
logging.error('Cannot apply record changes.') logging.error('Cannot apply record changes.')
@ -1054,8 +1055,8 @@ class Record(object):
self.auto_ptr(domain, new_records, deleted_records) self.auto_ptr(domain, new_records, deleted_records)
logging.info('Record was applied successfully.') logging.info('Record was applied successfully.')
return {'status': 'ok', 'msg': 'Record was applied successfully'} return {'status': 'ok', 'msg': 'Record was applied successfully'}
except Exception, e: except Exception as e:
logging.error("Cannot apply record changes to domain %s. DETAIL: %s" % (str(e), domain)) logging.error("Cannot apply record changes to domain %s. DETAIL: %s" % (e, domain))
return {'status': 'error', 'msg': 'There was something wrong, please contact administrator'} return {'status': 'error', 'msg': 'There was something wrong, please contact administrator'}
def auto_ptr(self, domain, new_records, deleted_records): def auto_ptr(self, domain, new_records, deleted_records):
@ -1097,7 +1098,7 @@ class Record(object):
self.delete(domain_reverse_name) self.delete(domain_reverse_name)
return {'status': 'ok', 'msg': 'Auto-PTR record was updated successfully'} return {'status': 'ok', 'msg': 'Auto-PTR record was updated successfully'}
except Exception as e: except Exception as e:
logging.error("Cannot update auto-ptr record changes to domain %s. DETAIL: %s" % (str(e), domain)) logging.error("Cannot update auto-ptr record changes to domain %s. DETAIL: %s" % (e, domain))
return {'status': 'error', 'msg': 'Auto-PTR creation failed. There was something wrong, please contact administrator.'} return {'status': 'error', 'msg': 'Auto-PTR creation failed. There was something wrong, please contact administrator.'}
def delete(self, domain): def delete(self, domain):
@ -1117,7 +1118,7 @@ class Record(object):
] ]
} }
try: try:
jdata = utils.fetch_json(urlparse.urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s' % domain), headers=headers, method='PATCH', data=data) jdata = utils.fetch_json(urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s' % domain), headers=headers, method='PATCH', data=data)
logging.debug(jdata) logging.debug(jdata)
return {'status': 'ok', 'msg': 'Record was removed successfully'} return {'status': 'ok', 'msg': 'Record was removed successfully'}
except: except:
@ -1191,11 +1192,11 @@ class Record(object):
] ]
} }
try: try:
jdata = utils.fetch_json(urlparse.urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s' % domain), headers=headers, method='PATCH', data=data) jdata = utils.fetch_json(urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s' % domain), headers=headers, method='PATCH', data=data)
logging.debug("dyndns data: " % data) logging.debug("dyndns data: " % data)
return {'status': 'ok', 'msg': 'Record was updated successfully'} return {'status': 'ok', 'msg': 'Record was updated successfully'}
except Exception, e: except Exception as e:
logging.error("Cannot add record %s/%s/%s to domain %s. DETAIL: %s" % (self.name, self.type, self.data, domain, str(e))) logging.error("Cannot add record %s/%s/%s to domain %s. DETAIL: %s" % (self.name, self.type, self.data, domain, e))
return {'status': 'error', 'msg': 'There was something wrong, please contact administrator'} return {'status': 'error', 'msg': 'There was something wrong, please contact administrator'}
@ -1217,7 +1218,7 @@ class Server(object):
headers['X-API-Key'] = PDNS_API_KEY headers['X-API-Key'] = PDNS_API_KEY
try: try:
jdata = utils.fetch_json(urlparse.urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/%s/config' % self.server_id), headers=headers, method='GET') jdata = utils.fetch_json(urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/%s/config' % self.server_id), headers=headers, method='GET')
return jdata return jdata
except: except:
logging.error("Can not get server configuration.") logging.error("Can not get server configuration.")
@ -1232,7 +1233,7 @@ class Server(object):
headers['X-API-Key'] = PDNS_API_KEY headers['X-API-Key'] = PDNS_API_KEY
try: try:
jdata = utils.fetch_json(urlparse.urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/%s/statistics' % self.server_id), headers=headers, method='GET') jdata = utils.fetch_json(urljoin(PDNS_STATS_URL, API_EXTENDED_URL + '/servers/%s/statistics' % self.server_id), headers=headers, method='GET')
return jdata return jdata
except: except:
logging.error("Can not get server statistics.") logging.error("Can not get server statistics.")

View file

@ -18,7 +18,7 @@ from werkzeug.security import gen_salt
from .models import User, Domain, Record, Server, History, Anonymous, Setting, DomainSetting from .models import User, Domain, Record, Server, History, Anonymous, Setting, DomainSetting
from app import app, login_manager, github from app import app, login_manager, github
from lib import utils from app.lib import utils
jinja2.filters.FILTERS['display_record_name'] = utils.display_record_name jinja2.filters.FILTERS['display_record_name'] = utils.display_record_name
@ -34,36 +34,43 @@ if StrictVersion(PDNS_VERSION) >= StrictVersion('4.0.0'):
else: else:
NEW_SCHEMA = False NEW_SCHEMA = False
@app.context_processor @app.context_processor
def inject_fullscreen_layout_setting(): def inject_fullscreen_layout_setting():
fullscreen_layout_setting = Setting.query.filter(Setting.name == 'fullscreen_layout').first() fullscreen_layout_setting = Setting.query.filter(Setting.name == 'fullscreen_layout').first()
return dict(fullscreen_layout_setting=strtobool(fullscreen_layout_setting.value)) return dict(fullscreen_layout_setting=strtobool(fullscreen_layout_setting.value))
@app.context_processor @app.context_processor
def inject_record_helper_setting(): def inject_record_helper_setting():
record_helper_setting = Setting.query.filter(Setting.name == 'record_helper').first() record_helper_setting = Setting.query.filter(Setting.name == 'record_helper').first()
return dict(record_helper_setting=strtobool(record_helper_setting.value)) return dict(record_helper_setting=strtobool(record_helper_setting.value))
@app.context_processor @app.context_processor
def inject_login_ldap_first_setting(): def inject_login_ldap_first_setting():
login_ldap_first_setting = Setting.query.filter(Setting.name == 'login_ldap_first').first() login_ldap_first_setting = Setting.query.filter(Setting.name == 'login_ldap_first').first()
return dict(login_ldap_first_setting=strtobool(login_ldap_first_setting.value)) return dict(login_ldap_first_setting=strtobool(login_ldap_first_setting.value))
@app.context_processor @app.context_processor
def inject_default_record_table_size_setting(): def inject_default_record_table_size_setting():
default_record_table_size_setting = Setting.query.filter(Setting.name == 'default_record_table_size').first() default_record_table_size_setting = Setting.query.filter(Setting.name == 'default_record_table_size').first()
return dict(default_record_table_size_setting=default_record_table_size_setting.value) return dict(default_record_table_size_setting=default_record_table_size_setting.value)
@app.context_processor @app.context_processor
def inject_default_domain_table_size_setting(): def inject_default_domain_table_size_setting():
default_domain_table_size_setting = Setting.query.filter(Setting.name == 'default_domain_table_size').first() default_domain_table_size_setting = Setting.query.filter(Setting.name == 'default_domain_table_size').first()
return dict(default_domain_table_size_setting=default_domain_table_size_setting.value) return dict(default_domain_table_size_setting=default_domain_table_size_setting.value)
@app.context_processor @app.context_processor
def inject_auto_ptr_setting(): def inject_auto_ptr_setting():
auto_ptr_setting = Setting.query.filter(Setting.name == 'auto_ptr').first() auto_ptr_setting = Setting.query.filter(Setting.name == 'auto_ptr').first()
return dict(auto_ptr_setting=strtobool(auto_ptr_setting.value)) return dict(auto_ptr_setting=strtobool(auto_ptr_setting.value))
# START USER AUTHENTICATION HANDLER # START USER AUTHENTICATION HANDLER
@app.before_request @app.before_request
def before_request(): def before_request():
@ -92,6 +99,7 @@ def dyndns_login_required(f):
return f(*args, **kwargs) return f(*args, **kwargs)
return decorated_function return decorated_function
@login_manager.request_loader @login_manager.request_loader
def login_via_authorization_header(request): def login_via_authorization_header(request):
auth_header = request.headers.get('Authorization') auth_header = request.headers.get('Authorization')
@ -100,8 +108,7 @@ def login_via_authorization_header(request):
try: try:
auth_header = base64.b64decode(auth_header) auth_header = base64.b64decode(auth_header)
username,password = auth_header.split(":") username,password = auth_header.split(":")
except TypeError, e: except TypeError as e:
error = e.message['desc'] if 'desc' in e.message else e
return None return None
user = User(username=username, password=password, plain_text_password=password) user = User(username=username, password=password, plain_text_password=password)
try: try:
@ -111,10 +118,9 @@ def login_via_authorization_header(request):
else: else:
login_user(user, remember = False) login_user(user, remember = False)
return user return user
except Exception, e: except:
return None return None
return None return None
# END USER AUTHENTICATION HANDLER # END USER AUTHENTICATION HANDLER
# START CUSTOMIZE DECORATOR # START CUSTOMIZE DECORATOR
@ -132,18 +138,22 @@ def admin_role_required(f):
def http_bad_request(e): def http_bad_request(e):
return redirect(url_for('error', code=400)) return redirect(url_for('error', code=400))
@app.errorhandler(401) @app.errorhandler(401)
def http_unauthorized(e): def http_unauthorized(e):
return redirect(url_for('error', code=401)) return redirect(url_for('error', code=401))
@app.errorhandler(404) @app.errorhandler(404)
def http_internal_server_error(e): def http_internal_server_error(e):
return redirect(url_for('error', code=404)) return redirect(url_for('error', code=404))
@app.errorhandler(500) @app.errorhandler(500)
def http_page_not_found(e): def http_page_not_found(e):
return redirect(url_for('error', code=500)) return redirect(url_for('error', code=500))
@app.route('/error/<string:code>') @app.route('/error/<string:code>')
def error(code, msg=None): def error(code, msg=None):
supported_code = ('400', '401', '404', '500') supported_code = ('400', '401', '404', '500')
@ -152,6 +162,7 @@ def error(code, msg=None):
else: else:
return render_template('errors/404.html'), 404 return render_template('errors/404.html'), 404
@app.route('/register', methods=['GET']) @app.route('/register', methods=['GET'])
def register(): def register():
SIGNUP_ENABLED = app.config['SIGNUP_ENABLED'] SIGNUP_ENABLED = app.config['SIGNUP_ENABLED']
@ -160,12 +171,14 @@ def register():
else: else:
return render_template('errors/404.html'), 404 return render_template('errors/404.html'), 404
@app.route('/github/login') @app.route('/github/login')
def github_login(): def github_login():
if not app.config.get('GITHUB_OAUTH_ENABLE'): if not app.config.get('GITHUB_OAUTH_ENABLE'):
return abort(400) return abort(400)
return github.authorize(callback=url_for('authorized', _external=True)) return github.authorize(callback=url_for('authorized', _external=True))
@app.route('/login', methods=['GET', 'POST']) @app.route('/login', methods=['GET', 'POST'])
@login_manager.unauthorized_handler @login_manager.unauthorized_handler
def login(): def login():
@ -224,9 +237,8 @@ def login():
auth = user.is_validate(method=auth_method) auth = user.is_validate(method=auth_method)
if auth == False: if auth == False:
return render_template('login.html', error='Invalid credentials', ldap_enabled=LDAP_ENABLED, login_title=LOGIN_TITLE, basic_enabled=BASIC_ENABLED, signup_enabled=SIGNUP_ENABLED) return render_template('login.html', error='Invalid credentials', ldap_enabled=LDAP_ENABLED, login_title=LOGIN_TITLE, basic_enabled=BASIC_ENABLED, signup_enabled=SIGNUP_ENABLED)
except Exception, e: except Exception as e:
error = e.message['desc'] if 'desc' in e.message else e return render_template('login.html', error=e, ldap_enabled=LDAP_ENABLED, login_title=LOGIN_TITLE, basic_enabled=BASIC_ENABLED, signup_enabled=SIGNUP_ENABLED)
return render_template('login.html', error=error, ldap_enabled=LDAP_ENABLED, login_title=LOGIN_TITLE, basic_enabled=BASIC_ENABLED, signup_enabled=SIGNUP_ENABLED)
# check if user enabled OPT authentication # check if user enabled OPT authentication
if user.otp_secret: if user.otp_secret:
@ -255,9 +267,9 @@ def login():
return render_template('login.html', username=username, password=password, ldap_enabled=LDAP_ENABLED, login_title=LOGIN_TITLE, basic_enabled=BASIC_ENABLED, signup_enabled=SIGNUP_ENABLED) return render_template('login.html', username=username, password=password, ldap_enabled=LDAP_ENABLED, login_title=LOGIN_TITLE, basic_enabled=BASIC_ENABLED, signup_enabled=SIGNUP_ENABLED)
else: else:
return render_template('register.html', error=result) return render_template('register.html', error=result)
except Exception, e: except Exception as e:
error = e.message['desc'] if 'desc' in e.message else e return render_template('register.html', error=e)
return render_template('register.html', error=error)
@app.route('/logout') @app.route('/logout')
def logout(): def logout():
@ -284,7 +296,7 @@ def dashboard():
server = Server(server_id='localhost') server = Server(server_id='localhost')
statistics = server.get_statistic() statistics = server.get_statistic()
if statistics: if statistics:
uptime = filter(lambda uptime: uptime['name'] == 'uptime', statistics)[0]['value'] uptime = list([uptime for uptime in statistics if uptime['name'] == 'uptime'])[0]['value']
else: else:
uptime = 0 uptime = 0
return render_template('dashboard.html', domains=domains, domain_count=domain_count, users=users, history_number=history_number, uptime=uptime, histories=history) return render_template('dashboard.html', domains=domains, domain_count=domain_count, users=users, history_number=history_number, uptime=uptime, histories=history)
@ -417,8 +429,7 @@ def record_apply(domain_name):
""" """
#TODO: filter removed records / name modified records. #TODO: filter removed records / name modified records.
try: try:
pdata = request.data jdata = request.json
jdata = json.loads(pdata)
r = Record() r = Record()
result = r.apply(domain_name, jdata) result = r.apply(domain_name, jdata)
@ -429,7 +440,7 @@ def record_apply(domain_name):
else: else:
return make_response(jsonify( result ), 400) return make_response(jsonify( result ), 400)
except: except:
print traceback.format_exc() traceback.print_exc()
return make_response(jsonify( {'status': 'error', 'msg': 'Error when applying new changes'} ), 500) return make_response(jsonify( {'status': 'error', 'msg': 'Error when applying new changes'} ), 500)
@ -441,8 +452,7 @@ def record_update(domain_name):
Pulling the records update from its Master Pulling the records update from its Master
""" """
try: try:
pdata = request.data jdata = request.json
jdata = json.loads(pdata)
domain_name = jdata['domain'] domain_name = jdata['domain']
d = Domain() d = Domain()
@ -452,7 +462,7 @@ def record_update(domain_name):
else: else:
return make_response(jsonify( {'status': 'error', 'msg': result['msg']} ), 500) return make_response(jsonify( {'status': 'error', 'msg': result['msg']} ), 500)
except: except:
print traceback.format_exc() traceback.print_exc()
return make_response(jsonify( {'status': 'error', 'msg': 'Error when applying new changes'} ), 500) return make_response(jsonify( {'status': 'error', 'msg': 'Error when applying new changes'} ), 500)
@ -464,9 +474,9 @@ def record_delete(domain_name, record_name, record_type):
r = Record(name=record_name, type=record_type) r = Record(name=record_name, type=record_type)
result = r.delete(domain=domain_name) result = r.delete(domain=domain_name)
if result['status'] == 'error': if result['status'] == 'error':
print result['msg'] print(result['msg'])
except: except:
print traceback.format_exc() traceback.print_exc()
return redirect(url_for('error', code=500)), 500 return redirect(url_for('error', code=500)), 500
return redirect(url_for('domain', domain_name=domain_name)) return redirect(url_for('domain', domain_name=domain_name))
@ -478,6 +488,7 @@ def domain_dnssec(domain_name):
dnssec = domain.get_domain_dnssec(domain_name) dnssec = domain.get_domain_dnssec(domain_name)
return make_response(jsonify(dnssec), 200) return make_response(jsonify(dnssec), 200)
@app.route('/domain/<string:domain_name>/managesetting', methods=['GET', 'POST']) @app.route('/domain/<string:domain_name>/managesetting', methods=['GET', 'POST'])
@login_required @login_required
@admin_role_required @admin_role_required
@ -488,9 +499,9 @@ def admin_setdomainsetting(domain_name):
# {'action': 'set_setting', 'setting': 'default_action, 'value': 'True'} # {'action': 'set_setting', 'setting': 'default_action, 'value': 'True'}
# #
try: try:
pdata = request.data jdata = request.json
jdata = json.loads(pdata)
data = jdata['data'] data = jdata['data']
if jdata['action'] == 'set_setting': if jdata['action'] == 'set_setting':
new_setting = data['setting'] new_setting = data['setting']
new_value = str(data['value']) new_value = str(data['value'])
@ -514,7 +525,7 @@ def admin_setdomainsetting(domain_name):
else: else:
return make_response(jsonify( { 'status': 'error', 'msg': 'Action not supported.' } ), 400) return make_response(jsonify( { 'status': 'error', 'msg': 'Action not supported.' } ), 400)
except: except:
print traceback.format_exc() traceback.print_exc()
return make_response(jsonify( { 'status': 'error', 'msg': 'There is something wrong, please contact Administrator.' } ), 400) return make_response(jsonify( { 'status': 'error', 'msg': 'There is something wrong, please contact Administrator.' } ), 400)
@ -531,12 +542,13 @@ def admin():
history_number = History.query.count() history_number = History.query.count()
if statistics: if statistics:
uptime = filter(lambda uptime: uptime['name'] == 'uptime', statistics)[0]['value'] uptime = list([uptime for uptime in statistics if uptime['name'] == 'uptime'])[0]['value']
else: else:
uptime = 0 uptime = 0
return render_template('admin.html', domains=domains, users=users, configs=configs, statistics=statistics, uptime=uptime, history_number=history_number) return render_template('admin.html', domains=domains, users=users, configs=configs, statistics=statistics, uptime=uptime, history_number=history_number)
@app.route('/admin/user/create', methods=['GET', 'POST']) @app.route('/admin/user/create', methods=['GET', 'POST'])
@login_required @login_required
@admin_role_required @admin_role_required
@ -562,6 +574,7 @@ def admin_createuser():
return redirect(url_for('admin_manageuser')) return redirect(url_for('admin_manageuser'))
@app.route('/admin/manageuser', methods=['GET', 'POST']) @app.route('/admin/manageuser', methods=['GET', 'POST'])
@login_required @login_required
@admin_role_required @admin_role_required
@ -576,8 +589,7 @@ def admin_manageuser():
# {'action': 'delete_user', 'data': 'username'} # {'action': 'delete_user', 'data': 'username'}
# #
try: try:
pdata = request.data jdata = request.json
jdata = json.loads(pdata)
data = jdata['data'] data = jdata['data']
if jdata['action'] == 'delete_user': if jdata['action'] == 'delete_user':
@ -614,7 +626,7 @@ def admin_manageuser():
else: else:
return make_response(jsonify( { 'status': 'error', 'msg': 'Action not supported.' } ), 400) return make_response(jsonify( { 'status': 'error', 'msg': 'Action not supported.' } ), 400)
except: except:
print traceback.format_exc() traceback.print_exc()
return make_response(jsonify( { 'status': 'error', 'msg': 'There is something wrong, please contact Administrator.' } ), 400) return make_response(jsonify( { 'status': 'error', 'msg': 'There is something wrong, please contact Administrator.' } ), 400)
@ -637,6 +649,7 @@ def admin_history():
histories = History.query.all() histories = History.query.all()
return render_template('admin_history.html', histories=histories) return render_template('admin_history.html', histories=histories)
@app.route('/admin/settings', methods=['GET']) @app.route('/admin/settings', methods=['GET'])
@login_required @login_required
@admin_role_required @admin_role_required
@ -645,6 +658,7 @@ def admin_settings():
settings = Setting.query.filter(Setting.name != 'maintenance') settings = Setting.query.filter(Setting.name != 'maintenance')
return render_template('admin_settings.html', settings=settings) return render_template('admin_settings.html', settings=settings)
@app.route('/admin/setting/<string:setting>/toggle', methods=['POST']) @app.route('/admin/setting/<string:setting>/toggle', methods=['POST'])
@login_required @login_required
@admin_role_required @admin_role_required
@ -655,19 +669,21 @@ def admin_settings_toggle(setting):
else: else:
return make_response(jsonify( { 'status': 'error', 'msg': 'Unable to toggle setting.' } ), 500) return make_response(jsonify( { 'status': 'error', 'msg': 'Unable to toggle setting.' } ), 500)
@app.route('/admin/setting/<string:setting>/edit', methods=['POST']) @app.route('/admin/setting/<string:setting>/edit', methods=['POST'])
@login_required @login_required
@admin_role_required @admin_role_required
def admin_settings_edit(setting): def admin_settings_edit(setting):
pdata = request.data jdata = request.json
jdata = json.loads(pdata)
new_value = jdata['value'] new_value = jdata['value']
result = Setting().set(setting, new_value) result = Setting().set(setting, new_value)
if (result): if (result):
return make_response(jsonify( { 'status': 'ok', 'msg': 'Toggled setting successfully.' } ), 200) return make_response(jsonify( { 'status': 'ok', 'msg': 'Toggled setting successfully.' } ), 200)
else: else:
return make_response(jsonify( { 'status': 'error', 'msg': 'Unable to toggle setting.' } ), 500) return make_response(jsonify( { 'status': 'error', 'msg': 'Unable to toggle setting.' } ), 500)
@app.route('/user/profile', methods=['GET', 'POST']) @app.route('/user/profile', methods=['GET', 'POST'])
@login_required @login_required
def user_profile(): def user_profile():
@ -682,7 +698,7 @@ def user_profile():
# json data # json data
if request.data: if request.data:
jdata = json.loads(request.data) jdata = request.json
data = jdata['data'] data = jdata['data']
if jdata['action'] == 'enable_otp': if jdata['action'] == 'enable_otp':
enable_otp = data['enable_otp'] enable_otp = data['enable_otp']
@ -702,7 +718,6 @@ def user_profile():
save_file_name = current_user.username + '.' + file_extension save_file_name = current_user.username + '.' + file_extension
file.save(os.path.join(app.config['UPLOAD_DIR'], 'avatar', save_file_name)) file.save(os.path.join(app.config['UPLOAD_DIR'], 'avatar', save_file_name))
# update user profile # update user profile
user = User(username=current_user.username, plain_text_password=new_password, firstname=firstname, lastname=lastname, email=email, avatar=save_file_name, reload_info=False) user = User(username=current_user.username, plain_text_password=new_password, firstname=firstname, lastname=lastname, email=email, avatar=save_file_name, reload_info=False)
user.update_profile() user.update_profile()
@ -737,6 +752,7 @@ def dyndns_checkip():
# route covers the default ddclient 'web' setting for the checkip service # route covers the default ddclient 'web' setting for the checkip service
return render_template('dyndns.html', response=request.environ.get('HTTP_X_REAL_IP', request.remote_addr)) return render_template('dyndns.html', response=request.environ.get('HTTP_X_REAL_IP', request.remote_addr))
@app.route('/nic/update', methods=['GET', 'POST']) @app.route('/nic/update', methods=['GET', 'POST'])
@dyndns_login_required @dyndns_login_required
def dyndns_update(): def dyndns_update():

View file

@ -5,7 +5,7 @@ basedir = os.path.abspath(os.path.dirname(__file__))
WTF_CSRF_ENABLED = True WTF_CSRF_ENABLED = True
SECRET_KEY = 'We are the world' SECRET_KEY = 'We are the world'
BIND_ADDRESS = '127.0.0.1' BIND_ADDRESS = '127.0.0.1'
PORT = 9393 PORT = 9191
LOGIN_TITLE = "PDNS" LOGIN_TITLE = "PDNS"
# TIMEOUT - for large zones # TIMEOUT - for large zones

59
configs/development.py Normal file
View file

@ -0,0 +1,59 @@
import os
basedir = os.path.abspath(os.path.dirname(__file__))
# BASIC APP CONFIG
WTF_CSRF_ENABLED = True
SECRET_KEY = 'changeme'
LOG_LEVEL = 'DEBUG'
LOG_FILE = 'log.txt'
# TIMEOUT - for large zones
TIMEOUT = 10
# UPLOAD DIR
UPLOAD_DIR = os.path.join(basedir, 'upload')
# DATABASE CONFIG FOR MYSQL
DB_USER = 'powerdnsadmin'
DB_PASSWORD = 'powerdnsadminpassword'
DB_HOST = 'docker.for.mac.localhost'
DB_NAME = 'powerdnsadmin'
#MySQL
SQLALCHEMY_DATABASE_URI = 'mysql://'+DB_USER+':'+DB_PASSWORD+'@'+DB_HOST+'/'+DB_NAME
SQLALCHEMY_MIGRATE_REPO = os.path.join(basedir, 'db_repository')
SQLALCHEMY_TRACK_MODIFICATIONS = True
# AUTHENTICATION CONFIG
BASIC_ENABLED = True
SIGNUP_ENABLED = True
## LDAP CONFIG
# LDAP_TYPE = 'ldap'
# LDAP_URI = 'ldaps://your-ldap-server:636'
# LDAP_USERNAME = 'cn=dnsuser,ou=users,ou=services,dc=duykhanh,dc=me'
# LDAP_PASSWORD = 'dnsuser'
# LDAP_SEARCH_BASE = 'ou=System Admins,ou=People,dc=duykhanh,dc=me'
## Additional options only if LDAP_TYPE=ldap
# LDAP_USERNAMEFIELD = 'uid'
# LDAP_FILTER = '(objectClass=inetorgperson)'
## GITHUB AUTHENTICATION
# GITHUB_OAUTH_ENABLE = False
# GITHUB_OAUTH_KEY = 'G0j1Q15aRsn36B3aD6nwKLiYbeirrUPU8nDd1wOC'
# GITHUB_OAUTH_SECRET = '0WYrKWePeBDkxlezzhFbDn1PBnCwEa0vCwVFvy6iLtgePlpT7WfUlAa9sZgm'
# GITHUB_OAUTH_SCOPE = 'email'
# GITHUB_OAUTH_URL = 'http://127.0.0.1:5000/api/v3/'
# GITHUB_OAUTH_TOKEN = 'http://127.0.0.1:5000/oauth/token'
# GITHUB_OAUTH_AUTHORIZE = 'http://127.0.0.1:5000/oauth/authorize'
# POWERDNS CONFIG
PDNS_STATS_URL = 'http://192.168.100.100:8081/'
PDNS_API_KEY = 'changeme'
PDNS_VERSION = '4.1.1'
# RECORDS ALLOWED TO EDIT
RECORDS_ALLOW_EDIT = ['A', 'AAAA', 'CNAME', 'SPF', 'PTR', 'MX', 'TXT']
# EXPERIMENTAL FEATURES
PRETTY_IPV6_PTR = False

View file

@ -1,13 +1,16 @@
#!/usr/bin/env python #!/usr/bin/env python3
import sys
import time
import os.path
import traceback
from migrate.versioning import api from migrate.versioning import api
from config import SQLALCHEMY_DATABASE_URI from config import SQLALCHEMY_DATABASE_URI
from config import SQLALCHEMY_MIGRATE_REPO from config import SQLALCHEMY_MIGRATE_REPO
from app import db from app import db
from app.models import Role, Setting from app.models import Role, Setting
import os.path
import time
import sys
def start(): def start():
wait_time = get_waittime_from_env() wait_time = get_waittime_from_env()
@ -22,13 +25,14 @@ def get_waittime_from_env():
return int(os.environ.get('WAITFOR_DB', 1)) return int(os.environ.get('WAITFOR_DB', 1))
def connect_db(wait_time): def connect_db(wait_time):
for i in xrange(0, wait_time): for i in range(0, wait_time):
print("INFO: Wait for database server") print("INFO: Wait for database server")
sys.stdout.flush() sys.stdout.flush()
try: try:
db.create_all() db.create_all()
return True return True
except: except:
traceback.print_exc()
time.sleep(1) time.sleep(1)
return False return False
@ -36,14 +40,14 @@ def connect_db(wait_time):
def init_roles(db, role_names): def init_roles(db, role_names):
# Get key name of data # Get key name of data
name_of_roles = map(lambda r: r.name, role_names) name_of_roles = [r.name for r in role_names]
# Query to get current data # Query to get current data
rows = db.session.query(Role).filter(Role.name.in_(name_of_roles)).all() rows = db.session.query(Role).filter(Role.name.in_(name_of_roles)).all()
name_of_rows = map(lambda r: r.name, rows) name_of_rows = [r.name for r in rows]
# Check which data that need to insert # Check which data that need to insert
roles = filter(lambda r: r.name not in name_of_rows, role_names) roles = [r for r in role_names if r.name not in name_of_rows]
# Insert data # Insert data
for role in roles: for role in roles:
@ -52,14 +56,14 @@ def init_roles(db, role_names):
def init_settings(db, setting_names): def init_settings(db, setting_names):
# Get key name of data # Get key name of data
name_of_settings = map(lambda r: r.name, setting_names) name_of_settings = [r.name for r in setting_names]
# Query to get current data # Query to get current data
rows = db.session.query(Setting).filter(Setting.name.in_(name_of_settings)).all() rows = db.session.query(Setting).filter(Setting.name.in_(name_of_settings)).all()
# Check which data that need to insert # Check which data that need to insert
name_of_rows = map(lambda r: r.name, rows) name_of_rows = [r.name for r in rows]
settings = filter(lambda r: r.name not in name_of_rows, setting_names) settings = [r for r in setting_names if r.name not in name_of_rows]
# Insert data # Insert data
for setting in settings: for setting in settings:

View file

@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
from migrate.versioning import api from migrate.versioning import api
from config import SQLALCHEMY_DATABASE_URI from config import SQLALCHEMY_DATABASE_URI
from config import SQLALCHEMY_MIGRATE_REPO from config import SQLALCHEMY_MIGRATE_REPO

View file

@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
import imp import imp
from migrate.versioning import api from migrate.versioning import api
from app import db from app import db

View file

@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
from migrate.versioning import api from migrate.versioning import api
from config import SQLALCHEMY_DATABASE_URI from config import SQLALCHEMY_DATABASE_URI
from config import SQLALCHEMY_MIGRATE_REPO from config import SQLALCHEMY_MIGRATE_REPO

22
docker-compose.dev.yml Normal file
View file

@ -0,0 +1,22 @@
version: "2.1"
services:
powerdns-admin:
build:
context: .
dockerfile: Dockerfile
image: powerdns-admin
container_name: powerdns-admin
mem_limit: 256M
memswap_limit: 256M
tty: true
command: /usr/bin/supervisord -c /etc/supervisord.conf
ports:
- "9191:9191"
volumes:
- .:/powerdns-admin/
- "./configs/development.py:/powerdns-admin/config.py"
logging:
driver: json-file
options:
max-size: 50m

View file

@ -1,50 +0,0 @@
version: '2'
services:
powerdns-authoritative:
image: winggundamth/powerdns-mysql:trusty
hostname: powerdns-authoritative
depends_on:
- powerdns-authoritative-mariadb
links:
- powerdns-authoritative-mariadb:mysqldb
ports:
- 172.17.0.1:53:53/udp
- 8081:8081
environment:
- PDNS_DB_HOST=mysqldb
- PDNS_DB_USERNAME=root
- PDNS_DB_NAME=powerdns
- PDNS_DB_PASSWORD=PowerDNSPassword
- PDNS_API_KEY=PowerDNSAPIKey
powerdns-authoritative-mariadb:
image: mariadb:10.1.15
hostname: powerdns-authoritative-mariadb
environment:
- MYSQL_DATABASE=powerdns
- MYSQL_ROOT_PASSWORD=PowerDNSPassword
powerdns-admin:
image: winggundamth/powerdns-admin:trusty
hostname: powerdns-admin
depends_on:
- powerdns-admin-mariadb
- powerdns-authoritative
links:
- powerdns-admin-mariadb:mysqldb
- powerdns-authoritative:powerdns-server
volumes:
- ./:/home/web/powerdns-admin
ports:
- 9393:9393
environment:
- WAITFOR_DB=60
powerdns-admin-mariadb:
image: mariadb:10.1.15
hostname: powerdns-admin-mariadb
environment:
- MYSQL_DATABASE=powerdns-admin
- MYSQL_ROOT_PASSWORD=PowerDNSAdminPassword

View file

@ -1,14 +1,16 @@
Flask>=0.10 Flask==0.12.2
Flask-WTF>=0.11 Flask-WTF==0.14.2
Flask-Login>=0.2.11 Flask-Login==0.4.1
configobj==5.0.5 Flask-OAuthlib==0.9.4
bcrypt==3.1.0 Flask-SQLAlchemy==2.3.2
requests==2.7.0 SQLAlchemy==1.2.5
python-ldap==2.4.21
Flask-SQLAlchemy==2.1
SQLAlchemy==1.0.9
sqlalchemy-migrate==0.10.0 sqlalchemy-migrate==0.10.0
pyotp==2.2.1 mysqlclient==1.3.12
qrcode==5.3 configobj==5.0.6
Flask-OAuthlib==0.9.3 bcrypt==3.1.4
dnspython>=1.12.0 requests==2.18.4
python-ldap==3.0.0
pyotp==2.2.6
qrcode==6.0
dnspython==1.15.0
gunicorn==19.7.1

23
supervisord.conf Normal file
View file

@ -0,0 +1,23 @@
[unix_http_server]
file=/tmp/supervisor.sock ; (the path to the socket file)
chown=nobody:nogroup ; socket file uid:gid owner
[supervisord]
logfile=/tmp/supervisord.log ; (main log file;default $CWD/supervisord.log)
logfile_maxbytes=50MB ; (max main logfile bytes b4 rotation;default 50MB)
logfile_backups=10 ; (num of main logfile rotation backups;default 10)
loglevel=info ; (log level;default info; others: debug,warn,trace)
pidfile=/tmp/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
nodaemon=true ; (start in foreground if true;default false)
minfds=1024 ; (min. avail startup file descriptors;default 1024)
minprocs=200 ; (min. avail process descriptors;default 200)
[program:powerdns-admin]
command=/usr/local/bin/gunicorn -t 120 --workers 4 --bind '0.0.0.0:9191' --log-level info app:app
directory=/powerdns-admin
autostart=true
priority=999
user=www-data
redirect_stderr=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0