parent
385b67c3c5
commit
e3c7e8de33
|
|
@ -6,16 +6,33 @@ use Livewire\Component;
|
||||||
|
|
||||||
class Fail2BanCard extends Component
|
class Fail2BanCard extends Component
|
||||||
{
|
{
|
||||||
public bool $available = true;
|
public bool $available = true; // fail2ban-client vorhanden?
|
||||||
public bool $permDenied = false; // neu
|
public bool $permDenied = false; // Socket/Root-Rechte fehlen?
|
||||||
public int $activeBans = 0;
|
public int $activeBans = 0; // Summe gebannter IPs über alle Jails
|
||||||
public array $jails = [];
|
public array $jails = []; // [['name'=>'sshd','banned'=>2,'ips'=>['1.2.3.4',...]], ...]
|
||||||
public array $topIps = [];
|
public array $topIps = []; // [['ip'=>'x.x.x.x','count'=>N], ...]
|
||||||
|
|
||||||
// ...
|
public function mount(): void
|
||||||
|
{
|
||||||
|
$this->load();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.ui.security.fail2-ban-card');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wird vom Button "Neu prüfen" genutzt
|
||||||
|
public function refresh(): void
|
||||||
|
{
|
||||||
|
$this->load(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------- intern --------------------- */
|
||||||
|
|
||||||
protected function load(bool $force = false): void
|
protected function load(bool $force = false): void
|
||||||
{
|
{
|
||||||
|
// existiert fail2ban-client?
|
||||||
$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;
|
||||||
|
|
@ -26,9 +43,8 @@ class Fail2BanCard extends Component
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ping → prüft zugleich Rechte (liefert Fehlertext, wenn Socket gesperrt)
|
// ping → prüft zugleich Rechte (bei Permission-Fehler kommt Klartext)
|
||||||
[$ok, $raw] = $this->f2b('ping'); // ok==true wenn "pong"
|
[$ok, $raw] = $this->f2b('ping'); // ok == "pong" erkannt
|
||||||
|
|
||||||
if (!$ok && stripos($raw, 'permission denied') !== false) {
|
if (!$ok && stripos($raw, 'permission denied') !== false) {
|
||||||
$this->available = true;
|
$this->available = true;
|
||||||
$this->permDenied = true;
|
$this->permDenied = true;
|
||||||
|
|
@ -38,7 +54,7 @@ class Fail2BanCard extends Component
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Jails
|
// Jails auflisten
|
||||||
[, $status] = $this->f2b('status');
|
[, $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))) : [];
|
||||||
|
|
@ -60,7 +76,7 @@ class Fail2BanCard extends Component
|
||||||
$this->topIps = $this->collectTopIps();
|
$this->topIps = $this->collectTopIps();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** führt fail2ban-client aus; mit sudo-Fallback; gibt [ok, output] zurück */
|
/** führt fail2ban-client via sudo aus; gibt [ok, output] zurück */
|
||||||
private function f2b(string $args): array
|
private function f2b(string $args): array
|
||||||
{
|
{
|
||||||
$sudo = '/usr/bin/sudo';
|
$sudo = '/usr/bin/sudo';
|
||||||
|
|
@ -80,9 +96,22 @@ class Fail2BanCard extends Component
|
||||||
return preg_match($pattern, $haystack, $m) ? trim($m[1]) : null;
|
return preg_match($pattern, $haystack, $m) ? trim($m[1]) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Zählt die häufigsten IPs aus den letzten Fail2Ban-Logs (ban/unban Events) */
|
||||||
private function collectTopIps(): array
|
private function collectTopIps(): array
|
||||||
{
|
{
|
||||||
// … (deine vorhandene Methode aus meiner letzten Antwort – passt)
|
// Zieh nur fail2ban.log, nicht auth/mail – präziser & schneller
|
||||||
// lässt du unverändert.
|
$cmd = 'tail -n 2000 /var/log/fail2ban.log 2>/dev/null'
|
||||||
|
. ' | grep -Eo "([0-9]{1,3}\.){3}[0-9]{1,3}"'
|
||||||
|
. ' | sort | uniq -c | sort -nr | head -5';
|
||||||
|
$log = (string) @shell_exec($cmd);
|
||||||
|
$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]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $rows;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,10 +58,11 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-4 flex justify-end">
|
<div class="mt-4 flex justify-end">
|
||||||
<button wire:click="refresh"
|
<button wire:click="refresh" wire:loading.attr="disabled"
|
||||||
class="px-3 py-1.5 text-[12px] rounded-lg bg-white/5 border border-white/10 hover:bg-white/10">
|
class="px-3 py-1.5 text-[12px] rounded-lg bg-white/5 border border-white/10 hover:bg-white/10">
|
||||||
<i class="ph ph-arrows-clockwise text-[13px]"></i>
|
<i class="ph ph-arrows-clockwise text-[13px]"></i>
|
||||||
Neu prüfen
|
<span wire:loading.remove>Neu prüfen</span>
|
||||||
|
<span wire:loading>prüfe…</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue