> ## Content Index
> Fetch the complete content index at: https://blog.lange-it.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# TLSA | Validiere TLSA Records mit CheckMK
- URL: https://blog.lange-it.com/tlsa-validiere-tlsa-records-mit-checkmk/
- Published: 2025-10-28T11:50:34.000Z
- Updated: 2026-03-01T15:17:39.000Z
- Author: Peter Lange
- Tags: SSL-Security, TLSA, Checkmk, Monitoring

# 🧩 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:

```bash
/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

```bash
#!/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

```