This a pipe backend for PowerDNS to MongoDB written in Python.
Requirements
- pymongo python library
- powerdns min. version 4.x
The MongoDB schema (for example DB: dns / Collection: records)
For SOA records: { "name":"example.org", "type":"SOA", "content":"", "ttl": 300, "primary": "ns1.example.org", "mail": "admin.example.org", "serial": 2018030311, "refresh": 86400, "retry": 7200, "expire": 3600000, "nttl": 3600 } For standard records: { "name":"www.example.org", "type":"A", "ttl": 300, "content": "1.1.1.1" }
Install PowerDNS
apt install pdns-server pdns-backend-pipe
Install pymongo Library
pip install pymongo
The Backend Script (for example: /opt/pdns/backend.py)
You can download it from GitHub – https://github.com/lanbugs/powerdns_mongodb_backend
Parts of the code based on: https://gist.github.com/sokratisg/10069682
#!/usr/bin/env python import sys from pymongo import MongoClient # Config mongo_host = "127.0.0.1" mongo_port = 27017 mongo_db = "dns" mongo_collation = "records" class Lookup(object): ttl = 30 def __init__(self, query): (_type, qname, qclass, qtype, _id, ip) = query self.has_result = False qname_lower = qname.lower() self.results = [] self.results.append('LOG\t%s-%s-%s-%s-%s-%s' % (_type, qname, qclass, qtype, _id, ip)) self.has_result = True client = MongoClient(mongo_host, mongo_port, connect=False) db = client[mongo_db] coll = db[mongo_collation] if qtype == "ANY": records = coll.find({"name": qname_lower}) else: records = coll.find({"type": qtype, "name": qname_lower}) if records: for record in records: if record['type'] == "SOA": """ { "name":"example.org", "type":"SOA", "content":"", "ttl": 300, "primary": "ns1.example.org", "mail": "admin.example.org", "serial": 2018030311, "refresh": 86400, "retry": 7200, "expire": 3600000, "nttl": 3600 } """ try: self.results.append( 'DATA\t%s\t%s\t%s\t%s\t-1\t%s\t%s\t%s\t%s\t%s\t%s\t%s' % ( qname_lower, qclass, qtype, record['ttl'], record['primary'], record['mail'], record['serial'], record['refresh'], record['retry'], record['expire'], record['nttl'] )) self.has_result = True except: self.results.append('LOG\t %s SOA Record currupt maybe fields are missing.' % qname_lower) else: """ { "name":"www.example.org", "type":"A", "ttl": 300, "content": "1.1.1.1" } """ self.results.append('DATA\t%s\t%s\t%s\t%d\t-1\t%s' % ( qname_lower, qclass, record['type'], record['ttl'], record['content'])) self.has_result = True def str_result(self): if self.has_result: return '\n'.join(self.results) else: return '' class DNSbackend(object): def __init__(self, filein, fileout): self.filein = filein self.fileout = fileout self._process_requests() def _fprint(self, message): self.fileout.write(message + '\n') self.fileout.flush() def _process_requests(self): first_time = True while 1: rawline = self.filein.readline() if rawline == '': return line = rawline.rstrip() if first_time: if line == 'HELO\t1': self._fprint('OK\tPython backend ready.') else: rawline = self.filein.readline() sys.exit(1) first_time = False else: query = line.split('\t') if len(query) != 6: self._fprint('LOG\tPowerDNS sent unparseable line') self._fprint('FAIL') else: lookup = Lookup(query) if lookup.has_result: pdns_result = lookup.str_result() self._fprint(pdns_result) self._fprint('END') if __name__ == "__main__": infile = sys.stdin outfile = sys.stdout try: DNSbackend(infile, outfile) except: raise
Don`t forget to make the backend.py script executable to the world.
PowerDNS Configuration /etc/powernds/pdns.d/pdns.local.conf
# Here come the local changes the user made, like configuration of # the several backends that exist. launch=pipe pipe-command=/opt/pdns/backend.py
Try…
Launch the PowerDNS service in monitor mode
root@pdnsdev:~# /etc/init.d/pdns monitor Jun 20 21:10:11 Reading random entropy from '/dev/urandom' Jun 20 21:10:11 Loading '/usr/lib/x86_64-linux-gnu/pdns/libpipebackend.so' Jun 20 21:10:11 [PIPEBackend] This is the pipe backend version 4.0.0-alpha2 reporting Jun 20 21:10:11 Loading '/usr/lib/x86_64-linux-gnu/pdns/libbindbackend.so' Jun 20 21:10:11 [bind2backend] This is the bind backend version 4.0.0-alpha2 reporting Jun 20 21:10:11 This is a standalone pdns Jun 20 21:10:11 UDP server bound to 0.0.0.0:53 Jun 20 21:10:11 Unable to enable timestamp reporting for socket Jun 20 21:10:11 UDPv6 server bound to [::]:53 Jun 20 21:10:11 TCP server bound to 0.0.0.0:53 Jun 20 21:10:11 TCPv6 server bound to [::]:53 Jun 20 21:10:11 PowerDNS Authoritative Server 4.0.0-alpha2 (C) 2001-2016 PowerDNS.COM BV Jun 20 21:10:11 Using 64-bits mode. Built using gcc 5.3.1 20160330. Jun 20 21:10:11 PowerDNS comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it according to the terms of the GPL version 2. Jun 20 21:10:11 Set effective group id to 118 Jun 20 21:10:11 Set effective user id to 112 Jun 20 21:10:11 Creating backend connection for TCP % Jun 20 21:10:11 Backend launched with banner: OK Python backend ready. Jun 20 21:10:11 [bindbackend] Parsing 0 domain(s), will report when done Jun 20 21:10:11 [bindbackend] Done parsing domains, 0 rejected, 0 new, 0 removed Jun 20 21:10:11 About to create 3 backend threads for UDP Jun 20 21:10:11 Backend launched with banner: OK Python backend ready. Jun 20 21:10:11 Backend launched with banner: OK Python backend ready. Jun 20 21:10:11 Done launching threads, ready to distribute questions Jun 20 21:10:11 Backend launched with banner: OK Python backend ready.
Do some querys
user@pdnsdev:~$ dig example.org @127.0.0.1 SOA ; <<>> DiG 9.10.3-P4-Ubuntu <<>> example.org @127.0.0.1 SOA ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 63891 ;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; WARNING: recursion requested but not available ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 1680 ;; QUESTION SECTION: ;example.org. IN SOA ;; ANSWER SECTION: example.org. 300 IN SOA ns1.example.org. admin.example.org. 2018030311 86400 7200 3600000 3600 ;; Query time: 13 msec ;; SERVER: 127.0.0.1#53(127.0.0.1) ;; WHEN: Thu Jun 20 21:20:22 CEST 2018 ;; MSG SIZE rcvd: 86 user@pdnsdev:~$ dig www.example.org @127.0.0.1 ; <<>> DiG 9.10.3-P4-Ubuntu <<>> www.example.org @127.0.0.1 ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 896 ;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; WARNING: recursion requested but not available ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 1680 ;; QUESTION SECTION: ;www.example.org. IN A ;; ANSWER SECTION: www.example.org. 300 IN A 1.1.1.1 ;; Query time: 26 msec ;; SERVER: 127.0.0.1#53(127.0.0.1) ;; WHEN: Thu Jun 20 21:20:44 CEST 2018 ;; MSG SIZE rcvd: 60
Have fun