#!/bin/env python ''' 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 yaml import dns from dns import resolver # list of puzzle authoritative name servers pitc_nameservers = [ 'ns1.dnsimple.com.', 'ns2.dnsimple.com.', 'ns3.dnsimple.com.', 'ns4.dnsimple.com.', 'ns5.dnsmadeeasy.com.', 'ns6.dnsmadeeasy.com.', 'ns7.dnsmadeeasy.com.' ] # list of puzzle managed zone files pitc_domains = [ 'puzzle.ch.yaml', 'puzzle.yaml', 'nonpuzzle.yaml' ] # configure opendns resolver resolver = resolver.Resolver() resolver.nameservers = ['208.67.222.222','208.67.220.220'] def do_check(domains, nameservers, verbose=False): ''' dsc: Implementatin of the check loop which writes output if verbose is set. Checks if the authoritative ns returned for domain x is in the list of nameservers provided as argument. arg: [list], domains to check arg: [list], verified nameservers arg: [boolean], verbose printing or not ret: [boolean], true if all checked nameservers are ok, false otherwise ''' for domain in domains: returned_nameservers = get_authoritative_ns(domain) for ret_ns in returned_nameservers: try: if verify_authoritative_ns(ret_ns, nameservers): if verbose: print("NS for domain {} is {}".format(domain, ret_ns)) else: print("ERROR: NS for {} is {}, not managed by Puzzle!".format(domain, ret_ns), file=sys.stderr) return False except TypeError: print("List of domains or nameservers contains errors.") return False return True def get_authoritative_ns(domain): ''' dsc: Query the domain and return the authoritative name servers. arg: [str], domain to query ret: [list], list of nameservers or empty list on error. ''' try: answer = resolver.resolve(domain,'NS') servers = [ str(server) for server in answer ] except dns.resolver.NXDOMAIN as err: print(err) return [] else: return servers def get_domains_from_yaml(filenames): ''' dsc: Loads domain names from a list of yaml files. arg: [list], filenames ret: [list], arbitrary list of domain names, emptylist on err ''' domains = [] try: for file in filenames: with open(file, 'r', encoding="ascii") as zone_file: yaml_data = yaml.safe_load(zone_file) yaml_list = list(yaml_data.get('zones')) domains.extend(yaml_list) except FileNotFoundError as err: print(err) return [] else: return domains def verify_authoritative_ns(nameserver, verified_nameservers): ''' dsc: Verifies if the authoritative NS belongs to the puzzle managed NS. arg: [str], nameserver to test arg: [list], a list of verified nameservers ret: [boolean], true if ok; false if nok. ''' if not isinstance(nameserver, str): print("Type of nameserver must be string!") raise TypeError if not isinstance(verified_nameservers, list): print("Type of nameservers must be list!") raise TypeError if nameserver in verified_nameservers: return True return False if __name__ == '__main__': VERBOSE = False if '-v' in sys.argv: VERBOSE = True puzzle_domains = get_domains_from_yaml(pitc_domains) if do_check(puzzle_domains, pitc_nameservers, VERBOSE): sys.exit(0) else: sys.exit(1)