Fix: Mailbox Stats über Dovecot mit config/mailpool.php

main v1.0.109
boban 2025-10-31 01:08:26 +01:00
parent 02e558bf4b
commit e833033074
2 changed files with 24 additions and 102 deletions

View File

@ -32,58 +32,11 @@ class Fail2BanCard extends Component
public function openDetails(string $jail): void
{
// KORREKTER DISPATCH für wire-elements/modal
$this->dispatch('openModal', 'ui.security.modal.fail2-ban-jail-modal', ['jail' => $jail]);
$this->dispatch('openModal', component: 'ui.security.modal.fail2-ban-jail-modal', arguments: ['jail' => $jail]);
}
/* ------------------- intern ------------------- */
// protected function load(bool $force = false): void
// {
// $bin = trim((string)@shell_exec('command -v fail2ban-client 2>/dev/null')) ?: '';
// if ($bin === '') {
// $this->available = false;
// $this->permDenied = false;
// $this->activeBans = 0;
// $this->jails = [];
// return;
// }
//
// [$ok, $raw] = $this->f2b('ping');
// if (!$ok && stripos($raw, 'permission denied') !== false) {
// $this->available = true;
// $this->permDenied = true;
// $this->activeBans = 0;
// $this->jails = [];
// return;
// }
//
// [, $status] = $this->f2b('status');
// $jailsLn = $this->firstMatch('/Jail list:\s*(.+)$/mi', $status);
// $jails = $jailsLn ? array_filter(array_map('trim', preg_split('/\s*,\s*/', $jailsLn))) : [];
//
// $rows = [];
// $sum = 0;
// foreach ($jails as $j) {
// [, $s] = $this->f2b('status '.escapeshellarg($j));
// $bantime = $this->getBantime($j);
// $ipList = $this->firstMatch('/Banned IP list:\s*(.+)$/mi', $s) ?: '';
// $ips = $ipList !== '' ? array_values(array_filter(array_map('trim', preg_split('/\s+/', $ipList)))) : [];
//
// $ipDetails = $this->buildIpDetails($j, $ips, $bantime);
//
// [, $s] = $this->f2b('status ' . escapeshellarg($j));
// $banned = (int)($this->firstMatch('/Currently banned:\s+(\d+)/i', $s) ?: 0);
// $bantime = $this->getBantime($j);
// $rows[] = ['name' => $j, 'banned' => $banned, 'bantime' => $bantime];
// $sum += $banned;
// }
//
// $this->available = true;
// $this->permDenied = false;
// $this->activeBans = $sum;
// $this->jails = $rows;
// }
protected function load(bool $force = false): void
{
$bin = trim((string)@shell_exec('command -v fail2ban-client 2>/dev/null')) ?: '';
@ -139,15 +92,6 @@ class Fail2BanCard extends Component
$this->jails = $rows;
}
// private function f2b(string $args): array
// {
// $sudo = '/usr/bin/sudo';
// $f2b = '/usr/bin/fail2ban-client';
// $out = (string)@shell_exec("timeout 2 $sudo -n $f2b $args 2>&1");
// $ok = str_contains($out, 'Status') || str_contains($out, 'Jail list') || str_contains($out, 'pong');
// return [$ok, $out];
// }
private function f2b(string $args): array
{
$sudo = '/usr/bin/sudo';
@ -171,14 +115,6 @@ class Fail2BanCard extends Component
return 600; // konservativer Fallback
}
// private function getBantime(string $jail): int
// {
// [, $out] = $this->f2b('get ' . escapeshellarg($jail) . ' bantime');
// $val = trim($out);
// if (preg_match('/-?\d+/', $val, $m)) return (int)$m[0];
// return 600;
// }
/** Letzten Ban-Zeitpunkt (Unix-Timestamp) aus /var/log/fail2ban.log ermitteln. */
private function lastBanTimestamp(string $jail, string $ip): ?int
{
@ -237,18 +173,12 @@ class Fail2BanCard extends Component
}
return $out;
}
private function firstMatch(string $pattern, string $haystack): ?string
{
return preg_match($pattern, $haystack, $m) ? trim($m[1]) : null;
}
// private function firstMatch(string $pattern, string $haystack): ?string
// {
// return preg_match($pattern, $haystack, $m) ? trim($m[1]) : null;
// }
}

View File

@ -144,42 +144,34 @@ class Fail2BanJailModal extends ModalComponent
}
/** letzte "Ban <IP>"-Zeile parsen -> Unix-Timestamp */
/** letzte "Ban <IP>"-Zeile -> Unix-Timestamp aus /var/log/fail2ban.log */
private function lastBanTimestamp(string $jail, string $ip): ?int
{
$jailQ = escapeshellarg($jail);
$ipQ = escapeshellarg($ip);
$file = '/var/log/fail2ban.log';
if (!is_readable($file)) return null;
// 1) Durchsuche fail2ban.log + rotierte + .gz
$cmdFiles = "sh -c 'ls -1 /var/log/fail2ban.log* 2>/dev/null | wc -l'";
$haveFiles = ((int) @shell_exec($cmdFiles)) > 0;
// nur das Dateiende lesen (performant, auch bei großen Logs)
$tailBytes = 400000; // 400 KB
$size = @filesize($file) ?: 0;
$seek = max(0, $size - $tailBytes);
$line = '';
if ($haveFiles) {
// zgrep -h: ohne Dateinamen; -E für Regex; tail -n1: letzte Ban-Zeile
$pattern = escapeshellarg(sprintf('\\[%s\\] Ban %s', $jail, $ip));
$cmd = "zgrep -h -E \"\\[${jail}\\]\\s+Ban\\s+${ip}\" /var/log/fail2ban.log* 2>/dev/null | tail -n 1";
$line = (string) @shell_exec($cmd);
}
$fh = @fopen($file, 'rb');
if (!$fh) return null;
if ($seek > 0) fseek($fh, $seek);
$data = stream_get_contents($fh) ?: '';
fclose($fh);
// 2) Fallback: journald (wenn kein Dateilog)
if (trim($line) === '') {
// seit 14 Tagen scannen, Format wie im file-log
$cmdJ = "journalctl -u fail2ban --since '14 days ago' 2>/dev/null | grep -F \"[{$jail}] Ban {$ip}\" | tail -n 1";
$line = (string) @shell_exec($cmdJ);
}
// Beispiel: 2025-10-30 22:34:20,797 ... [sshd] Ban 193.46.255.244
$j = preg_quote($jail, '/');
$p = preg_quote($ip, '/');
$pattern = '/^(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2}:\d{2}),\d+.*\['.$j.'\]\s+Ban\s+'.$p.'\s*$/m';
$line = trim($line);
if ($line === '') return null;
// Datum am Anfang: 2025-10-29 18:07:11,436 ...
if (preg_match('/^(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2}:\d{2})/', $line, $m)) {
$ts = strtotime($m[1].' '.$m[2]);
return $ts ?: null;
}
// journald kann anderes Präfix haben; versuche generischen ISO-Zeitstempel irgendwo in der Zeile
if (preg_match('/(\d{4}-\d{2}-\d{2})[ T](\d{2}:\d{2}:\d{2})/', $line, $m)) {
$ts = strtotime($m[1].' '.$m[2]);
return $ts ?: null;
if (preg_match_all($pattern, $data, $m) && !empty($m[1])) {
$date = end($m[1]);
$time = end($m[2]);
$dt = \DateTime::createFromFormat('Y-m-d H:i:s', "$date $time",
new \DateTimeZone(date_default_timezone_get()));
return $dt ? $dt->getTimestamp() : null;
}
return null;
}