From e35f89bc57b06f7eb4f34d0ecb7d0d7f7def317d Mon Sep 17 00:00:00 2001 From: Sebastian Lohff Date: Tue, 2 May 2017 02:44:33 +0200 Subject: [PATCH] Config for dns sync script --- bin/dns-sync | 75 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 55 insertions(+), 20 deletions(-) diff --git a/bin/dns-sync b/bin/dns-sync index 7f1b717..4482de2 100755 --- a/bin/dns-sync +++ b/bin/dns-sync @@ -1,6 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- import argparse +import configparser import os import sys import json @@ -26,9 +27,10 @@ def _parser(): #description='do some awesome foo', ) - #parser.add_argument("-p", "--port", default=2323, type=int, help="Your port") - #parser.add_argument("-v", "--verbose", default=False, action="store_true", help="Be more verbose") - + parser.add_argument("--pdns-host", default="127.0.0.1", help="PDNS host") + parser.add_argument("--pdns-port", default=8081, help="PDNS port") + parser.add_argument("-c", "--config", default=None, type=argparse.FileType("r"), help="Path to config file (default path: ./dns-sync.conf, /etc/dns-sync.conf)") + parser.add_argument("--api-key", default=None, help="PDNS API key") parser.add_argument("--version", action="version", version="%(prog)s " + __VERSION__) return parser @@ -61,6 +63,8 @@ def mergeDomains(zoneData, pdnsData): def removeOldDomains(zoneData, pdnsData): rrDel = [] + #print("zone data", zoneData) + #print("pdnsData", pdnsData) for entry in pdnsData['rrsets']: # search for name/type in domain dict. if non-existtant mark for deletion # this could be much more efficient with a dict! name: [rrset...] @@ -84,7 +88,7 @@ def handleNameserver(ns, servers, usedNameservers, domains): domains.append((ns.name, "AAAA", [ns.glueIPv6])) -def getDomainsFromQueryset(qs, zone, glueRecords, usedNameservers): +def getDomainsFromQueryset(qs, zone, glueRecords, usedNameservers, v4reverse=False): for domain in qs: servers = [] for ns in domain.nameservers.all(): @@ -99,10 +103,30 @@ def getDomainsFromQueryset(qs, zone, glueRecords, usedNameservers): zone.append((domain.getZone(), "NS", servers)) + if v4reverse: + # for ipv4 reverse we have to do some extra work in case of classless delegations + # see RFC2317 + net = domain.parentNet.getNetwork() + if net.prefixlen % 8 != 0: + revZone = domain.getZone() + parts = str(net.network_address).split(".") + baseZone = ".".join(reversed(parts[:net.prefixlen // 8])) + ".in-addr.arpa." + startNo = int(parts[net.prefixlen // 8]) + lenExp = 8 - (net.prefixlen % 8) + + for i in range(2 ** lenExp): + no = startNo + i + zone.append(("%d.%s" % (no, baseZone), "CNAME", ["%d.%s" % (no, revZone)])) + def mergeDomainsWithPdns(s, args, zone, zoneData, protectedRecords=[]): - url = "http://potatos.portal.dn:8081/api/v1/servers/localhost/zones/%s" % (zone,) + url = "http://%s:%s/api/v1/servers/localhost/zones/%s" % (args.pdns_host, args.pdns_port, zone,) pdnsData = s.get(url).json() + baseProtectedRecords = [ + (zone, "NS", []), + (zone, "SOA", []), + ] + # add dn. (NS + glue Nameservers) newDomains = mergeDomains(zoneData, pdnsData) print("Add/replace", newDomains) @@ -110,22 +134,38 @@ def mergeDomainsWithPdns(s, args, zone, zoneData, protectedRecords=[]): if len(newDomains) > 0: r = s.patch(url, data=json.dumps({'rrsets': newDomains})) if r.status_code != 204: - raise RuntimeError("Could not update records in powerdns") + raise RuntimeError("Could not update records in powerdns, API returned %d" % r.status_code) - delDomains = removeOldDomains(zoneData, zoneData + protectedRecords) + delDomains = removeOldDomains(zoneData + protectedRecords + baseProtectedRecords, pdnsData) print("Del", delDomains) r = s.patch(url, data=json.dumps({'rrsets': delDomains})) if r.status_code != 204: - raise RuntimeError("Could not update records in powerdns") + raise RuntimeError("Could not update records in powerdns, API returned %d" % r.status_code) def main(): parser = _parser() args = parser.parse_args() + config = configparser.ConfigParser() + if args.config: + config.read_file(args.config) + else: + config.read([os.path.join(os.path.abspath(__file__), "dns-sync.conf"), "/etc/dns-sync.conf"]) + #print(config) + #print(config.get("DEFAULT", "api-key")) + #print(config.has_section("DEFAULT"), config.has_option("DEFAULT", "api-key")) + + if args.api_key: + config["DEFAULT"]["api-key"] = args.api_key + + if not config.has_option("DEFAULT", "api-key"): + print("Error: Could not find api-key (not present in config under [DEFAULT]; not given via command line)", file=sys.stderr) + sys.exit(2) + s = requests.Session() s.headers = { - 'X-API-Key': 'bei2aequ2OBi', + 'X-API-Key': config.get("DEFAULT", "api-key"), 'Accept': 'application/json' } @@ -143,21 +183,16 @@ def main(): qs = ReverseZone.objects.annotate(nsCount=Count('nameservers')).filter(nsCount__gt=0).order_by("parentNet__address") qs4 = filter(lambda _revz: _revz.getNetwork().version == 4, qs) qs6 = filter(lambda _revz: _revz.getNetwork().version == 6, qs) - getDomainsFromQueryset(qs4, rzone4, domains, usedNameservers) + getDomainsFromQueryset(qs4, rzone4, domains, usedNameservers, v4reverse=True) getDomainsFromQueryset(qs6, rzone6, domains, usedNameservers) - print("dn.", domains) - print("v4", rzone4) - print("v6", rzone6) - - protectedRecords = [ - ("dn.", "NS", []), - ("dn.", "SOA", []), - ] + #print("dn.", domains) + #print("v4", rzone4) + #print("v6", rzone6) - mergeDomainsWithPdns(s, args, "dn.", domains, protectedRecords) + mergeDomainsWithPdns(s, args, "dn.", domains) mergeDomainsWithPdns(s, args, "10.in-addr.arpa.", rzone4) - mergeDomainsWithPdns(s, args, "3.2.7.c.3.a.d.f.ip6.arpa.") + mergeDomainsWithPdns(s, args, "3.2.7.c.3.a.d.f.ip6.arpa.", rzone6)