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

set -o posix
set -o errexit
set -o pipefail
set -o nounset
#set -o xtrace

#$title$ RFC4255: Using DNS to Securely Publish Secure Shell (SSH) Key Fingerprints
#$check$ valid fingerprint in DNS for the SSH server running on this host
#$ref$ KB: DomainNameSystemSecurityExtensions
#$author$ Rafal Rzeczkowski
#$version$ 0.7.1

level_check long

#CHANGELOG
#0.50	initial
#0.51	escape whitespace properly in awk comparison
#0.52	tie SSHFP requirement to the presence of AAAA record
#0.53	distinguish between the absence of any or AAAA DNS records
#0.54	display data to be added to DNS, not the command needed to generate it
#0.55	more exact temporary file cleanup
#0.56	consider only the first hash type printed by ssh-keygen
#0.57	added descriptive metadata header
#0.58	display SSHFP data only in debug mode
#0.60	support for multiple records
#0.61	exit and cleanup TMP files directly without use of delay flags
#0.62	restyle according to https://kb.clearcable.ca/KB/ProgrammingStyleStandards
#0.63	convert "host" output to lowercase as a workaround for bug in host 9.11
#0.64	downgrade failure report level to caution
#0.7.0	decouple SSHFP check from AAAA presence
#0.7.1	process CNAME records for HOSTNAME

if ! host -t AAAA $HOSTNAME. >/dev/null 2>&1; then
	unknown 'this node has no DNS record at all - will not check SSHFP'
	exit
fi

TMP_DNS_SSHFP=$(mktemp)
if ! host -t SSHFP $HOSTNAME. > $TMP_DNS_SSHFP 2>/dev/null; then
	fail warning "SSHFP record for $HOSTNAME was not found in DNS (query failed)"
	helpmsg 'verify that DNS is operating correctly'
	rm $TMP_DNS_SSHFP
	exit
elif ! [[ -s $TMP_DNS_SSHFP ]]; then
	fail warning 'SSHFP record was empty'
	helpmsg 'unusual DNS query failure'
	rm $TMP_DNS_SSHFP
	exit
else
	# remote data
	IFS=$'\n'
	DNS_RECORDs=($(awk --assign HOSTNAME=$HOSTNAME '
	{
	if (/^;;/)
		next
	else if (match($0,"^" HOSTNAME " is an alias for ([^[:space:]]+)[.]$",alias))
		HOSTNAME=alias[1]
	else if (tolower($1)==HOSTNAME && $2" "$3" "$4=="has SSHFP record")
		print $5" "$6" "tolower($7) tolower($8)
	}' $TMP_DNS_SSHFP))
	echodebug "DNS has ${#DNS_RECORDs[*]} SSHFP records for $HOSTNAME"
	unset IFS
	rm $TMP_DNS_SSHFP
fi

# local data
KEYGEN_CMD="ssh-keygen -r $HOSTNAME."
TMP_SSH_KEYGEN=$(mktemp)
$KEYGEN_CMD > $TMP_SSH_KEYGEN
IFS=$'\n'
SSHD_RECORDs=($(awk --assign HOSTNAME=$HOSTNAME '{if($1==HOSTNAME"."){print $4" "$5" "$6}}' $TMP_SSH_KEYGEN))
unset IFS
rm $TMP_SSH_KEYGEN

if [[ ${#DNS_RECORDs[*]} -eq 0 ]]; then
	fail caution "no SSHFP record(s) for $HOSTNAME were found in DNS"
	helpmsg "add the output of {$KEYGEN_CMD} command to the appropriate DNS zone"
	exit
fi

IFS=$'\n'
for SSHD_RECORD in ${SSHD_RECORDs[*]}; do
	if is_element_of $SSHD_RECORD ${DNS_RECORDs[*]}; then
		echodebug "SSHFP $SSHD_RECORD"
	else
		fail caution "SSHFP record {$SSHD_RECORD} is missing"
		helpmsg 'determine why the record was changed and correct it in DNS, or restore the previous SSH key'
		exit
	fi
done
ok
