#!/bin/bash
# vim: cindent:shiftwidth=4:tabstop=4:smarttab:textwidth=100

#$title$ Domain Name System Security Extensions
#$check$ synchronization status of root KSKs, basic local resolver validation
#$ref$ https://www.icann.org/resources/pages/ksk-rollover
#$author$ Rafal Rzeczkowski
#$version$ 0.53

level_check long

#CHANGELOG
#0.50	initial
#0.51	use IPv6 for queries whenever not explicitly disabled
#0.52	verify that the local key database exists
#0.53	clarify help message text - remove journal file as well

KEY_DB='/var/cache/bind/managed-keys.bind'

DISABLE_IPV6=$(sysctl -n net.ipv6.conf.all.disable_ipv6)
if [[ $DISABLE_IPV6 -gt 0 ]]; then
	LOCALHOST='localhost'
else
	LOCALHOST='ip6-localhost'
fi
echodebug "selected $LOCALHOST as the query target"

KSK_ROOT=($(
dig +multiline +recurse DNSKEY . @$LOCALHOST | awk '
{
if ($1~"^;|^$")
	next
else if ($1==".")
	flag=$5
else if ($1==")")
	if (flag==257)
		print $(NF)
	}'|sort --numeric-sort))
echodebug "KSK set from root servers: ${KSK_ROOT[*]}"

if [[ ! -s "$KEY_DB" ]]; then
	fail warning "key database $KEY_DB is missing"
	helpmsg 'wait a few minutes until BIND regenerates the file'
	exit
fi

KSK_FILE=($(
awk '
{
if ($1=="KEYDATA")
	valid=1
else if ($1=="dlv.isc.org" && $2="KEYDATA")
	valid=0
else if ($1==")")
	if (valid)
		print $(NF)
	}' $KEY_DB|sort --numeric-sort))
if [[ ${#KSK_FILE[*]} -eq 0 ]]; then
	fail warning "unable to fine any KSKs in $KEY_DB"
	helpmsg 'plugin API/parsing error?'
	exit
fi
echodebug "KSK set from local cache file: ${KSK_FILE[*]}"

if [[ "${KSK_ROOT[*]}" = "${KSK_FILE[*]}" ]]; then
	echodebug 'KSK sets match (OK)'
else
	fail warning "KSK set mismatch: found {${KSK_FILE[*]}} in managed-keys, expected {${KSK_ROOT[*]}}"
	helpmsg "sudo rm $KEY_DB* && sudo service bind9 restart"
	exit
fi

# https://www.dnssec-tools.org/testzone/
RECORD_BAD='dnssec-failed.org.'
RECORD_GOOD='org.'
RECORD_TYPE='SOA'

RESULT_BAD=$(dig +short $RECORD_BAD $RECORD_TYPE @$LOCALHOST)
if [[ -z "$RESULT_BAD" ]]; then
	echodebug "recursive $RECORD_TYPE query for {$RECORD_BAD} returns an empty result (OK)"
else
	fail warning "recursive $RECORD_TYPE query for {$RECORD_BAD} returns {$RESULT_BAD}"
	helpmsg 'DNSSEC validation fails to detect an invalid record'
	exit
fi

RESULT_GOOD=$(dig +short $RECORD_GOOD $RECORD_TYPE @$LOCALHOST)
if [[ -n "$RESULT_GOOD" ]]; then
	echodebug "recursive $RECORD_TYPE query for {$RECORD_GOOD} returns {$RESULT_GOOD}"
else
	fail warning "recursive $RECORD_TYPE query for {$RECORD_GOOD} returns an empty result"
	helpmsg 'DNSSEC validation fails on a valid record'
	exit
fi

ok
