Files
maierch 64138831a8 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>
2026-07-10 19:13:36 +02:00

229 lines
7.8 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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