mailwolt/app/Livewire/Ui/Security/Fail2banSettings.php

544 lines
18 KiB
PHP

<?php
namespace App\Livewire\Ui\Security;
use Livewire\Attributes\On;
use Livewire\Component;
use App\Models\Fail2banSetting;
use App\Models\Fail2banIpList;
use Illuminate\Validation\ValidationException;
class Fail2banSettings extends Component
{
// Formfelder
public int $bantime;
public int $max_bantime;
public bool $bantime_increment;
public float $bantime_factor;
public int $max_retry;
public int $findtime;
public int $cidr_v4;
public int $cidr_v6;
public bool $external_mode;
public array $whitelist = [];
public array $blacklist = [];
public Fail2banSetting $settings;
#[On('f2b:refresh')]
public function refreshLists(): void
{
$this->whitelist = Fail2banIpList::visibleWhitelist()->pluck('ip')->toArray();
$this->blacklist = Fail2banIpList::visibleBlacklist()->pluck('ip')->toArray();
}
public function mount(): void
{
$this->settings = Fail2banSetting::first() ?? Fail2banSetting::create([
'bantime' => 3600,
'max_bantime' => 43200,
'bantime_increment' => true,
'bantime_factor' => 1.5,
'max_retry' => 3,
'findtime' => 600,
'cidr_v4' => 32,
'cidr_v6' => 128,
'external_mode' => false,
]);
$this->fill([
'bantime' => (int)$this->settings->bantime,
'max_bantime' => (int)$this->settings->max_bantime,
'bantime_increment' => (bool)$this->settings->bantime_increment,
'bantime_factor' => (float)$this->settings->bantime_factor,
'max_retry' => (int)$this->settings->max_retry,
'findtime' => (int)$this->settings->findtime,
'cidr_v4' => (int)$this->settings->cidr_v4,
'cidr_v6' => (int)$this->settings->cidr_v6,
'external_mode' => (bool)$this->settings->external_mode,
]);
$this->refreshLists();
}
public function save(): void
{
$this->validate([
'bantime' => 'required|integer|min:60',
'max_bantime' => 'required|integer|min:60',
'bantime_factor' => 'required|numeric|min:1',
'max_retry' => 'required|integer|min:1',
'findtime' => 'required|integer|min:60',
'cidr_v4' => 'required|integer|min:8|max:32',
'cidr_v6' => 'required|integer|min:8|max:128',
]);
try {
// Einstellungen speichern
$this->settings->update([
'bantime' => $this->bantime,
'max_bantime' => $this->max_bantime,
'bantime_increment' => $this->bantime_increment,
'bantime_factor' => $this->bantime_factor,
'max_retry' => $this->max_retry,
'findtime' => $this->findtime,
'cidr_v4' => $this->cidr_v4,
'cidr_v6' => $this->cidr_v6,
'external_mode' => $this->external_mode,
]);
// Config-Dateien schreiben
$this->writeDefaultsConfig();
$this->writeWhitelistConfig();
// Fail2Ban reload
$this->runCommand('sudo -n /usr/bin/fail2ban-client reload');
$this->dispatch('toast',
type: 'success',
badge: 'Fail2Ban',
title: 'Einstellungen gespeichert',
text: 'Die Fail2Ban-Konfiguration wurde erfolgreich übernommen und ist jetzt aktiv.',
duration: 6000,
);
} catch (\Throwable $e) {
$this->dispatch('toast',
type: 'error',
badge: 'Fail2Ban',
title: 'Fehler beim Anwenden',
text: 'Die neuen Einstellungen konnten nicht angewendet werden: ' . $e->getMessage(),
duration: 8000,
);
}
}
/* ---------------- Config-Dateien ---------------- */
protected function writeDefaultsConfig(): void
{
$s = $this->settings;
$content = <<<CONF
[DEFAULT]
bantime = {$s->bantime}
findtime = {$s->findtime}
maxretry = {$s->max_retry}
bantime.increment = {$this->boolToStr($s->bantime_increment)}
bantime.factor = {$s->bantime_factor}
bantime.maxtime = {$s->max_bantime}
CONF;
$this->writeRootFileViaTee('/etc/fail2ban/jail.d/00-mailwolt-defaults.local', $content);
}
protected function writeWhitelistConfig(): void
{
// zieht System + User-Whitelist
$ips = Fail2banIpList::allWhitelistForConfig();
$ignore = implode(' ', array_unique(array_filter($ips)));
$content = "[DEFAULT]\nignoreip = {$ignore}\n";
$this->writeRootFileViaTee('/etc/fail2ban/jail.d/mailwolt-whitelist.local', $content);
}
/* ---------------- Helper ---------------- */
private function writeRootFileViaTee(string $target, string $content): void
{
if (!preg_match('#^/etc/fail2ban/jail\.d/[A-Za-z0-9._-]+\.local$#', $target)) {
throw new \RuntimeException("Illegal path: $target");
}
$cmd = sprintf('sudo -n /usr/bin/tee %s >/dev/null', escapeshellarg($target));
$desc = [
0 => ['pipe', 'r'],
1 => ['pipe', 'w'],
2 => ['pipe', 'w'],
];
$proc = proc_open($cmd, $desc, $pipes);
if (!is_resource($proc)) {
throw new \RuntimeException('tee start fehlgeschlagen');
}
fwrite($pipes[0], $content);
fclose($pipes[0]);
stream_get_contents($pipes[1]);
stream_get_contents($pipes[2]);
$code = proc_close($proc);
if ($code !== 0) {
throw new \RuntimeException("tee failed writing to {$target}");
}
}
private function runCommand(string $cmd): void
{
$output = [];
$return = 0;
exec($cmd . ' 2>&1', $output, $return);
if ($return !== 0) {
throw new \RuntimeException("Command failed ($return): {$cmd}\n" . implode("\n", $output));
}
}
private function boolToStr(bool $v): string
{
return $v ? 'true' : 'false';
}
public function render()
{
return view('livewire.ui.security.fail2ban-settings');
}
}
//namespace App\Livewire\Ui\Security;
//
//use Livewire\Attributes\On;
//use Livewire\Component;
//use App\Models\Fail2banSetting;
//use App\Models\Fail2banIpList;
//
//class Fail2banSettings extends Component
//{
// // Formfelder
// public int $bantime;
// public int $max_bantime;
// public bool $bantime_increment;
// public float $bantime_factor;
// public int $max_retry;
// public int $findtime;
// public int $cidr_v4;
// public int $cidr_v6;
// public bool $external_mode;
//
// public array $whitelist = [];
// public array $blacklist = [];
//
// public Fail2banSetting $settings;
//
// #[On('f2b:refresh')]
// public function refreshLists(): void
// {
// $this->whitelist = Fail2banIpList::visibleWhitelist()->pluck('ip')->toArray();
// $this->blacklist = Fail2banIpList::visibleBlacklist()->pluck('ip')->toArray();
// }
//
// public function mount(): void
// {
// // Setting holen oder Defaults anlegen
// $this->settings = Fail2banSetting::first() ?? Fail2banSetting::create([
// 'bantime' => 3600,
// 'max_bantime' => 43200,
// 'bantime_increment' => true,
// 'bantime_factor' => 1.5,
// 'max_retry' => 3,
// 'findtime' => 600,
// 'cidr_v4' => 32,
// 'cidr_v6' => 128,
// 'external_mode' => false,
// ]);
//
// // Properties befüllen
// $this->fill([
// 'bantime' => (int)$this->settings->bantime,
// 'max_bantime' => (int)$this->settings->max_bantime,
// 'bantime_increment' => (bool)$this->settings->bantime_increment,
// 'bantime_factor' => (float)$this->settings->bantime_factor,
// 'max_retry' => (int)$this->settings->max_retry,
// 'findtime' => (int)$this->settings->findtime,
// 'cidr_v4' => (int)$this->settings->cidr_v4,
// 'cidr_v6' => (int)$this->settings->cidr_v6,
// 'external_mode' => (bool)$this->settings->external_mode,
// ]);
//
// $this->refreshLists();
// }
//
// public function save(): void
// {
// $this->validate([
// 'bantime' => 'required|integer|min:60',
// 'max_bantime' => 'required|integer|min:60',
// 'bantime_factor' => 'required|numeric|min:1',
// 'max_retry' => 'required|integer|min:1',
// 'findtime' => 'required|integer|min:60',
// 'cidr_v4' => 'required|integer|min:8|max:32',
// 'cidr_v6' => 'required|integer|min:8|max:128',
// ]);
//
// // Einstellungen speichern
// $this->settings->update([
// 'bantime' => $this->bantime,
// 'max_bantime' => $this->max_bantime,
// 'bantime_increment' => $this->bantime_increment,
// 'bantime_factor' => $this->bantime_factor,
// 'max_retry' => $this->max_retry,
// 'findtime' => $this->findtime,
// 'cidr_v4' => $this->cidr_v4,
// 'cidr_v6' => $this->cidr_v6,
// 'external_mode' => $this->external_mode,
// ]);
//
// // Config-Dateien schreiben
// $this->writeDefaultsConfig();
// $this->writeWhitelistConfig();
//
// // Fail2Ban reload
// $this->runCommand('sudo -n /usr/bin/fail2ban-client reload');
//
// $this->dispatch('toast',
// type: 'done',
// badge: 'Fail2Ban',
// title: 'Einstellungen gespeichert',
// text: 'Die Fail2Ban-Konfiguration wurde erfolgreich übernommen und ist jetzt aktiv.',
// duration: 6000,
// );
// }
//
// protected function writeDefaultsConfig(): void
// {
// $s = $this->settings;
//
// $content = <<<CONF
//[DEFAULT]
//bantime = {$s->bantime}
//findtime = {$s->findtime}
//maxretry = {$s->max_retry}
//bantime.increment = {$this->boolToStr($s->bantime_increment)}
//bantime.factor = {$s->bantime_factor}
//bantime.maxtime = {$s->max_bantime}
//CONF;
//
// $this->writeRootFileViaTee('/etc/fail2ban/jail.d/00-mailwolt-defaults.local', $content);
// }
//
// protected function writeWhitelistConfig(): void
// {
// $ips = Fail2banIpList::where('type', 'whitelist')->pluck('ip')->toArray();
// $ignore = implode(' ', array_unique(array_filter($ips)));
//
// $content = "[DEFAULT]\nignoreip = {$ignore}\n";
//
// $this->writeRootFileViaTee('/etc/fail2ban/jail.d/mailwolt-whitelist.local', $content);
// }
//
// /**
// * Schreibt Root-Dateien sicher via `sudo tee`
// */
// private function writeRootFileViaTee(string $target, string $content): void
// {
// if (!preg_match('#^/etc/fail2ban/jail\.d/[A-Za-z0-9._-]+\.local$#', $target)) {
// throw new \RuntimeException("Illegal path: $target");
// }
//
// $cmd = sprintf('sudo -n /usr/bin/tee %s >/dev/null', escapeshellarg($target));
//
// $descriptorspec = [
// 0 => ['pipe', 'r'],
// 1 => ['pipe', 'w'],
// 2 => ['pipe', 'w'],
// ];
//
// $proc = proc_open($cmd, $descriptorspec, $pipes, null, null);
// if (!is_resource($proc)) {
// throw new \RuntimeException('Failed to start tee');
// }
//
// fwrite($pipes[0], $content);
// fclose($pipes[0]);
// stream_get_contents($pipes[1]);
// stream_get_contents($pipes[2]);
// $exitCode = proc_close($proc);
//
// if ($exitCode !== 0) {
// throw new \RuntimeException("tee failed writing to {$target}");
// }
// }
//
// /**
// * Führt Systembefehle aus und wirft Exception bei Fehlern
// */
// private function runCommand(string $cmd): void
// {
// $output = [];
// $return = 0;
// exec($cmd . ' 2>&1', $output, $return);
//
// if ($return !== 0) {
// throw new \RuntimeException("Command failed ($return): {$cmd}\n" . implode("\n", $output));
// }
// }
//
// private function boolToStr(bool $v): string
// {
// return $v ? 'true' : 'false';
// }
//
// public function render()
// {
// return view('livewire.ui.security.fail2ban-settings');
// }
//}
//
//namespace App\Livewire\Ui\Security;
//
//use Livewire\Attributes\On;
//use Livewire\Component;
//use App\Models\Fail2banSetting;
//use App\Models\Fail2banIpList;
//
//class Fail2banSettings extends Component
//{
// // Formfelder
// public int $bantime;
// public int $max_bantime;
// public bool $bantime_increment;
// public float $bantime_factor;
// public int $max_retry;
// public int $findtime;
// public int $cidr_v4;
// public int $cidr_v6;
// public bool $external_mode;
//
// public array $whitelist = [];
// public array $blacklist = [];
//
// public Fail2banSetting $settings;
//
// #[On('f2b:refresh')]
// public function refreshLists(): void
// {
// $this->whitelist = Fail2banIpList::where('type', 'whitelist')->pluck('ip')->toArray();
// $this->blacklist = Fail2banIpList::where('type', 'blacklist')->pluck('ip')->toArray();
// }
//
// public function mount(): void
// {
// // Setting holen oder mit Defaults anlegen
// $this->settings = Fail2banSetting::first() ?? Fail2banSetting::create([
// 'bantime' => 3600, 'max_bantime' => 43200, 'bantime_increment' => true,
// 'bantime_factor' => 1.5, 'max_retry' => 3, 'findtime' => 600,
// 'cidr_v4' => 32, 'cidr_v6' => 128, 'external_mode' => false,
// ]);
//
// // Properties füllen (KEINE Mixed-Objekte in Inputs binden)
// $this->fill([
// 'bantime' => (int)$this->settings->bantime,
// 'max_bantime' => (int)$this->settings->max_bantime,
// 'bantime_increment' => (bool)$this->settings->bantime_increment,
// 'bantime_factor' => (float)$this->settings->bantime_factor,
// 'max_retry' => (int)$this->settings->max_retry,
// 'findtime' => (int)$this->settings->findtime,
// 'cidr_v4' => (int)$this->settings->cidr_v4,
// 'cidr_v6' => (int)$this->settings->cidr_v6,
// 'external_mode' => (bool)$this->settings->external_mode,
// ]);
//
// $this->whitelist = Fail2banIpList::where('type','whitelist')->pluck('ip')->toArray();
// $this->blacklist = Fail2banIpList::where('type','blacklist')->pluck('ip')->toArray();
// }
//
// public function save(): void
// {
// $this->validate([
// 'bantime' => 'required|integer|min:60',
// 'max_bantime' => 'required|integer|min:60',
// 'bantime_factor' => 'required|numeric|min:1',
// 'max_retry' => 'required|integer|min:1',
// 'findtime' => 'required|integer|min:60',
// 'cidr_v4' => 'required|integer|min:8|max:32',
// 'cidr_v6' => 'required|integer|min:8|max:128',
// ]);
//
// $this->settings->update([
// 'bantime' => $this->bantime,
// 'max_bantime' => $this->max_bantime,
// 'bantime_increment' => $this->bantime_increment,
// 'bantime_factor' => $this->bantime_factor,
// 'max_retry' => $this->max_retry,
// 'findtime' => $this->findtime,
// 'cidr_v4' => $this->cidr_v4,
// 'cidr_v6' => $this->cidr_v6,
// 'external_mode' => $this->external_mode,
// ]);
//
// $this->writeDefaultsConfig();
// $this->writeWhitelistConfig();
//
// @shell_exec('sudo fail2ban-client reload');
// $this->dispatch('notify', message: 'Gespeichert & Fail2Ban neu geladen.');
// }
//
// protected function writeDefaultsConfig(): void
// {
// $s = $this->settings;
// $content = <<<CONF
//[DEFAULT]
//bantime = {$s->bantime}
//findtime = {$s->findtime}
//maxretry = {$s->max_retry}
//bantime.increment = {$this->boolToStr($s->bantime_increment)}
//bantime.factor = {$s->bantime_factor}
//bantime.maxtime = {$s->max_bantime}
//CONF;
// file_put_contents('/etc/fail2ban/jail.d/00-mailwolt-defaults.local', $content);
// }
//
// protected function writeWhitelistConfig(): void
// {
// $ips = Fail2banIpList::where('type','whitelist')->pluck('ip')->toArray();
// $ignore = implode(' ', array_unique(array_filter($ips)));
// $content = "[DEFAULT]\nignoreip = {$ignore}\n";
// file_put_contents('/etc/fail2ban/jail.d/mailwolt-whitelist.local', $content);
// }
//
// private function writeRootFileViaTee(string $target, string $content): void
// {
// // Nur erlaubte Pfade (Hardening)
// if (!preg_match('#^/etc/fail2ban/jail\.d/[A-Za-z0-9._-]+\.local$#', $target)) {
// throw new \RuntimeException("Illegal path: $target");
// }
//
// $cmd = sprintf('sudo -n /usr/bin/tee %s >/dev/null', escapeshellarg($target));
//
// $descriptorspec = [
// 0 => ['pipe', 'r'], // stdin -> tee
// 1 => ['pipe', 'w'], // stdout
// 2 => ['pipe', 'w'], // stderr
// ];
//
// $proc = proc_open($cmd, $descriptorspec, $pipes, null, null);
// if (!is_resource($proc)) {
// throw new \RuntimeException('Failed to start tee');
// }
//
// fwrite($pipes[0], $content);
// fclose($pipes[0]);
// $stdout = stream_get_contents($pipes[1]); fclose($pipes[1]);
// $stderr = stream_get_contents($pipes[2]); fclose($pipes[2]);
//
// $code = proc_close($proc);
// if ($code !== 0) {
// throw new \RuntimeException("tee failed (code $code): $stderr $stdout");
// }
// }
//
// private function boolToStr(bool $v): string
// {
// return $v ? 'true' : 'false';
// }
//
// public function render()
// {
// return view('livewire.ui.security.fail2ban-settings');
// }
//}