add -v switch
This commit is contained in:
164
dnsverify.py
164
dnsverify.py
@@ -1,76 +1,116 @@
|
|||||||
#!/bin/env python
|
#!/usr/bin/env python
|
||||||
|
# author: Philipp Gassmann <gassmann@puzzle.ch>
|
||||||
'''
|
# check if domains configured with octodns are configured to use our nameservers
|
||||||
Check whether the authoritative nameservers returned for all puzzle managed
|
# Usage: ./dnsverify.py 2> /dev/null
|
||||||
domains belong to the list of pitc_nameservers and fail if one does not.
|
|
||||||
'''
|
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import yaml
|
import yaml
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
from dns import resolver
|
import dns.query
|
||||||
|
import dns.resolver
|
||||||
|
from dns.exception import DNSException
|
||||||
|
|
||||||
# list of puzzle authoritative name servers
|
def puzzle_nameservers():
|
||||||
pitc_nameservers = [
|
dnspool = [
|
||||||
'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
|
||||||
|
|
||||||
# list of puzzle managed zone files
|
# query_authoritative_ns() based on https://stackoverflow.com/questions/4066614/how-can-i-find-the-authoritative-dns-server-for-a-domain-using-dnspython
|
||||||
pitc_domains = [ 'puzzle.ch.yaml', 'puzzle.yaml', 'nonpuzzle.yaml' ]
|
def query_authoritative_ns(domain, log=lambda msg: None):
|
||||||
|
|
||||||
# configure opendns resolver
|
#default_resolver = dns.resolver.get_default_resolver()
|
||||||
resolver = resolver.Resolver()
|
my_resolver = dns.resolver.Resolver()
|
||||||
resolver.nameservers = ['208.67.222.222','208.67.220.220']
|
my_resolver.nameservers = ['8.8.8.8']
|
||||||
|
|
||||||
def get_authoritative_ns(domains):
|
nameserver = my_resolver.nameservers[0]
|
||||||
'''
|
|
||||||
dsc: Query the domains and return the authoritative name server.
|
n = domain.split('.')
|
||||||
arg: [list], domain to query
|
|
||||||
ret: [str], nameserver
|
for i in range(len(n), 0, -1):
|
||||||
'''
|
sub = '.'.join(n[i-1:])
|
||||||
for domain in domains:
|
|
||||||
answers = resolver.resolve(domain,'NS')
|
log('Looking up %s on %s' % (sub, nameserver))
|
||||||
for server in answers:
|
query = dns.message.make_query(sub, dns.rdatatype.NS)
|
||||||
if not verify_authoritative_ns(str(server)):
|
response = dns.query.tcp(query, nameserver)
|
||||||
print("ERROR: {} got answer from {}, not managed by puzzle".format(domain, server), file=sys.stderr)
|
|
||||||
return False
|
rcode = response.rcode()
|
||||||
|
if rcode != dns.rcode.NOERROR:
|
||||||
|
if rcode == dns.rcode.NXDOMAIN:
|
||||||
|
raise Exception('%s does not exist.' % (sub))
|
||||||
else:
|
else:
|
||||||
#print("{} got answer from {}".format(domain, server))
|
raise Exception('Error %s' % (dns.rcode.to_text(rcode)))
|
||||||
pass
|
|
||||||
|
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 get_domains(filenames):
|
def verify_domains_from_config(filename):
|
||||||
'''
|
status_success = True
|
||||||
dsc: Loads domain names from a list of yaml files.
|
domains = domains_from_config(filename)
|
||||||
arg: [list], filenames
|
for domain in domains:
|
||||||
ret: [list], arbitrary list of domain names
|
if not verify_whois(domain):
|
||||||
'''
|
status_success = False
|
||||||
domains = []
|
return status_success
|
||||||
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__":
|
||||||
dns = get_domains(pitc_domains)
|
global_status_success = True
|
||||||
if not get_authoritative_ns(dns):
|
for domain in [
|
||||||
sys.exit(1)
|
'./puzzle.ch.yaml',
|
||||||
sys.exit(0)
|
'./puzzle.yaml',
|
||||||
|
'./nonpuzzle.yaml'
|
||||||
|
]:
|
||||||
|
if not verify_domains_from_config(domain):
|
||||||
|
global_status_success = False
|
||||||
|
if not global_status_success:
|
||||||
|
exit(1)
|
||||||
|
|||||||
Reference in New Issue
Block a user