Skip to content

Instantly share code, notes, and snippets.

@kbabioch
Created February 9, 2019 16:31
Show Gist options
  • Save kbabioch/db1e3d88c435d35ad71289b46ee14e65 to your computer and use it in GitHub Desktop.
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
#! /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