dnssec_check: DNSSEC-Status-Pruefung fuer Bash und PowerShell

Neue Skripte dnssec_check (Bash, dig) und dnssec_check.ps1 (PowerShell,
Resolve-DnsName): pruefen fuer hinterlegbare Domains den DNSSEC-Status
(aktiv/inaktiv/fail) inkl. Resolver-Selbsttest. README erweitert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
maierch
2026-07-10 19:13:36 +02:00
parent a2482df6af
commit 64138831a8
3 changed files with 499 additions and 0 deletions
Executable
+228
View File
@@ -0,0 +1,228 @@
#!/usr/bin/env bash
#
# dnssec_check - prüft für hinterlegte Domains, ob DNSSEC aktiviert und
# gültig signiert ist.
#
# Zielsystem : Ubuntu 22.04
# Benötigt : dig (Paket: bind9-dnsutils bzw. dnsutils)
# Installation: sudo apt install bind9-dnsutils
#
# Status-Bedeutung:
# aktiv - Domain ist DNSSEC-signiert UND wurde erfolgreich validiert
# (Antwort NOERROR mit gesetztem AD-Flag = Authenticated Data)
# inaktiv - Domain ist NICHT signiert (kein DNSSEC vorhanden)
# (Antwort NOERROR, aber KEIN AD-Flag)
# fail - DNSSEC vorhanden, aber Validierung schlägt fehl / Fehler
# (SERVFAIL vom validierenden Resolver o. ä.)
#
# Wichtig: Das Skript fragt einen *validierenden* Resolver ab (Default der
# lokale BIND auf 127.0.0.1). Ohne validierenden Resolver ist die Prüfung
# nicht aussagekräftig.
#
# Aufruf:
# ./dnssec_check # nutzt die unten hinterlegte Liste
# ./dnssec_check -r 192.168.1.1 # anderer Resolver
# ./dnssec_check example.com # nur diese Domain(s) prüfen
#
set -u
# ------------------------------------------------------------------
# Konfiguration
# ------------------------------------------------------------------
# Validierender Resolver (z. B. der lokale BIND). Per -r überschreibbar.
RESOLVER="127.0.0.1"
# >>> Hier die zu prüfenden Domains eintragen <<<
DOMAINS=(
"isc.org" # signiert -> erwartet: aktiv
"dnssec-failed.org" # kaputt -> erwartet: fail
"maier3000.de" # signiert -> erwartet: aktiv
"maierch.de" # signiert -> erwartet: aktiv
"maierchr.de" # signiert -> erwartet: aktiv
"citeq.de" # unsigniert-> erwartet: inaktiv
)
# Abfragetyp und dig-Optionen (Timeout/Versuche)
QTYPE="A"
DIG_OPTS=(+time=3 +tries=2)
# ------------------------------------------------------------------
# Ab hier nichts mehr ändern
# ------------------------------------------------------------------
usage() {
cat >&2 <<EOF
Aufruf: $0 [-r resolver] [-t qtype] [-s|--self-check] [domain ...]
-r resolver DNS-Resolver, der geprüft wird (Default: $RESOLVER)
-t qtype Abfragetyp (Default: $QTYPE)
-s, --self-check nur prüfen, ob der Resolver DNSSEC validiert (keine Domainliste)
EOF
exit 1
}
SELF_ONLY=0
# Lange Option --self-check auf -s abbilden
# (Guard fuer leere Argumentliste wegen bash 3.2 unter macOS + 'set -u')
if [[ $# -gt 0 ]]; then
args=()
for a in "$@"; do
case "$a" in
--self-check) args+=("-s") ;;
*) args+=("$a") ;;
esac
done
set -- "${args[@]}"
fi
while getopts ":r:t:sh" opt; do
case "$opt" in
r) RESOLVER="$OPTARG" ;;
t) QTYPE="$OPTARG" ;;
s) SELF_ONLY=1 ;;
h) usage ;;
*) usage ;;
esac
done
shift $((OPTIND - 1))
# Domains von der Kommandozeile überschreiben die Liste
if [[ $# -gt 0 ]]; then
DOMAINS=("$@")
fi
# dig vorhanden?
if ! command -v dig >/dev/null 2>&1; then
echo "Fehler: 'dig' nicht gefunden. Installieren mit: sudo apt install bind9-dnsutils" >&2
exit 2
fi
# Farben nur, wenn Ausgabe ins Terminal geht
if [[ -t 1 ]]; then
C_GREEN=$'\033[32m'; C_YELLOW=$'\033[33m'; C_RED=$'\033[31m'; C_RST=$'\033[0m'
else
C_GREEN=""; C_YELLOW=""; C_RED=""; C_RST=""
fi
# Feld aus einer dig-Ausgabe ziehen ------------------------------------
get_status() { sed -n 's/.*status: \([A-Z]*\).*/\1/p' | head -n1; }
get_flags() { sed -n 's/.*flags: \([a-z ]*\);.*/\1/p' | head -n1; }
# Kernprüfung: liefert aktiv | inaktiv | fail -------------------------
check_domain() {
local domain="$1" out status flags cdstatus
out=$(dig "@$RESOLVER" "${DIG_OPTS[@]}" +dnssec "$domain" "$QTYPE" 2>/dev/null)
if [[ -z "$out" ]]; then
echo "fail"; return
fi
status=$(printf '%s\n' "$out" | get_status)
flags=$(printf '%s\n' "$out" | get_flags)
case "$status" in
NOERROR|NXDOMAIN)
# AD-Flag = vom Resolver erfolgreich validiert (= signiert)
if [[ " $flags " == *" ad "* ]]; then
echo "aktiv"
else
echo "inaktiv"
fi
;;
SERVFAIL)
# Gegenprobe mit +cd (checking disabled): kommt dann eine
# gültige Antwort, lag der SERVFAIL an der DNSSEC-Validierung.
cdstatus=$(dig "@$RESOLVER" "${DIG_OPTS[@]}" +cd "$domain" "$QTYPE" 2>/dev/null | get_status)
if [[ "$cdstatus" == "NOERROR" || "$cdstatus" == "NXDOMAIN" ]]; then
echo "fail" # DNSSEC-Signatur/Kette ungültig
else
echo "fail" # sonstiger Auflösungsfehler
fi
;;
*)
echo "fail"
;;
esac
}
# Selbsttest: validiert der Resolver überhaupt? ----------------------
# Nutzt zwei Referenzdomains:
# - eine absichtlich kaputt signierte (muss SERVFAIL liefern)
# - eine korrekt signierte (muss AD-Flag liefern)
# Rückgabe: 0 = validiert, 1 = validiert nicht, 2 = unklar/Netzfehler
SELF_BAD_DOMAIN="dnssec-failed.org" # bewusst falsch signiert (Verisign)
SELF_GOOD_DOMAIN="isc.org" # korrekt signiert
self_check() {
local bad_status cd_status good good_status good_flags
bad_status=$(dig "@$RESOLVER" "${DIG_OPTS[@]}" +dnssec "$SELF_BAD_DOMAIN" A 2>/dev/null | get_status)
cd_status=$( dig "@$RESOLVER" "${DIG_OPTS[@]}" +cd "$SELF_BAD_DOMAIN" A 2>/dev/null | get_status)
good=$( dig "@$RESOLVER" "${DIG_OPTS[@]}" +dnssec "$SELF_GOOD_DOMAIN" A 2>/dev/null)
good_status=$(printf '%s\n' "$good" | get_status)
good_flags=$( printf '%s\n' "$good" | get_flags)
# Netz-/Erreichbarkeitsproblem?
if [[ -z "$bad_status" || -z "$good_status" ]]; then
printf 'Resolver-Selbsttest: %sUNKLAR%s (Resolver %s nicht erreichbar?)\n' \
"$C_YELLOW" "$C_RST" "$RESOLVER"
return 2
fi
# Kriterien fuer "validiert":
# kaputte Domain -> SERVFAIL, mit +cd aber aufloesbar
# gute Domain -> NOERROR mit AD-Flag
if [[ "$bad_status" == "SERVFAIL" && ( "$cd_status" == "NOERROR" || "$cd_status" == "NXDOMAIN" ) \
&& "$good_status" == "NOERROR" && " $good_flags " == *" ad "* ]]; then
printf 'Resolver-Selbsttest: %sVALIDIERT DNSSEC%s (%s=SERVFAIL, %s=AD)\n' \
"$C_GREEN" "$C_RST" "$SELF_BAD_DOMAIN" "$SELF_GOOD_DOMAIN"
return 0
else
printf 'Resolver-Selbsttest: %sKEINE VALIDIERUNG%s (%s=%s, +cd=%s, %s=%s flags:%s)\n' \
"$C_RED" "$C_RST" \
"$SELF_BAD_DOMAIN" "$bad_status" "$cd_status" \
"$SELF_GOOD_DOMAIN" "$good_status" "$good_flags"
return 1
fi
}
# ------------------------------------------------------------------
# Ausgabe
# ------------------------------------------------------------------
# Nur Selbsttest gewuenscht?
if [[ "$SELF_ONLY" -eq 1 ]]; then
printf 'Resolver: %s (%s)\n' "$RESOLVER" "$(date '+%Y-%m-%d %H:%M:%S')"
self_check
exit $?
fi
printf '%s\n' "DNSSEC-Prüfung über Resolver: $RESOLVER ($(date '+%Y-%m-%d %H:%M:%S'))"
# Immer zuerst prüfen, ob der Resolver validiert sonst sind die
# Domain-Ergebnisse nicht aussagekräftig.
self_check
sc_rc=$?
if [[ "$sc_rc" -ne 0 ]]; then
printf '%sHinweis:%s Resolver validiert nicht/unklar "aktiv/fail" unten sind nur eingeschränkt verlässlich.\n' \
"$C_YELLOW" "$C_RST"
fi
echo
printf '%-40s %s\n' "DOMAIN" "STATUS"
printf '%-40s %s\n' "----------------------------------------" "--------"
rc=0
for d in "${DOMAINS[@]}"; do
st=$(check_domain "$d")
case "$st" in
aktiv) col="$C_GREEN" ;;
inaktiv) col="$C_YELLOW" ;;
*) col="$C_RED"; rc=1 ;;
esac
printf '%-40s %s%s%s\n' "$d" "$col" "$st" "$C_RST"
done
# Exit-Code: 0 = kein fail, 1 = mindestens ein fail (nützlich für Monitoring/cron)
exit $rc