Files
zabbix-mailcert-check/mailcert_check.sh

189 lines
5.8 KiB
Bash
Executable File

#!/usr/bin/env bash
DEBUG="${DEBUG:-0}" # Setze DEBUG=1 für Debug-Ausgaben
debug() {
if [[ "$DEBUG" == "1" ]]; then
echo "[DEBUG] $*" >&2
fi
}
# Standardwerte
timeout_sec=5
err_code=-65535
valid_codes=(0)
print_usage() {
echo $err_code
if [ -t 1 ]; then
cat >&2 <<-EOT
Verwendung: $(basename "$0") expire|valid|json host|ip [port[/starttls-proto]] [tls_sni_domain] [timeout] [tls_option[,selfsigned_allowed]] [extra_s_client_args ... ]
Prüft SSL-Zertifikat auf Ablauf und Gültigkeit via OpenSSL.
Parameter:
- port: Standard 443
- starttls-proto: optional, z.B. smtp, ftp, ldap (z.B. 25/smtp)
- tls_sni_domain: Domain für TLS SNI, Standard: host
- timeout: max Wartezeit (Sek), Standard: $timeout_sec
- tls_option: tls1, tls1_2, tls_auto etc.
- selfsigned_allowed: erlaubt selbstsignierte Zertifikate
- extra_s_client_args: zusätzliche OpenSSL s_client Parameter
Rückgabewerte:
* expire:
Anzahl verbleibender Tage (0 oder negativ bei abgelaufen)
$err_code bei Fehler
* valid:
1 = gültig, 0 = ungültig, $err_code bei Fehler
* json:
JSON mit expire_days, valid, return_code, return_text
Oder JSON mit error_code und error_message bei Fehler
Fehler werden auf der Konsole nur bei Terminalausgabe angezeigt.
EOT
fi
}
exit_with_error() {
local msg="$*"
if [[ "$mode" == "json" ]]; then
echo "{\"error_code\": $err_code, \"error_message\": \"$msg\"}"
else
echo $err_code
if [ -t 1 ]; then echo "Fehler: $msg" >&2; fi
fi
exit 0
}
output_and_exit() {
echo "$1"
exit 0
}
calculate_days_left() {
local expiry_str
expiry_str=$(printf "%s" "$openssl_output" \
| sed -n '/-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/p' \
| openssl x509 -noout -dates 2>/dev/null \
| grep '^notAfter=' | cut -d= -f2)
debug "Ablaufdatum: $expiry_str"
[[ -z "$expiry_str" ]] && exit_with_error "Konnte Ablaufdatum nicht ermitteln"
expiry_epoch=$($date_cmd -d "$expiry_str" +%s) || exit_with_error "Ungültiges Datum: $expiry_str"
now_epoch=$($date_cmd +%s)
debug "Epoch-Zeitpunkte: jetzt=$now_epoch, ablauf=$expiry_epoch"
echo $(( (expiry_epoch - now_epoch) / 86400 ))
}
# Standard date Befehl ermitteln (Linux oder macOS mit gdate)
date_cmd="date"
if date --version 2>&1 | grep -qi 'busybox'; then
exit_with_error "Busybox date wird nicht unterstützt"
fi
# Prüfen auf benötigte Tools
for tool in timeout openssl $date_cmd; do
type "$tool" >/dev/null 2>&1 || exit_with_error "Benötigtes Programm nicht gefunden: $tool"
done
# Eingabeparameter
mode="$1"
host="$2"
port_and_proto="${3:-443}"
tls_sni="${4:-$host}"
timeout="${5:-$timeout_sec}"
tls_opts="$6"
extra_args=("${@:7}")
debug "Modus: $mode"
debug "Host: $host"
debug "Port/Proto: $port_and_proto"
debug "SNI: $tls_sni"
debug "Timeout: $timeout"
debug "TLS-Optionen: $tls_opts"
debug "Extra-Args: ${extra_args[*]}"
# Port und optionales starttls-Protokoll extrahieren
IFS='/' read -r port proto <<< "$port_and_proto"
starttls_flag=""
starttls_proto=""
if [[ -n "$proto" && "$proto" != "tls" ]]; then
starttls_flag="-starttls"
starttls_proto="$proto"
fi
# Parametervalidierung
[[ "$mode" =~ ^(expire|valid|json)$ ]] || exit_with_error "Ungültiger Modus: $mode"
[[ "$port" =~ ^[0-9]+$ ]] || exit_with_error "Port muss numerisch sein"
(( port >= 1 && port <= 65535 )) || exit_with_error "Port muss zwischen 1 und 65535 liegen"
[[ -z "$starttls_proto" || "$starttls_proto" =~ ^[a-z0-9]+$ ]] || exit_with_error "Ungültiges StartTLS-Protokoll"
[[ "$timeout" =~ ^[0-9]+$ ]] || exit_with_error "Timeout muss numerisch sein"
# Unterstützung für IDN Domains (Punycode)
if type idn >/dev/null 2>&1; then
host=$(idn "$host" 2>/dev/null || echo "$host")
tls_sni=$(idn "$tls_sni" 2>/dev/null || echo "$tls_sni")
fi
# TLS Optionen parsen
IFS=',' read -r -a tls_options_arr <<< "$tls_opts"
tls_flag=""
for opt in "${tls_options_arr[@]}"; do
if [[ "$opt" == "self_signed_ok" ]]; then
valid_codes+=(18 19 20 21)
elif [[ "$opt" == "tls_auto" ]]; then
:
elif [[ "$opt" == tls* || "$opt" == ssl* || "$opt" == dtls* ]]; then
tls_flag="-$opt"
fi
done
# Zertifikat abfragen
debug "Aufruf: openssl s_client $starttls_flag $starttls_proto -connect $host:$port -servername $tls_sni -verify_hostname $tls_sni $tls_flag ${extra_args[*]}"
openssl_output=$(timeout "$timeout" openssl s_client $starttls_flag $starttls_proto -connect "$host:$port" -servername "$tls_sni" -verify_hostname "$tls_sni" $tls_flag "${extra_args[@]}" 2>/dev/null <<<"")
debug "OpenSSL-Ausgabe erhalten"
debug "$(echo "$openssl_output" | head -n 10)"
# Prüfung, ob Ausgabe gültig ist
if ! grep -q '^ *Verify return code:' <<< "$openssl_output"; then
exit_with_error "Konnte Zertifikat nicht abrufen"
fi
if [[ "$mode" == "expire" ]]; then
days_left=$(calculate_days_left)
output_and_exit "$days_left"
else
verify_line=$(grep '^ *Verify return code:' <<< "$openssl_output" | head -n1 | tr -s ' ')
verify_code=$(cut -d' ' -f4 <<< "$verify_line")
verify_text=$(sed -n 's/^ *Verify return code: [0-9]* (\(.*\))/\1/p' <<< "$verify_line")
debug "Verify Code: $verify_code"
debug "Verify Text: $verify_text"
valid=0
for code in "${valid_codes[@]}"; do
if [[ "$code" == "$verify_code" ]]; then
valid=1
break
fi
done
if [[ "$mode" == "valid" ]]; then
output_and_exit "$valid"
elif [[ "$mode" == "json" ]]; then
days_left=$(calculate_days_left)
printf '{"expire_days": %d, "valid": %d, "return_code": %s, "return_text": "%s"}\n' \
"$days_left" "$valid" "$verify_code" "$verify_text"
exit 0
fi
fi