Created
February 9, 2019 16:31
-
-
Save kbabioch/db1e3d88c435d35ad71289b46ee14e65 to your computer and use it in GitHub Desktop.
Checks for consistency of SOA records in zone belonging to a given name on all authoritative nameservers
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env python3 | |
import argparse | |
import logging | |
import re | |
import subprocess | |
import sys | |
# Exit codes | |
EXIT_OK = 0 | |
EXIT_WARNING = 1 | |
EXIT_CRITICAL = 2 | |
EXIT_UNKNOWN = 3 | |
# Regular expression matching SOA records (in dig output) | |
RE_SOA = re.compile('^SOA') | |
# Setup argument parser | |
parser = argparse.ArgumentParser(description='Checks for consistency of SOA records in zone belonging to a given name on all authoritative nameservers') | |
parser.add_argument('name', metavar='NAME', help='Name that should be checked', type=str) | |
parser.add_argument('-v', '--verbose', dest='verbosity', help='Increase verbosity', action='store_true') | |
# -4 and -6 are mutually exclusive, so create a new group for them | |
inetparser = parser.add_mutually_exclusive_group() | |
inetparser.add_argument('-4', dest='ipv4', help='Use IPv4 only', action='store_true') | |
inetparser.add_argument('-6', dest='ipv6', help='Use IPv6 only', action='store_true') | |
# Parse arguments | |
args = parser.parse_args() | |
# Setup logging | |
logger = logging.getLogger() | |
handler = logging.StreamHandler() | |
logger.addHandler(handler) | |
if args.verbosity: | |
logger.setLevel(logging.DEBUG) | |
# Construct command to execute | |
cmd = ['dig', '+nssearch'] | |
if args.ipv4: | |
cmd.append('-4') | |
if args.ipv6: | |
cmd.append('-6') | |
cmd.append(args.name) | |
cmd = subprocess.run(cmd, capture_output=True, encoding=sys.getdefaultencoding()) | |
logger.debug('dig returned with exit code: {}'.format(cmd.returncode)) | |
# Holds the SOA record from previous line, so it can be compared against current line | |
prev_soa = [] | |
# True when there is a mismatch | |
mismatch = False | |
# Iterate over all lines from dig output | |
for line in cmd.stdout.splitlines(): | |
# Example output of dig command | |
# SOA ns1.google.com. dns-admin.google.com. 233200846 900 900 1800 60 from server 216.239.32.10 in 42 ms. | |
# SOA ns1.google.com. dns-admin.google.com. 233200846 900 900 1800 60 from server 216.239.38.10 in 48 ms. | |
# SOA ns1.google.com. dns-admin.google.com. 233200846 900 900 1800 60 from server 216.239.36.10 in 51 ms. | |
# SOA ns1.google.com. dns-admin.google.com. 233200846 900 900 1800 60 from server 216.239.34.10 in 54 ms. | |
# Output all lines (including comments and errors, etc.) | |
print(line) | |
# Skip further processing on all lines not containing a valid SOA record (e.g. comments, errors, etc.) | |
if not RE_SOA.match(line): | |
logger.debug('Line does not contain a valid SOA record, skipping') | |
continue | |
# Ignore last elements of SOA lines (e.g. ping time, etc.) | |
fields = line.split(' ') | |
soa = fields[:8] | |
ns = fields[10] | |
# Compare SOA record against previous SOA record | |
if prev_soa != [] and prev_soa != soa: | |
# Output error message (similar to comments output by dig) | |
logger.debug('Mismatching SOA record served by {}, {} vs. {}'.format(ns, prev_soa, soa)) | |
print(';; SOA mismatch') | |
# Set mismatch flag to true (for further processing) | |
mismatch = True | |
# Previous line matched this one | |
else: | |
# Store SOA record for next iteration | |
prev_soa = soa | |
# Check return code (e.g. not all nameservers could be reached, etc.) | |
if cmd.returncode != 0: | |
sys.exit(EXIT_CRITICAL) | |
# Check whether there was a mismatch in any SOA record | |
if mismatch: | |
sys.exit(EXIT_WARNING) | |
# Indiciate success by default | |
sys.exit(EXIT_OK) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment