X-Git-Url: https://git.siccegge.de//index.cgi?p=tools.git;a=blobdiff_plain;f=dnssec-check;h=162d77c01d5cdcf25232d289a9c5409868445269;hp=5753be14f14d2979ae97d057de72353f2206bd7f;hb=HEAD;hpb=c0e20bfec90862d61b75687eb9ce8f1c85ea34c3 diff --git a/dnssec-check b/dnssec-check old mode 100644 new mode 100755 index 5753be1..162d77c --- a/dnssec-check +++ b/dnssec-check @@ -1,7 +1,10 @@ #!/usr/bin/python +from __future__ import print_function import ldns -import unbound +from unbound import ub_ctx, idn2dname, ub_strerror +from unbound import RR_TYPE_SOA, RR_TYPE_DNSKEY, RR_TYPE_RRSIG, RR_TYPE_NS +from unbound import RR_TYPE_A, RR_TYPE_AAAA from optparse import OptionParser import sys from datetime import datetime, timedelta @@ -18,20 +21,73 @@ def parse_rrsig_expire(expirestring): return delta def check_dnssec_expire(resolver, name, warn, crit): - s, result = resolver.resolve(name) + for rrtype in [RR_TYPE_SOA, RR_TYPE_DNSKEY]: + s, result = resolver.resolve(name, rrtype=rrtype) + if 0 != s: + ub_strerror(s) + return 3 + + if not result.secure: + print("CRIT (does not verify) %s" % (name, )) + return 2 + + s, packet = ldns.ldns_wire2pkt(result.packet) + rrsigs = packet.rr_list_by_type(RR_TYPE_RRSIG, ldns.LDNS_SECTION_ANSWER).rrs() + + for rrsig in rrsigs: + delta = parse_rrsig_expire(str(rrsig.rrsig_expiration())) + + if delta < crit: + print("CRIT (expires in %s) %s" % (delta, name)) + return 2 + elif delta < warn: + print("WARN (expires in %s) %s" % (delta, name)) + return 1 + return 0 + +def check_zone_synced(resolver, name): + s, result = resolver.resolve(name, RR_TYPE_NS) if 0 != s: - pass + ub_strerror(s) + return 3 - s, packet = ldns.ldns_wire2pkt(result.packet) - rrsigs = packet.rr_list_by_type(unbound.RR_TYPE_RRSIG, ldns.LDNS_SECTION_ANSWER).rrs() - for rrsig in rrsigs: - delta = parse_rrsig_expire(str(rrsig.rrsig_expiration())) + if not result.secure: + print("CRIT (does not verify) %s" % (name, )) + return 2 - if delta < crit: - print "CRIT (%s) %s" % (delta, name) - elif delta < warn: - print "WARN (%s) %s" % (delta, name) + nameservers = result.data.as_domain_list() + nsips = [] + for nameserver in nameservers: + for rrtype in [RR_TYPE_AAAA, RR_TYPE_A]: + s, result = resolver.resolve(nameserver, rrtype=rrtype) + if 0 != s: + ub_strerror(s) + return 3 + nsips = nsips + result.data.as_address_list() + results = dict() + for ip in nsips: + newres = ub_ctx() + newres.set_fwd(ip) + s, result = newres.resolve(name, rrtype=RR_TYPE_SOA) + if 0 != s: + ub_strerror(s) + return 3 + + s, result = ldns.ldns_wire2pkt(result.packet) + soas = list(result.rr_list_by_type(RR_TYPE_SOA, + ldns.LDNS_SECTION_ANSWER).rrs()) + + if len(soas) != 1: + return 3 + serial = str(soas[0]).split()[6] + results[serial] = results.get(serial, []) + [ip] + + if len(results) == 1: + return 0 + else: + print("CRIT (different SOAs): %s", results) + return 2 def main(): parser = OptionParser() @@ -48,14 +104,40 @@ def main(): parser.add_option("-c", "--critical-days", action="store", type=int, dest="crit", default=2, help="minimum remaining validity in days before a warning is issued") - + parser.add_option("-v", action="store_true", dest="verbose", default=False) + parser.add_option("-q", action="store_false", dest="verbose") opts, _args = parser.parse_args() - resolver = unbound.ub_ctx() + if not opts.names: + parser.error("needs at least one DNS name") + + resolver = ub_ctx() resolver.add_ta_file(opts.ancor) - - for name in opts.names: - check_dnssec_expire(resolver, name, timedelta(opts.warn), timedelta(opts.crit)) + encoding = sys.getfilesystemencoding() + + final = 0 + for name in opts.names: + name = idn2dname(name.decode(encoding)) + result1 = check_zone_synced(resolver, name) + if result1 == 2: + final = 2 + elif result1 == 1 and final != 2: + final = 1 + elif result1 == 3 and final not in [1, 2]: + final = 3 + + result2 = check_dnssec_expire(resolver, name, + timedelta(opts.warn), timedelta(opts.crit)) + if result1 + result2 == 0 and opts.verbose: + print("OK %s" % name) + if result2 == 2: + final = 2 + elif result2 == 1 and final != 2: + final = 1 + elif result2 == 3 and final not in [1, 2]: + final = 3 + + sys.exit(final) if __name__ == "__main__": main()