add -v switch

This commit is contained in:
2021-08-18 01:09:55 +02:00
parent 3ccf5c11e8
commit 4c98f1576a

View File

@@ -1,116 +1,80 @@
#!/usr/bin/env python #!/bin/env python
# author: Philipp Gassmann <gassmann@puzzle.ch>
# check if domains configured with octodns are configured to use our nameservers '''
# Usage: ./dnsverify.py 2> /dev/null Check whether the authoritative nameservers returned for all puzzle managed
domains belong to the list of pitc_nameservers and fail if one does not.
usage: ./dnsverify.py [-v]
'''
import sys import sys
import yaml import yaml
from pprint import pprint
import dns.query from dns import resolver
import dns.resolver
from dns.exception import DNSException
def puzzle_nameservers(): # list of puzzle authoritative name servers
dnspool = [ pitc_nameservers = [
"ns1.dnsimple.com.", 'ns1.dnsimple.com.',
"ns2.dnsimple.com.", 'ns2.dnsimple.com.',
"ns3.dnsimple.com.", 'ns3.dnsimple.com.',
"ns4.dnsimple.com.", 'ns4.dnsimple.com.',
"ns5.dnsmadeeasy.com.", 'ns5.dnsmadeeasy.com.',
"ns6.dnsmadeeasy.com.", 'ns6.dnsmadeeasy.com.',
"ns7.dnsmadeeasy.com." ] 'ns7.dnsmadeeasy.com.'
return dnspool ]
# query_authoritative_ns() based on https://stackoverflow.com/questions/4066614/how-can-i-find-the-authoritative-dns-server-for-a-domain-using-dnspython # list of puzzle managed zone files
def query_authoritative_ns(domain, log=lambda msg: None): pitc_domains = [ 'puzzle.ch.yaml', 'puzzle.yaml', 'nonpuzzle.yaml' ]
#default_resolver = dns.resolver.get_default_resolver() # configure opendns resolver
my_resolver = dns.resolver.Resolver() resolver = resolver.Resolver()
my_resolver.nameservers = ['8.8.8.8'] resolver.nameservers = ['208.67.222.222','208.67.220.220']
nameserver = my_resolver.nameservers[0] def get_authoritative_ns(domains, verbose=False):
'''
n = domain.split('.') dsc: Query the domains and return the authoritative name server.
arg: [list], domain to query
for i in range(len(n), 0, -1): ret: [str], nameserver
sub = '.'.join(n[i-1:]) '''
for domain in domains:
log('Looking up %s on %s' % (sub, nameserver)) answers = resolver.resolve(domain,'NS')
query = dns.message.make_query(sub, dns.rdatatype.NS) for server in answers:
response = dns.query.tcp(query, nameserver) if not verify_authoritative_ns(str(server)):
print("ERROR: {} got answer from {}, not managed by puzzle".format(domain, server), file=sys.stderr)
rcode = response.rcode() return False
if rcode != dns.rcode.NOERROR: elif verbose:
if rcode == dns.rcode.NXDOMAIN: print("{} got answer from {}".format(domain, server))
raise Exception('%s does not exist.' % (sub))
else:
raise Exception('Error %s' % (dns.rcode.to_text(rcode)))
if len(response.authority) > 0:
rrsets = response.authority
elif len(response.additional) > 0:
rrsets = [response.additional]
else:
rrsets = response.answer
# Handle all RRsets, not just the first one
for rrset in rrsets:
for rr in rrset:
if rr.rdtype == dns.rdatatype.SOA:
log('Same server is authoritative for %s' % (sub))
elif rr.rdtype == dns.rdatatype.A:
ns = rr.items[0].address
log('Glue record for %s: %s' % (rr.name, ns))
elif rr.rdtype == dns.rdatatype.NS:
authority = rr.target
nameserver = my_resolver.query(authority).rrset[0].to_text()
log('%s [%s] is authoritative for %s; ttl %i' %
(authority, nameserver, sub, rrset.ttl))
result = rrset
else:
# IPv6 glue records etc
#log('Ignoring %s' % (rr))
pass
return result
def log (msg):
sys.stderr.write(msg + u'\n')
def domains_from_config(filename):
filecontent = open(filename, 'r')
config_domains = yaml.load(filecontent, Loader=yaml.FullLoader)
domains = config_domains['zones'].keys()
return domains
def verify_whois(domain):
domain = domain.strip('.')
result = query_authoritative_ns(domain, log)
for entry in result:
nameserver = entry.target.to_text()
if not nameserver in puzzle_nameservers():
print('Domain %s has authoritative NS %s which is not in puzzle_nameservers' % (domain, nameserver))
return False
log('Domain %s is ok' % (domain))
return True return True
def verify_domains_from_config(filename): def get_domains(filenames):
status_success = True '''
domains = domains_from_config(filename) dsc: Loads domain names from a list of yaml files.
for domain in domains: arg: [list], filenames
if not verify_whois(domain): ret: [list], arbitrary list of domain names
status_success = False '''
return status_success domains = []
for file in filenames:
with open(file, 'r') as zone_file:
yaml_data = yaml.safe_load(zone_file)
yaml_list = list(yaml_data.get('zones'))
domains.extend(yaml_list)
return domains
def verify_authoritative_ns(nameserver):
'''
dsc: Verifies if the authoritative NS belongs to the puzzle managed NS.
arg: [str], nameserver
ret: [boolean], true if ok; false if nok.
'''
if nameserver in pitc_nameservers:
return True
return False
if __name__ == "__main__": if __name__ == '__main__':
global_status_success = True VERBOSE = False
for domain in [ if '-v' in sys.argv:
'./puzzle.ch.yaml', VERBOSE = True
'./puzzle.yaml', dns = get_domains(pitc_domains)
'./nonpuzzle.yaml' if not get_authoritative_ns(dns, VERBOSE):
]: sys.exit(1)
if not verify_domains_from_config(domain): sys.exit(0)
global_status_success = False
if not global_status_success:
exit(1)