From 7ee42f90e625260dde724e2d79723917637bef9c Mon Sep 17 00:00:00 2001 From: Thore Cimbal Date: Thu, 20 Aug 2026 12:00:00 +0000 Subject: [PATCH] monitoring: Dashboard fuer blockierte ClamAV-Inhalte (management #0077) Sichtbar machen, wie oft ClamAV tatsaechlich etwas blockiert - bisher nur in Logzeilen zu finden. Zwei Abweichungen vom Issue-Text, beide gemessen statt angenommen: 1. Das Issue schlaegt {app="synapse-main"} vor. Das Label app existiert in dieser Loki nicht - Cluster-Logs liegen unter job="loki.source.kubernetes.k8s_logs" mit instance="/:". Die vorgeschlagene Query haette dauerhaft ein leeres Panel ergeben. 2. Das Issue kennt nur das Synapse-Modul. Seit dem Client-seitigen Scannen gibt es eine zweite Quelle (clamav-http-scanner), und die ist die wichtigere: Sie greift beim Senden UND beim Empfangen und deckt damit auch verschluesselte Raeume ab. Das Synapse-Modul sieht nur unverschluesselte Uploads - deshalb steht es dort auf null, waehrend der Client sieben Treffer zeigt. Drittes Panel zeigt Fail-open-Faelle: Ist clamd nicht erreichbar, laesst der Scanner Dateien bewusst durch. Ohne dieses Panel bliebe genau das unsichtbar - rot ab dem ersten Fall. Alle acht Queries gegen die laufende Loki geprueft, nicht nur geschrieben. --- .../dashboards/security/clamav-blocks.json | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 monitoring/grafana/dashboards/security/clamav-blocks.json diff --git a/monitoring/grafana/dashboards/security/clamav-blocks.json b/monitoring/grafana/dashboards/security/clamav-blocks.json new file mode 100644 index 0000000..4e01f9b --- /dev/null +++ b/monitoring/grafana/dashboards/security/clamav-blocks.json @@ -0,0 +1,146 @@ +{ + "uid": "threadnet-clamav", + "title": "ClamAV — blockierte Inhalte", + "description": "Wie oft ClamAV tatsaechlich etwas blockiert (management #0077). Zwei Quellen: der Client-seitige Scanner (clamav-http-scanner, greift beim Senden UND beim Empfangen, deckt auch E2E-verschluesselte Raeume ab) und das Synapse-Modul (nur unverschluesselte Uploads). Das dritte Panel zeigt Fail-open-Faelle - wenn clamd nicht erreichbar ist, laesst der Scanner Dateien bewusst durch; ohne dieses Panel bliebe das unsichtbar.", + "tags": ["security", "clamav", "matrix"], + "timezone": "browser", + "schemaVersion": 41, + "editable": true, + "time": { "from": "now-7d", "to": "now" }, + "refresh": "5m", + "panels": [ + { + "type": "stat", + "title": "Blockiert — Client (Senden + Empfangen)", + "description": "clamav-http-scanner. Deckt auch verschluesselte Raeume ab, weil der Client vor dem Verschluesseln bzw. nach dem Entschluesseln prueft.", + "gridPos": { "h": 4, "w": 8, "x": 0, "y": 0 }, + "datasource": { "type": "loki", "uid": "cfk1pam8svmkgd" }, + "targets": [ + { + "refId": "A", + "datasource": { "type": "loki", "uid": "cfk1pam8svmkgd" }, + "queryType": "instant", + "expr": "sum(count_over_time({job=\"loki.source.kubernetes.k8s_logs\", instance=~\"matrix/clamav-http-scanner.*\"} |= \"ClamAV flagged\" [$__range]))" + } + ], + "options": { "reduceOptions": { "calcs": ["lastNotNull"] }, "colorMode": "value", "graphMode": "none" }, + "fieldConfig": { + "defaults": { + "unit": "short", + "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "orange", "value": 1 } ] } + }, + "overrides": [] + } + }, + { + "type": "stat", + "title": "Blockiert — Synapse-Modul", + "description": "Server-seitiges Modul, sieht nur unverschluesselte Uploads. Null ist hier der Normalfall, solange der Client schon vor dem Upload blockt.", + "gridPos": { "h": 4, "w": 8, "x": 8, "y": 0 }, + "datasource": { "type": "loki", "uid": "cfk1pam8svmkgd" }, + "targets": [ + { + "refId": "A", + "datasource": { "type": "loki", "uid": "cfk1pam8svmkgd" }, + "queryType": "instant", + "expr": "sum(count_over_time({job=\"loki.source.kubernetes.k8s_logs\", instance=~\"matrix/matrix-stack-synapse.*\"} |= \"ClamAV rejected an upload\" [$__range]))" + } + ], + "options": { "reduceOptions": { "calcs": ["lastNotNull"] }, "colorMode": "value", "graphMode": "none" }, + "fieldConfig": { + "defaults": { + "unit": "short", + "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "orange", "value": 1 } ] } + }, + "overrides": [] + } + }, + { + "type": "stat", + "title": "Fail-open — Scanner nicht erreichbar", + "description": "Wenn clamd nicht antwortet, laesst der Scanner die Datei bewusst durch (fail-open). Jeder Treffer hier ist eine Datei, die UNGEPRUEFT durchging - deshalb rot ab eins.", + "gridPos": { "h": 4, "w": 8, "x": 16, "y": 0 }, + "datasource": { "type": "loki", "uid": "cfk1pam8svmkgd" }, + "targets": [ + { + "refId": "A", + "datasource": { "type": "loki", "uid": "cfk1pam8svmkgd" }, + "queryType": "instant", + "expr": "sum(count_over_time({job=\"loki.source.kubernetes.k8s_logs\", instance=~\"matrix/clamav-http-scanner.*\"} |= \"ClamAV scan failed\" [$__range]))" + } + ], + "options": { "reduceOptions": { "calcs": ["lastNotNull"] }, "colorMode": "background", "graphMode": "none" }, + "fieldConfig": { + "defaults": { + "unit": "short", + "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "red", "value": 1 } ] } + }, + "overrides": [] + } + }, + { + "type": "timeseries", + "title": "Blockierte Inhalte ueber Zeit", + "gridPos": { "h": 8, "w": 14, "x": 0, "y": 4 }, + "datasource": { "type": "loki", "uid": "cfk1pam8svmkgd" }, + "targets": [ + { + "refId": "A", + "datasource": { "type": "loki", "uid": "cfk1pam8svmkgd" }, + "legendFormat": "Client (Senden/Empfangen)", + "expr": "sum(count_over_time({job=\"loki.source.kubernetes.k8s_logs\", instance=~\"matrix/clamav-http-scanner.*\"} |= \"ClamAV flagged\" [$__interval]))" + }, + { + "refId": "B", + "datasource": { "type": "loki", "uid": "cfk1pam8svmkgd" }, + "legendFormat": "Synapse-Modul", + "expr": "sum(count_over_time({job=\"loki.source.kubernetes.k8s_logs\", instance=~\"matrix/matrix-stack-synapse.*\"} |= \"ClamAV rejected an upload\" [$__interval]))" + }, + { + "refId": "C", + "datasource": { "type": "loki", "uid": "cfk1pam8svmkgd" }, + "legendFormat": "Fail-open", + "expr": "sum(count_over_time({job=\"loki.source.kubernetes.k8s_logs\", instance=~\"matrix/clamav-http-scanner.*\"} |= \"ClamAV scan failed\" [$__interval]))" + } + ], + "fieldConfig": { + "defaults": { "unit": "short", "custom": { "drawStyle": "bars", "fillOpacity": 60, "stacking": { "mode": "normal" } } }, + "overrides": [] + }, + "options": { "legend": { "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "mode": "multi" } } + }, + { + "type": "table", + "title": "Nach Signatur", + "description": "Welche Schadsoftware tatsaechlich auftaucht. Die Signatur wird aus der Logzeile geschnitten.", + "gridPos": { "h": 8, "w": 10, "x": 14, "y": 4 }, + "datasource": { "type": "loki", "uid": "cfk1pam8svmkgd" }, + "targets": [ + { + "refId": "A", + "datasource": { "type": "loki", "uid": "cfk1pam8svmkgd" }, + "queryType": "instant", + "format": "table", + "expr": "sum by (signature) (count_over_time({job=\"loki.source.kubernetes.k8s_logs\", instance=~\"matrix/clamav-http-scanner.*\"} |= \"ClamAV flagged\" | regexp \"ClamAV flagged an upload/download: (?P[^\\\\s]+)\" [$__range]))" + } + ], + "transformations": [ { "id": "organize", "options": { "excludeByName": { "Time": true } } } ], + "fieldConfig": { "defaults": {}, "overrides": [] } + }, + { + "type": "logs", + "title": "Ereignisse", + "description": "Rohzeilen beider Quellen, neueste zuerst.", + "gridPos": { "h": 9, "w": 24, "x": 0, "y": 12 }, + "datasource": { "type": "loki", "uid": "cfk1pam8svmkgd" }, + "targets": [ + { + "refId": "A", + "datasource": { "type": "loki", "uid": "cfk1pam8svmkgd" }, + "expr": "{job=\"loki.source.kubernetes.k8s_logs\", instance=~\"matrix/(clamav-http-scanner|matrix-stack-synapse).*\"} |~ \"ClamAV (flagged|rejected an upload|scan failed)\"" + } + ], + "options": { "showTime": true, "sortOrder": "Descending", "wrapLogMessage": true } + } + ] +}