diff --git a/README.md b/README.md new file mode 100644 index 0000000..d02cae3 --- /dev/null +++ b/README.md @@ -0,0 +1,124 @@ +# Zabbix Template – TLS-Zertifikatüberwachung für Mail- und Webdienste + +Dieses Zabbix-Template ermöglicht eine umfassende Überwachung von TLS-Zertifikaten, die für Webserver (HTTPS) sowie E-Mail-Dienste wie SMTP und IMAP verwendet werden. Es überprüft automatisch die Gültigkeit der Zertifikate, erkennt abgelaufene oder ungültige Zertifikate und gibt rechtzeitig Warnungen aus, bevor ein Zertifikat abläuft. + + + +## Funktionen + +Das Template bietet eine Vielzahl nützlicher Funktionen zur Zertifikatsüberwachung: + +- Automatische Prüfung der TLS-Zertifikatsgültigkeit (gültig oder ungültig) +- Frühzeitige Benachrichtigung, wenn ein Zertifikat kurz vor dem Ablauf steht +- Unterstützung mehrerer Protokolle, darunter HTTPS, SMTP und IMAP +- Integration von Triggern für unterschiedliche Zustände, wie z. B. abgelaufene oder bald ablaufende Zertifikate +- Konfigurierbare Schwellenwerte und Parameter mithilfe von Makros auf Host-Ebene +- Unterstützung moderner TLS-Funktionen wie STARTTLS, Server Name Indication (SNI) und benutzerdefinierbarer Verbindungszeitüberschreitungen (Timeouts) + + + +## Aufbau und enthaltene Dateien + +Das Paket besteht aus folgenden zentralen Komponenten: + +- **`templatemailcert_check.xml`**: Das eigentliche Zabbix-Template, das über das Webinterface importiert wird. +- **`userparameters_mailcert_check.conf`**: Konfigurationsdatei für den Zabbix-Agent, welche benutzerdefinierte Prüfbefehle bereitstellt. +- **`mailcert_check.sh`**: Ein Bash-Skript, das die Zertifikatsinformationen abfragt und verarbeitet. + + + +## Installationsanleitung + +### Schritt 1: Skript installieren + +Kopiere das Bash-Skript in ein geeignetes Verzeichnis auf dem Zielsystem und mache es ausführbar: + +```bash +sudo cp mailcert_check.sh /usr/local/bin/ +sudo chmod +x /usr/local/bin/mailcert_check.sh +``` + +### Schritt 2: Zabbix-Agent konfigurieren + +Die Datei mit den benutzerdefinierten Parametern muss in das Konfigurationsverzeichnis des Zabbix-Agenten kopiert werden. Anschließend ist ein Neustart des Agenten erforderlich: + +```bash +sudo cp userparameters_mailcert_check.conf /etc/zabbix/zabbix_agentd.d/ +sudo systemctl restart zabbix-agent +``` + +### Schritt 3: Template in Zabbix importieren + +1. Melde dich im Zabbix-Frontend an. +2. Navigiere zu **Configuration → Templates**. +3. Klicke auf **Import**. +4. Wähle die Datei `template_mailcert_check.xml` aus und lade sie hoch. +5. Bestätige den Import. + +### Schritt 4: Template einem Host zuweisen + +1. Öffne im Zabbix-Frontend den gewünschten Host. +2. Gehe zum Reiter **Templates**. +3. Füge das Template **Template Mail Certificate Monitoring** hinzu. +4. Speichere die Änderungen. + + + +## Konfiguration über Makros + +Auf Host-Ebene lassen sich mithilfe von Makros die zu überwachenden Ziele und Parameter definieren. Hier ein Beispiel für die SMTP-Zertifikatsüberwachung: + +```yaml +{$TLS_SMTP_DOMAIN} = mail.example.com +{$TLS_SMTP_PORT} = 587 +{$TLS_SMTP_STARTTLS} = smtp +{$TLS_SMTP_SNI} = mail.example.com +{$TLS_SMTP_TIMEOUT} = 10 +{$TLS_SMTP_UPDATEINTERVAL} = 3600 +{$TLS_SMTP_EXPIRESWITHIN} = 14 +``` + +Diese Makros ermöglichen eine flexible und gezielte Konfiguration pro Host. + + + +## Beispielhafte Darstellung in Zabbix + +Nach erfolgreicher Einrichtung werden im Zabbix-Frontend automatisch Items und Trigger erstellt. Dazu zählen unter anderem: + +- Einträge zur Restlaufzeit (in Tagen) eines Zertifikats +- Statusmeldungen zu ungültigen oder bald ablaufenden Zertifikaten +- Trigger, die bei Erreichen der definierten Schwellenwerte Warnungen auslösen + + + +![image-20250723220013360](assets/image-20250723220013360.png) + + + +![image-20250726093817364](assets/image-20250726093817364.png) + + + +## Manueller Test des Prüfskripts + +Zur Überprüfung der Skriptfunktionalität kann ein manueller Aufruf wie folgt erfolgen: + +``` +/usr/local/bin/mailcert_check.sh expire mail.example.com 587/smtp mail.example.com 10 +``` + +Dabei werden Domain, Port, Protokoll, SNI und Timeout übergeben. + + + +## Kompatibilität + +- Unterstützt wird **Zabbix ab Version 6.4** +- Abhängigkeiten: `bash`, `openssl` + + + +## Lizenz + +Dieses Projekt steht unter der **MIT-Lizenz** und kann frei verwendet, modifiziert und verteilt werden. \ No newline at end of file diff --git a/assets/image-20250723220013360.png b/assets/image-20250723220013360.png new file mode 100644 index 0000000..3616ef9 Binary files /dev/null and b/assets/image-20250723220013360.png differ diff --git a/assets/image-20250726093817364.png b/assets/image-20250726093817364.png new file mode 100644 index 0000000..bb21660 Binary files /dev/null and b/assets/image-20250726093817364.png differ diff --git a/mailcert_check.sh b/mailcert_check.sh new file mode 100755 index 0000000..2bb134b --- /dev/null +++ b/mailcert_check.sh @@ -0,0 +1,188 @@ +#!/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 + diff --git a/template_mailcert_check.xml b/template_mailcert_check.xml new file mode 100644 index 0000000..469c713 --- /dev/null +++ b/template_mailcert_check.xml @@ -0,0 +1,267 @@ + + + 6.4 + + + a571c0d144b14fd4a87a9d9b2aa9fcd6 + Templates/Applications + + + + + + + diff --git a/userparameters_mailcert_check.conf b/userparameters_mailcert_check.conf new file mode 100644 index 0000000..ba78a87 --- /dev/null +++ b/userparameters_mailcert_check.conf @@ -0,0 +1,5 @@ +# Parameters: +# [port[/starttls protocol]] [domain for TLS SNI] [check timeout] [tls version] [tls_version,[self_signed_ok]] +UserParameter=mailcert_check_valid[*], /usr/local/bin/mailcert_check.sh valid "$1" "$2" "$3" "$4" "$5" +UserParameter=mailcert_check_expire[*], /usr/local/bin/mailcert_check.sh expire "$1" "$2" "$3" "$4" "$5" +UserParameter=mailcert_check_json[*], /usr/local/bin/mailcert_check.sh json "$1" "$2" "$3" "$4" "$5"