diff --git a/services/posture-check/daily-status-report.sh b/services/posture-check/daily-status-report.sh index 2b3c53a..13048ef 100755 --- a/services/posture-check/daily-status-report.sh +++ b/services/posture-check/daily-status-report.sh @@ -29,6 +29,7 @@ TRAEFIK_ACME_PATH="${TRAEFIK_ACME_PATH:-/mnt/user/appdata/traefik/letsencrypt/ac NOISE_PATTERNS_FILE="${NOISE_PATTERNS_FILE:-/mnt/user/services/homelab-infra/services/posture-check/log-noise.patterns}" NORMALIZE_NOISE_SCRIPT="${NORMALIZE_NOISE_SCRIPT:-/mnt/user/services/homelab-infra/services/posture-check/lib/normalize-noise-patterns.sh}" NOISE_ESCALATION_THRESHOLD="${NOISE_ESCALATION_THRESHOLD:-500}" +NOISE_ESCALATION_EXEMPT_FILE="${NOISE_ESCALATION_EXEMPT_FILE:-/mnt/user/services/homelab-infra/services/posture-check/noise-escalation-exempt.patterns}" NOISE_BREAKDOWN_TOP_N="${NOISE_BREAKDOWN_TOP_N:-10}" POSTURE_CHECK_FILE="${POSTURE_CHECK_FILE:-/mnt/user/services/posture-check/last.json}" LOCK_FILE="${LOCK_FILE:-/tmp/homelab-daily-report.lock}" @@ -880,12 +881,35 @@ collect_log_highlights() { fi fi - # Threshold escalation: how many patterns produced more than the threshold? - local noise_threshold_exceeded=0 + # Escalation-exempt patterns: known noise that is also permanently very loud + # (e.g. Unraid mdadm parse spam). Without this, such a pattern would keep the + # report stuck at >= WARNUNG forever and devalue the OK/WARNUNG/KRITISCH + # signal. Exempt patterns are still counted/shown as noise, but do NOT count + # toward noise_threshold_exceeded. New/unexpected loud patterns still escalate. + local noise_exempt="$TMP_DIR/noise-escalation-exempt.normalized" + : > "$noise_exempt" + if [ -f "$NOISE_ESCALATION_EXEMPT_FILE" ]; then + grep -Ev '^[[:space:]]*(#|$)' "$NOISE_ESCALATION_EXEMPT_FILE" 2>/dev/null \ + | sed -E 's/^[[:space:]]+//; s/[[:space:]]+$//' \ + | grep -v '^$' > "$noise_exempt" || : > "$noise_exempt" + fi + + # Threshold escalation: how many NON-exempt patterns exceeded the threshold? + local noise_threshold_exceeded=0 noise_threshold_exempt=0 if [ -s "$noise_by_pattern" ]; then - noise_threshold_exceeded="$(awk -v t="$NOISE_ESCALATION_THRESHOLD" '$1 > t { n++ } END { print n + 0 }' "$noise_by_pattern")" + noise_threshold_exceeded="$(awk -F '\t' -v t="$NOISE_ESCALATION_THRESHOLD" ' + NR == FNR { exempt[$0] = 1; next } + $1 > t && !($2 in exempt) { n++ } + END { print n + 0 } + ' "$noise_exempt" "$noise_by_pattern")" + noise_threshold_exempt="$(awk -F '\t' -v t="$NOISE_ESCALATION_THRESHOLD" ' + NR == FNR { exempt[$0] = 1; next } + $1 > t && ($2 in exempt) { n++ } + END { print n + 0 } + ' "$noise_exempt" "$noise_by_pattern")" fi set_summary "noise_threshold_exceeded" "$noise_threshold_exceeded" + set_summary "noise_threshold_exempt" "$noise_threshold_exempt" local hit_count attention_count known_noise_count hit_count="$(count_lines < "$hits")" @@ -906,6 +930,9 @@ collect_log_highlights() { if [ "$noise_threshold_exceeded" -gt 0 ]; then append "- WARNUNG: $noise_threshold_exceeded Pattern ueberschreit(en) die Schwelle - bitte pruefen ob noch wirklich Noise." fi + if [ "${noise_threshold_exempt:-0}" -gt 0 ]; then + append "- Hinweis: $noise_threshold_exempt laute(s) Pattern ist/sind als bewusst eskalations-befreit markiert (siehe \`$NOISE_ESCALATION_EXEMPT_FILE\`) und loesen keine WARNUNG aus." + fi append "" if [ "$attention_count" -eq 0 ]; then @@ -955,22 +982,32 @@ collect_log_highlights() { if [ -s "$noise_by_pattern" ]; then append "#### Pattern mit den meisten Treffern" append "" - append "| Pattern | Anzahl |" - append "|---|---:|" + append "| Pattern | Anzahl | Hinweis |" + append "|---|---:|---|" head -n "$NOISE_BREAKDOWN_TOP_N" "$noise_by_pattern" \ | while IFS="$(printf '\t')" read -r cnt pat; do - local short="$pat" + local short="$pat" note="" + # Mark patterns that are deliberately exempt from escalation. + if [ -s "$noise_exempt" ] && grep -Fxq -- "$pat" "$noise_exempt"; then + if [ "$cnt" -gt "$NOISE_ESCALATION_THRESHOLD" ]; then + note="eskalations-befreit" + fi + elif [ "$cnt" -gt "$NOISE_ESCALATION_THRESHOLD" ]; then + note="ueber Schwelle" + fi if [ "${#short}" -gt 80 ]; then short="${short:0:77}..." fi # Escape pipe characters that would break the markdown table. short="${short//|/\\|}" - append "| \`$short\` | $cnt |" + append "| \`$short\` | $cnt | $note |" done append "" fi if [ "$noise_threshold_exceeded" -gt 0 ]; then - append "Bewertung: $noise_threshold_exceeded Pattern ueberschreit(en) die Eskalations-Schwelle ($NOISE_ESCALATION_THRESHOLD). Bitte pruefen, ob die als Noise eingeordneten Meldungen noch fachlich Noise sind oder ob sich ein echter Vorfall darunter versteckt." + append "Bewertung: $noise_threshold_exceeded nicht-befreite(s) Pattern ueberschreit(en) die Eskalations-Schwelle ($NOISE_ESCALATION_THRESHOLD). Bitte pruefen, ob die als Noise eingeordneten Meldungen noch fachlich Noise sind oder ob sich ein echter Vorfall darunter versteckt." + elif [ "${noise_threshold_exempt:-0}" -gt 0 ]; then + append "Bewertung: Kein nicht-befreites Pattern ueberschreitet die Eskalations-Schwelle ($NOISE_ESCALATION_THRESHOLD). $noise_threshold_exempt lautes Pattern ist bewusst eskalations-befreit und mit Begruendung dokumentiert." else append "Bewertung: Kein Pattern ueberschreitet die Eskalations-Schwelle ($NOISE_ESCALATION_THRESHOLD)." fi diff --git a/services/posture-check/noise-escalation-exempt.patterns b/services/posture-check/noise-escalation-exempt.patterns new file mode 100644 index 0000000..0430fe8 --- /dev/null +++ b/services/posture-check/noise-escalation-exempt.patterns @@ -0,0 +1,32 @@ +# noise-escalation-exempt.patterns - Daily Operations Report +# +# Pattern, die als Rauschen bekannt UND dauerhaft sehr laut sind, sollen die +# Eskalations-Schwelle (NOISE_ESCALATION_THRESHOLD) nicht in eine WARNUNG +# uebersetzen. Ohne diese Ausnahme haengt der Report-Status strukturell auf +# >= WARNUNG fest (z. B. mdadm-Noise auf Unraid feuert dauerhaft > 5000/Tag), +# was die OK/WARNUNG/KRITISCH-Ampel entwertet. +# +# Wirkung: Ein hier gelistetes Pattern wird weiterhin als Noise gezaehlt und +# in der Breakdown-Tabelle gezeigt (mit Markierung "eskalations-befreit"), +# zaehlt aber NICHT mehr zu noise_threshold_exceeded. Neue/unerwartete laute +# Patterns loesen weiterhin eine WARNUNG aus. +# +# Format: +# - Exakte Pattern-Zeile wie in log-noise.patterns (nach Normalisierung: +# getrimmt, ohne Kommentar). Muss zeichengenau dem Eintrag entsprechen. +# - Zeilen mit '#' sind Kommentare, Leerzeilen werden ignoriert. +# +# Eine Befreiung heisst NICHT "ignorieren", sondern "Volumen ist als Noise +# akzeptiert; nur die ESKALATION ist abgeschaltet". +# +# Last reviewed: 2026-06-10 + +# node-exporter kann /proc/mdstat auf Unraid nicht parsen (eigener Array- +# Treiber, kein Linux-mdadm). Dauerhaft > 5000/Tag, rein kosmetisch. +# Re-check: nur bei Migration auf echtes mdadm-RAID. +monitoring-node-exporter.*mdadm.*Cannot parse /host/proc/mdstat + +# Fritz!Box sendet RFC-1035-widrige Multi-Question-SOA-Queries fuer +# myfritz.net/myfritz.link; AdGuard lehnt sie ab. ~1000+/Tag, kein Impact. +# Re-check: falls derselbe Fehler fuer Nicht-AVM-Domains auftaucht. +adguard.*bad question section.*only 1 question allowed