parent
4d1fd64158
commit
ee44ff3def
|
|
@ -7,10 +7,9 @@ use Livewire\Component;
|
||||||
|
|
||||||
class Fail2BanCard extends Component
|
class Fail2BanCard extends Component
|
||||||
{
|
{
|
||||||
public bool $available = true; // fail2ban-client vorhanden?
|
public bool $available = true;
|
||||||
public bool $permDenied = false; // Socket/Root-Rechte fehlen?
|
public bool $permDenied = false;
|
||||||
public int $activeBans = 0; // Summe gebannter IPs
|
public int $activeBans = 0;
|
||||||
/** @var array<int,array{name:string,banned:int,bantime:int}> */
|
|
||||||
public array $jails = [];
|
public array $jails = [];
|
||||||
|
|
||||||
public function mount(): void
|
public function mount(): void
|
||||||
|
|
@ -28,12 +27,13 @@ class Fail2BanCard extends Component
|
||||||
$this->load(true);
|
$this->load(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optional: öffnet später dein Detail-Modal/Tab
|
|
||||||
public function openDetails(string $jail): void
|
public function openDetails(string $jail): void
|
||||||
{
|
{
|
||||||
$this->dispatch('openModal', component: 'ui.security.modal.fail2-ban-jail-modal', arguments: ['jail' => $jail]);
|
// KORREKTER DISPATCH für wire-elements/modal
|
||||||
|
$this->dispatch('openModal', 'ui.security.modal.fail2ban-jail-modal', ['jail' => $jail]);
|
||||||
}
|
}
|
||||||
/* ---------------- intern ---------------- */
|
|
||||||
|
/* ------------------- intern ------------------- */
|
||||||
|
|
||||||
protected function load(bool $force = false): void
|
protected function load(bool $force = false): void
|
||||||
{
|
{
|
||||||
|
|
@ -46,7 +46,6 @@ class Fail2BanCard extends Component
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rechtecheck
|
|
||||||
[$ok, $raw] = $this->f2b('ping');
|
[$ok, $raw] = $this->f2b('ping');
|
||||||
if (!$ok && stripos($raw, 'permission denied') !== false) {
|
if (!$ok && stripos($raw, 'permission denied') !== false) {
|
||||||
$this->available = true;
|
$this->available = true;
|
||||||
|
|
@ -56,18 +55,16 @@ class Fail2BanCard extends Component
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Jails laden
|
|
||||||
[, $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))) : [];
|
||||||
|
|
||||||
$rows = [];
|
$rows = [];
|
||||||
$sum = 0;
|
$sum = 0;
|
||||||
|
|
||||||
foreach ($jails as $j) {
|
foreach ($jails as $j) {
|
||||||
[, $s] = $this->f2b('status ' . escapeshellarg($j));
|
[, $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);
|
||||||
$bantime = $this->getBantime($j); // Sek.; -1 = permanent
|
$bantime = $this->getBantime($j);
|
||||||
$rows[] = ['name' => $j, 'banned' => $banned, 'bantime' => $bantime];
|
$rows[] = ['name' => $j, 'banned' => $banned, 'bantime' => $bantime];
|
||||||
$sum += $banned;
|
$sum += $banned;
|
||||||
}
|
}
|
||||||
|
|
@ -78,15 +75,12 @@ class Fail2BanCard extends Component
|
||||||
$this->jails = $rows;
|
$this->jails = $rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** sudo + fail2ban-client ausführen; [ok, output] */
|
|
||||||
private function f2b(string $args): array
|
private function f2b(string $args): array
|
||||||
{
|
{
|
||||||
$sudo = '/usr/bin/sudo';
|
$sudo = '/usr/bin/sudo';
|
||||||
$f2b = '/usr/bin/fail2ban-client';
|
$f2b = '/usr/bin/fail2ban-client';
|
||||||
$out = (string)@shell_exec("timeout 2 $sudo -n $f2b $args 2>&1");
|
$out = (string)@shell_exec("timeout 2 $sudo -n $f2b $args 2>&1");
|
||||||
$ok = stripos($out, 'Status') !== false
|
$ok = str_contains($out, 'Status') || str_contains($out, 'Jail list') || str_contains($out, 'pong');
|
||||||
|| stripos($out, 'Jail list') !== false
|
|
||||||
|| stripos($out, 'pong') !== false;
|
|
||||||
return [$ok, $out];
|
return [$ok, $out];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -95,7 +89,7 @@ class Fail2BanCard extends Component
|
||||||
[, $out] = $this->f2b('get ' . escapeshellarg($jail) . ' bantime');
|
[, $out] = $this->f2b('get ' . escapeshellarg($jail) . ' bantime');
|
||||||
$val = trim($out);
|
$val = trim($out);
|
||||||
if (preg_match('/-?\d+/', $val, $m)) return (int)$m[0];
|
if (preg_match('/-?\d+/', $val, $m)) return (int)$m[0];
|
||||||
return 600; // defensiver Default
|
return 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function firstMatch(string $pattern, string $haystack): ?string
|
private function firstMatch(string $pattern, string $haystack): ?string
|
||||||
|
|
@ -105,6 +99,110 @@ class Fail2BanCard extends Component
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//namespace App\Livewire\Ui\Security;
|
||||||
|
//
|
||||||
|
//use Livewire\Component;
|
||||||
|
//
|
||||||
|
//class Fail2BanCard extends Component
|
||||||
|
//{
|
||||||
|
// public bool $available = true; // fail2ban-client vorhanden?
|
||||||
|
// public bool $permDenied = false; // Socket/Root-Rechte fehlen?
|
||||||
|
// public int $activeBans = 0; // Summe gebannter IPs
|
||||||
|
// /** @var array<int,array{name:string,banned:int,bantime:int}> */
|
||||||
|
// public array $jails = [];
|
||||||
|
//
|
||||||
|
// public function mount(): void
|
||||||
|
// {
|
||||||
|
// $this->load();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public function render()
|
||||||
|
// {
|
||||||
|
// return view('livewire.ui.security.fail2-ban-card');
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public function refresh(): void
|
||||||
|
// {
|
||||||
|
// $this->load(true);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Optional: öffnet später dein Detail-Modal/Tab
|
||||||
|
// public function openDetails(string $jail): void
|
||||||
|
// {
|
||||||
|
// $this->dispatch('openModal', 'ui.security.modal.fail2-ban-jail-modal', ['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;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Rechtecheck
|
||||||
|
// [$ok, $raw] = $this->f2b('ping');
|
||||||
|
// if (!$ok && stripos($raw, 'permission denied') !== false) {
|
||||||
|
// $this->available = true;
|
||||||
|
// $this->permDenied = true;
|
||||||
|
// $this->activeBans = 0;
|
||||||
|
// $this->jails = [];
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Jails laden
|
||||||
|
// [, $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));
|
||||||
|
// $banned = (int)($this->firstMatch('/Currently banned:\s+(\d+)/i', $s) ?: 0);
|
||||||
|
// $bantime = $this->getBantime($j); // Sek.; -1 = permanent
|
||||||
|
// $rows[] = ['name' => $j, 'banned' => $banned, 'bantime' => $bantime];
|
||||||
|
// $sum += $banned;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// $this->available = true;
|
||||||
|
// $this->permDenied = false;
|
||||||
|
// $this->activeBans = $sum;
|
||||||
|
// $this->jails = $rows;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /** sudo + fail2ban-client ausführen; [ok, output] */
|
||||||
|
// 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 = stripos($out, 'Status') !== false
|
||||||
|
// || stripos($out, 'Jail list') !== false
|
||||||
|
// || stripos($out, 'pong') !== false;
|
||||||
|
// return [$ok, $out];
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// 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; // defensiver Default
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private function firstMatch(string $pattern, string $haystack): ?string
|
||||||
|
// {
|
||||||
|
// return preg_match($pattern, $haystack, $m) ? trim($m[1]) : null;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
//namespace App\Livewire\Ui\Security;
|
//namespace App\Livewire\Ui\Security;
|
||||||
//
|
//
|
||||||
//use Livewire\Component;
|
//use Livewire\Component;
|
||||||
|
|
|
||||||
|
|
@ -30,14 +30,22 @@
|
||||||
<div class="rounded-xl border border-white/10 bg-white/5 px-3 py-2">
|
<div class="rounded-xl border border-white/10 bg-white/5 px-3 py-2">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<div class="text-white/85 font-medium">{{ $j['name'] }}</div>
|
<div class="text-white/85 font-medium">{{ $j['name'] }}</div>
|
||||||
|
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
|
<span class="text-[11px] text-white/60">
|
||||||
|
Bannzeit:
|
||||||
|
@if($j['bantime'] === -1)
|
||||||
|
permanent
|
||||||
|
@else
|
||||||
|
{{ $j['bantime'] }}s
|
||||||
|
@endif
|
||||||
|
</span>
|
||||||
<span class="px-2 py-0.5 rounded-full border text-[11px]
|
<span class="px-2 py-0.5 rounded-full border text-[11px]
|
||||||
{{ $j['banned']>0 ? 'text-amber-200 border-amber-400/30 bg-amber-500/10' : 'text-white/60 border-white/20 bg-white/5' }}">
|
{{ $j['banned']>0 ? 'text-amber-200 border-amber-400/30 bg-amber-500/10' : 'text-white/60 border-white/20 bg-white/5' }}">
|
||||||
{{ $j['banned'] }} gebannt
|
{{ $j['banned'] }} gebannt
|
||||||
</span>
|
</span>
|
||||||
{{-- Optional: Details öffnen (Tab/Modal) --}}
|
|
||||||
<button wire:click="openDetails('{{ $j['name'] }}')"
|
{{-- fix: stop event bubbling --}}
|
||||||
|
<button wire:click.stop="openDetails('{{ $j['name'] }}')"
|
||||||
class="text-[11px] px-2 py-0.5 rounded border border-white/15 bg-white/5 hover:bg-white/10">
|
class="text-[11px] px-2 py-0.5 rounded border border-white/15 bg-white/5 hover:bg-white/10">
|
||||||
Details
|
Details
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -79,6 +87,68 @@
|
||||||
{{-- @endif--}}
|
{{-- @endif--}}
|
||||||
{{-- </div>--}}
|
{{-- </div>--}}
|
||||||
|
|
||||||
|
{{-- @if(!$available)--}}
|
||||||
|
{{-- <div class="text-sm text-white/60">fail2ban-client wurde nicht gefunden.</div>--}}
|
||||||
|
{{-- @elseif($permDenied)--}}
|
||||||
|
{{-- <div class="text-sm text-amber-200">--}}
|
||||||
|
{{-- Keine Berechtigung auf <code class="font-mono">/var/run/fail2ban/fail2ban.sock</code>.--}}
|
||||||
|
{{-- <span class="opacity-80">Sudo-Regel prüfen.</span>--}}
|
||||||
|
{{-- </div>--}}
|
||||||
|
{{-- @else--}}
|
||||||
|
{{-- <div class="space-y-2">--}}
|
||||||
|
{{-- @forelse($jails as $j)--}}
|
||||||
|
{{-- <div class="rounded-xl border border-white/10 bg-white/5 px-3 py-2">--}}
|
||||||
|
{{-- <div class="flex items-center justify-between">--}}
|
||||||
|
{{-- <div class="text-white/85 font-medium">{{ $j['name'] }}</div>--}}
|
||||||
|
|
||||||
|
{{-- <div class="flex items-center gap-2">--}}
|
||||||
|
{{-- <span class="px-2 py-0.5 rounded-full border text-[11px]--}}
|
||||||
|
{{-- {{ $j['banned']>0 ? 'text-amber-200 border-amber-400/30 bg-amber-500/10' : 'text-white/60 border-white/20 bg-white/5' }}">--}}
|
||||||
|
{{-- {{ $j['banned'] }} gebannt--}}
|
||||||
|
{{-- </span>--}}
|
||||||
|
{{-- --}}{{-- Optional: Details öffnen (Tab/Modal) --}}
|
||||||
|
{{-- <button wire:click="openDetails('{{ $j['name'] }}')"--}}
|
||||||
|
{{-- class="text-[11px] px-2 py-0.5 rounded border border-white/15 bg-white/5 hover:bg-white/10">--}}
|
||||||
|
{{-- Details--}}
|
||||||
|
{{-- </button>--}}
|
||||||
|
{{-- </div>--}}
|
||||||
|
{{-- </div>--}}
|
||||||
|
{{-- </div>--}}
|
||||||
|
{{-- @empty--}}
|
||||||
|
{{-- <div class="text-sm text-white/60">Keine Jails gefunden.</div>--}}
|
||||||
|
{{-- @endforelse--}}
|
||||||
|
{{-- </div>--}}
|
||||||
|
|
||||||
|
{{-- <div class="mt-4 flex justify-end">--}}
|
||||||
|
{{-- <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">--}}
|
||||||
|
{{-- <i class="ph ph-arrows-clockwise text-[13px]"></i>--}}
|
||||||
|
{{-- <span wire:loading.remove>Neu prüfen</span>--}}
|
||||||
|
{{-- <span wire:loading>prüfe…</span>--}}
|
||||||
|
{{-- </button>--}}
|
||||||
|
{{-- </div>--}}
|
||||||
|
{{-- @endif--}}
|
||||||
|
{{--</div>--}}
|
||||||
|
|
||||||
|
{{--<div class="glass-card p-4 rounded-2xl border border-white/10 bg-white/5">--}}
|
||||||
|
{{-- <div class="flex items-center justify-between mb-3">--}}
|
||||||
|
{{-- <div class="inline-flex items-center gap-2 bg-white/5 border border-white/10 px-2.5 py-1 rounded-full">--}}
|
||||||
|
{{-- <i class="ph ph-shield-checkered text-white/70 text-[13px]"></i>--}}
|
||||||
|
{{-- <span class="text-[11px] uppercase text-white/70">Fail2Ban</span>--}}
|
||||||
|
{{-- </div>--}}
|
||||||
|
|
||||||
|
{{-- @if($available)--}}
|
||||||
|
{{-- <span class="px-2 py-0.5 rounded-full border text-xs--}}
|
||||||
|
{{-- {{ $activeBans>0 ? 'text-amber-200 border-amber-400/30 bg-amber-500/10' : 'text-emerald-300 border-emerald-400/30 bg-emerald-500/10' }}">--}}
|
||||||
|
{{-- {{ $activeBans }} aktuell--}}
|
||||||
|
{{-- </span>--}}
|
||||||
|
{{-- @else--}}
|
||||||
|
{{-- <span class="px-2 py-0.5 rounded-full border text-xs text-rose-300 border-rose-400/30 bg-rose-500/10">--}}
|
||||||
|
{{-- nicht installiert--}}
|
||||||
|
{{-- </span>--}}
|
||||||
|
{{-- @endif--}}
|
||||||
|
{{-- </div>--}}
|
||||||
|
|
||||||
{{-- @if(!$available)--}}
|
{{-- @if(!$available)--}}
|
||||||
{{-- <div class="text-sm text-white/60">fail2ban-client wurde nicht gefunden.</div>--}}
|
{{-- <div class="text-sm text-white/60">fail2ban-client wurde nicht gefunden.</div>--}}
|
||||||
{{-- @else--}}
|
{{-- @else--}}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue