parent
6c3cde5f65
commit
9acea7b89b
|
|
@ -4,12 +4,18 @@ namespace App\Livewire\Ui\Security;
|
||||||
|
|
||||||
use Livewire\Attributes\On;
|
use Livewire\Attributes\On;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Illuminate\Support\Str;
|
|
||||||
|
|
||||||
class Fail2banBanlist extends Component
|
class Fail2banBanlist extends Component
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* null oder '*' => alle Jails
|
||||||
|
* 'recidive' => nur dieses Jail
|
||||||
|
* 'mailwolt-blacklist' etc.
|
||||||
|
*/
|
||||||
|
public ?string $jail = null;
|
||||||
|
|
||||||
|
/** @var array<int,string> */
|
||||||
public array $banned = [];
|
public array $banned = [];
|
||||||
public string $jail = 'mailwolt-blacklist'; // Dein dedizierter Jail
|
|
||||||
|
|
||||||
#[On('f2b:refresh')]
|
#[On('f2b:refresh')]
|
||||||
public function refreshList(): void
|
public function refreshList(): void
|
||||||
|
|
@ -17,61 +23,88 @@ class Fail2banBanlist extends Component
|
||||||
$this->loadBannedIps();
|
$this->loadBannedIps();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function mount(): void
|
public function mount(?string $jail = null): void
|
||||||
{
|
{
|
||||||
|
// erlaubt optionalen Param via <livewire ... :jail="'recidive'" />
|
||||||
|
$this->jail = $jail;
|
||||||
$this->loadBannedIps();
|
$this->loadBannedIps();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function loadBannedIps(): void
|
|
||||||
{
|
|
||||||
$output = @shell_exec(sprintf('sudo -n /usr/bin/fail2ban-client status %s 2>&1', escapeshellarg($this->jail)));
|
|
||||||
|
|
||||||
if (!$output) {
|
|
||||||
$this->banned = [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Beispielausgabe:
|
|
||||||
// Status for the jail: mailwolt-blacklist
|
|
||||||
// |- Filter
|
|
||||||
// | |- Currently failed: 0
|
|
||||||
// | `- Total failed: 0
|
|
||||||
// `- Actions
|
|
||||||
// |- Currently banned: 2
|
|
||||||
// | `- IP list: 203.0.113.45 198.51.100.22
|
|
||||||
// `- Total banned: 2
|
|
||||||
|
|
||||||
if (preg_match('/IP list:\s*(.+)$/mi', $output, $m)) {
|
|
||||||
$ips = preg_split('/\s+/', trim($m[1]));
|
|
||||||
$this->banned = array_values(array_filter($ips, fn($ip) => filter_var($ip, FILTER_VALIDATE_IP)));
|
|
||||||
} else {
|
|
||||||
$this->banned = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function unban(string $ip): void
|
|
||||||
{
|
|
||||||
if (!filter_var($ip, FILTER_VALIDATE_IP)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$ipEsc = escapeshellarg($ip);
|
|
||||||
$cmd = sprintf('sudo -n /usr/bin/fail2ban-client set %s unbanip %s 2>&1', escapeshellarg($this->jail), $ipEsc);
|
|
||||||
@shell_exec($cmd);
|
|
||||||
|
|
||||||
$this->dispatch('toast',
|
|
||||||
type: 'info',
|
|
||||||
badge: 'Fail2Ban',
|
|
||||||
title: 'IP entbannt',
|
|
||||||
text: "Die IP {$ip} wurde erfolgreich entbannt.",
|
|
||||||
duration: 6000,
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->refreshList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
return view('livewire.ui.security.fail2ban-banlist');
|
return view('livewire.ui.security.fail2ban-banlist');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ================= core ================= */
|
||||||
|
|
||||||
|
private function loadBannedIps(): void
|
||||||
|
{
|
||||||
|
$jails = $this->jailList();
|
||||||
|
|
||||||
|
// ggf. nur ein bestimmtes Jail anzeigen
|
||||||
|
if (is_string($this->jail) && $this->jail !== '' && $this->jail !== '*') {
|
||||||
|
$jails = in_array($this->jail, $jails, true) ? [$this->jail] : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$ips = [];
|
||||||
|
foreach ($jails as $j) {
|
||||||
|
$out = $this->f2b("status ".escapeshellarg($j));
|
||||||
|
if (preg_match('/IP list:\s*(.+)$/mi', $out, $m)) {
|
||||||
|
$parts = preg_split('/\s+/', trim($m[1]));
|
||||||
|
foreach ($parts as $ip) {
|
||||||
|
if (filter_var($ip, FILTER_VALIDATE_IP)) $ips[] = $ip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->banned = array_values(array_unique($ips));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Entbannt IP in allen passenden Jails */
|
||||||
|
public function unban(string $ip): void
|
||||||
|
{
|
||||||
|
if (!filter_var($ip, FILTER_VALIDATE_IP)) return;
|
||||||
|
|
||||||
|
$jails = $this->jailList();
|
||||||
|
if (is_string($this->jail) && $this->jail !== '' && $this->jail !== '*') {
|
||||||
|
$jails = in_array($this->jail, $jails, true) ? [$this->jail] : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$cnt = 0;
|
||||||
|
foreach ($jails as $j) {
|
||||||
|
$this->f2b("set ".escapeshellarg($j)." unbanip ".escapeshellarg($ip));
|
||||||
|
$cnt++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->loadBannedIps();
|
||||||
|
|
||||||
|
$this->dispatch('toast',
|
||||||
|
type: 'done',
|
||||||
|
badge: 'Fail2Ban',
|
||||||
|
title: 'IP entbannt',
|
||||||
|
text: ($this->jail && $this->jail !== '*')
|
||||||
|
? "IP {$ip} in Jail „{$this->jail}“ entbannt."
|
||||||
|
: "IP {$ip} in {$cnt} Jail(s) entbannt.",
|
||||||
|
duration: 5000,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================= helpers ================= */
|
||||||
|
|
||||||
|
/** Liefert Liste aller Jails über fail2ban-client */
|
||||||
|
private function jailList(): array
|
||||||
|
{
|
||||||
|
$out = $this->f2b('status');
|
||||||
|
if (preg_match('/Jail list:\s*(.+)$/mi', $out, $m)) {
|
||||||
|
$jails = array_map('trim', preg_split('/\s*,\s*/', trim($m[1])));
|
||||||
|
return array_values(array_filter($jails, fn($v) => $v !== ''));
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** führt fail2ban-client via sudo aus und gibt stdout zurück */
|
||||||
|
private function f2b(string $args): string
|
||||||
|
{
|
||||||
|
return (string) @shell_exec('sudo -n /usr/bin/fail2ban-client '.$args.' 2>&1');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<div class="glass-card p-4 rounded-2xl border border-white/10 bg-white/5"
|
<div class="glass-card p-4 rounded-2xl border border-white/10 bg-white/5"
|
||||||
wire:poll.2s="refresh">
|
@if($running) wire:poll.2s="refresh" @endif>
|
||||||
|
|
||||||
<div class="flex items-center justify-between mb-2">
|
<div class="flex items-center justify-between mb-2">
|
||||||
<div class="inline-flex items-center gap-2 bg-white/5 border border-white/10 px-2.5 py-1 rounded-full">
|
<div class="inline-flex items-center gap-2 bg-white/5 border border-white/10 px-2.5 py-1 rounded-full">
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue