From fc54993c9389062a792e19f18ee082121aeffa50 Mon Sep 17 00:00:00 2001 From: Petr Menšík Date: Mar 19 2024 12:02:59 +0000 Subject: 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. --- diff --git a/Sanity/caching-forwarder-dnssec/main.fmf b/Sanity/caching-forwarder-dnssec/main.fmf new file mode 100644 index 0000000..d84d444 --- /dev/null +++ b/Sanity/caching-forwarder-dnssec/main.fmf @@ -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 diff --git a/Sanity/caching-forwarder-dnssec/test.sh b/Sanity/caching-forwarder-dnssec/test.sh new file mode 100755 index 0000000..0db5e87 --- /dev/null +++ b/Sanity/caching-forwarder-dnssec/test.sh @@ -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