diff --git a/app/Console/Commands/SpamAvCollectCommand.php b/app/Console/Commands/SpamAvCollectCommand.php index bed9a37..38b5614 100644 --- a/app/Console/Commands/SpamAvCollectCommand.php +++ b/app/Console/Commands/SpamAvCollectCommand.php @@ -15,42 +15,117 @@ class SpamAvCollectCommand extends Command { $this->info('Collecting Spam/AV metrics…'); - // Rspamd counters (kein Root nötig) - $out = trim(@shell_exec('rspamc counters 2>/dev/null') ?? ''); - $ham = preg_match('/\bham\s*:\s*(\d+)/i', $out, $m1) ? (int)$m1[1] : 0; - $spam = preg_match('/\bspam\s*:\s*(\d+)/i', $out, $m2) ? (int)$m2[1] : 0; - $reject = preg_match('/\breject\s*:\s*(\d+)/i', $out, $m3) ? (int)$m3[1] : 0; + $rpC = '/usr/bin/rspamc'; + $rpA = '/usr/bin/rspamadm'; - // ClamAV Version + Signatur-Datum robust ohne Datei-Zugriff - $clamLine = trim((string) @shell_exec('clamd --version 2>/dev/null || clamscan --version 2>/dev/null')); - $clamVer = $clamLine !== '' ? $clamLine : '–'; + // --- RSPAMD: erst 'stat' (sicher), sonst 'counters' als Fallback --- + $ham = $spam = $reject = 0; - // Aus clamd/clamscan-Output das Datum am Ende herausziehen (Format: ".../Sun Oct 26 09:42:43 2025") + $stat = trim(@shell_exec("$rpC -h 127.0.0.1:11334 stat 2>/dev/null") ?? ''); + if ($stat !== '') { + $ham = preg_match('/Messages treated as ham:\s*(\d+)/i', $stat, $m) ? (int)$m[1] : 0; + $spam = preg_match('/Messages treated as spam:\s*(\d+)/i', $stat, $m) ? (int)$m[1] : 0; + $reject = preg_match('/Messages with action reject:\s*(\d+)/i', $stat, $m) ? (int)$m[1] : 0; + } else { + $cnt = trim(@shell_exec("$rpC -h 127.0.0.1:11334 counters 2>/dev/null") ?? ''); + $ham = preg_match('/\bham\s*:\s*(\d+)/i', $cnt, $m) ? (int)$m[1] : 0; + $spam = preg_match('/\bspam\s*:\s*(\d+)/i', $cnt, $m) ? (int)$m[1] : 0; + $reject = preg_match('/\breject\s*:\s*(\d+)/i', $cnt, $m) ? (int)$m[1] : 0; + } + + // Rspamd Version + $rspamdVer = trim(@shell_exec("$rpA --version 2>/dev/null") ?? ''); + if ($rspamdVer === '') { + $rspamdVer = trim(@shell_exec("dpkg-query -W -f='\${Version}\n' rspamd 2>/dev/null") ?? '–'); + } + + // --- CLAMAV: Versionzeile inkl. Signaturdatum ausgeben --- + $clamLine = trim(@shell_exec('clamd --version 2>/dev/null || clamscan --version 2>/dev/null') ?? '') ?: '–'; + $clamVer = $clamLine; + + // Versuch, Datum aus der Versionzeile zu ziehen (kein Zugriff auf freshclam.log nötig) $sigUpdated = null; - if ($clamLine && preg_match('#/([^/]+\d{4})$#', $clamLine, $m)) { - // $m[1] ist z.B. "Sun Oct 26 09:42:43 2025" - $sigUpdated = $m[1]; + if (preg_match('#/([^/]+ [0-9]{2} [0-9:]{8} [0-9]{4})$#', $clamLine, $m)) { + $sigUpdated = $m[1]; // z.B. "Sun Oct 26 09:42:43 2025" + } else { + // Fallback: journalctl (falls Gruppe adm) + $jl = trim(@shell_exec('journalctl -u freshclam -n 50 --no-pager 2>/dev/null | grep -i "Database updated" | tail -n1') ?? ''); + if ($jl) $sigUpdated = $jl; } $data = [ - 'ts' => time(), - 'ham' => $ham, - 'spam' => $spam, - 'reject' => $reject, - 'rspamdVer' => trim((string) @shell_exec('rspamadm version 2>/dev/null')) ?: '–', - 'clamVer' => $clamVer, + 'ts' => time(), + 'ham' => $ham, + 'spam' => $spam, + 'reject' => $reject, + 'rspamdVer' => $rspamdVer ?: '–', + 'clamVer' => $clamVer, 'sigUpdated' => $sigUpdated, ]; - // Persistieren (DB→Redis) + kurzer UI-Cache Setting::set('spamav.metrics', $data); Cache::put('dash.spamav', $data, 60); $this->info(sprintf( 'ham=%d spam=%d reject=%d | rspamd=%s | clam=%s', - $data['ham'], $data['spam'], $data['reject'], $data['rspamdVer'], $data['clamVer'] + $ham, $spam, $reject, $data['rspamdVer'], $clamVer )); return self::SUCCESS; } } +// +//namespace App\Console\Commands; +// +//use Illuminate\Console\Command; +//use Illuminate\Support\Facades\Cache; +//use App\Models\Setting; +// +//class SpamAvCollectCommand extends Command +//{ +// protected $signature = 'spamav:collect'; +// protected $description = 'Collect Rspamd/ClamAV metrics and persist to Settings (DB→Redis)'; +// +// public function handle(): int +// { +// $this->info('Collecting Spam/AV metrics…'); +// +// // Rspamd counters (kein Root nötig) +// $out = trim(@shell_exec('rspamc counters 2>/dev/null') ?? ''); +// $ham = preg_match('/\bham\s*:\s*(\d+)/i', $out, $m1) ? (int)$m1[1] : 0; +// $spam = preg_match('/\bspam\s*:\s*(\d+)/i', $out, $m2) ? (int)$m2[1] : 0; +// $reject = preg_match('/\breject\s*:\s*(\d+)/i', $out, $m3) ? (int)$m3[1] : 0; +// +// // ClamAV Version + Signatur-Datum robust ohne Datei-Zugriff +// $clamLine = trim((string) @shell_exec('clamd --version 2>/dev/null || clamscan --version 2>/dev/null')); +// $clamVer = $clamLine !== '' ? $clamLine : '–'; +// +// // Aus clamd/clamscan-Output das Datum am Ende herausziehen (Format: ".../Sun Oct 26 09:42:43 2025") +// $sigUpdated = null; +// if ($clamLine && preg_match('#/([^/]+\d{4})$#', $clamLine, $m)) { +// // $m[1] ist z.B. "Sun Oct 26 09:42:43 2025" +// $sigUpdated = $m[1]; +// } +// +// $data = [ +// 'ts' => time(), +// 'ham' => $ham, +// 'spam' => $spam, +// 'reject' => $reject, +// 'rspamdVer' => trim((string) @shell_exec('rspamadm version 2>/dev/null')) ?: '–', +// 'clamVer' => $clamVer, +// 'sigUpdated' => $sigUpdated, +// ]; +// +// // Persistieren (DB→Redis) + kurzer UI-Cache +// Setting::set('spamav.metrics', $data); +// Cache::put('dash.spamav', $data, 60); +// +// $this->info(sprintf( +// 'ham=%d spam=%d reject=%d | rspamd=%s | clam=%s', +// $data['ham'], $data['spam'], $data['reject'], $data['rspamdVer'], $data['clamVer'] +// )); +// +// return self::SUCCESS; +// } +//} diff --git a/app/Livewire/Ui/Security/SpamAvCard.php b/app/Livewire/Ui/Security/SpamAvCard.php index 620172d..c6ed2f5 100644 --- a/app/Livewire/Ui/Security/SpamAvCard.php +++ b/app/Livewire/Ui/Security/SpamAvCard.php @@ -52,11 +52,11 @@ class SpamAvCard extends Component } // 3) Auf Properties mappen - $this->ham = (int)($data['ham'] ?? 0); - $this->spam = (int)($data['spam'] ?? 0); - $this->reject = (int)($data['reject'] ?? 0); - $this->rspamdVer = (string)($data['rspamdVer'] ?? '–'); - $this->clamVer = (string)($data['clamVer'] ?? '–'); + $this->ham = (int)($data['ham'] ?? 0); + $this->spam = (int)($data['spam'] ?? 0); + $this->reject = (int)($data['reject'] ?? 0); + $this->rspamdVer = (string)($data['rspamdVer'] ?? '–'); // <- wichtig + $this->clamVer = (string)($data['clamVer'] ?? '–'); $this->sigUpdated = $data['sigUpdated'] ?? null; } @@ -77,13 +77,6 @@ class SpamAvCard extends Component $sigUpdated = $m[1]; } -//// Fallback: Versuch über journalctl (falls adm-Rechte vorhanden) -// if (!$sigUpdated) { -// $jl = trim(shell_exec('journalctl -u freshclam -n 50 --no-pager 2>/dev/null | grep -i "Database updated" | tail -n1') ?? ''); -// if ($jl) $sigUpdated = $jl; -// } - - return [ 'ts' => time(), 'ham' => $ham,