chore: Inital Code commit

This commit is contained in:
2025-07-26 10:34:26 +02:00
parent 0b200a2f5c
commit d92c3657f9
6 changed files with 584 additions and 0 deletions

124
README.md Normal file
View File

@@ -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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

188
mailcert_check.sh Executable file
View File

@@ -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

267
template_mailcert_check.xml Normal file
View File

@@ -0,0 +1,267 @@
<?xml version="1.0" encoding="UTF-8"?>
<zabbix_export>
<version>6.4</version>
<template_groups>
<template_group>
<uuid>a571c0d144b14fd4a87a9d9b2aa9fcd6</uuid>
<name>Templates/Applications</name>
</template_group>
</template_groups>
<templates>
<template>
<uuid>c2b74f8e940244aea1c469c8a63e3a3a</uuid>
<template>Template Mail Certificate Monitoring</template>
<name>Template Mail Certificate Monitoring</name>
<groups>
<group>
<name>Templates/Applications</name>
</group>
</groups>
<items>
<item>
<uuid>161cd7c9ab6f4a15bd740ca33ab32c10</uuid>
<name>TLS: Days until WEB certificate expires</name>
<key>mailcert_check_expire[{$TLS_WEB_DOMAIN},{$TLS_WEB_PORT}/{$TLS_WEB_STARTTLS},{$TLS_WEB_SNI},{$TLS_WEB_TIMEOUT}]</key>
<delay>{$TLS_WEB_UPDATEINTERVAL}</delay>
<history>7d</history>
<trends>14d</trends>
<units>d</units>
<tags>
<tag>
<tag>Application</tag>
<value>Mail Certificates</value>
</tag>
</tags>
<triggers>
<trigger>
<uuid>dd76bdafc530428a99443a4e6211af38</uuid>
<expression>last(/Template Mail Certificate Monitoring/mailcert_check_expire[{$TLS_WEB_DOMAIN},{$TLS_WEB_PORT}/{$TLS_WEB_STARTTLS},{$TLS_WEB_SNI},{$TLS_WEB_TIMEOUT}])&lt;{$TLS_WEB_EXPIRESWITHIN}</expression>
<name>WEB Certificate for {HOST.NAME} expires soon ({ITEM.VALUE} days left)</name>
<priority>AVERAGE</priority>
</trigger>
</triggers>
</item>
<item>
<uuid>9e32e8f9af2740ea89437634e443c26e</uuid>
<name>TLS: WEB Certificate validity</name>
<key>mailcert_check_valid[{$TLS_WEB_DOMAIN},{$TLS_WEB_PORT}/{$TLS_WEB_STARTTLS},{$TLS_WEB_SNI},{$TLS_WEB_TIMEOUT}]</key>
<delay>{$TLS_WEB_UPDATEINTERVAL}</delay>
<history>7d</history>
<trends>14d</trends>
<tags>
<tag>
<tag>Application</tag>
<value>Mail Certificates</value>
</tag>
</tags>
<triggers>
<trigger>
<uuid>81cd1ee8eda946ebb216e90c1bffbcd1</uuid>
<expression>last(/Template Mail Certificate Monitoring/mailcert_check_valid[{$TLS_WEB_DOMAIN},{$TLS_WEB_PORT}/{$TLS_WEB_STARTTLS},{$TLS_WEB_SNI},{$TLS_WEB_TIMEOUT}])&lt;&gt;1</expression>
<name>SSL certificate invalid</name>
<priority>DISASTER</priority>
</trigger>
</triggers>
</item>
<item>
<uuid>f2ab69c1fbb2436ca59b90cfe0efdd9b</uuid>
<name>TLS: Days until SMTP certificate expires</name>
<key>mailcert_check_expire[{$TLS_SMTP_DOMAIN},{$TLS_SMTP_PORT}/{$TLS_SMTP_STARTTLS},{$TLS_SMTP_SNI},{$TLS_SMTP_TIMEOUT}]</key>
<delay>{$TLS_SMTP_UPDATEINTERVAL}</delay>
<history>7d</history>
<trends>14d</trends>
<units>d</units>
<tags>
<tag>
<tag>Application</tag>
<value>Mail Certificates</value>
</tag>
</tags>
<triggers>
<trigger>
<uuid>3baec6e0f5a04a43b3cc9a0729a97106</uuid>
<expression>last(/Template Mail Certificate Monitoring/mailcert_check_expire[{$TLS_SMTP_DOMAIN},{$TLS_SMTP_PORT}/{$TLS_SMTP_STARTTLS},{$TLS_SMTP_SNI},{$TLS_SMTP_TIMEOUT}])&lt;{$TLS_SMTP_EXPIRESWITHIN}</expression>
<name>SMTP Certificate for {HOST.NAME} expires soon ({ITEM.VALUE} days left)</name>
<priority>AVERAGE</priority>
</trigger>
</triggers>
</item>
<item>
<uuid>22b9d1c4f7bb4418be3adbdac6c3ecef</uuid>
<name>TLS: SMTP Certificate validity</name>
<key>mailcert_check_valid[{$TLS_SMTP_DOMAIN},{$TLS_SMTP_PORT}/{$TLS_SMTP_STARTTLS},{$TLS_SMTP_SNI},{$TLS_SMTP_TIMEOUT}]</key>
<delay>{$TLS_SMTP_UPDATEINTERVAL}</delay>
<history>7d</history>
<trends>14d</trends>
<tags>
<tag>
<tag>Application</tag>
<value>Mail Certificates</value>
</tag>
</tags>
<triggers>
<trigger>
<uuid>18d377fe3b1c481f974fba1c18231804</uuid>
<expression>last(/Template Mail Certificate Monitoring/mailcert_check_valid[{$TLS_SMTP_DOMAIN},{$TLS_SMTP_PORT}/{$TLS_SMTP_STARTTLS},{$TLS_SMTP_SNI},{$TLS_SMTP_TIMEOUT}])&lt;&gt;1</expression>
<name>SSL certificate invalid</name>
<priority>DISASTER</priority>
</trigger>
</triggers>
</item>
<item>
<uuid>e39f1312f1ab4287afb41694f3504c76</uuid>
<name>TLS: Days until IMAP certificate expires</name>
<key>mailcert_check_expire[{$TLS_IMAP_DOMAIN},{$TLS_IMAP_PORT}/{$TLS_IMAP_STARTTLS},{$TLS_IMAP_SNI},{$TLS_IMAP_TIMEOUT}]</key>
<delay>{$TLS_IMAP_UPDATEINTERVAL}</delay>
<history>7d</history>
<trends>14d</trends>
<units>d</units>
<tags>
<tag>
<tag>Application</tag>
<value>Mail Certificates</value>
</tag>
</tags>
<triggers>
<trigger>
<uuid>57517245a0c742559e36655cd85dd66d</uuid>
<expression>last(/Template Mail Certificate Monitoring/mailcert_check_expire[{$TLS_IMAP_DOMAIN},{$TLS_IMAP_PORT}/{$TLS_IMAP_STARTTLS},{$TLS_IMAP_SNI},{$TLS_IMAP_TIMEOUT}])&lt;{$TLS_IMAP_EXPIRESWITHIN}</expression>
<name>IMAP Certificate for {HOST.NAME} expires soon ({ITEM.VALUE} days left)</name>
<priority>AVERAGE</priority>
</trigger>
</triggers>
</item>
<item>
<uuid>78026be8b8ec4e009063c129c92493a1</uuid>
<name>TLS: IMAP Certificate validity</name>
<key>mailcert_check_valid[{$TLS_IMAP_DOMAIN},{$TLS_IMAP_PORT}/{$TLS_IMAP_STARTTLS},{$TLS_IMAP_SNI},{$TLS_IMAP_TIMEOUT}]</key>
<delay>{$TLS_IMAP_UPDATEINTERVAL}</delay>
<history>7d</history>
<trends>14d</trends>
<tags>
<tag>
<tag>Application</tag>
<value>Mail Certificates</value>
</tag>
</tags>
<triggers>
<trigger>
<uuid>1f94230b59824b5f83277204b2acb484</uuid>
<expression>last(/Template Mail Certificate Monitoring/mailcert_check_valid[{$TLS_IMAP_DOMAIN},{$TLS_IMAP_PORT}/{$TLS_IMAP_STARTTLS},{$TLS_IMAP_SNI},{$TLS_IMAP_TIMEOUT}])&lt;&gt;1</expression>
<name>SSL certificate invalid</name>
<priority>DISASTER</priority>
</trigger>
</triggers>
</item>
</items>
<macros>
<macro>
<macro>{$TLS_WEB_EXPIRESWITHIN}</macro>
<value>14</value>
<description>Number of days before the expiration of the certificate.</description>
</macro>
<macro>
<macro>{$TLS_WEB_DOMAIN}</macro>
<value></value>
<description>WEB Domainname or IP to check</description>
</macro>
<macro>
<macro>{$TLS_WEB_SNI}</macro>
<value></value>
<description>SNI value</description>
</macro>
<macro>
<macro>{$TLS_WEB_PORT}</macro>
<value>443</value>
<description>Target port (usually 443)</description>
</macro>
<macro>
<macro>{$TLS_WEB_STARTTLS}</macro>
<value>tls</value>
<description>SSL Options (usually tls)</description>
</macro>
<macro>
<macro>{$TLS_WEB_TIMEOUT}</macro>
<value>10</value>
<description>Timeout in seconds</description>
</macro>
<macro>
<macro>{$TLS_WEB_UPDATEINTERVAL}</macro>
<value>3600</value>
<description>How often to update certificate information in seconds</description>
</macro>
<macro>
<macro>{$TLS_SMTP_EXPIRESWITHIN}</macro>
<value>14</value>
<description>Number of days before the expiration of the certificate.</description>
</macro>
<macro>
<macro>{$TLS_SMTP_DOMAIN}</macro>
<value></value>
<description>SMTP Domainname or IP to check</description>
</macro>
<macro>
<macro>{$TLS_SMTP_SNI}</macro>
<value></value>
<description>SNI value</description>
</macro>
<macro>
<macro>{$TLS_SMTP_PORT}</macro>
<value>587</value>
<description>Target port (usually 587)</description>
</macro>
<macro>
<macro>{$TLS_SMTP_STARTTLS}</macro>
<value>smtp</value>
<description>SSL Options (usually smtp)</description>
</macro>
<macro>
<macro>{$TLS_SMTP_TIMEOUT}</macro>
<value>10</value>
<description>Timeout in seconds</description>
</macro>
<macro>
<macro>{$TLS_SMTP_UPDATEINTERVAL}</macro>
<value>3600</value>
<description>How often to update certificate information in seconds</description>
</macro>
<macro>
<macro>{$TLS_IMAP_EXPIRESWITHIN}</macro>
<value>14</value>
<description>Number of days before the expiration of the certificate.</description>
</macro>
<macro>
<macro>{$TLS_IMAP_DOMAIN}</macro>
<value></value>
<description>IMAP Domainname or IP to check</description>
</macro>
<macro>
<macro>{$TLS_IMAP_SNI}</macro>
<value></value>
<description>SNI value</description>
</macro>
<macro>
<macro>{$TLS_IMAP_PORT}</macro>
<value>993</value>
<description>Target port (usually 993)</description>
</macro>
<macro>
<macro>{$TLS_IMAP_STARTTLS}</macro>
<value>tls</value>
<description>SSL Options (usually tls)</description>
</macro>
<macro>
<macro>{$TLS_IMAP_TIMEOUT}</macro>
<value>10</value>
<description>Timeout in seconds</description>
</macro>
<macro>
<macro>{$TLS_IMAP_UPDATEINTERVAL}</macro>
<value>3600</value>
<description>How often to update certificate information in seconds</description>
</macro>
</macros>
</template>
</templates>
</zabbix_export>

View File

@@ -0,0 +1,5 @@
# Parameters:
# <hostname or IP> [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"