BIND DNS RESTful API
This commit is contained in:
commit
34ffc81772
157
bind-api.py
Executable file
157
bind-api.py
Executable file
@ -0,0 +1,157 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import ConfigParser
|
||||
import dns.tsigkeyring
|
||||
import dns.resolver
|
||||
import dns.update
|
||||
import dns.query
|
||||
import dns.zone
|
||||
|
||||
from dns.rdatatype import *
|
||||
|
||||
from flask import Flask, jsonify
|
||||
from werkzeug.routing import BaseConverter
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
class RegexConverter(BaseConverter):
|
||||
def __init__(self, url_map, *items):
|
||||
super(RegexConverter, self).__init__(url_map)
|
||||
self.regex = items[0]
|
||||
|
||||
|
||||
app.url_map.converters['regex'] = RegexConverter
|
||||
|
||||
|
||||
def parse_config(config):
|
||||
"""
|
||||
Parse the user config and retreieve the nameserver, username and
|
||||
password required to perform dynamic DNS updates
|
||||
"""
|
||||
options = {}
|
||||
parser = ConfigParser.ConfigParser()
|
||||
parser.read(config)
|
||||
|
||||
options['nameserver'] = parser.get('Nameserver', 'nameserver')
|
||||
options['username'] = parser.get('Auth', 'username')
|
||||
options['password'] = parser.get('Auth', 'password')
|
||||
|
||||
options['zones'] = [i + '.' for i in parser.get('Zones', 'valid').split(",")]
|
||||
|
||||
return options
|
||||
|
||||
|
||||
@app.route('/dns/zone/<string:zone_name>', methods=['GET'])
|
||||
def get_zone(zone_name):
|
||||
"""
|
||||
Query a DNS zone file and get every record and return it in JSON
|
||||
format
|
||||
"""
|
||||
config = parse_config('config.ini')
|
||||
|
||||
record_types = ['A', 'AAAA', 'CNAME', 'MX', 'NS', 'TXT', 'SOA']
|
||||
valid_zones = config['zones']
|
||||
|
||||
records = {}
|
||||
|
||||
if not zone_name.endswith('.'):
|
||||
zone_name = zone_name + '.'
|
||||
|
||||
if zone_name not in valid_zones:
|
||||
return jsonify({'error': 'zone file not permitted'})
|
||||
|
||||
try:
|
||||
zone = dns.zone.from_xfr(dns.query.xfr(config['nameserver'], zone_name))
|
||||
except dns.exception.FormError:
|
||||
return jsonify({'fail': zone_name})
|
||||
|
||||
for (name, ttl, rdata) in zone.iterate_rdatas():
|
||||
if rdata.rdtype != SOA:
|
||||
if records.get(str(name), 0):
|
||||
records[str(name)] = records[str(name)] + [{'Answer': str(rdata), 'RecordType': rdata.rdtype, 'TTL': ttl}]
|
||||
else:
|
||||
records[str(name)] = [{'Answer': str(rdata), 'RecordType': rdata.rdtype, 'TTL': ttl}]
|
||||
|
||||
return jsonify({zone_name: records})
|
||||
|
||||
|
||||
@app.route('/dns/record/<string:domain>', methods=['GET'])
|
||||
def get_record(domain):
|
||||
"""
|
||||
Allow users to request the records for a particualr record
|
||||
"""
|
||||
config = parse_config('config.ini')
|
||||
|
||||
record_types = ['A', 'AAAA', 'CNAME', 'MX', 'NS', 'TXT', 'SOA']
|
||||
valid_zones = config['zones']
|
||||
|
||||
record = {}
|
||||
|
||||
valid = [True for i in valid_zones if domain.endswith(i)]
|
||||
|
||||
"""
|
||||
Only allow the valid zones to be queried, this should stop
|
||||
TLD outside of your nameserver from being queried
|
||||
"""
|
||||
if valid:
|
||||
for record_type in record_types:
|
||||
try:
|
||||
answers = dns.resolver.query(domain, record_type)
|
||||
except dns.resolver.NoAnswer:
|
||||
continue
|
||||
|
||||
record.update({record_type: [str(i) for i in answers.rrset]})
|
||||
|
||||
return jsonify({domain: record})
|
||||
else:
|
||||
return jsonify({'error': 'zone not permitted'})
|
||||
|
||||
|
||||
@app.route('/dns/record/<regex("update|create"):action>/<string:domain>/<int:ttl>/<string:record_type>/<string:response>', methods=['PUT', 'POST'])
|
||||
def dns_mgmt(action, domain, ttl, record_type, response):
|
||||
"""
|
||||
Allow users to update existing records
|
||||
"""
|
||||
config = parse_config('config.ini')
|
||||
|
||||
record_types = ['A', 'AAAA', 'CNAME', 'MX', 'NS', 'TXT', 'SOA']
|
||||
valid_zones = config['zones']
|
||||
|
||||
zone = ''
|
||||
zone = '.'.join(dns.name.from_text(domain).labels[1:])
|
||||
|
||||
if record_type not in record_types:
|
||||
return jsonify({'error': 'not a valid record type'})
|
||||
|
||||
if zone not in valid_zones:
|
||||
return jsonify({'error': 'not a valid zone'})
|
||||
|
||||
"""
|
||||
If the user is only updating make sure the record exists before
|
||||
attempting to perform a dynamic update. This will
|
||||
"""
|
||||
if action == 'update':
|
||||
try:
|
||||
answer = dns.resolver.query('.'.join([domain, zone]), record_type)
|
||||
except dns.resolver.NXDOMAIN:
|
||||
return jsonify({'error': 'does not exist'})
|
||||
|
||||
tsig = dns.tsigkeyring.from_text({config['username']: config['password']})
|
||||
|
||||
update = dns.update.Update(zone, keyring=tsig)
|
||||
update.replace(dns.name.from_text(domain).labels[0], ttl, str(record_type), str(response))
|
||||
|
||||
try:
|
||||
response = dns.query.tcp(update, config['nameserver'])
|
||||
except:
|
||||
return jsonify({'error': 'unable to update domain'})
|
||||
|
||||
if response.rcode() == 0:
|
||||
return jsonify({domain: 'successfully updated'})
|
||||
else:
|
||||
return jsonify({domain: 'failed to update'})
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(debug=True)
|
9
config.ini
Normal file
9
config.ini
Normal file
@ -0,0 +1,9 @@
|
||||
[Nameserver]
|
||||
nameserver = 172.16.33.135
|
||||
|
||||
[Auth]
|
||||
username = TRANSFER
|
||||
password = /h9xIk0tbF3A0lOzy6BYAQ==
|
||||
|
||||
[Zones]
|
||||
valid = ops.zone.org,eng.zone.net
|
9
requirements.txt
Normal file
9
requirements.txt
Normal file
@ -0,0 +1,9 @@
|
||||
Flask==0.10.1
|
||||
Jinja2==2.7.1
|
||||
MarkupSafe==0.18
|
||||
Werkzeug==0.9.4
|
||||
argparse==1.2.1
|
||||
distribute==0.6.24
|
||||
dnspython==1.11.1
|
||||
itsdangerous==0.23
|
||||
wsgiref==0.1.2
|
Loading…
Reference in New Issue
Block a user