TLSA | Validiere TLSA Records mit CheckMK

TLSA | Validiere TLSA Records mit CheckMK
Photo by John / Unsplash

🧩 Checkmk TLSA Validation Plugin

Dieses Dokument beschreibt, wie du das TLSA Validation Script als Active Check in Checkmk einbindest und über ein Custom Attribute Ports pro Host konfigurierbar machst.


📜 1. Skriptinstallation

Kopiere das Skript check_tlsa nach:

/usr/lib/nagios/plugins/check_tlsa
chmod +x /usr/lib/nagios/plugins/check_tlsa

Das Skript prüft TLSA-DNS-Records für SMTP/IMAP/POP3/HTTPS Ports und gibt eine Checkmk-kompatible Ausgabe zurück.


⚙️ 2. Custom Attribute in Checkmk

Erstelle ein benutzerdefiniertes Host-Attribut:

Feldname Wert
Name TLSA_PORTS
Label TLSA Ports
Type Text
Default Value (optional z. B. 25,465,587)
Description Ports für TLSA-Check

Speichern und Änderungen aktivieren.


🔍 3. Active Check Regel in WATO

Gehe zu:
Setup → Active Checks → Classical active and passive Monitoring checks

Erstelle eine neue Regel mit:

Parameter Wert
Command line $USER1$/check_tlsa -d $HOSTADDRESS$ -p $_HOSTTLSA_PORTS$
Service description TLSA Check

Zuweisung: Nur für Hosts aktivieren, die TLSA verwenden.


🖥️ 4. Hostkonfiguration

Unter Custom Attributes im Host:

Attribut Beispielwert
TLSA_PORTS 25,587,465

Das Skript prüft dann nur diese Ports.


📊 5. Beispielausgabe

OK - TLSA Validation erfolgreich
Port 25: OK - TLSA stimmt überein
Port 587: OK - kein TLSA Eintrag vorhanden
Port 465: CRIT - TLSA Mismatch (usage=3 selector=1 mtype=1) | ports=3 ok=2 warn=0 crit=1
  • 0 → OK
  • 1 → WARN
  • 2 → CRIT
  • 3 → UNKNOWN

Perfdata: ports, ok, warn, crit


🧩 6. Alternative Nutzung (Local Check)

Wenn du TLSA lokal auf einem Host prüfen willst:

  • Skript nach /usr/lib/check_mk_agent/local/ kopieren
  • Ausgabeformat anpassen:
    0 TLSA-Check - OK - TLSA Validation erfolgreich
    

Dann wird der Check direkt vom Agenten geliefert.


✅ Vorteile der Active-Check-Variante

  • Zentrale TLSA-Prüfung von Monitoring-Server
  • Keine Konfiguration pro Agent nötig
  • Dynamische Portwahl per Custom Attribute
  • Perfekte Integration in Checkmk-Checklogik

Script check_tlsa

#!/usr/bin/env bash
# check_tlsa robust

# !!! set -e entfernt, nur -u beibehalten
set -uo pipefail

usage() {
  echo "Usage: $0 -d <domain> -p <ports>"
  exit 3
}

DOMAIN=""
PORTS=""

while getopts ":d:p:" opt; do
  case "$opt" in
    d) DOMAIN="$OPTARG" ;;
    p) PORTS="$OPTARG" ;;
    *) usage ;;
  esac
done

if [[ -z "${DOMAIN}" ]]; then
  usage
fi

if [[ -z "${PORTS}" ]]; then
  echo "UNKNOWN - keine Ports angegeben"
  exit 3
fi

IFS=',' read -r -a ports <<< "$PORTS"

dig_cmd() {
  dig +short TLSA "$1" 2>/dev/null || true
}

fetch_leaf_cert_pem() {
  local host="$1" port="$2" sni="$3"
  local starttls_opt=""
  case "$port" in
    25|587) starttls_opt="-starttls smtp" ;;
    110) starttls_opt="-starttls pop3" ;;
    143) starttls_opt="-starttls imap" ;;
  esac

  openssl s_client -connect "${host}:${port}" -servername "${sni}" $starttls_opt -showcerts < /dev/null 2>/dev/null \
  | awk '/-----BEGIN CERTIFICATE-----/{p=1} p{print} /-----END CERTIFICATE-----/{print; exit}'
}

pem_to_der() {
  local pem_data="$1" out="$2"
  printf "%s\n" "$pem_data" > /tmp/_tlsa_tmp_cert.pem
  openssl x509 -in /tmp/_tlsa_tmp_cert.pem -outform der -out "$out" 2>/dev/null || return 1
}

pem_to_spki_der() {
  local pem_data="$1" out="$2"
  printf "%s\n" "$pem_data" > /tmp/_tlsa_tmp_cert.pem
  openssl x509 -in /tmp/_tlsa_tmp_cert.pem -pubkey -noout 2>/dev/null \
    | openssl pkey -pubin -outform der -out "$out" 2>/dev/null || return 1
}

normalize_hex() {
  echo "$1" | tr -d '[:space:]' | tr '[:upper:]' '[:lower:]'
}

overall_status=0
crit_count=0
warn_count=0
ok_count=0
messages=()

for p in "${ports[@]}"; do
  qname="_${p}._tcp.${DOMAIN}."
  tlsa_lines=$(dig_cmd "$qname")

  if [[ -z "$tlsa_lines" ]]; then
    messages+=("Port $p: OK - kein TLSA Eintrag vorhanden")
    ((ok_count++))
    continue
  fi

  cert_pem=$(fetch_leaf_cert_pem "$DOMAIN" "$p" "$DOMAIN")
  if [[ -z "$cert_pem" ]]; then
    messages+=("Port $p: CRIT - kein Zertifikat abrufbar")
    ((crit_count++))
    overall_status=2
    continue
  fi

  port_status=0
  matched=0
  errors=()

  while IFS= read -r line; do
    [[ -z "$line" ]] && continue
    line=$(echo "$line" | sed -e 's/^"//' -e 's/"$//')
    usage_val=$(echo "$line" | awk '{print $1}')
    selector=$(echo "$line" | awk '{print $2}')
    mtype=$(echo "$line" | awk '{print $3}')
    data=$(echo "$line" | cut -d' ' -f4-)
    data=$(normalize_hex "$data")

    tmp_der="/tmp/_tlsa_${p}_$$.der"

    if [[ "$selector" == "0" ]]; then
      pem_to_der "$cert_pem" "$tmp_der" || { errors+=("Fehler beim Konvertieren (full cert)"); port_status=2; continue; }
    else
      pem_to_spki_der "$cert_pem" "$tmp_der" || { errors+=("Fehler beim Konvertieren (SPKI)"); port_status=2; continue; }
    fi

    case "$mtype" in
      0) got=$(xxd -p -c 999999 "$tmp_der" | tr -d '\n') ;;
      1) got=$(sha256sum "$tmp_der" | awk '{print $1}') ;;
      2) got=$(sha512sum "$tmp_der" | awk '{print $1}') ;;
      *) errors+=("unbekannter mtype=$mtype"); port_status=1; continue ;;
    esac

    rm -f "$tmp_der" /tmp/_tlsa_tmp_cert.pem

    if [[ -n "${got}" && "${got}" == "${data}" ]]; then
      matched=1
    else
      errors+=("TLSA Mismatch (usage=$usage_val selector=$selector mtype=$mtype)")
      port_status=2
    fi
  done <<< "$tlsa_lines"

  if ((port_status == 0 && matched == 1)); then
    messages+=("Port $p: OK - TLSA stimmt überein")
    ((ok_count++))
  elif ((port_status == 1)); then
    messages+=("Port $p: WARN - ${errors[*]}")
    ((warn_count++))
    ((overall_status = overall_status < 1 ? 1 : overall_status))
  else
    messages+=("Port $p: CRIT - ${errors[*]}")
    ((crit_count++))
    overall_status=2
  fi
done

output=$(printf "%s\n" "${messages[@]}")

if ((overall_status == 0)); then
  echo -e "OK - TLSA Validation erfolgreich\n$output | ports=${#ports[@]} ok=$ok_count warn=$warn_count crit=$crit_count"
elif ((overall_status == 1)); then
  echo -e "WARN - TLSA Probleme erkannt\n$output | ports=${#ports[@]} ok=$ok_count warn=$warn_count crit=$crit_count"
else
  echo -e "CRIT - TLSA Fehler erkannt\n$output | ports=${#ports[@]} ok=$ok_count warn=$warn_count crit=$crit_count"
fi

exit $overall_status

Read more

Wireguard | Tunnel Status überwachen

Wireguard | Tunnel Status überwachen

Automatischer Neustart des WireGuard-Tunnels bei Verbindungsverlust Diese Anleitung beschreibt, wie du deinen WireGuard-Tunnel (wg-quick) automatisch neu starten kannst, wenn der Remote-Endpunkt nicht erreichbar ist. 1️⃣ systemd-Konfiguration für automatischen Neustart WireGuard-Interfaces, die mit wg-quick up wg0 gestartet werden, erzeugen normalerweise einen systemd-Dienst namens [email protected]. Bearbeite die Service-Konfiguration: sudo systemctl