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 🙂
Hallo,
wunderbare Arbeit, vielen Dank!
Ist in Zukunft noch geplant DNSSEC zu unterstützen? Oder ist dies zu aufwendig zu implementieren?
Da ich eh mongoDB als Datenbank verwende und nur MySQL wegen dem DNS, wäre es schön komplett wechseln zu können.
Viele Grüße,
Norbert