Zabbix: SSL-Zertifikate automatisch überwachen — Komplettanleitung

SSL-Zertifikate mit Zabbix automatisch überwachen. External Scripts, Auto-Discovery über Nginx Proxy Manager und Trigger für ablaufende Zertifikate einrichten.

Abgelaufene SSL-Zertifikate sind der Klassiker unter den Homelab-Pannen: Plötzlich funktioniert nichts mehr, Browser warnen vor Sicherheitsrisiken und im schlimmsten Fall bemerkt man’s erst, wenn der Chef anruft. In diesem Tutorial zeige ich dir, wie du mit Zabbix eine vollautomatische Überwachung aller SSL-Zertifikate einrichtest — inklusive Auto-Discovery über Nginx Proxy Manager, Trigger für 30/14/7-Tage-Warnungen und Telegram-Alerts.

Warum SSL-Zertifikate monitoren?#

Let’s Encrypt hat uns kostenlose SSL-Zertifikate gebracht — aber sie laufen nach 90 Tagen ab. Wenn dein Certbot-Renewal aus irgendeinem Grund fehlschlägt (falsche DNS-Einträge, Rate-Limits, fehlende Permissions), merkst du’s oft erst, wenn’s zu spät ist.

Die typischen Probleme:

  • Let’s Encrypt Zertifikate: Laufen nach 90 Tagen ab, Renewal kann fehlschlagen
  • Wildcard-Zertifikate: Besonders kritisch, weil sie oft mehrere Services betreffen
  • Vergessene Subdomains: Du hast eine Domain mal für ein Projekt angelegt und vergessen
  • Externe Services: APIs, Webhooks oder Integrationen die auf gültige Zerts angewiesen sind

Genau hier setzt unser Zabbix SSL-Monitoring an: Alle Domains werden automatisch erkannt, geprüft und du bekommst rechtzeitig Bescheid bevor’s kritisch wird.

Voraussetzungen#

Bevor wir loslegen, stelle sicher dass du folgendes hast:

  • Zabbix Server (mindestens Version 6.0, besser 7.x)
  • Nginx Proxy Manager (optional, für Auto-Discovery)
  • Root-Zugriff auf deinen Zabbix-Server
  • Grundwissen Zabbix: Wenn du Zabbix noch nicht kennst, lies zuerst meinen Zabbix Monitoring Homelab Einsteiger-Guide

Architektur: Wie funktioniert’s?#

Unser Setup besteht aus drei Komponenten:

  1. Discovery-Script (discover_ssl_domains.sh): Liest alle Domains aus Nginx Proxy Manager SQLite DB
  2. Check-Script (check_ssl.sh): Prüft ein einzelnes Zertifikat und gibt die verbleibenden Tage zurück
  3. Zabbix Template: Nutzt die Scripts für Auto-Discovery und Item-Generierung

Der Workflow:

Nginx Proxy Manager DB → Discovery Script → Zabbix LLD → Items pro Domain → Trigger bei <30/14/7 Tagen

External Scripts erstellen#

Zabbix External Scripts liegen unter /usr/lib/zabbix/externalscripts/ (Debian/Ubuntu) oder /usr/lib64/zabbix/externalscripts/ (RHEL/CentOS). Die Scripts müssen:

  • Ausführbar sein (chmod +x)
  • Dem Zabbix-User gehören oder von ihm lesbar sein
  • Ergebnisse auf STDOUT ausgeben

Script 1: Discovery Script#

Erstelle /usr/lib/zabbix/externalscripts/discover_ssl_domains.sh:

#!/bin/bash
# discover_ssl_domains.sh - Liest alle Domains aus Nginx Proxy Manager SQLite DB

NPM_DB="/opt/nginxproxymanager/data/database.sqlite"

# Prüfe ob DB existiert
if [ ! -f "$NPM_DB" ]; then
    echo ''
    exit 0
fi

# Lese alle Domains aus der DB
DOMAINS=$(sqlite3 "$NPM_DB" "SELECT DISTINCT domain_names FROM proxy_host;" 2>/dev/null | tr ',' '\n' | sort -u)

# Baue JSON für Zabbix LLD
echo -n '{"data":['

FIRST=1
for DOMAIN in $DOMAINS; do
    # Überspringe leere Zeilen und Wildcards
    if [ -z "$DOMAIN" ] || [[ "$DOMAIN" == *"*"* ]]; then
        continue
    fi
    
    if [ $FIRST -eq 1 ]; then
        FIRST=0
    else
        echo -n ","
    fi
    
    echo -n "{
        \"\":\"$DOMAIN\",
        \"\":\"443\"
    }"
done

echo ']}'

Hinweis: Wenn du Nginx Proxy Manager nicht verwendest, kannst du die Domains auch aus einer statischen Liste laden:

#!/bin/bash
# Alternative: Statische Domainliste

DOMAINS=(
    "example.com"
    "mail.example.com"
    "cloud.example.com"
)

echo -n '{"data":['
FIRST=1
for DOMAIN in "$"; do
    if [ $FIRST -eq 1 ]; then
        FIRST=0
    else
        echo -n ","
    fi
    echo -n "{
        \"\":\"$DOMAIN\",
        \"\":\"443\"
    }"
done
echo ']}'

Script 2: SSL Check Script#

Erstelle /usr/lib/zabbix/externalscripts/check_ssl.sh:

#!/bin/bash
# check_ssl.sh - Prüft SSL-Zertifikat und gibt verbleibende Tage zurück

DOMAIN="$1"
PORT="$"

if [ -z "$DOMAIN" ]; then
    echo "ERROR: No domain specified"
    exit 1
fi

# Hole Zertifikat und extrahiere Ablaufdatum
EXPIRY=$(echo | openssl s_client -servername "$DOMAIN" -connect "$DOMAIN:$PORT" 2>/dev/null | \
         openssl x509 -noout -enddate 2>/dev/null | \
         cut -d= -f2)

if [ -z "$EXPIRY" ]; then
    echo "ERROR: Could not retrieve certificate for $DOMAIN:$PORT"
    exit 1
fi

# Konvertiere zu Unix Timestamp
EXPIRY_TIMESTAMP=$(date -d "$EXPIRY" +%s 2>/dev/null)
NOW_TIMESTAMP=$(date +%s)

# Berechne verbleibende Tage
DAYS_LEFT=$(( ($EXPIRY_TIMESTAMP - $NOW_TIMESTAMP) / 86400 ))

echo "$DAYS_LEFT"

Scripts installieren#

# Scripts erstellen
cd /usr/lib/zabbix/externalscripts/
sudo vim discover_ssl_domains.sh  # Inhalt einfügen
sudo vim check_ssl.sh             # Inhalt einfügen

# Ausführbar machen
sudo chmod +x discover_ssl_domains.sh check_ssl.sh

# Owner setzen
sudo chown zabbix:zabbix discover_ssl_domains.sh check_ssl.sh

# Testen
sudo -u zabbix ./discover_ssl_domains.sh
sudo -u zabbix ./check_ssl.sh example.com 443

Expected Output Discovery:

{"data":[
    ":"example.com","":"443"},
    ":"mail.example.com","":"443"}
]}

Expected Output Check:

67

(Zahl = Tage bis Ablauf)

Zabbix Template konfigurieren#

Jetzt erstellen wir das Zabbix Template mit Low-Level Discovery.

Discovery Rule erstellen#

  1. Configuration → Templates → Create template

    • Template name: Template SSL Certificate Monitoring
    • Groups: Templates/Applications
  2. Discovery rules → Create discovery rule

    • Name: SSL Domain Discovery
    • Type: External check
    • Key: discover_ssl_domains.sh
    • Update interval: 1h

Item Prototype erstellen#

In der Discovery Rule → Item prototypes → Create item prototype:

  • Name: SSL Certificate Expiry:
  • Type: External check
  • Key: check_ssl.sh[,]
  • Type of information: Numeric (unsigned)
  • Units: days
  • Update interval: 12h
  • History storage period: 90d
  • Trend storage period: 365d

Beschreibung: Days until SSL certificate expires for

Trigger Prototypes erstellen#

Jetzt kommen die Trigger die uns warnen wenn’s brenzlig wird:

Trigger 1: Critical (7 Tage)#

  • Name: SSL Certificate expires in less than 7 days:

  • Expression:

    last(/Template SSL Certificate Monitoring/check_ssl.sh[,])<7
    
  • Severity: High

  • Description:

    The SSL certificate for  will expire in  days!
    
    Action required: Renew certificate immediately.
    

Trigger 2: Warning (14 Tage)#

  • Name: SSL Certificate expires in less than 14 days:

  • Expression:

    last(/Template SSL Certificate Monitoring/check_ssl.sh[,])<14
    
  • Severity: Warning

  • Manual close: Yes

  • Description:

    The SSL certificate for  will expire in  days.
    
    Please check renewal process.
    

Trigger 3: Information (30 Tage)#

  • Name: SSL Certificate expires in less than 30 days:

  • Expression:

    last(/Template SSL Certificate Monitoring/check_ssl.sh[,])<30
    
  • Severity: Information

  • Manual close: Yes

  • Description:

    The SSL certificate for  will expire in  days.
    

Tipp: Die Schwellwerte kannst du über Macros flexibel machen:

  = 7
  = 14
  = 30

Dann in der Expression: last(...)<

Host zuweisen#

  1. Configuration → Hosts
  2. Host auswählen (z.B. deinen Zabbix Server)
  3. Templates → Link new templates
  4. Template SSL Certificate Monitoring auswählen
  5. Update

Nach spätestens 1 Stunde (Discovery-Intervall) sollten die ersten Items auftauchen:

Monitoring → Latest data → Filter: Host = [Your Host]

Du solltest nun für jede entdeckte Domain ein Item sehen mit dem aktuellen Wert (Tage bis Ablauf).

Nginx Proxy Manager Integration#

Wenn du Nginx Proxy Manager verwendest, ist die Auto-Discovery besonders elegant: Jede neue Proxy Host wird automatisch ins Monitoring aufgenommen.

NPM SQLite DB Zugriff#

Nginx Proxy Manager speichert alle Domains in /opt/nginxproxymanager/data/database.sqlite. Unser Discovery-Script liest direkt daraus:

SELECT DISTINCT domain_names FROM proxy_host;

Beispiel-Output:

cloud.example.com
mail.example.com,webmail.example.com
git.example.com

Das Script splittet Mehrfach-Domains am Komma und erstellt für jede einen eigenen Discovery-Eintrag.

Permissions prüfen#

# Zabbix-User muss die DB lesen können
sudo chmod 644 /opt/nginxproxymanager/data/database.sqlite
sudo chown npmuser:zabbix /opt/nginxproxymanager/data/database.sqlite

Alternativ kannst du einen Cronjob einrichten der die Domains periodisch exportiert:

# /etc/cron.hourly/export-npm-domains
#!/bin/bash
sqlite3 /opt/nginxproxymanager/data/database.sqlite \
    "SELECT DISTINCT domain_names FROM proxy_host;" > /tmp/npm_domains.txt
chmod 644 /tmp/npm_domains.txt

Telegram-Alerts einrichten#

Damit du auch unterwegs Bescheid bekommst wenn ein Zertifikat abläuft, richten wir Telegram-Benachrichtigungen ein:

1. Telegram Bot erstellen#

  1. Schreibe @BotFather auf Telegram
  2. /newbot → Namen vergeben
  3. API Token kopieren (z.B. 123456789:ABCdefGHIjklMNOpqrsTUVwxyz)

2. Chat ID herausfinden#

# Sende dir selbst eine Nachricht über den Bot, dann:
curl https://api.telegram.org/bot<YOUR_TOKEN>/getUpdates

Kopiere die chat_id aus der Response.

3. Media Type in Zabbix konfigurieren#

  1. Administration → Media types → Telegram
  2. Token: Dein Bot-Token
  3. Test mit deiner Chat-ID

4. User-Media einrichten#

  1. Administration → Users → [Dein User]
  2. Media → Add
  3. Type: Telegram
  4. Send to: Deine Chat-ID
  5. Severity: Alle aktivieren

5. Action erstellen#

Configuration → Actions → Trigger actions → Create action:

  • Name: SSL Certificate Expiry Notification
  • Conditions:
    • Trigger severity >= Warning
    • Trigger name contains SSL Certificate
  • Operations:
    • Send message to user: [Dein User]

    • Message template:

      🔒 SSL Alert: 
      
      Host: 
      Domain: 
      Days left: 
      
      Severity: 
      Time:  
      

Jetzt bekommst du Telegram-Nachrichten sobald ein Zertifikat die Schwellwerte unterschreitet!

Troubleshooting#

“ERROR: Could not retrieve certificate”#

Mögliche Ursachen:

  • Domain ist nicht erreichbar (DNS, Firewall)
  • Port ist falsch (nicht 443)
  • SNI (Server Name Indication) fehlt

Lösung: Prüfe manuell:

openssl s_client -servername example.com -connect example.com:443 < /dev/null

Discovery findet keine Domains#

Prüfen:

sudo -u zabbix /usr/lib/zabbix/externalscripts/discover_ssl_domains.sh

Häufige Fehler:

  • NPM DB-Pfad falsch (prüfe /opt/nginxproxymanager/data/database.sqlite)
  • Permissions fehlen (chmod 644)
  • SQLite3 nicht installiert (apt install sqlite3)

Items werden nicht erstellt#

  1. Discovery Rule ausführen: Configuration → Hosts → Discovery → “Execute now”
  2. Logs prüfen: tail -f /var/log/zabbix/zabbix_server.log
  3. Script-Output testen: External scripts müssen valides JSON zurückgeben

Zabbix Agent vs External Scripts#

Häufige Verwechslung: External Scripts laufen auf dem Zabbix Server, nicht auf dem Agent! Der Zabbix Server führt das Script aus und kontaktiert die Zieldomains direkt.

Das bedeutet:

  • Keine Agent-Installation auf dem Ziel nötig
  • Zabbix Server braucht Outbound-Zugriff auf Port 443
  • Scripts müssen auf dem Zabbix Server liegen

False Positives bei Wildcard-Zertifikaten#

Wenn du Wildcard-Zertifikate (*.example.com) verwendest, können Subdomains mehrfach gemessen werden.

Lösung: Filter im Discovery-Script:

# Überspringe Domains die von Wildcard abgedeckt sind
if [[ "$DOMAIN" == *.example.com ]]; then
    continue
fi

Oder: Prüfe nur das Wildcard-Zertifikat direkt.

Erweiterte Konfiguration#

Multi-Port Monitoring#

Wenn du Services auf anderen Ports überwachst (z.B. SMTPS 465, IMAPS 993):

Im Discovery-Script:

echo -n "{
    \"\":\"mail.example.com\",
    \"\":\"465\",
    \"\":\"SMTPS\"
}"

Item-Key: check_ssl.sh[,]

Subject Alternative Names (SAN) prüfen#

Manche Zertifikate decken mehrere Domains ab (SAN). Um alle zu erfassen:

#!/bin/bash
# check_ssl_san.sh - Extrahiert alle SANs

DOMAIN="$1"
openssl s_client -servername "$DOMAIN" -connect "$DOMAIN:443" 2>/dev/null | \
    openssl x509 -noout -text | \
    grep -A1 "Subject Alternative Name" | \
    tail -n1 | \
    sed 's/DNS://g' | \
    tr ',' '\n'

Zertifikat-Aussteller überwachen#

Prüfe ob das Zertifikat von Let’s Encrypt kommt:

ISSUER=$(echo | openssl s_client -servername "$DOMAIN" -connect "$DOMAIN:$PORT" 2>/dev/null | \
         openssl x509 -noout -issuer | \
         grep -o "Let's Encrypt")

if [ -z "$ISSUER" ]; then
    echo "0"  # Nicht Let's Encrypt
else
    echo "1"  # Let's Encrypt
fi

Best Practices#

  1. Update-Intervalle:

    • Discovery: 1h (findet neue Domains schnell)
    • Items: 12h (Zertifikate ändern sich selten)
  2. History & Trends:

    • History: 90d (3 Let’s Encrypt Zyklen)
    • Trends: 365d (Jahresübersicht)
  3. Trigger-Schwellwerte:

    • 30 Tage: Information (frühe Vorwarnung)
    • 14 Tage: Warning (Zeit zum Handeln)
    • 7 Tage: High (kritisch, sofort reagieren)
  4. Dependencies:

    • Setze Dependencies auf Host-Erreichbarkeit
    • Verhindert Alert-Spam bei Netzwerkausfall
  5. Maintenance Windows:

    • Plane Maintenance während Zertifikat-Renewals
    • Verhindert False-Positives während Renewal-Prozess

🛒 Empfohlene Tools#

📊

Zabbix Monitoring Kit

Fertige Templates und Dashboards für SSL-Monitoring, Proxmox, Docker und mehr. Sofort einsatzbereit in deinem Zabbix. Monitoring Kit holen → 🖥️

Hetzner Cloud

Dedizierten Monitoring-Server betreiben? Hetzner Cloud ab 4€/Monat. Hetzner ausprobieren →

Zusammenfassung#

Mit diesem Setup hast du ein vollautomatisches SSL-Zertifikat-Monitoring das:

✅ Alle Domains automatisch erkennt (via NPM oder statische Liste)
✅ Ablaufdaten täglich prüft
✅ Dich rechtzeitig warnt (30/14/7 Tage)
✅ Via Telegram Bescheid gibt
✅ Komplett wartungsfrei läuft

Nie wieder abgelaufene Zertifikate — dein Homelab dankt’s dir, deine User auch.

Fragen oder Probleme? Schreib mir oder hinterlasse einen Kommentar!


Update 2026-02-08: Nginx Proxy Manager Integration und Telegram-Alerts hinzugefügt.


Einige Links auf dieser Seite sind Affiliate-Links. Wenn du über diese Links einkaufst, erhalte ich eine kleine Provision — für dich ändert sich am Preis nichts. So unterstützt du diesen Blog und ermöglichst weitere kostenlose Tutorials. Danke! 🙏

[« Vorherige]
UniFi WLAN optimieren: Kanäle, Sendeleistung und Band Steering richtig einstellen