From 251f2d9c8fa79252c1995e8663de71b2bc389dcf Mon Sep 17 00:00:00 2001 From: boban Date: Wed, 29 Oct 2025 18:34:08 +0100 Subject: [PATCH] =?UTF-8?q?Fix:=20Mailbox=20Stats=20=C3=BCber=20Dovecot=20?= =?UTF-8?q?mit=20config/mailpool.php?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Exceptions/Handler.php | 26 ++++ app/Livewire/Ui/Security/Fail2BanCard.php | 125 +++++++++++++++--- resources/js/utils/events.js | 6 + resources/views/auth/login.blade.php | 2 +- resources/views/auth/signup.blade.php | 2 +- resources/views/layouts/app.blade.php | 14 ++ resources/views/layouts/blank.blade.php | 29 ++++ .../ui/mail/dns-health-card.blade.php | 3 +- .../ui/security/fail2-ban-card.blade.php | 75 ++++++++--- .../livewire/ui/security/rbl-card.blade.php | 4 +- 10 files changed, 246 insertions(+), 40 deletions(-) create mode 100644 app/Exceptions/Handler.php create mode 100644 resources/views/layouts/blank.blade.php diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php new file mode 100644 index 0000000..7d75c99 --- /dev/null +++ b/app/Exceptions/Handler.php @@ -0,0 +1,26 @@ +expectsJson()) { + return response()->json([ + 'message' => 'session_expired', + 'redirect' => route('login'), + ], 419); + } + return redirect() + ->route('login') + ->with('warning', 'Deine Sitzung ist abgelaufen. Bitte melde dich erneut an.'); + } + return parent::render($request, $e); + } +} diff --git a/app/Livewire/Ui/Security/Fail2BanCard.php b/app/Livewire/Ui/Security/Fail2BanCard.php index 60fd589..274602e 100644 --- a/app/Livewire/Ui/Security/Fail2BanCard.php +++ b/app/Livewire/Ui/Security/Fail2BanCard.php @@ -6,29 +6,116 @@ use Livewire\Component; class Fail2BanCard extends Component { - public int $activeBans = 0; - public array $topIps = []; // [['ip'=>'1.2.3.4','count'=>12],...] + public bool $available = true; // ob fail2ban vorhanden ist + public int $activeBans = 0; // Summe über alle Jails + /** @var array}> */ + public array $jails = []; // je Jail: Name, Anzahl, IPs (gekürzt) + /** @var array */ + public array $topIps = []; // Top IPs aus Log/Journal (Ban-Events) - public function mount(): void { $this->load(); } - public function render() { return view('livewire.ui.security.fail2-ban-card'); } - public function refresh(): void { $this->load(true); } - - protected function load(bool $force=false): void + public function mount(): void { - $status = @shell_exec('fail2ban-client status 2>/dev/null') ?? ''; - $bans = preg_match('/Currently banned:\s+(\d+)/i', $status, $m) ? (int)$m[1] : 0; - $this->activeBans = $bans; + $this->load(); + } - // quick & rough: last 1000 lines auth/mail logs → top IPs - $log = @shell_exec('tail -n 1000 /var/log/auth.log /var/log/mail.log 2>/dev/null | grep -Eo "([0-9]{1,3}\.){3}[0-9]{1,3}" | sort | uniq -c | sort -nr | head -5'); - $rows = []; - if ($log) { - foreach (preg_split('/\R+/', trim($log)) as $l) { - if (preg_match('/^\s*(\d+)\s+(\d+\.\d+\.\d+\.\d+)/', $l, $m)) { - $rows[] = ['ip'=>$m[2],'count'=>(int)$m[1]]; - } + public function render() + { + return view('livewire.ui.security.fail2-ban-card'); + } + + public function refresh(): void + { + $this->load(true); + } + + protected function load(bool $force = false): void + { + // 0) vorhanden? + $bin = trim((string) @shell_exec('command -v fail2ban-client 2>/dev/null')) ?: ''; + if ($bin === '') { + $this->available = false; + $this->activeBans = 0; + $this->jails = []; + $this->topIps = []; + return; + } + + // 1) Jails ermitteln + $status = (string) (@shell_exec("timeout 2 $bin status 2>/dev/null") ?? ''); + $jailsLn = $this->firstMatch('/Jail list:\s*(.+)$/mi', $status); + $jails = $jailsLn ? array_filter(array_map('trim', preg_split('/\s*,\s*/', $jailsLn))) : []; + + $total = 0; + $rows = []; + + foreach ($jails as $j) { + $s = (string) (@shell_exec("timeout 2 $bin status ".escapeshellarg($j)." 2>/dev/null") ?? ''); + $banned = (int) ($this->firstMatch('/Currently banned:\s+(\d+)/i', $s) ?: 0); + + // „Banned IP list:“ kann fehlen → leeres Array + $ipList = $this->firstMatch('/Banned IP list:\s*(.+)$/mi', $s) ?: ''; + $ips = $ipList !== '' ? array_values(array_filter(array_map('trim', preg_split('/\s+/', $ipList)))) : []; + + $rows[] = [ + 'name' => $j, + 'banned' => $banned, + // nur die ersten 8 zur UI-Anzeige + 'ips' => array_slice($ips, 0, 8), + ]; + $total += $banned; + } + + $this->available = true; + $this->activeBans = $total; + $this->jails = $rows; + + // 2) Top-IPs aus den letzten Ban-Events + $this->topIps = $this->collectTopIps(); + } + + /** Extrahiert erste Regex-Gruppe oder null */ + private function firstMatch(string $pattern, string $haystack): ?string + { + return preg_match($pattern, $haystack, $m) ? trim($m[1]) : null; + } + + /** Zählt „Ban “ aus fail2ban.log (Fallback: journalctl) */ + private function collectTopIps(): array + { + $lines = ''; + if (is_readable('/var/log/fail2ban.log')) { + // letzte 500 Zeilen reichen völlig + $lines = (string) (@shell_exec('tail -n 500 /var/log/fail2ban.log 2>/dev/null') ?? ''); + } + + if ($lines === '') { + // Fallback: Journal (falls rsyslog die Datei nicht schreibt) + $lines = (string) (@shell_exec('timeout 2 journalctl -u fail2ban -n 500 --no-pager 2>/dev/null') ?? ''); + } + + if ($lines === '') { + return []; + } + + // Nur Ban-Events, IP extrahieren + $ips = []; + foreach (preg_split('/\R+/', $lines) as $ln) { + if (stripos($ln, 'Ban ') === false) continue; + if (preg_match('/\b(\d{1,3}(?:\.\d{1,3}){3})\b/', $ln, $m)) { + $ip = $m[1]; + $ips[$ip] = ($ips[$ip] ?? 0) + 1; } } - $this->topIps = $rows; + + if (!$ips) return []; + + arsort($ips); + $top = array_slice($ips, 0, 5, true); + + $out = []; + foreach ($top as $ip => $cnt) { + $out[] = ['ip' => $ip, 'count' => (int)$cnt]; + } + return $out; } } diff --git a/resources/js/utils/events.js b/resources/js/utils/events.js index 5f382e4..fcfc0ed 100644 --- a/resources/js/utils/events.js +++ b/resources/js/utils/events.js @@ -1,5 +1,11 @@ Livewire.on('reload-page', e => setTimeout(() => window.location.reload(), e.delay || 0)); +// document.addEventListener('livewire:error', e => { +// if (e.detail.status === 419) { +// console.warn('Session expired – refreshing...'); +// window.location.reload(); +// } +// }); // // import { showToast } from '../ui/toast.js' // // // — Livewire-Hooks (global) diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php index 5b50058..a4b1e9d 100644 --- a/resources/views/auth/login.blade.php +++ b/resources/views/auth/login.blade.php @@ -1,5 +1,5 @@ {{-- resources/views/auth/login.blade.php --}} -@extends('layouts.app') +@extends('layouts.blank') @section('title', 'Login') diff --git a/resources/views/auth/signup.blade.php b/resources/views/auth/signup.blade.php index 0f6c965..7f7b207 100644 --- a/resources/views/auth/signup.blade.php +++ b/resources/views/auth/signup.blade.php @@ -1,4 +1,4 @@ -@extends('layouts.app') +@extends('layouts.blank') @section('title', 'Konto erstellen') diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php index e9dc459..99764c5 100644 --- a/resources/views/layouts/app.blade.php +++ b/resources/views/layouts/app.blade.php @@ -34,6 +34,20 @@ @vite(['resources/js/app.js']) @livewireScripts @livewire('wire-elements-modal') + {{----}} diff --git a/resources/views/layouts/blank.blade.php b/resources/views/layouts/blank.blade.php new file mode 100644 index 0000000..284f7a1 --- /dev/null +++ b/resources/views/layouts/blank.blade.php @@ -0,0 +1,29 @@ + + + + + + + + @yield('title', config('app.name')) + + @vite(['resources/css/app.css']) + @livewireStyles + + + + +
+
+
+
+ @yield('content') +
+
+
+ +@vite(['resources/js/app.js']) +@livewireScripts +@livewire('wire-elements-modal') + + diff --git a/resources/views/livewire/ui/mail/dns-health-card.blade.php b/resources/views/livewire/ui/mail/dns-health-card.blade.php index 6de937a..61e6fd5 100644 --- a/resources/views/livewire/ui/mail/dns-health-card.blade.php +++ b/resources/views/livewire/ui/mail/dns-health-card.blade.php @@ -7,6 +7,7 @@
@@ -16,7 +17,7 @@ @forelse($rows as $r) + + @endif diff --git a/resources/views/livewire/ui/security/rbl-card.blade.php b/resources/views/livewire/ui/security/rbl-card.blade.php index 9e2fab0..f53aa6b 100644 --- a/resources/views/livewire/ui/security/rbl-card.blade.php +++ b/resources/views/livewire/ui/security/rbl-card.blade.php @@ -54,9 +54,7 @@