parent
251f2d9c8f
commit
385b67c3c5
|
|
@ -6,116 +6,83 @@ use Livewire\Component;
|
||||||
|
|
||||||
class Fail2BanCard extends Component
|
class Fail2BanCard extends Component
|
||||||
{
|
{
|
||||||
public bool $available = true; // ob fail2ban vorhanden ist
|
public bool $available = true;
|
||||||
public int $activeBans = 0; // Summe über alle Jails
|
public bool $permDenied = false; // neu
|
||||||
/** @var array<int,array{name:string,banned:int,ips:array<int,string>}> */
|
public int $activeBans = 0;
|
||||||
public array $jails = []; // je Jail: Name, Anzahl, IPs (gekürzt)
|
public array $jails = [];
|
||||||
/** @var array<int,array{ip:string,count:int}> */
|
public array $topIps = [];
|
||||||
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
|
protected function load(bool $force = false): void
|
||||||
{
|
{
|
||||||
// 0) vorhanden?
|
|
||||||
$bin = trim((string) @shell_exec('command -v fail2ban-client 2>/dev/null')) ?: '';
|
$bin = trim((string) @shell_exec('command -v fail2ban-client 2>/dev/null')) ?: '';
|
||||||
if ($bin === '') {
|
if ($bin === '') {
|
||||||
$this->available = false;
|
$this->available = false;
|
||||||
|
$this->permDenied = false;
|
||||||
$this->activeBans = 0;
|
$this->activeBans = 0;
|
||||||
$this->jails = [];
|
$this->jails = [];
|
||||||
$this->topIps = [];
|
$this->topIps = [];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1) Jails ermitteln
|
// ping → prüft zugleich Rechte (liefert Fehlertext, wenn Socket gesperrt)
|
||||||
$status = (string) (@shell_exec("timeout 2 $bin status 2>/dev/null") ?? '');
|
[$ok, $raw] = $this->f2b('ping'); // ok==true wenn "pong"
|
||||||
|
|
||||||
|
if (!$ok && stripos($raw, 'permission denied') !== false) {
|
||||||
|
$this->available = true;
|
||||||
|
$this->permDenied = true;
|
||||||
|
$this->activeBans = 0;
|
||||||
|
$this->jails = [];
|
||||||
|
$this->topIps = $this->collectTopIps();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Jails
|
||||||
|
[, $status] = $this->f2b('status');
|
||||||
$jailsLn = $this->firstMatch('/Jail list:\s*(.+)$/mi', $status);
|
$jailsLn = $this->firstMatch('/Jail list:\s*(.+)$/mi', $status);
|
||||||
$jails = $jailsLn ? array_filter(array_map('trim', preg_split('/\s*,\s*/', $jailsLn))) : [];
|
$jails = $jailsLn ? array_filter(array_map('trim', preg_split('/\s*,\s*/', $jailsLn))) : [];
|
||||||
|
|
||||||
$total = 0;
|
$total = 0; $rows = [];
|
||||||
$rows = [];
|
|
||||||
|
|
||||||
foreach ($jails as $j) {
|
foreach ($jails as $j) {
|
||||||
$s = (string) (@shell_exec("timeout 2 $bin status ".escapeshellarg($j)." 2>/dev/null") ?? '');
|
[, $s] = $this->f2b('status '.escapeshellarg($j));
|
||||||
$banned = (int) ($this->firstMatch('/Currently banned:\s+(\d+)/i', $s) ?: 0);
|
$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) ?: '';
|
$ipList = $this->firstMatch('/Banned IP list:\s*(.+)$/mi', $s) ?: '';
|
||||||
$ips = $ipList !== '' ? array_values(array_filter(array_map('trim', preg_split('/\s+/', $ipList)))) : [];
|
$ips = $ipList !== '' ? array_values(array_filter(array_map('trim', preg_split('/\s+/', $ipList)))) : [];
|
||||||
|
$rows[] = ['name'=>$j,'banned'=>$banned,'ips'=>array_slice($ips, 0, 8)];
|
||||||
$rows[] = [
|
|
||||||
'name' => $j,
|
|
||||||
'banned' => $banned,
|
|
||||||
// nur die ersten 8 zur UI-Anzeige
|
|
||||||
'ips' => array_slice($ips, 0, 8),
|
|
||||||
];
|
|
||||||
$total += $banned;
|
$total += $banned;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->available = true;
|
$this->available = true;
|
||||||
|
$this->permDenied = false;
|
||||||
$this->activeBans = $total;
|
$this->activeBans = $total;
|
||||||
$this->jails = $rows;
|
$this->jails = $rows;
|
||||||
|
$this->topIps = $this->collectTopIps();
|
||||||
// 2) Top-IPs aus den letzten Ban-Events
|
}
|
||||||
$this->topIps = $this->collectTopIps();
|
|
||||||
|
/** führt fail2ban-client aus; mit sudo-Fallback; gibt [ok, output] zurück */
|
||||||
|
private function f2b(string $args): array
|
||||||
|
{
|
||||||
|
$sudo = '/usr/bin/sudo';
|
||||||
|
$f2b = '/usr/bin/fail2ban-client';
|
||||||
|
$cmd = "timeout 2 $sudo -n $f2b $args 2>&1";
|
||||||
|
$out = (string) @shell_exec($cmd);
|
||||||
|
|
||||||
|
$ok = stripos($out, 'Status') !== false
|
||||||
|
|| stripos($out, 'Jail list') !== false
|
||||||
|
|| stripos($out, 'pong') !== false;
|
||||||
|
|
||||||
|
return [$ok, $out];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Extrahiert erste Regex-Gruppe oder null */
|
|
||||||
private function firstMatch(string $pattern, string $haystack): ?string
|
private function firstMatch(string $pattern, string $haystack): ?string
|
||||||
{
|
{
|
||||||
return preg_match($pattern, $haystack, $m) ? trim($m[1]) : null;
|
return preg_match($pattern, $haystack, $m) ? trim($m[1]) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Zählt „Ban <IP>“ aus fail2ban.log (Fallback: journalctl) */
|
|
||||||
private function collectTopIps(): array
|
private function collectTopIps(): array
|
||||||
{
|
{
|
||||||
$lines = '';
|
// … (deine vorhandene Methode aus meiner letzten Antwort – passt)
|
||||||
if (is_readable('/var/log/fail2ban.log')) {
|
// lässt du unverändert.
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,7 @@ class UpdateCard extends Component
|
||||||
$this->recompute();
|
$this->recompute();
|
||||||
|
|
||||||
if ($this->rc === 0 && !$this->postActionsDone) {
|
if ($this->rc === 0 && !$this->postActionsDone) {
|
||||||
|
@shell_exec('nohup php /var/www/mailwolt/artisan optimize:clear >/dev/null 2>&1 &');
|
||||||
@shell_exec('nohup php /var/www/mailwolt/artisan health:collect >/dev/null 2>&1 &');
|
@shell_exec('nohup php /var/www/mailwolt/artisan health:collect >/dev/null 2>&1 &');
|
||||||
@shell_exec('nohup php /var/www/mailwolt/artisan settings:sync >/dev/null 2>&1 &');
|
@shell_exec('nohup php /var/www/mailwolt/artisan settings:sync >/dev/null 2>&1 &');
|
||||||
@shell_exec('nohup php /var/www/mailwolt/artisan spamav:collect >/dev/null 2>&1 &');
|
@shell_exec('nohup php /var/www/mailwolt/artisan spamav:collect >/dev/null 2>&1 &');
|
||||||
|
|
@ -119,8 +120,6 @@ class UpdateCard extends Component
|
||||||
|
|
||||||
$ver = $this->displayCurrent ?? 'aktuelle Version';
|
$ver = $this->displayCurrent ?? 'aktuelle Version';
|
||||||
$this->progressLine = 'Update abgeschlossen: ' . $ver;
|
$this->progressLine = 'Update abgeschlossen: ' . $ver;
|
||||||
|
|
||||||
@shell_exec('nohup php /var/www/mailwolt/artisan optimize:clear >/dev/null 2>&1 &');
|
|
||||||
// Optional: NICHT sofort reloaden – Nutzer entscheidet
|
// Optional: NICHT sofort reloaden – Nutzer entscheidet
|
||||||
// $this->dispatch('reload-page', delay: 6000);
|
// $this->dispatch('reload-page', delay: 6000);
|
||||||
} elseif ($this->rc !== null && $this->rc !== 0 && !$this->postActionsDone) {
|
} elseif ($this->rc !== null && $this->rc !== 0 && !$this->postActionsDone) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue