116 lines
3.2 KiB
PHP
116 lines
3.2 KiB
PHP
<?php
|
|
|
|
namespace App\Livewire\Modals;
|
|
|
|
use App\Models\AuditEvent;
|
|
use App\Models\Server;
|
|
use App\Services\FirewallService;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use LivewireUI\Modal\ModalComponent;
|
|
use Throwable;
|
|
|
|
/**
|
|
* Add a firewall rule. For ufw the operator picks action/protocol/port/source; for
|
|
* firewalld (an allow-list default zone) it opens a port. All validation happens in
|
|
* FirewallService — this modal only collects and forwards. Adding is non-destructive,
|
|
* so no R5 confirm (deleting a rule is the guarded path).
|
|
*/
|
|
class FirewallRule extends ModalComponent
|
|
{
|
|
public int $serverId;
|
|
|
|
public string $tool = 'ufw';
|
|
|
|
public string $action = 'allow';
|
|
|
|
public string $proto = 'tcp';
|
|
|
|
public string $port = '';
|
|
|
|
public string $from = '';
|
|
|
|
public ?string $error = null;
|
|
|
|
public function mount(int $serverId, string $tool = 'ufw'): void
|
|
{
|
|
abort_unless(auth()->user()?->can('manage-network'), 403);
|
|
|
|
$this->serverId = $serverId;
|
|
$this->tool = $tool === 'firewalld' ? 'firewalld' : 'ufw';
|
|
}
|
|
|
|
public static function modalMaxWidth(): string
|
|
{
|
|
return 'lg';
|
|
}
|
|
|
|
public function save(FirewallService $firewall): void
|
|
{
|
|
abort_unless(auth()->user()?->can('manage-network'), 403);
|
|
|
|
$this->error = null;
|
|
|
|
$server = Server::find($this->serverId);
|
|
if (! $server) {
|
|
$this->error = __('common.server_not_found');
|
|
|
|
return;
|
|
}
|
|
|
|
$port = trim($this->port) !== '' ? (int) $this->port : null;
|
|
|
|
try {
|
|
$res = $firewall->addRule($server, $this->action, $this->proto, $port, $this->from);
|
|
} catch (Throwable $e) {
|
|
$this->error = $e->getMessage();
|
|
|
|
return;
|
|
}
|
|
|
|
if (! $res['ok']) {
|
|
$this->error = $res['output'] !== '' ? $res['output'] : __('modals.firewall_rule.error_add_failed');
|
|
|
|
return;
|
|
}
|
|
|
|
AuditEvent::create([
|
|
'user_id' => Auth::id(),
|
|
'server_id' => $server->id,
|
|
'actor' => Auth::user()?->name ?? 'system',
|
|
'action' => 'firewall.rule_add',
|
|
'target' => $server->name.' · '.$this->summary($port),
|
|
'ip' => request()->ip(),
|
|
]);
|
|
|
|
$this->dispatch('firewallChanged');
|
|
$this->dispatch('notify', message: __('modals.firewall_rule.notify_added'));
|
|
$this->closeModal();
|
|
}
|
|
|
|
/** Short human summary of the rule for the audit log. */
|
|
private function summary(?int $port): string
|
|
{
|
|
if ($this->tool === 'firewalld') {
|
|
return __('modals.firewall_rule.summary_port_opened', [
|
|
'port' => (string) $port,
|
|
'proto' => $this->proto === 'udp' ? 'udp' : 'tcp',
|
|
]);
|
|
}
|
|
|
|
$parts = [$this->action];
|
|
if ($port !== null) {
|
|
$parts[] = $port.($this->proto !== 'any' ? '/'.$this->proto : '');
|
|
}
|
|
if (trim($this->from) !== '') {
|
|
$parts[] = __('modals.firewall_rule.summary_from', ['source' => trim($this->from)]);
|
|
}
|
|
|
|
return implode(' ', $parts);
|
|
}
|
|
|
|
public function render()
|
|
{
|
|
return view('livewire.modals.firewall-rule');
|
|
}
|
|
}
|