Skip to content

Instantly share code, notes, and snippets.

@kbabioch
Created February 14, 2019 21:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kbabioch/e6c18e899f647dd59d259bd3ca2ce2ca to your computer and use it in GitHub Desktop.
Save kbabioch/e6c18e899f647dd59d259bd3ca2ce2ca to your computer and use it in GitHub Desktop.
Checks NSEC3PARAM for a given zone
#! /usr/bin/env python3
#
# Copyright (c) 2019 Karol Babioch <karol@babioch.de>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import argparse
import datetime
import logging
import sqlite3
import subprocess
import sys
# Valid exit codes (Nagios / Icinga)
EXIT_OK = 0
EXIT_WARNING = 1
EXIT_CRITICAL = 2
EXIT_UNKNOWN = 3
# TODO Make this configurable
DB = './nsec3params.db'
# Setup argument parser
parser = argparse.ArgumentParser(description='Checks the NSEC3PARAM record for a zone')
parser.add_argument('zone', metavar='ZONE', help='Name of zone that NSEC3PARAM record should be checked', type=str)
parser.add_argument('-w', '--warning', dest='warning', help='Exit with warning if record has not changed for this amount of days (default: 0)', type=int)
parser.add_argument('-c', '--critical', dest='critical', help='Exit with critical if record has not changed for this amount of days (default: 0)', type=int)
parser.add_argument('-ns', '--nameserver', dest='nameserver', help='Specify a resolver explicitly (default: resolver used by system)', type=str)
parser.add_argument('-d', '--debug', dest='debug', help='Enable debugging output', action='store_true')
# Parse arguments
args = parser.parse_args()
# Setup logging
logger = logging.getLogger()
handler = logging.StreamHandler()
logger.addHandler(handler)
if args.debug:
logger.setLevel(logging.DEBUG)
# Construct command to execute
cmd = ['dig']
if args.nameserver:
cmd.extend(['@{}'.format(args.nameserver)])
cmd.extend(['+short', args.zone, 'NSEC3PARAM'])
# Run command
cmd = subprocess.run(cmd, capture_output=True, encoding=sys.getdefaultencoding())
# Some debug output
logger.debug('args: {}'.format(args))
logger.debug('cmd: {}'.format(cmd))
# Check if command was executed successfully
if cmd.returncode != 0:
print('Query for NSEC3PARAMS unsuccessful')
sys.exit(EXIT_UNKNOWN)
record = cmd.stdout.rstrip()
logger.debug('NSEC3PARAM: \'{}\''.format(record))
if not record:
print('No NSEC3PARAM record found for {}'.format(args.zone))
sys.exit(EXIT_CRITICAL)
try:
# Connect to database (using context manager)
with sqlite3.connect(DB, detect_types=sqlite3.PARSE_DECLTYPES) as con:
#con = sqlite3.connect(DB, detect_types=sqlite3.PARSE_DECLTYPES)
cur = con.cursor()
# Initialize database (not totally normalized to keep it simple)
sql = '''
CREATE TABLE IF NOT EXISTS nsec3params (
id INTEGER PRIMARY KEY,
name VARCHAR(255) NOT NULL,
record VARCHAR(255),
timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
'''
cur.execute(sql)
# Get last NSEC3PARAM record for this domain from database
cur.execute('SELECT record, timestamp FROM nsec3params WHERE name = ? ORDER BY timestamp DESC LIMIT 1;', (args.zone,))
result = cur.fetchone()
# Exit code in case nothing goes wrong
exitcode = EXIT_OK
# There is (at least) one NSEC3PARAM for this zone in the database
if result:
(lastrecord, lasttimestamp) = result
logger.debug('Last NSEC3PARAM entry for {} in database: \'{}\''.format(args.zone, lastrecord))
# Check if record has changed since last invocation
if lastrecord != record:
logger.debug('Last NSEC3PARAM differs from current one, inserting current one into database')
cur.execute('INSERT INTO nsec3params (name, record) VALUES (?, ?)', (args.zone, record))
# Record remained the same
else:
logger.debug('Last NSEC3PARAM has not changed since: {}'.format(lasttimestamp))
# Check whether warning threshold has been reached
if args.warning and (lasttimestamp + datetime.timedelta(args.warning) < datetime.datetime.now()):
logger.debug('Warning threshold of {} days reached'.format(args.warning))
exitcode = EXIT_WARNING
# Check whether critical threshold has been reached
if args.critical and (lasttimestamp + datetime.timedelta(args.critical) < datetime.datetime.now()):
logger.debug('Critical threshold of {} days reached'.format(args.critical))
exitcode = EXIT_CRITICAL
# No NSEC3PARAM record for this zone yet
else:
logger.debug('Found no NSEC3PARAM entry for {} in database, inserting current one'.format(args.zone))
cur.execute('INSERT INTO nsec3params (name, record) VALUES (?, ?)', (args.zone, record))
# Catch any error (most likely database related)
except Exception as e:
logger.debug('Exception: {}'.format(e))
exitcode = EXIT_UNKNOWN
# Quit with the correct exit code
logger.debug('Exit code: {}'.format(exitcode))
sys.exit(exitcode)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment