From 81860d18510e8350fd26b6581ccc61fab60b8834 Mon Sep 17 00:00:00 2001 From: boban Date: Fri, 31 Oct 2025 03:14:26 +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 --- .../Ui/Security/Modal/Fail2BanJailModal.php | 86 +++++++++++++++---- 1 file changed, 71 insertions(+), 15 deletions(-) diff --git a/app/Livewire/Ui/Security/Modal/Fail2BanJailModal.php b/app/Livewire/Ui/Security/Modal/Fail2BanJailModal.php index 790a448..696dbc3 100644 --- a/app/Livewire/Ui/Security/Modal/Fail2BanJailModal.php +++ b/app/Livewire/Ui/Security/Modal/Fail2BanJailModal.php @@ -21,31 +21,48 @@ class Fail2BanJailModal extends ModalComponent { $jail = $this->jail; - [, $s] = $this->f2b('status '.escapeshellarg($jail)); - $ipList = $this->firstMatch('/Banned IP list:\s*(.+)$/mi', $s) ?: ''; - $ips = $ipList !== '' ? array_values(array_filter(array_map('trim', preg_split('/\s+/', $ipList)))) : []; + [, $s] = $this->f2b('status '.escapeshellarg($jail)); + $ipList = $this->firstMatch('/Banned IP list:\s*(.+)$/mi', $s) ?: ''; + $ips = $ipList !== '' ? array_values(array_filter(array_map('trim', preg_split('/\s+/', $ipList)))) : []; - $bantime = $this->getBantime($jail); + $defaultBantime = $this->getBantime($jail); $rows = []; foreach ($ips as $ip) { - $banAt = $this->lastBanTimestamp($jail, $ip); // Unix-Timestamp oder null + $banAt = null; $until = null; $remaining = null; - $remaining = null; $until = null; - if ($bantime === -1) { - $remaining = -1; // permanent - } elseif ($banAt !== null) { - $remaining = max(0, $bantime - (time() - $banAt)); - $until = $remaining > 0 ? $banAt + $bantime : null; - } else { - $remaining = -2; // ≈ Approximation (Bantime bekannt, Start unbekannt) + // 1) Primärquelle: DB + if ($info = $this->banInfoFromDb($jail, $ip)) { + $banAt = $info['banned_at']; + if ((int)$info['expire'] === -1) { + $remaining = -1; // permanent + } elseif ((int)$info['expire'] > 0) { + $until = (int)$info['expire']; + $remaining = max(0, $until - time()); + } + } + + // 2) Fallback: Log + Jail-Bantime + if ($remaining === null) { + $banAt = $banAt ?? $this->lastBanTimestamp($jail, $ip); + if ($banAt !== null) { + $remaining = max(0, $defaultBantime - (time() - $banAt)); + $until = $remaining > 0 ? $banAt + $defaultBantime : null; + } else { + $remaining = -2; // ~approx + } + } + + // 3) Wenn 0 Sekunden, aber Fail2Ban hält die IP noch → „verlängert/unbekannt“ + if ($remaining === 0 && $this->isStillBanned($jail, $ip)) { + $remaining = -2; // markiere als „~ unbekannt/verlängert“ } [$timeText, $metaText, $boxClass] = $this->present($remaining, $banAt, $until); $rows[] = [ 'ip' => $ip, - 'bantime' => $bantime, + 'bantime' => $defaultBantime, 'banned_at' => $banAt, 'remaining' => $remaining, 'until' => $until, @@ -57,7 +74,7 @@ class Fail2BanJailModal extends ModalComponent $this->rows = $rows; } - + /** ROBUST: findet Binaries automatisch */ private function bin(string $name): string { @@ -117,6 +134,18 @@ class Fail2BanJailModal extends ModalComponent return null; } + private function getDbFile(): string + { + [, $out] = $this->f2b('get dbfile'); + $path = trim($out); + return $path !== '' ? $path : '/var/lib/fail2ban/fail2ban.sqlite3'; + } + + private function sql(string $s): string + { + return "'".str_replace("'", "''", $s)."'"; + } + /** Darstellung: permanent / Restzeit / abgelaufen / ~approx / unbekannt */ private function present(?int $remaining, ?int $banAt, ?int $until): array { @@ -155,6 +184,33 @@ class Fail2BanJailModal extends ModalComponent return [$ok, $out]; } + private function banInfoFromDb(string $jail, string $ip): ?array + { + $sudo = $this->bin('sudo'); + $sqlite = $this->bin('sqlite3'); + $db = $this->getDbFile(); + + $q = sprintf( + "SELECT timeofban, expiretime FROM bans WHERE jail=%s AND ip=%s ORDER BY timeofban DESC LIMIT 1", + $this->sql($jail), + $this->sql($ip) + ); + + $cmd = "$sudo -n $sqlite -readonly ".escapeshellarg($db).' '.escapeshellarg($q); + $out = trim((string)@shell_exec($cmd)); + if ($out === '') return null; + + [$timeofban, $expire] = array_map('intval', explode('|', $out)) + [null, null]; + return ['banned_at' => $timeofban ?: null, 'expire' => $expire ?? null]; + } + + private function isStillBanned(string $jail, string $ip): bool + { + [, $out] = $this->f2b('get '.escapeshellarg($jail).' banip '.escapeshellarg($ip)); + // gibt in der Regel "1" (banned) oder nichts/0 zurück + return (bool)preg_match('/\b1\b/', $out); + } + private function getBantime(string $jail): int { [, $out] = $this->f2b('get '.escapeshellarg($jail).' bantime');