1*00b67f09SDavid van Moolenbroek#!@PYTHON@ 2*00b67f09SDavid van Moolenbroek############################################################################ 3*00b67f09SDavid van Moolenbroek# Copyright (C) 2013, 2014 Internet Systems Consortium, Inc. ("ISC") 4*00b67f09SDavid van Moolenbroek# 5*00b67f09SDavid van Moolenbroek# Permission to use, copy, modify, and/or distribute this software for any 6*00b67f09SDavid van Moolenbroek# purpose with or without fee is hereby granted, provided that the above 7*00b67f09SDavid van Moolenbroek# copyright notice and this permission notice appear in all copies. 8*00b67f09SDavid van Moolenbroek# 9*00b67f09SDavid van Moolenbroek# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10*00b67f09SDavid van Moolenbroek# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11*00b67f09SDavid van Moolenbroek# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12*00b67f09SDavid van Moolenbroek# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13*00b67f09SDavid van Moolenbroek# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14*00b67f09SDavid van Moolenbroek# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15*00b67f09SDavid van Moolenbroek# PERFORMANCE OF THIS SOFTWARE. 16*00b67f09SDavid van Moolenbroek############################################################################ 17*00b67f09SDavid van Moolenbroek 18*00b67f09SDavid van Moolenbroekimport argparse 19*00b67f09SDavid van Moolenbroekimport os 20*00b67f09SDavid van Moolenbroekimport glob 21*00b67f09SDavid van Moolenbroekimport sys 22*00b67f09SDavid van Moolenbroekimport re 23*00b67f09SDavid van Moolenbroekimport time 24*00b67f09SDavid van Moolenbroekimport calendar 25*00b67f09SDavid van Moolenbroekfrom collections import defaultdict 26*00b67f09SDavid van Moolenbroekimport pprint 27*00b67f09SDavid van Moolenbroek 28*00b67f09SDavid van Moolenbroekprog='dnssec-coverage' 29*00b67f09SDavid van Moolenbroek 30*00b67f09SDavid van Moolenbroek# These routines permit platform-independent location of BIND 9 tools 31*00b67f09SDavid van Moolenbroekif os.name == 'nt': 32*00b67f09SDavid van Moolenbroek import win32con 33*00b67f09SDavid van Moolenbroek import win32api 34*00b67f09SDavid van Moolenbroek 35*00b67f09SDavid van Moolenbroekdef prefix(bindir = ''): 36*00b67f09SDavid van Moolenbroek if os.name != 'nt': 37*00b67f09SDavid van Moolenbroek return os.path.join('@prefix@', bindir) 38*00b67f09SDavid van Moolenbroek 39*00b67f09SDavid van Moolenbroek bind_subkey = "Software\\ISC\\BIND" 40*00b67f09SDavid van Moolenbroek hKey = None 41*00b67f09SDavid van Moolenbroek keyFound = True 42*00b67f09SDavid van Moolenbroek try: 43*00b67f09SDavid van Moolenbroek hKey = win32api.RegOpenKeyEx(win32con.HKEY_LOCAL_MACHINE, bind_subkey) 44*00b67f09SDavid van Moolenbroek except: 45*00b67f09SDavid van Moolenbroek keyFound = False 46*00b67f09SDavid van Moolenbroek if keyFound: 47*00b67f09SDavid van Moolenbroek try: 48*00b67f09SDavid van Moolenbroek (namedBase, _) = win32api.RegQueryValueEx(hKey, "InstallDir") 49*00b67f09SDavid van Moolenbroek except: 50*00b67f09SDavid van Moolenbroek keyFound = False 51*00b67f09SDavid van Moolenbroek win32api.RegCloseKey(hKey) 52*00b67f09SDavid van Moolenbroek if keyFound: 53*00b67f09SDavid van Moolenbroek return os.path.join(namedBase, bindir) 54*00b67f09SDavid van Moolenbroek return os.path.join(win32api.GetSystemDirectory(), bindir) 55*00b67f09SDavid van Moolenbroek 56*00b67f09SDavid van Moolenbroek######################################################################## 57*00b67f09SDavid van Moolenbroek# Class Event 58*00b67f09SDavid van Moolenbroek######################################################################## 59*00b67f09SDavid van Moolenbroekclass Event: 60*00b67f09SDavid van Moolenbroek """ A discrete key metadata event, e.g., Publish, Activate, Inactive, 61*00b67f09SDavid van Moolenbroek Delete. Stores the date of the event, and identifying information about 62*00b67f09SDavid van Moolenbroek the key to which the event will occur.""" 63*00b67f09SDavid van Moolenbroek 64*00b67f09SDavid van Moolenbroek def __init__(self, _what, _key): 65*00b67f09SDavid van Moolenbroek now = time.time() 66*00b67f09SDavid van Moolenbroek self.what = _what 67*00b67f09SDavid van Moolenbroek self.when = _key.metadata[_what] 68*00b67f09SDavid van Moolenbroek self.key = _key 69*00b67f09SDavid van Moolenbroek self.keyid = _key.keyid 70*00b67f09SDavid van Moolenbroek self.sep = _key.sep 71*00b67f09SDavid van Moolenbroek self.zone = _key.zone 72*00b67f09SDavid van Moolenbroek self.alg = _key.alg 73*00b67f09SDavid van Moolenbroek 74*00b67f09SDavid van Moolenbroek def __repr__(self): 75*00b67f09SDavid van Moolenbroek return repr((self.when, self.what, self.keyid, self.sep, 76*00b67f09SDavid van Moolenbroek self.zone, self.alg)) 77*00b67f09SDavid van Moolenbroek 78*00b67f09SDavid van Moolenbroek def showtime(self): 79*00b67f09SDavid van Moolenbroek return time.strftime("%a %b %d %H:%M:%S UTC %Y", self.when) 80*00b67f09SDavid van Moolenbroek 81*00b67f09SDavid van Moolenbroek def showkey(self): 82*00b67f09SDavid van Moolenbroek return self.key.showkey() 83*00b67f09SDavid van Moolenbroek 84*00b67f09SDavid van Moolenbroek def showkeytype(self): 85*00b67f09SDavid van Moolenbroek return self.key.showkeytype() 86*00b67f09SDavid van Moolenbroek 87*00b67f09SDavid van Moolenbroek######################################################################## 88*00b67f09SDavid van Moolenbroek# Class Key 89*00b67f09SDavid van Moolenbroek######################################################################## 90*00b67f09SDavid van Moolenbroekclass Key: 91*00b67f09SDavid van Moolenbroek """An individual DNSSEC key. Identified by path, zone, algorithm, keyid. 92*00b67f09SDavid van Moolenbroek Contains a dictionary of metadata events.""" 93*00b67f09SDavid van Moolenbroek 94*00b67f09SDavid van Moolenbroek def __init__(self, keyname): 95*00b67f09SDavid van Moolenbroek directory = os.path.dirname(keyname) 96*00b67f09SDavid van Moolenbroek key = os.path.basename(keyname) 97*00b67f09SDavid van Moolenbroek (zone, alg, keyid) = key.split('+') 98*00b67f09SDavid van Moolenbroek keyid = keyid.split('.')[0] 99*00b67f09SDavid van Moolenbroek key = [zone, alg, keyid] 100*00b67f09SDavid van Moolenbroek key_file = directory + os.sep + '+'.join(key) + ".key" 101*00b67f09SDavid van Moolenbroek private_file = directory + os.sep + '+'.join(key) + ".private" 102*00b67f09SDavid van Moolenbroek 103*00b67f09SDavid van Moolenbroek self.zone = zone[1:-1] 104*00b67f09SDavid van Moolenbroek self.alg = int(alg) 105*00b67f09SDavid van Moolenbroek self.keyid = int(keyid) 106*00b67f09SDavid van Moolenbroek 107*00b67f09SDavid van Moolenbroek kfp = open(key_file, "r") 108*00b67f09SDavid van Moolenbroek for line in kfp: 109*00b67f09SDavid van Moolenbroek if line[0] == ';': 110*00b67f09SDavid van Moolenbroek continue 111*00b67f09SDavid van Moolenbroek tokens = line.split() 112*00b67f09SDavid van Moolenbroek if not tokens: 113*00b67f09SDavid van Moolenbroek continue 114*00b67f09SDavid van Moolenbroek 115*00b67f09SDavid van Moolenbroek if tokens[1].lower() in ('in', 'ch', 'hs'): 116*00b67f09SDavid van Moolenbroek septoken = 3 117*00b67f09SDavid van Moolenbroek self.ttl = args.keyttl 118*00b67f09SDavid van Moolenbroek if not self.ttl: 119*00b67f09SDavid van Moolenbroek vspace() 120*00b67f09SDavid van Moolenbroek print("WARNING: Unable to determine TTL for DNSKEY %s." % 121*00b67f09SDavid van Moolenbroek self.showkey()) 122*00b67f09SDavid van Moolenbroek print("\t Using 1 day (86400 seconds); re-run with the -d " 123*00b67f09SDavid van Moolenbroek "option for more\n\t accurate results.") 124*00b67f09SDavid van Moolenbroek self.ttl = 86400 125*00b67f09SDavid van Moolenbroek else: 126*00b67f09SDavid van Moolenbroek septoken = 4 127*00b67f09SDavid van Moolenbroek self.ttl = int(tokens[1]) if not args.keyttl else args.keyttl 128*00b67f09SDavid van Moolenbroek 129*00b67f09SDavid van Moolenbroek if (int(tokens[septoken]) & 0x1) == 1: 130*00b67f09SDavid van Moolenbroek self.sep = True 131*00b67f09SDavid van Moolenbroek else: 132*00b67f09SDavid van Moolenbroek self.sep = False 133*00b67f09SDavid van Moolenbroek kfp.close() 134*00b67f09SDavid van Moolenbroek 135*00b67f09SDavid van Moolenbroek pfp = open(private_file, "rU") 136*00b67f09SDavid van Moolenbroek propDict = dict() 137*00b67f09SDavid van Moolenbroek for propLine in pfp: 138*00b67f09SDavid van Moolenbroek propDef = propLine.strip() 139*00b67f09SDavid van Moolenbroek if len(propDef) == 0: 140*00b67f09SDavid van Moolenbroek continue 141*00b67f09SDavid van Moolenbroek if propDef[0] in ('!', '#'): 142*00b67f09SDavid van Moolenbroek continue 143*00b67f09SDavid van Moolenbroek punctuation = [propDef.find(c) for c in ':= '] + [len(propDef)] 144*00b67f09SDavid van Moolenbroek found = min([ pos for pos in punctuation if pos != -1 ]) 145*00b67f09SDavid van Moolenbroek name = propDef[:found].rstrip() 146*00b67f09SDavid van Moolenbroek value = propDef[found:].lstrip(":= ").rstrip() 147*00b67f09SDavid van Moolenbroek propDict[name] = value 148*00b67f09SDavid van Moolenbroek 149*00b67f09SDavid van Moolenbroek if("Publish" in propDict): 150*00b67f09SDavid van Moolenbroek propDict["Publish"] = time.strptime(propDict["Publish"], 151*00b67f09SDavid van Moolenbroek "%Y%m%d%H%M%S") 152*00b67f09SDavid van Moolenbroek 153*00b67f09SDavid van Moolenbroek if("Activate" in propDict): 154*00b67f09SDavid van Moolenbroek propDict["Activate"] = time.strptime(propDict["Activate"], 155*00b67f09SDavid van Moolenbroek "%Y%m%d%H%M%S") 156*00b67f09SDavid van Moolenbroek 157*00b67f09SDavid van Moolenbroek if("Inactive" in propDict): 158*00b67f09SDavid van Moolenbroek propDict["Inactive"] = time.strptime(propDict["Inactive"], 159*00b67f09SDavid van Moolenbroek "%Y%m%d%H%M%S") 160*00b67f09SDavid van Moolenbroek 161*00b67f09SDavid van Moolenbroek if("Delete" in propDict): 162*00b67f09SDavid van Moolenbroek propDict["Delete"] = time.strptime(propDict["Delete"], 163*00b67f09SDavid van Moolenbroek "%Y%m%d%H%M%S") 164*00b67f09SDavid van Moolenbroek 165*00b67f09SDavid van Moolenbroek if("Revoke" in propDict): 166*00b67f09SDavid van Moolenbroek propDict["Revoke"] = time.strptime(propDict["Revoke"], 167*00b67f09SDavid van Moolenbroek "%Y%m%d%H%M%S") 168*00b67f09SDavid van Moolenbroek pfp.close() 169*00b67f09SDavid van Moolenbroek self.metadata = propDict 170*00b67f09SDavid van Moolenbroek 171*00b67f09SDavid van Moolenbroek def showkey(self): 172*00b67f09SDavid van Moolenbroek return "%s/%03d/%05d" % (self.zone, self.alg, self.keyid); 173*00b67f09SDavid van Moolenbroek 174*00b67f09SDavid van Moolenbroek def showkeytype(self): 175*00b67f09SDavid van Moolenbroek return ("KSK" if self.sep else "ZSK") 176*00b67f09SDavid van Moolenbroek 177*00b67f09SDavid van Moolenbroek # ensure that the gap between Publish and Activate is big enough 178*00b67f09SDavid van Moolenbroek def check_prepub(self): 179*00b67f09SDavid van Moolenbroek now = time.time() 180*00b67f09SDavid van Moolenbroek 181*00b67f09SDavid van Moolenbroek if (not "Activate" in self.metadata): 182*00b67f09SDavid van Moolenbroek debug_print("No Activate information in key: %s" % self.showkey()) 183*00b67f09SDavid van Moolenbroek return False 184*00b67f09SDavid van Moolenbroek a = calendar.timegm(self.metadata["Activate"]) 185*00b67f09SDavid van Moolenbroek 186*00b67f09SDavid van Moolenbroek if (not "Publish" in self.metadata): 187*00b67f09SDavid van Moolenbroek debug_print("No Publish information in key: %s" % self.showkey()) 188*00b67f09SDavid van Moolenbroek if a > now: 189*00b67f09SDavid van Moolenbroek vspace() 190*00b67f09SDavid van Moolenbroek print("WARNING: Key %s (%s) is scheduled for activation but \n" 191*00b67f09SDavid van Moolenbroek "\t not for publication." % 192*00b67f09SDavid van Moolenbroek (self.showkey(), self.showkeytype())) 193*00b67f09SDavid van Moolenbroek return False 194*00b67f09SDavid van Moolenbroek p = calendar.timegm(self.metadata["Publish"]) 195*00b67f09SDavid van Moolenbroek 196*00b67f09SDavid van Moolenbroek now = time.time() 197*00b67f09SDavid van Moolenbroek if p < now and a < now: 198*00b67f09SDavid van Moolenbroek return True 199*00b67f09SDavid van Moolenbroek 200*00b67f09SDavid van Moolenbroek if p == a: 201*00b67f09SDavid van Moolenbroek vspace() 202*00b67f09SDavid van Moolenbroek print ("WARNING: %s (%s) is scheduled to be published and\n" 203*00b67f09SDavid van Moolenbroek "\t activated at the same time. This could result in a\n" 204*00b67f09SDavid van Moolenbroek "\t coverage gap if the zone was previously signed." % 205*00b67f09SDavid van Moolenbroek (self.showkey(), self.showkeytype())) 206*00b67f09SDavid van Moolenbroek print("\t Activation should be at least %s after publication." 207*00b67f09SDavid van Moolenbroek % duration(self.ttl)) 208*00b67f09SDavid van Moolenbroek return True 209*00b67f09SDavid van Moolenbroek 210*00b67f09SDavid van Moolenbroek if a < p: 211*00b67f09SDavid van Moolenbroek vspace() 212*00b67f09SDavid van Moolenbroek print("WARNING: Key %s (%s) is active before it is published" % 213*00b67f09SDavid van Moolenbroek (self.showkey(), self.showkeytype())) 214*00b67f09SDavid van Moolenbroek return False 215*00b67f09SDavid van Moolenbroek 216*00b67f09SDavid van Moolenbroek if (a - p < self.ttl): 217*00b67f09SDavid van Moolenbroek vspace() 218*00b67f09SDavid van Moolenbroek print("WARNING: Key %s (%s) is activated too soon after\n" 219*00b67f09SDavid van Moolenbroek "\t publication; this could result in coverage gaps due to\n" 220*00b67f09SDavid van Moolenbroek "\t resolver caches containing old data." 221*00b67f09SDavid van Moolenbroek % (self.showkey(), self.showkeytype())) 222*00b67f09SDavid van Moolenbroek print("\t Activation should be at least %s after publication." % 223*00b67f09SDavid van Moolenbroek duration(self.ttl)) 224*00b67f09SDavid van Moolenbroek return False 225*00b67f09SDavid van Moolenbroek 226*00b67f09SDavid van Moolenbroek return True 227*00b67f09SDavid van Moolenbroek 228*00b67f09SDavid van Moolenbroek # ensure that the gap between Inactive and Delete is big enough 229*00b67f09SDavid van Moolenbroek def check_postpub(self, timespan = None): 230*00b67f09SDavid van Moolenbroek if not timespan: 231*00b67f09SDavid van Moolenbroek timespan = self.ttl 232*00b67f09SDavid van Moolenbroek 233*00b67f09SDavid van Moolenbroek now = time.time() 234*00b67f09SDavid van Moolenbroek 235*00b67f09SDavid van Moolenbroek if (not "Delete" in self.metadata): 236*00b67f09SDavid van Moolenbroek debug_print("No Delete information in key: %s" % self.showkey()) 237*00b67f09SDavid van Moolenbroek return False 238*00b67f09SDavid van Moolenbroek d = calendar.timegm(self.metadata["Delete"]) 239*00b67f09SDavid van Moolenbroek 240*00b67f09SDavid van Moolenbroek if (not "Inactive" in self.metadata): 241*00b67f09SDavid van Moolenbroek debug_print("No Inactive information in key: %s" % self.showkey()) 242*00b67f09SDavid van Moolenbroek if d > now: 243*00b67f09SDavid van Moolenbroek vspace() 244*00b67f09SDavid van Moolenbroek print("WARNING: Key %s (%s) is scheduled for deletion but\n" 245*00b67f09SDavid van Moolenbroek "\t not for inactivation." % 246*00b67f09SDavid van Moolenbroek (self.showkey(), self.showkeytype())) 247*00b67f09SDavid van Moolenbroek return False 248*00b67f09SDavid van Moolenbroek i = calendar.timegm(self.metadata["Inactive"]) 249*00b67f09SDavid van Moolenbroek 250*00b67f09SDavid van Moolenbroek if d < now and i < now: 251*00b67f09SDavid van Moolenbroek return True 252*00b67f09SDavid van Moolenbroek 253*00b67f09SDavid van Moolenbroek if (d < i): 254*00b67f09SDavid van Moolenbroek vspace() 255*00b67f09SDavid van Moolenbroek print("WARNING: Key %s (%s) is scheduled for deletion before\n" 256*00b67f09SDavid van Moolenbroek "\t inactivation." % (self.showkey(), self.showkeytype())) 257*00b67f09SDavid van Moolenbroek return False 258*00b67f09SDavid van Moolenbroek 259*00b67f09SDavid van Moolenbroek if (d - i < timespan): 260*00b67f09SDavid van Moolenbroek vspace() 261*00b67f09SDavid van Moolenbroek print("WARNING: Key %s (%s) scheduled for deletion too soon after\n" 262*00b67f09SDavid van Moolenbroek "\t deactivation; this may result in coverage gaps due to\n" 263*00b67f09SDavid van Moolenbroek "\t resolver caches containing old data." 264*00b67f09SDavid van Moolenbroek % (self.showkey(), self.showkeytype())) 265*00b67f09SDavid van Moolenbroek print("\t Deletion should be at least %s after inactivation." % 266*00b67f09SDavid van Moolenbroek duration(timespan)) 267*00b67f09SDavid van Moolenbroek return False 268*00b67f09SDavid van Moolenbroek 269*00b67f09SDavid van Moolenbroek return True 270*00b67f09SDavid van Moolenbroek 271*00b67f09SDavid van Moolenbroek######################################################################## 272*00b67f09SDavid van Moolenbroek# class Zone 273*00b67f09SDavid van Moolenbroek######################################################################## 274*00b67f09SDavid van Moolenbroekclass Zone: 275*00b67f09SDavid van Moolenbroek """Stores data about a specific zone""" 276*00b67f09SDavid van Moolenbroek 277*00b67f09SDavid van Moolenbroek def __init__(self, _name, _keyttl = None, _maxttl = None): 278*00b67f09SDavid van Moolenbroek self.name = _name 279*00b67f09SDavid van Moolenbroek self.keyttl = _keyttl 280*00b67f09SDavid van Moolenbroek self.maxttl = _maxttl 281*00b67f09SDavid van Moolenbroek 282*00b67f09SDavid van Moolenbroek def load(self, filename): 283*00b67f09SDavid van Moolenbroek if not args.compilezone: 284*00b67f09SDavid van Moolenbroek sys.stderr.write(prog + ': FATAL: "named-compilezone" not found\n') 285*00b67f09SDavid van Moolenbroek exit(1) 286*00b67f09SDavid van Moolenbroek 287*00b67f09SDavid van Moolenbroek if not self.name: 288*00b67f09SDavid van Moolenbroek return 289*00b67f09SDavid van Moolenbroek 290*00b67f09SDavid van Moolenbroek maxttl = keyttl = None 291*00b67f09SDavid van Moolenbroek 292*00b67f09SDavid van Moolenbroek fp = os.popen("%s -o - %s %s 2> /dev/null" % 293*00b67f09SDavid van Moolenbroek (args.compilezone, self.name, filename)) 294*00b67f09SDavid van Moolenbroek for line in fp: 295*00b67f09SDavid van Moolenbroek fields = line.split() 296*00b67f09SDavid van Moolenbroek if not maxttl or int(fields[1]) > maxttl: 297*00b67f09SDavid van Moolenbroek maxttl = int(fields[1]) 298*00b67f09SDavid van Moolenbroek if fields[3] == "DNSKEY": 299*00b67f09SDavid van Moolenbroek keyttl = int(fields[1]) 300*00b67f09SDavid van Moolenbroek fp.close() 301*00b67f09SDavid van Moolenbroek 302*00b67f09SDavid van Moolenbroek self.keyttl = keyttl 303*00b67f09SDavid van Moolenbroek self.maxttl = maxttl 304*00b67f09SDavid van Moolenbroek 305*00b67f09SDavid van Moolenbroek############################################################################ 306*00b67f09SDavid van Moolenbroek# debug_print: 307*00b67f09SDavid van Moolenbroek############################################################################ 308*00b67f09SDavid van Moolenbroekdef debug_print(debugVar): 309*00b67f09SDavid van Moolenbroek """pretty print a variable iff debug mode is enabled""" 310*00b67f09SDavid van Moolenbroek if not args.debug_mode: 311*00b67f09SDavid van Moolenbroek return 312*00b67f09SDavid van Moolenbroek if type(debugVar) == str: 313*00b67f09SDavid van Moolenbroek print("DEBUG: " + debugVar) 314*00b67f09SDavid van Moolenbroek else: 315*00b67f09SDavid van Moolenbroek print("DEBUG: " + pprint.pformat(debugVar)) 316*00b67f09SDavid van Moolenbroek return 317*00b67f09SDavid van Moolenbroek 318*00b67f09SDavid van Moolenbroek############################################################################ 319*00b67f09SDavid van Moolenbroek# vspace: 320*00b67f09SDavid van Moolenbroek############################################################################ 321*00b67f09SDavid van Moolenbroek_firstline = True 322*00b67f09SDavid van Moolenbroekdef vspace(): 323*00b67f09SDavid van Moolenbroek """adds vertical space between two sections of output text if and only 324*00b67f09SDavid van Moolenbroek if this is *not* the first section being printed""" 325*00b67f09SDavid van Moolenbroek global _firstline 326*00b67f09SDavid van Moolenbroek if _firstline: 327*00b67f09SDavid van Moolenbroek _firstline = False 328*00b67f09SDavid van Moolenbroek else: 329*00b67f09SDavid van Moolenbroek print 330*00b67f09SDavid van Moolenbroek 331*00b67f09SDavid van Moolenbroek############################################################################ 332*00b67f09SDavid van Moolenbroek# vreset: 333*00b67f09SDavid van Moolenbroek############################################################################ 334*00b67f09SDavid van Moolenbroekdef vreset(): 335*00b67f09SDavid van Moolenbroek """reset vertical spacing""" 336*00b67f09SDavid van Moolenbroek global _firstline 337*00b67f09SDavid van Moolenbroek _firstline = True 338*00b67f09SDavid van Moolenbroek 339*00b67f09SDavid van Moolenbroek############################################################################ 340*00b67f09SDavid van Moolenbroek# getunit 341*00b67f09SDavid van Moolenbroek############################################################################ 342*00b67f09SDavid van Moolenbroekdef getunit(secs, size): 343*00b67f09SDavid van Moolenbroek """given a number of seconds, and a number of seconds in a larger unit of 344*00b67f09SDavid van Moolenbroek time, calculate how many of the larger unit there are and return both 345*00b67f09SDavid van Moolenbroek that and a remainder value""" 346*00b67f09SDavid van Moolenbroek bigunit = secs // size 347*00b67f09SDavid van Moolenbroek if bigunit: 348*00b67f09SDavid van Moolenbroek secs %= size 349*00b67f09SDavid van Moolenbroek return (bigunit, secs) 350*00b67f09SDavid van Moolenbroek 351*00b67f09SDavid van Moolenbroek############################################################################ 352*00b67f09SDavid van Moolenbroek# addtime 353*00b67f09SDavid van Moolenbroek############################################################################ 354*00b67f09SDavid van Moolenbroekdef addtime(output, unit, t): 355*00b67f09SDavid van Moolenbroek """add a formatted unit of time to an accumulating string""" 356*00b67f09SDavid van Moolenbroek if t: 357*00b67f09SDavid van Moolenbroek output += ("%s%d %s%s" % 358*00b67f09SDavid van Moolenbroek ((", " if output else ""), 359*00b67f09SDavid van Moolenbroek t, unit, ("s" if t > 1 else ""))) 360*00b67f09SDavid van Moolenbroek 361*00b67f09SDavid van Moolenbroek return output 362*00b67f09SDavid van Moolenbroek 363*00b67f09SDavid van Moolenbroek############################################################################ 364*00b67f09SDavid van Moolenbroek# duration: 365*00b67f09SDavid van Moolenbroek############################################################################ 366*00b67f09SDavid van Moolenbroekdef duration(secs): 367*00b67f09SDavid van Moolenbroek """given a length of time in seconds, print a formatted human duration 368*00b67f09SDavid van Moolenbroek in larger units of time 369*00b67f09SDavid van Moolenbroek """ 370*00b67f09SDavid van Moolenbroek # define units: 371*00b67f09SDavid van Moolenbroek minute = 60 372*00b67f09SDavid van Moolenbroek hour = minute * 60 373*00b67f09SDavid van Moolenbroek day = hour * 24 374*00b67f09SDavid van Moolenbroek month = day * 30 375*00b67f09SDavid van Moolenbroek year = day * 365 376*00b67f09SDavid van Moolenbroek 377*00b67f09SDavid van Moolenbroek # calculate time in units: 378*00b67f09SDavid van Moolenbroek (years, secs) = getunit(secs, year) 379*00b67f09SDavid van Moolenbroek (months, secs) = getunit(secs, month) 380*00b67f09SDavid van Moolenbroek (days, secs) = getunit(secs, day) 381*00b67f09SDavid van Moolenbroek (hours, secs) = getunit(secs, hour) 382*00b67f09SDavid van Moolenbroek (minutes, secs) = getunit(secs, minute) 383*00b67f09SDavid van Moolenbroek 384*00b67f09SDavid van Moolenbroek output = '' 385*00b67f09SDavid van Moolenbroek output = addtime(output, "year", years) 386*00b67f09SDavid van Moolenbroek output = addtime(output, "month", months) 387*00b67f09SDavid van Moolenbroek output = addtime(output, "day", days) 388*00b67f09SDavid van Moolenbroek output = addtime(output, "hour", hours) 389*00b67f09SDavid van Moolenbroek output = addtime(output, "minute", minutes) 390*00b67f09SDavid van Moolenbroek output = addtime(output, "second", secs) 391*00b67f09SDavid van Moolenbroek return output 392*00b67f09SDavid van Moolenbroek 393*00b67f09SDavid van Moolenbroek############################################################################ 394*00b67f09SDavid van Moolenbroek# parse_time 395*00b67f09SDavid van Moolenbroek############################################################################ 396*00b67f09SDavid van Moolenbroekdef parse_time(s): 397*00b67f09SDavid van Moolenbroek """convert a formatted time (e.g., 1y, 6mo, 15mi, etc) into seconds""" 398*00b67f09SDavid van Moolenbroek s = s.strip() 399*00b67f09SDavid van Moolenbroek 400*00b67f09SDavid van Moolenbroek # if s is an integer, we're done already 401*00b67f09SDavid van Moolenbroek try: 402*00b67f09SDavid van Moolenbroek n = int(s) 403*00b67f09SDavid van Moolenbroek return n 404*00b67f09SDavid van Moolenbroek except: 405*00b67f09SDavid van Moolenbroek pass 406*00b67f09SDavid van Moolenbroek 407*00b67f09SDavid van Moolenbroek # try to parse as a number with a suffix indicating unit of time 408*00b67f09SDavid van Moolenbroek r = re.compile('([0-9][0-9]*)\s*([A-Za-z]*)') 409*00b67f09SDavid van Moolenbroek m = r.match(s) 410*00b67f09SDavid van Moolenbroek if not m: 411*00b67f09SDavid van Moolenbroek raise Exception("Cannot parse %s" % s) 412*00b67f09SDavid van Moolenbroek (n, unit) = m.groups() 413*00b67f09SDavid van Moolenbroek n = int(n) 414*00b67f09SDavid van Moolenbroek unit = unit.lower() 415*00b67f09SDavid van Moolenbroek if unit[0] == 'y': 416*00b67f09SDavid van Moolenbroek return n * 31536000 417*00b67f09SDavid van Moolenbroek elif unit[0] == 'm' and unit[1] == 'o': 418*00b67f09SDavid van Moolenbroek return n * 2592000 419*00b67f09SDavid van Moolenbroek elif unit[0] == 'w': 420*00b67f09SDavid van Moolenbroek return n * 604800 421*00b67f09SDavid van Moolenbroek elif unit[0] == 'd': 422*00b67f09SDavid van Moolenbroek return n * 86400 423*00b67f09SDavid van Moolenbroek elif unit[0] == 'h': 424*00b67f09SDavid van Moolenbroek return n * 3600 425*00b67f09SDavid van Moolenbroek elif unit[0] == 'm' and unit[1] == 'i': 426*00b67f09SDavid van Moolenbroek return n * 60 427*00b67f09SDavid van Moolenbroek elif unit[0] == 's': 428*00b67f09SDavid van Moolenbroek return n 429*00b67f09SDavid van Moolenbroek else: 430*00b67f09SDavid van Moolenbroek raise Exception("Invalid suffix %s" % unit) 431*00b67f09SDavid van Moolenbroek 432*00b67f09SDavid van Moolenbroek############################################################################ 433*00b67f09SDavid van Moolenbroek# algname: 434*00b67f09SDavid van Moolenbroek############################################################################ 435*00b67f09SDavid van Moolenbroekdef algname(alg): 436*00b67f09SDavid van Moolenbroek """return the mnemonic for a DNSSEC algorithm""" 437*00b67f09SDavid van Moolenbroek names = (None, 'RSAMD5', 'DH', 'DSA', 'ECC', 'RSASHA1', 438*00b67f09SDavid van Moolenbroek 'NSEC3DSA', 'NSEC3RSASHA1', 'RSASHA256', None, 439*00b67f09SDavid van Moolenbroek 'RSASHA512', None, 'ECCGOST', 'ECDSAP256SHA256', 440*00b67f09SDavid van Moolenbroek 'ECDSAP384SHA384') 441*00b67f09SDavid van Moolenbroek name = None 442*00b67f09SDavid van Moolenbroek if alg in range(len(names)): 443*00b67f09SDavid van Moolenbroek name = names[alg] 444*00b67f09SDavid van Moolenbroek return (name if name else str(alg)) 445*00b67f09SDavid van Moolenbroek 446*00b67f09SDavid van Moolenbroek############################################################################ 447*00b67f09SDavid van Moolenbroek# list_events: 448*00b67f09SDavid van Moolenbroek############################################################################ 449*00b67f09SDavid van Moolenbroekdef list_events(eventgroup): 450*00b67f09SDavid van Moolenbroek """print a list of the events in an eventgroup""" 451*00b67f09SDavid van Moolenbroek if not eventgroup: 452*00b67f09SDavid van Moolenbroek return 453*00b67f09SDavid van Moolenbroek print (" " + eventgroup[0].showtime() + ":") 454*00b67f09SDavid van Moolenbroek for event in eventgroup: 455*00b67f09SDavid van Moolenbroek print (" %s: %s (%s)" % 456*00b67f09SDavid van Moolenbroek (event.what, event.showkey(), event.showkeytype())) 457*00b67f09SDavid van Moolenbroek 458*00b67f09SDavid van Moolenbroek############################################################################ 459*00b67f09SDavid van Moolenbroek# process_events: 460*00b67f09SDavid van Moolenbroek############################################################################ 461*00b67f09SDavid van Moolenbroekdef process_events(eventgroup, active, published): 462*00b67f09SDavid van Moolenbroek """go through the events in an event group in time-order, add to active 463*00b67f09SDavid van Moolenbroek list upon Activate event, add to published list upon Publish event, 464*00b67f09SDavid van Moolenbroek remove from active list upon Inactive event, and remove from published 465*00b67f09SDavid van Moolenbroek upon Delete event. Emit warnings when inconsistant states are reached""" 466*00b67f09SDavid van Moolenbroek for event in eventgroup: 467*00b67f09SDavid van Moolenbroek if event.what == "Activate": 468*00b67f09SDavid van Moolenbroek active.add(event.keyid) 469*00b67f09SDavid van Moolenbroek elif event.what == "Publish": 470*00b67f09SDavid van Moolenbroek published.add(event.keyid) 471*00b67f09SDavid van Moolenbroek elif event.what == "Inactive": 472*00b67f09SDavid van Moolenbroek if event.keyid not in active: 473*00b67f09SDavid van Moolenbroek vspace() 474*00b67f09SDavid van Moolenbroek print ("\tWARNING: %s (%s) scheduled to become inactive " 475*00b67f09SDavid van Moolenbroek "before it is active" % 476*00b67f09SDavid van Moolenbroek (event.showkey(), event.showkeytype())) 477*00b67f09SDavid van Moolenbroek else: 478*00b67f09SDavid van Moolenbroek active.remove(event.keyid) 479*00b67f09SDavid van Moolenbroek elif event.what == "Delete": 480*00b67f09SDavid van Moolenbroek if event.keyid in published: 481*00b67f09SDavid van Moolenbroek published.remove(event.keyid) 482*00b67f09SDavid van Moolenbroek else: 483*00b67f09SDavid van Moolenbroek vspace() 484*00b67f09SDavid van Moolenbroek print ("WARNING: key %s (%s) is scheduled for deletion before " 485*00b67f09SDavid van Moolenbroek "it is published, at %s" % 486*00b67f09SDavid van Moolenbroek (event.showkey(), event.showkeytype())) 487*00b67f09SDavid van Moolenbroek elif event.what == "Revoke": 488*00b67f09SDavid van Moolenbroek # We don't need to worry about the logic of this one; 489*00b67f09SDavid van Moolenbroek # just stop counting this key as either active or published 490*00b67f09SDavid van Moolenbroek if event.keyid in published: 491*00b67f09SDavid van Moolenbroek published.remove(event.keyid) 492*00b67f09SDavid van Moolenbroek if event.keyid in active: 493*00b67f09SDavid van Moolenbroek active.remove(event.keyid) 494*00b67f09SDavid van Moolenbroek 495*00b67f09SDavid van Moolenbroek return (active, published) 496*00b67f09SDavid van Moolenbroek 497*00b67f09SDavid van Moolenbroek############################################################################ 498*00b67f09SDavid van Moolenbroek# check_events: 499*00b67f09SDavid van Moolenbroek############################################################################ 500*00b67f09SDavid van Moolenbroekdef check_events(eventsList, ksk): 501*00b67f09SDavid van Moolenbroek """create lists of events happening at the same time, check for 502*00b67f09SDavid van Moolenbroek inconsistancies""" 503*00b67f09SDavid van Moolenbroek active = set() 504*00b67f09SDavid van Moolenbroek published = set() 505*00b67f09SDavid van Moolenbroek eventgroups = list() 506*00b67f09SDavid van Moolenbroek eventgroup = list() 507*00b67f09SDavid van Moolenbroek keytype = ("KSK" if ksk else "ZSK") 508*00b67f09SDavid van Moolenbroek 509*00b67f09SDavid van Moolenbroek # collect up all events that have the same time 510*00b67f09SDavid van Moolenbroek eventsfound = False 511*00b67f09SDavid van Moolenbroek for event in eventsList: 512*00b67f09SDavid van Moolenbroek # if checking ZSKs, skip KSKs, and vice versa 513*00b67f09SDavid van Moolenbroek if (ksk and not event.sep) or (event.sep and not ksk): 514*00b67f09SDavid van Moolenbroek continue 515*00b67f09SDavid van Moolenbroek 516*00b67f09SDavid van Moolenbroek # we found an appropriate (ZSK or KSK event) 517*00b67f09SDavid van Moolenbroek eventsfound = True 518*00b67f09SDavid van Moolenbroek 519*00b67f09SDavid van Moolenbroek # add event to current eventgroup 520*00b67f09SDavid van Moolenbroek if (not eventgroup or eventgroup[0].when == event.when): 521*00b67f09SDavid van Moolenbroek eventgroup.append(event) 522*00b67f09SDavid van Moolenbroek 523*00b67f09SDavid van Moolenbroek # if we're at the end of the list, we're done. if 524*00b67f09SDavid van Moolenbroek # we've found an event with a later time, start a new 525*00b67f09SDavid van Moolenbroek # eventgroup 526*00b67f09SDavid van Moolenbroek if (eventgroup[0].when != event.when): 527*00b67f09SDavid van Moolenbroek eventgroups.append(eventgroup) 528*00b67f09SDavid van Moolenbroek eventgroup = list() 529*00b67f09SDavid van Moolenbroek eventgroup.append(event) 530*00b67f09SDavid van Moolenbroek 531*00b67f09SDavid van Moolenbroek if eventgroup: 532*00b67f09SDavid van Moolenbroek eventgroups.append(eventgroup) 533*00b67f09SDavid van Moolenbroek 534*00b67f09SDavid van Moolenbroek for eventgroup in eventgroups: 535*00b67f09SDavid van Moolenbroek if (args.checklimit and 536*00b67f09SDavid van Moolenbroek calendar.timegm(eventgroup[0].when) > args.checklimit): 537*00b67f09SDavid van Moolenbroek print("Ignoring events after %s" % 538*00b67f09SDavid van Moolenbroek time.strftime("%a %b %d %H:%M:%S UTC %Y", 539*00b67f09SDavid van Moolenbroek time.gmtime(args.checklimit))) 540*00b67f09SDavid van Moolenbroek return True 541*00b67f09SDavid van Moolenbroek 542*00b67f09SDavid van Moolenbroek (active, published) = \ 543*00b67f09SDavid van Moolenbroek process_events(eventgroup, active, published) 544*00b67f09SDavid van Moolenbroek 545*00b67f09SDavid van Moolenbroek list_events(eventgroup) 546*00b67f09SDavid van Moolenbroek 547*00b67f09SDavid van Moolenbroek # and then check for inconsistencies: 548*00b67f09SDavid van Moolenbroek if len(active) == 0: 549*00b67f09SDavid van Moolenbroek print ("ERROR: No %s's are active after this event" % keytype) 550*00b67f09SDavid van Moolenbroek return False 551*00b67f09SDavid van Moolenbroek elif len(published) == 0: 552*00b67f09SDavid van Moolenbroek sys.stdout.write("ERROR: ") 553*00b67f09SDavid van Moolenbroek print ("ERROR: No %s's are published after this event" % keytype) 554*00b67f09SDavid van Moolenbroek return False 555*00b67f09SDavid van Moolenbroek elif len(published.intersection(active)) == 0: 556*00b67f09SDavid van Moolenbroek sys.stdout.write("ERROR: ") 557*00b67f09SDavid van Moolenbroek print (("ERROR: No %s's are both active and published " + 558*00b67f09SDavid van Moolenbroek "after this event") % keytype) 559*00b67f09SDavid van Moolenbroek return False 560*00b67f09SDavid van Moolenbroek 561*00b67f09SDavid van Moolenbroek if not eventsfound: 562*00b67f09SDavid van Moolenbroek print ("ERROR: No %s events found in '%s'" % 563*00b67f09SDavid van Moolenbroek (keytype, args.path)) 564*00b67f09SDavid van Moolenbroek return False 565*00b67f09SDavid van Moolenbroek 566*00b67f09SDavid van Moolenbroek return True 567*00b67f09SDavid van Moolenbroek 568*00b67f09SDavid van Moolenbroek############################################################################ 569*00b67f09SDavid van Moolenbroek# check_zones: 570*00b67f09SDavid van Moolenbroek# ############################################################################ 571*00b67f09SDavid van Moolenbroekdef check_zones(eventsList): 572*00b67f09SDavid van Moolenbroek """scan events per zone, algorithm, and key type, in order of occurrance, 573*00b67f09SDavid van Moolenbroek noting inconsistent states when found""" 574*00b67f09SDavid van Moolenbroek global foundprob 575*00b67f09SDavid van Moolenbroek 576*00b67f09SDavid van Moolenbroek foundprob = False 577*00b67f09SDavid van Moolenbroek zonesfound = False 578*00b67f09SDavid van Moolenbroek for zone in eventsList: 579*00b67f09SDavid van Moolenbroek if args.zone and zone != args.zone: 580*00b67f09SDavid van Moolenbroek continue 581*00b67f09SDavid van Moolenbroek 582*00b67f09SDavid van Moolenbroek zonesfound = True 583*00b67f09SDavid van Moolenbroek for alg in eventsList[zone]: 584*00b67f09SDavid van Moolenbroek if not args.no_ksk: 585*00b67f09SDavid van Moolenbroek vspace() 586*00b67f09SDavid van Moolenbroek print("Checking scheduled KSK events for zone %s, algorithm %s..." % 587*00b67f09SDavid van Moolenbroek (zone, algname(alg))) 588*00b67f09SDavid van Moolenbroek if not check_events(eventsList[zone][alg], True): 589*00b67f09SDavid van Moolenbroek foundprob = True 590*00b67f09SDavid van Moolenbroek else: 591*00b67f09SDavid van Moolenbroek print ("No errors found") 592*00b67f09SDavid van Moolenbroek 593*00b67f09SDavid van Moolenbroek if not args.no_zsk: 594*00b67f09SDavid van Moolenbroek vspace() 595*00b67f09SDavid van Moolenbroek print("Checking scheduled ZSK events for zone %s, algorithm %s..." % 596*00b67f09SDavid van Moolenbroek (zone, algname(alg))) 597*00b67f09SDavid van Moolenbroek if not check_events(eventsList[zone][alg], False): 598*00b67f09SDavid van Moolenbroek foundprob = True 599*00b67f09SDavid van Moolenbroek else: 600*00b67f09SDavid van Moolenbroek print ("No errors found") 601*00b67f09SDavid van Moolenbroek 602*00b67f09SDavid van Moolenbroek if not zonesfound: 603*00b67f09SDavid van Moolenbroek print("ERROR: No key events found for %s in '%s'" % 604*00b67f09SDavid van Moolenbroek (args.zone, args.path)) 605*00b67f09SDavid van Moolenbroek exit(1) 606*00b67f09SDavid van Moolenbroek 607*00b67f09SDavid van Moolenbroek############################################################################ 608*00b67f09SDavid van Moolenbroek# fill_eventsList: 609*00b67f09SDavid van Moolenbroek############################################################################ 610*00b67f09SDavid van Moolenbroekdef fill_eventsList(eventsList): 611*00b67f09SDavid van Moolenbroek """populate the list of events""" 612*00b67f09SDavid van Moolenbroek for zone, algorithms in keyDict.items(): 613*00b67f09SDavid van Moolenbroek for alg, keys in algorithms.items(): 614*00b67f09SDavid van Moolenbroek for keyid, keydata in keys.items(): 615*00b67f09SDavid van Moolenbroek if("Publish" in keydata.metadata): 616*00b67f09SDavid van Moolenbroek eventsList[zone][alg].append(Event("Publish", keydata)) 617*00b67f09SDavid van Moolenbroek if("Activate" in keydata.metadata): 618*00b67f09SDavid van Moolenbroek eventsList[zone][alg].append(Event("Activate", keydata)) 619*00b67f09SDavid van Moolenbroek if("Inactive" in keydata.metadata): 620*00b67f09SDavid van Moolenbroek eventsList[zone][alg].append(Event("Inactive", keydata)) 621*00b67f09SDavid van Moolenbroek if("Delete" in keydata.metadata): 622*00b67f09SDavid van Moolenbroek eventsList[zone][alg].append(Event("Delete", keydata)) 623*00b67f09SDavid van Moolenbroek 624*00b67f09SDavid van Moolenbroek eventsList[zone][alg] = sorted(eventsList[zone][alg], 625*00b67f09SDavid van Moolenbroek key=lambda event: event.when) 626*00b67f09SDavid van Moolenbroek 627*00b67f09SDavid van Moolenbroek foundprob = False 628*00b67f09SDavid van Moolenbroek if not keyDict: 629*00b67f09SDavid van Moolenbroek print("ERROR: No key events found in '%s'" % args.path) 630*00b67f09SDavid van Moolenbroek exit(1) 631*00b67f09SDavid van Moolenbroek 632*00b67f09SDavid van Moolenbroek############################################################################ 633*00b67f09SDavid van Moolenbroek# set_path: 634*00b67f09SDavid van Moolenbroek############################################################################ 635*00b67f09SDavid van Moolenbroekdef set_path(command, default=None): 636*00b67f09SDavid van Moolenbroek """find the location of a specified command. if a default is supplied 637*00b67f09SDavid van Moolenbroek and it works, we use it; otherwise we search PATH for a match. If 638*00b67f09SDavid van Moolenbroek not found, error and exit""" 639*00b67f09SDavid van Moolenbroek fpath = default 640*00b67f09SDavid van Moolenbroek if not fpath or not os.path.isfile(fpath) or not os.access(fpath, os.X_OK): 641*00b67f09SDavid van Moolenbroek path = os.environ["PATH"] 642*00b67f09SDavid van Moolenbroek if not path: 643*00b67f09SDavid van Moolenbroek path = os.path.defpath 644*00b67f09SDavid van Moolenbroek for directory in path.split(os.pathsep): 645*00b67f09SDavid van Moolenbroek fpath = directory + os.sep + command 646*00b67f09SDavid van Moolenbroek if os.path.isfile(fpath) or os.access(fpath, os.X_OK): 647*00b67f09SDavid van Moolenbroek break 648*00b67f09SDavid van Moolenbroek fpath = None 649*00b67f09SDavid van Moolenbroek 650*00b67f09SDavid van Moolenbroek return fpath 651*00b67f09SDavid van Moolenbroek 652*00b67f09SDavid van Moolenbroek############################################################################ 653*00b67f09SDavid van Moolenbroek# parse_args: 654*00b67f09SDavid van Moolenbroek############################################################################ 655*00b67f09SDavid van Moolenbroekdef parse_args(): 656*00b67f09SDavid van Moolenbroek """Read command line arguments, set global 'args' structure""" 657*00b67f09SDavid van Moolenbroek global args 658*00b67f09SDavid van Moolenbroek compilezone = set_path('named-compilezone', 659*00b67f09SDavid van Moolenbroek os.path.join(prefix('bin'), 'named-compilezone')) 660*00b67f09SDavid van Moolenbroek 661*00b67f09SDavid van Moolenbroek parser = argparse.ArgumentParser(description=prog + ': checks future ' + 662*00b67f09SDavid van Moolenbroek 'DNSKEY coverage for a zone') 663*00b67f09SDavid van Moolenbroek 664*00b67f09SDavid van Moolenbroek parser.add_argument('zone', type=str, help='zone to check') 665*00b67f09SDavid van Moolenbroek parser.add_argument('-K', dest='path', default='.', type=str, 666*00b67f09SDavid van Moolenbroek help='a directory containing keys to process', 667*00b67f09SDavid van Moolenbroek metavar='dir') 668*00b67f09SDavid van Moolenbroek parser.add_argument('-f', dest='filename', type=str, 669*00b67f09SDavid van Moolenbroek help='zone master file', metavar='file') 670*00b67f09SDavid van Moolenbroek parser.add_argument('-m', dest='maxttl', type=str, 671*00b67f09SDavid van Moolenbroek help='the longest TTL in the zone(s)', 672*00b67f09SDavid van Moolenbroek metavar='time') 673*00b67f09SDavid van Moolenbroek parser.add_argument('-d', dest='keyttl', type=str, 674*00b67f09SDavid van Moolenbroek help='the DNSKEY TTL', metavar='time') 675*00b67f09SDavid van Moolenbroek parser.add_argument('-r', dest='resign', default='1944000', 676*00b67f09SDavid van Moolenbroek type=int, help='the RRSIG refresh interval ' 677*00b67f09SDavid van Moolenbroek 'in seconds [default: 22.5 days]', 678*00b67f09SDavid van Moolenbroek metavar='time') 679*00b67f09SDavid van Moolenbroek parser.add_argument('-c', dest='compilezone', 680*00b67f09SDavid van Moolenbroek default=compilezone, type=str, 681*00b67f09SDavid van Moolenbroek help='path to \'named-compilezone\'', 682*00b67f09SDavid van Moolenbroek metavar='path') 683*00b67f09SDavid van Moolenbroek parser.add_argument('-l', dest='checklimit', 684*00b67f09SDavid van Moolenbroek type=str, default='0', 685*00b67f09SDavid van Moolenbroek help='Length of time to check for ' 686*00b67f09SDavid van Moolenbroek 'DNSSEC coverage [default: 0 (unlimited)]', 687*00b67f09SDavid van Moolenbroek metavar='time') 688*00b67f09SDavid van Moolenbroek parser.add_argument('-z', dest='no_ksk', 689*00b67f09SDavid van Moolenbroek action='store_true', default=False, 690*00b67f09SDavid van Moolenbroek help='Only check zone-signing keys (ZSKs)') 691*00b67f09SDavid van Moolenbroek parser.add_argument('-k', dest='no_zsk', 692*00b67f09SDavid van Moolenbroek action='store_true', default=False, 693*00b67f09SDavid van Moolenbroek help='Only check key-signing keys (KSKs)') 694*00b67f09SDavid van Moolenbroek parser.add_argument('-D', '--debug', dest='debug_mode', 695*00b67f09SDavid van Moolenbroek action='store_true', default=False, 696*00b67f09SDavid van Moolenbroek help='Turn on debugging output') 697*00b67f09SDavid van Moolenbroek parser.add_argument('-v', '--version', action='version', version='9.9.1') 698*00b67f09SDavid van Moolenbroek 699*00b67f09SDavid van Moolenbroek args = parser.parse_args() 700*00b67f09SDavid van Moolenbroek 701*00b67f09SDavid van Moolenbroek if args.no_zsk and args.no_ksk: 702*00b67f09SDavid van Moolenbroek print("ERROR: -z and -k cannot be used together."); 703*00b67f09SDavid van Moolenbroek exit(1) 704*00b67f09SDavid van Moolenbroek 705*00b67f09SDavid van Moolenbroek # convert from time arguments to seconds 706*00b67f09SDavid van Moolenbroek try: 707*00b67f09SDavid van Moolenbroek if args.maxttl: 708*00b67f09SDavid van Moolenbroek m = parse_time(args.maxttl) 709*00b67f09SDavid van Moolenbroek args.maxttl = m 710*00b67f09SDavid van Moolenbroek except: 711*00b67f09SDavid van Moolenbroek pass 712*00b67f09SDavid van Moolenbroek 713*00b67f09SDavid van Moolenbroek try: 714*00b67f09SDavid van Moolenbroek if args.keyttl: 715*00b67f09SDavid van Moolenbroek k = parse_time(args.keyttl) 716*00b67f09SDavid van Moolenbroek args.keyttl = k 717*00b67f09SDavid van Moolenbroek except: 718*00b67f09SDavid van Moolenbroek pass 719*00b67f09SDavid van Moolenbroek 720*00b67f09SDavid van Moolenbroek try: 721*00b67f09SDavid van Moolenbroek if args.resign: 722*00b67f09SDavid van Moolenbroek r = parse_time(args.resign) 723*00b67f09SDavid van Moolenbroek args.resign = r 724*00b67f09SDavid van Moolenbroek except: 725*00b67f09SDavid van Moolenbroek pass 726*00b67f09SDavid van Moolenbroek 727*00b67f09SDavid van Moolenbroek try: 728*00b67f09SDavid van Moolenbroek if args.checklimit: 729*00b67f09SDavid van Moolenbroek lim = args.checklimit 730*00b67f09SDavid van Moolenbroek r = parse_time(args.checklimit) 731*00b67f09SDavid van Moolenbroek if r == 0: 732*00b67f09SDavid van Moolenbroek args.checklimit = None 733*00b67f09SDavid van Moolenbroek else: 734*00b67f09SDavid van Moolenbroek args.checklimit = time.time() + r 735*00b67f09SDavid van Moolenbroek except: 736*00b67f09SDavid van Moolenbroek pass 737*00b67f09SDavid van Moolenbroek 738*00b67f09SDavid van Moolenbroek # if we've got the values we need from the command line, stop now 739*00b67f09SDavid van Moolenbroek if args.maxttl and args.keyttl: 740*00b67f09SDavid van Moolenbroek return 741*00b67f09SDavid van Moolenbroek 742*00b67f09SDavid van Moolenbroek # load keyttl and maxttl data from zonefile 743*00b67f09SDavid van Moolenbroek if args.zone and args.filename: 744*00b67f09SDavid van Moolenbroek try: 745*00b67f09SDavid van Moolenbroek zone = Zone(args.zone) 746*00b67f09SDavid van Moolenbroek zone.load(args.filename) 747*00b67f09SDavid van Moolenbroek if not args.maxttl: 748*00b67f09SDavid van Moolenbroek args.maxttl = zone.maxttl 749*00b67f09SDavid van Moolenbroek if not args.keyttl: 750*00b67f09SDavid van Moolenbroek args.keyttl = zone.maxttl 751*00b67f09SDavid van Moolenbroek except Exception as e: 752*00b67f09SDavid van Moolenbroek print("Unable to load zone data from %s: " % args.filename, e) 753*00b67f09SDavid van Moolenbroek 754*00b67f09SDavid van Moolenbroek if not args.maxttl: 755*00b67f09SDavid van Moolenbroek vspace() 756*00b67f09SDavid van Moolenbroek print ("WARNING: Maximum TTL value was not specified. Using 1 week\n" 757*00b67f09SDavid van Moolenbroek "\t (604800 seconds); re-run with the -m option to get more\n" 758*00b67f09SDavid van Moolenbroek "\t accurate results.") 759*00b67f09SDavid van Moolenbroek args.maxttl = 604800 760*00b67f09SDavid van Moolenbroek 761*00b67f09SDavid van Moolenbroek############################################################################ 762*00b67f09SDavid van Moolenbroek# Main 763*00b67f09SDavid van Moolenbroek############################################################################ 764*00b67f09SDavid van Moolenbroekdef main(): 765*00b67f09SDavid van Moolenbroek global keyDict 766*00b67f09SDavid van Moolenbroek 767*00b67f09SDavid van Moolenbroek parse_args() 768*00b67f09SDavid van Moolenbroek path=args.path 769*00b67f09SDavid van Moolenbroek 770*00b67f09SDavid van Moolenbroek print ("PHASE 1--Loading keys to check for internal timing problems") 771*00b67f09SDavid van Moolenbroek keyDict = defaultdict(lambda : defaultdict(dict)) 772*00b67f09SDavid van Moolenbroek files = glob.glob(os.path.join(path, '*.private')) 773*00b67f09SDavid van Moolenbroek for infile in files: 774*00b67f09SDavid van Moolenbroek key = Key(infile) 775*00b67f09SDavid van Moolenbroek if args.zone and key.zone != args.zone: 776*00b67f09SDavid van Moolenbroek continue 777*00b67f09SDavid van Moolenbroek keyDict[key.zone][key.alg][key.keyid] = key 778*00b67f09SDavid van Moolenbroek key.check_prepub() 779*00b67f09SDavid van Moolenbroek if key.sep: 780*00b67f09SDavid van Moolenbroek key.check_postpub() 781*00b67f09SDavid van Moolenbroek else: 782*00b67f09SDavid van Moolenbroek key.check_postpub(args.maxttl + args.resign) 783*00b67f09SDavid van Moolenbroek 784*00b67f09SDavid van Moolenbroek vspace() 785*00b67f09SDavid van Moolenbroek print ("PHASE 2--Scanning future key events for coverage failures") 786*00b67f09SDavid van Moolenbroek vreset() 787*00b67f09SDavid van Moolenbroek 788*00b67f09SDavid van Moolenbroek eventsList = defaultdict(lambda : defaultdict(list)) 789*00b67f09SDavid van Moolenbroek fill_eventsList(eventsList) 790*00b67f09SDavid van Moolenbroek check_zones(eventsList) 791*00b67f09SDavid van Moolenbroek 792*00b67f09SDavid van Moolenbroek if foundprob: 793*00b67f09SDavid van Moolenbroek exit(1) 794*00b67f09SDavid van Moolenbroek else: 795*00b67f09SDavid van Moolenbroek exit(0) 796*00b67f09SDavid van Moolenbroek 797*00b67f09SDavid van Moolenbroekif __name__ == "__main__": 798*00b67f09SDavid van Moolenbroek main() 799