161 lines
4.7 KiB
PHP
161 lines
4.7 KiB
PHP
<?php
|
|
|
|
namespace App\Livewire\Ui\Security\Modal;
|
|
|
|
use LivewireUI\Modal\ModalComponent;
|
|
use App\Models\Fail2banIpList;
|
|
use Illuminate\Validation\ValidationException;
|
|
|
|
class Fail2banIpModal extends ModalComponent
|
|
{
|
|
/** 'whitelist' | 'blacklist' */
|
|
public string $type = 'whitelist';
|
|
|
|
/** 'add' | 'remove' */
|
|
public string $mode = 'add';
|
|
|
|
/** IP/CIDR im Formular */
|
|
public string $ip = '';
|
|
|
|
/** Für "remove" vorbefüllt */
|
|
public ?string $prefill = null;
|
|
|
|
public static function modalMaxWidth(): string { return 'lg'; }
|
|
|
|
public function mount(string $type = 'whitelist', string $mode = 'add', ?string $ip = null): void
|
|
{
|
|
$type = strtolower($type);
|
|
$mode = strtolower($mode);
|
|
|
|
if (!in_array($type, ['whitelist', 'blacklist'], true)) {
|
|
throw new \InvalidArgumentException('Invalid type');
|
|
}
|
|
if (!in_array($mode, ['add', 'remove'], true)) {
|
|
throw new \InvalidArgumentException('Invalid mode');
|
|
}
|
|
|
|
$this->type = $type;
|
|
$this->mode = $mode;
|
|
$this->ip = $ip ?? '';
|
|
$this->prefill = $ip;
|
|
}
|
|
|
|
public function render()
|
|
{
|
|
return view('livewire.ui.security.modal.fail2ban-ip-modal');
|
|
}
|
|
|
|
/* ---------------- actions ---------------- */
|
|
|
|
public function save(): void
|
|
{
|
|
$this->assertAddMode();
|
|
$ip = trim($this->ip);
|
|
|
|
if (!$this->isValidIpOrCidr($ip)) {
|
|
throw ValidationException::withMessages(['ip' => 'Ungültige IP oder CIDR.']);
|
|
}
|
|
|
|
// DB schreiben
|
|
Fail2banIpList::firstOrCreate(['ip' => $ip, 'type' => $this->type]);
|
|
|
|
if ($this->type === 'whitelist') {
|
|
$this->writeWhitelistConfig();
|
|
$this->reloadFail2ban();
|
|
} else {
|
|
// Blacklist = sofort bannen im dedizierten Jail
|
|
$this->banIp($ip);
|
|
}
|
|
|
|
$this->dispatch('f2b:refresh');
|
|
$this->dispatch('notify', message: ucfirst($this->type).' aktualisiert.');
|
|
$this->closeModal();
|
|
$this->dispatch('f2b:refresh'); // falls du eine Liste neu laden willst
|
|
}
|
|
|
|
public function remove(): void
|
|
{
|
|
$this->assertRemoveMode();
|
|
$ip = trim($this->prefill ?? $this->ip);
|
|
|
|
if ($ip === '') return;
|
|
|
|
Fail2banIpList::where('type', $this->type)->where('ip', $ip)->delete();
|
|
|
|
if ($this->type === 'whitelist') {
|
|
$this->writeWhitelistConfig();
|
|
$this->reloadFail2ban();
|
|
} else {
|
|
// aus Blacklist-Jail entbannen, falls noch aktiv
|
|
$this->unbanIp($ip);
|
|
}
|
|
|
|
$this->dispatch('f2b:refresh');
|
|
$this->dispatch('notify', message: ucfirst($this->type).' Eintrag entfernt.');
|
|
$this->closeModal();
|
|
$this->dispatch('f2b:refresh');
|
|
}
|
|
|
|
/* ---------------- helper ---------------- */
|
|
|
|
private function assertAddMode(): void
|
|
{
|
|
if ($this->mode !== 'add') throw new \LogicException('Wrong mode');
|
|
}
|
|
|
|
private function assertRemoveMode(): void
|
|
{
|
|
if ($this->mode !== 'remove') throw new \LogicException('Wrong mode');
|
|
}
|
|
|
|
private function isValidIpOrCidr(string $s): bool
|
|
{
|
|
// IP
|
|
if (filter_var($s, FILTER_VALIDATE_IP)) return true;
|
|
|
|
// CIDR
|
|
if (strpos($s, '/') !== false) {
|
|
[$ip, $mask] = explode('/', $s, 2);
|
|
if (!filter_var($ip, FILTER_VALIDATE_IP)) return false;
|
|
if (strpos($ip, ':') !== false) {
|
|
// IPv6
|
|
return ctype_digit($mask) && (int)$mask >= 8 && (int)$mask <= 128;
|
|
}
|
|
// IPv4
|
|
return ctype_digit($mask) && (int)$mask >= 8 && (int)$mask <= 32;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private function writeWhitelistConfig(): void
|
|
{
|
|
$ips = Fail2banIpList::where('type', 'whitelist')->pluck('ip')->toArray();
|
|
$ignore = implode(' ', array_unique(array_filter($ips)));
|
|
$content = "[DEFAULT]\nignoreip = {$ignore}\n";
|
|
|
|
$file = '/etc/fail2ban/jail.d/mailwolt-whitelist.local';
|
|
$tmp = $file.'.tmp';
|
|
@file_put_contents($tmp, $content, LOCK_EX);
|
|
@chmod($tmp, 0644);
|
|
@rename($tmp, $file);
|
|
}
|
|
|
|
private function reloadFail2ban(): void
|
|
{
|
|
@shell_exec('sudo fail2ban-client reload 2>&1');
|
|
}
|
|
|
|
private function banIp(string $ip): void
|
|
{
|
|
$ipEsc = escapeshellarg($ip);
|
|
@shell_exec("sudo fail2ban-client set mailwolt-blacklist banip {$ipEsc} 2>&1");
|
|
// optional: in DB zusätzlich behalten, damit UI konsistent ist (bereits oben getan)
|
|
}
|
|
|
|
private function unbanIp(string $ip): void
|
|
{
|
|
$ipEsc = escapeshellarg($ip);
|
|
@shell_exec("sudo fail2ban-client set mailwolt-blacklist unbanip {$ipEsc} 2>&1");
|
|
}
|
|
}
|