189 lines
5.8 KiB
Bash
Executable File
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
|
|
|