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:
Executable
+228
@@ -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
|
||||
Reference in New Issue
Block a user