Forwarder with DNSSEC enabled test

Basic sanity test configuring forwarder, with enabled dnssec. Check that
known signed domains are indeed signed and marked as such. Check that
few unsigned zones are also marked as unsigned.

Checks also trust anchor is trusted and automatically installed.
This commit is contained in:
Petr Menšík 2024-03-18 11:14:44 +01:00
commit fc54993c93
2 changed files with 222 additions and 0 deletions

View file

@ -0,0 +1,11 @@
summary: Configure bind as caching and validating forwarder
test: ./test.sh
framework: beakerlib
description: |
Configures named as a caching forwarder. Keep dnssec-validation enabled and try to
workaround for buggy nameservers provided from the network.
require+:
- bind
recommend+:
- bind-utils
- sed

View file

@ -0,0 +1,211 @@
#!/bin/bash
# vim: dict+=/usr/share/beakerlib/dictionary.vim cpt=.,w,b,u,t,i,k
. /usr/share/beakerlib/beakerlib.sh || exit 1
get_servers_conf() {
local RESOLV_CONF=${1:-/etc/resolv.conf}
# contains extra space at the end!
awk '$1 == "nameserver" { printf "%s ", $2 }' "$RESOLV_CONF"
}
get_servers() {
# avoids systemd-resolved breaking dnssec
local -a CONF_FILES=()
systemctl is-active --quiet NetworkManager && CONF_FILES+=("/run/NetworkManager/no-stub-resolv.conf")
systemctl is-active --quiet systemd-resolved && CONF_FILES+=("/run/systemd/resolve/resolv.conf")
CONF_FILES+=(/etc/resolv.conf)
for CONF in "${CONF_FILES[@]}"
do
# intentionally do not prefer local resolv.conf, because systemd-resolved is breaking it often
if [ -r "$CONF" ]; then
SERVERS=$(get_servers_conf "$CONF")
if [ -n "$SERVERS" ]; then
echo "$SERVERS"
break
fi
fi
done
}
dnssec_servers() {
local SERVERS="$(get_servers)"
if [ -z "$SERVERS" ]; then
rlFail "No nameservers obtained!"
return 1
fi
local DELV=$(type -p delv 2>/dev/null)
local DIG=$(type -p dig 2>/dev/null)
local FAILED_SERVERS=""
local SECURE_SERVERS=""
for NS in ${SERVERS}; do
if [ -n "$DELV" ]; then
if $DELV @$NS | grep -q '^; fully validated'; then
SECURE_SERVERS+="$NS "
else
FAILED_SERVERS+="$NS "
fi
elif [ -n "$DIG" ]; then
if $DIG +noall +answer +dnssec @$NS | grep -qw RRSIG; then
SECURE_SERVERS+="$NS "
else
FAILED_SERVERS+="$NS "
fi
fi
done
if [ -z "$SECURE_SERVERS" ]; then
rlFail "No servers from ${SERVERS} support DNSSEC! Fix the infrastructure!"
return 1
fi
echo "$SECURE_SERVERS"
if [ -n "$FAILED_SERVERS" ]; then
rlLogWarning "Servers not supporting DNSSEC: ${FAILED_SERVERS}"
fi
}
make_forwarders()
{
echo 'forwarders {';
for NS in ${@}
do
printf "\t%s;\n" $NS
done
echo '}; # autogenerated'
}
# Prints formatted used options in bind config
print_options()
{
named-checkconf -px "$@" | sed -ne '/^options {/,/^};/ p'
}
# Check whether option in $1 is used in options {} global block
has_option()
{
local OPTION="$1"
print_options | grep -qw "^\s*${OPTION}"
}
# Filter dig to print only desired section
# Input is dig output
buDigGetSection()
{
local SECTION="${1:-ANSWER}"
sed -ne "/^;; ${SECTION} SECTION:/,/^$/ p" | grep -vE '^(\s*$|;.*$)'
}
# Filter dig to print only desired value from double comment lines
# Input is dig output
buDigGetField()
{
local FIELD="$1"
grep "^;;.*\s${FIELD}:" | sed -e "s/.*\s${FIELD}:\s*\([^;,]*\)\([;,].*\|$\)/\1/"
}
buDigPseudosection()
{
sed -ne "/^;; OPT PSEUDOSECTION:/,/^;; QUESTION SECTION/ p" | grep -vE '^;; (OPT PSEUDO|QUESTION )SECTION:'
}
# Filter dig to print only desired value from single comment lines
# Useful for pseudosection
# Input is dig output
buDigGetField1()
{
local FIELD="$1"
grep "^;\s\(.*\s\)\?${FIELD}:" | sed -e "s/.*\s${FIELD}:\s*\([^;,]*\)\([;,].*\|$\)/\1/"
}
buDigSuccess()
{
rlRun -s "dig $*"
local STATUS="$(buDigGetField status < $rlRun_LOG)"
rlAssertEquals "Check result was positive" "$STATUS" NOERROR
}
# Ensure reply is signed and verified
buDigSuccessSecure()
{
rlRun -s "dig $*"
local STATUS="$(buDigGetField status < $rlRun_LOG)"
rlAssertEquals "Check dig result was positive" "$STATUS" NOERROR
local FLAGS="$(buDigGetField flags < $rlRun_LOG)"
rlRun "echo $FLAGS | grep -w ad" 0 "Check dig result has AD bit set"
}
# Ensure reply is positive but insecure
buDigSuccessInsecure()
{
rlRun -s "dig $*"
local STATUS="$(buDigGetField status < $rlRun_LOG)"
rlAssertEquals "Check dig result was positive" "$STATUS" NOERROR
local FLAGS="$(buDigGetField flags < $rlRun_LOG)"
rlRun "echo $FLAGS | grep -vw ad" 0 "Check dig result has AD bit unset"
}
# Extract KSK key id from dig
buDigKskId()
{
dig +nocrypto +short -t dnskey "$@" | awk '$1 == 257 { sub("]", "", $7); print $7 }'
}
rlJournalStart
rlPhaseStartSetup
rlRun "tmp=\$(mktemp -d)" 0 "Create tmp directory"
rlRun "pushd $tmp"
rlRun "set -o pipefail"
rlRun "SECURE_SERVERS=\"$(dnssec_servers)\"" || rlFail "No secure servers obtained"
rlFileBackup --missing-ok /etc/named/forwarders.conf
rlFileBackup /etc/named.conf
rlRun "make_forwarders ${SECURE_SERVERS} > /etc/named/forwarders.conf" 0
if ! has_option forwarders; then
rlLog "Inserting include to generated forwarders"
rlRun "sed -i -e '/^s*options\s*{/ a include \"/etc/named/forwarders.conf\";' /etc/named.conf"
fi
rlRun "named-checkconf" 0 "Test generated configuration is acccepted"
rlRun "rlServiceStop named"
rlFileBackup --missing-ok /var/named/dynamic/managed-keys.bind{,.jnl}
rlRun "rm -f /var/named/dynamic/managed-keys.bind{,.jnl}"
rlRun "rlServiceStart named"
rlPhaseEnd
rlPhaseStartTest "Basic test"
buDigSuccessSecure @localhost . DNSKEY
buDigSuccessSecure @localhost
KEYID=$(buDigKskId @localhost .)
rlRun "rndc secroots"
rlRun "grep \"^./RSASHA256/$KEYID\" /var/named/data/named.secroots" 0 "Check trust anchor is trusted"
rlPhaseEnd
rlPhaseStartTest "Host tests"
for H in example.{org,com,net} fedoraproject.org isc.org
do
buDigSuccessSecure @localhost $H A
buDigSuccessSecure @localhost $H AAAA
done
for H in {org,com,net}
do
buDigSuccessSecure @localhost $H NS
buDigSuccessSecure @localhost $H DS
done
for H in {a,d,f}.root-servers.net
do
buDigSuccessInsecure @localhost $H A
buDigSuccessInsecure @localhost $H AAAA
done
[ "$DEBUG" = y ] && PS1="test-debug $PS1" $SHELL -i
rlPhaseEnd
rlPhaseStartCleanup
rlRun "popd"
rlRun "rm -f /etc/named/forwarders.conf"
rlRun "rm -r $tmp" 0 "Remove tmp directory"
rlFileRestore
rlRun "rlServiceRestore named"
rlPhaseEnd
rlJournalEnd