feat: add Firewall management section to Proxmox, including routing and help documentation for enhanced security configuration
This commit is contained in:
379
extensions/Servers/Proxmox/Livewire/Firewall.php
Normal file
379
extensions/Servers/Proxmox/Livewire/Firewall.php
Normal file
@@ -0,0 +1,379 @@
|
||||
<?php
|
||||
|
||||
namespace Paymenter\Extensions\Servers\Proxmox\Livewire;
|
||||
|
||||
use Illuminate\Http\Client\RequestException;
|
||||
use Paymenter\Extensions\Servers\Proxmox\Models\Server;
|
||||
use Paymenter\Extensions\Servers\Proxmox\Proxmox;
|
||||
|
||||
class Firewall extends Component
|
||||
{
|
||||
public bool $firewallEnabled = false;
|
||||
public array $rules = [];
|
||||
public array $aliases = [];
|
||||
public array $ipsets = [];
|
||||
public bool $loading = false;
|
||||
public string $error = '';
|
||||
|
||||
// Rule creation/editing
|
||||
public ?int $editingRulePos = null;
|
||||
public string $ruleAction = 'ACCEPT';
|
||||
public string $ruleSource = '';
|
||||
public string $ruleDest = '';
|
||||
public string $ruleProto = '';
|
||||
public string $ruleDport = '';
|
||||
public string $ruleSport = '';
|
||||
public string $ruleComment = '';
|
||||
public bool $ruleEnable = true;
|
||||
public string $ruleMacro = '';
|
||||
public string $ruleIface = '';
|
||||
public string $ruleLog = '';
|
||||
|
||||
// Alias management
|
||||
public string $aliasName = '';
|
||||
public string $aliasCidr = '';
|
||||
public string $aliasComment = '';
|
||||
|
||||
// IPSet management
|
||||
public string $ipsetName = '';
|
||||
public string $ipsetComment = '';
|
||||
|
||||
public function mount(Server $server): void
|
||||
{
|
||||
parent::mount($server);
|
||||
$this->loadFirewallOptions();
|
||||
$this->loadFirewallRules();
|
||||
$this->loadAliases();
|
||||
$this->loadIpsets();
|
||||
}
|
||||
|
||||
public function loadFirewallOptions(): void
|
||||
{
|
||||
try {
|
||||
$proxmox = new Proxmox;
|
||||
$response = $proxmox->request(
|
||||
'/nodes/' . $this->server->node->name . '/qemu/' . $this->server->vm_id . '/firewall/options'
|
||||
);
|
||||
$data = $response->json()['data'] ?? [];
|
||||
$this->firewallEnabled = isset($data['enable']) && $data['enable'] == 1;
|
||||
$this->error = '';
|
||||
} catch (\Exception $e) {
|
||||
$this->error = 'Failed to load firewall options: ' . $e->getMessage();
|
||||
$this->firewallEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
public function toggleFirewall(): void
|
||||
{
|
||||
$this->loading = true;
|
||||
try {
|
||||
$proxmox = new Proxmox;
|
||||
$response = $proxmox->request(
|
||||
'/nodes/' . $this->server->node->name . '/qemu/' . $this->server->vm_id . '/firewall/options',
|
||||
'put',
|
||||
['enable' => $this->firewallEnabled ? 0 : 1]
|
||||
);
|
||||
|
||||
if ($response->successful()) {
|
||||
$this->firewallEnabled = !$this->firewallEnabled;
|
||||
$this->notify('Firewall ' . ($this->firewallEnabled ? 'enabled' : 'disabled') . ' successfully', 'success');
|
||||
$this->loadFirewallOptions();
|
||||
} else {
|
||||
$message = $response->json()['errors'][0]['message'] ?? 'Unknown error';
|
||||
$this->notify('Failed to toggle firewall: ' . $message, 'error');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->notify('Failed to toggle firewall: ' . $e->getMessage(), 'error');
|
||||
} finally {
|
||||
$this->loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
public function loadFirewallRules(): void
|
||||
{
|
||||
try {
|
||||
$proxmox = new Proxmox;
|
||||
$response = $proxmox->request(
|
||||
'/nodes/' . $this->server->node->name . '/qemu/' . $this->server->vm_id . '/firewall/rules'
|
||||
);
|
||||
$data = $response->json()['data'] ?? [];
|
||||
$this->rules = is_array($data) ? $data : [];
|
||||
$this->error = '';
|
||||
} catch (\Exception $e) {
|
||||
$this->error = 'Failed to load firewall rules: ' . $e->getMessage();
|
||||
$this->rules = [];
|
||||
}
|
||||
}
|
||||
|
||||
public function startCreateRule(): void
|
||||
{
|
||||
$this->resetRuleForm();
|
||||
$this->editingRulePos = null;
|
||||
}
|
||||
|
||||
public function startEditRule(int $pos): void
|
||||
{
|
||||
$rule = collect($this->rules)->firstWhere('pos', $pos);
|
||||
if ($rule) {
|
||||
$this->editingRulePos = $pos;
|
||||
$this->ruleAction = $rule['action'] ?? 'ACCEPT';
|
||||
$this->ruleSource = $rule['source'] ?? '';
|
||||
$this->ruleDest = $rule['dest'] ?? '';
|
||||
$this->ruleProto = $rule['proto'] ?? '';
|
||||
$this->ruleDport = $rule['dport'] ?? '';
|
||||
$this->ruleSport = $rule['sport'] ?? '';
|
||||
$this->ruleComment = $rule['comment'] ?? '';
|
||||
$this->ruleEnable = !isset($rule['enable']) || $rule['enable'] == 1;
|
||||
$this->ruleMacro = $rule['macro'] ?? '';
|
||||
$this->ruleIface = $rule['iface'] ?? '';
|
||||
$this->ruleLog = $rule['log'] ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
public function resetRuleForm(): void
|
||||
{
|
||||
$this->ruleAction = 'ACCEPT';
|
||||
$this->ruleSource = '';
|
||||
$this->ruleDest = '';
|
||||
$this->ruleProto = '';
|
||||
$this->ruleDport = '';
|
||||
$this->ruleSport = '';
|
||||
$this->ruleComment = '';
|
||||
$this->ruleEnable = true;
|
||||
$this->ruleMacro = '';
|
||||
$this->ruleIface = '';
|
||||
$this->ruleLog = '';
|
||||
}
|
||||
|
||||
public function saveRule(): void
|
||||
{
|
||||
$this->loading = true;
|
||||
try {
|
||||
$proxmox = new Proxmox;
|
||||
$payload = array_filter([
|
||||
'action' => $this->ruleAction,
|
||||
'source' => $this->ruleSource ?: null,
|
||||
'dest' => $this->ruleDest ?: null,
|
||||
'proto' => $this->ruleProto ?: null,
|
||||
'dport' => $this->ruleDport ?: null,
|
||||
'sport' => $this->ruleSport ?: null,
|
||||
'comment' => $this->ruleComment ?: null,
|
||||
'enable' => $this->ruleEnable ? 1 : 0,
|
||||
'macro' => $this->ruleMacro ?: null,
|
||||
'iface' => $this->ruleIface ?: null,
|
||||
'log' => $this->ruleLog ?: null,
|
||||
], fn($value) => $value !== null);
|
||||
|
||||
if ($this->editingRulePos !== null) {
|
||||
// Update existing rule
|
||||
$response = $proxmox->request(
|
||||
'/nodes/' . $this->server->node->name . '/qemu/' . $this->server->vm_id . '/firewall/rules/' . $this->editingRulePos,
|
||||
'put',
|
||||
$payload
|
||||
);
|
||||
} else {
|
||||
// Create new rule
|
||||
$response = $proxmox->request(
|
||||
'/nodes/' . $this->server->node->name . '/qemu/' . $this->server->vm_id . '/firewall/rules',
|
||||
'post',
|
||||
$payload
|
||||
);
|
||||
}
|
||||
|
||||
if ($response->successful()) {
|
||||
$this->notify('Rule ' . ($this->editingRulePos !== null ? 'updated' : 'created') . ' successfully', 'success');
|
||||
$this->resetRuleForm();
|
||||
$this->editingRulePos = null;
|
||||
$this->loadFirewallRules();
|
||||
} else {
|
||||
$message = $response->json()['errors'][0]['message'] ?? 'Unknown error';
|
||||
$this->notify('Failed to save rule: ' . $message, 'error');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->notify('Failed to save rule: ' . $e->getMessage(), 'error');
|
||||
} finally {
|
||||
$this->loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
public function deleteRule(int $pos): void
|
||||
{
|
||||
$this->loading = true;
|
||||
try {
|
||||
$proxmox = new Proxmox;
|
||||
$response = $proxmox->request(
|
||||
'/nodes/' . $this->server->node->name . '/qemu/' . $this->server->vm_id . '/firewall/rules/' . $pos,
|
||||
'delete'
|
||||
);
|
||||
|
||||
if ($response->successful()) {
|
||||
$this->notify('Rule deleted successfully', 'success');
|
||||
$this->loadFirewallRules();
|
||||
} else {
|
||||
$message = $response->json()['errors'][0]['message'] ?? 'Unknown error';
|
||||
$this->notify('Failed to delete rule: ' . $message, 'error');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->notify('Failed to delete rule: ' . $e->getMessage(), 'error');
|
||||
} finally {
|
||||
$this->loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
public function loadAliases(): void
|
||||
{
|
||||
try {
|
||||
$proxmox = new Proxmox;
|
||||
$response = $proxmox->request(
|
||||
'/nodes/' . $this->server->node->name . '/qemu/' . $this->server->vm_id . '/firewall/aliases'
|
||||
);
|
||||
$data = $response->json()['data'] ?? [];
|
||||
$this->aliases = is_array($data) ? $data : [];
|
||||
} catch (\Exception $e) {
|
||||
$this->aliases = [];
|
||||
}
|
||||
}
|
||||
|
||||
public function createAlias(): void
|
||||
{
|
||||
if (empty($this->aliasName) || empty($this->aliasCidr)) {
|
||||
$this->notify('Name and CIDR are required', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
$this->loading = true;
|
||||
try {
|
||||
$proxmox = new Proxmox;
|
||||
$payload = [
|
||||
'name' => $this->aliasName,
|
||||
'cidr' => $this->aliasCidr,
|
||||
];
|
||||
if ($this->aliasComment) {
|
||||
$payload['comment'] = $this->aliasComment;
|
||||
}
|
||||
|
||||
$response = $proxmox->request(
|
||||
'/nodes/' . $this->server->node->name . '/qemu/' . $this->server->vm_id . '/firewall/aliases',
|
||||
'post',
|
||||
$payload
|
||||
);
|
||||
|
||||
if ($response->successful()) {
|
||||
$this->notify('Alias created successfully', 'success');
|
||||
$this->aliasName = '';
|
||||
$this->aliasCidr = '';
|
||||
$this->aliasComment = '';
|
||||
$this->loadAliases();
|
||||
} else {
|
||||
$message = $response->json()['errors'][0]['message'] ?? 'Unknown error';
|
||||
$this->notify('Failed to create alias: ' . $message, 'error');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->notify('Failed to create alias: ' . $e->getMessage(), 'error');
|
||||
} finally {
|
||||
$this->loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
public function deleteAlias(string $name): void
|
||||
{
|
||||
$this->loading = true;
|
||||
try {
|
||||
$proxmox = new Proxmox;
|
||||
$response = $proxmox->request(
|
||||
'/nodes/' . $this->server->node->name . '/qemu/' . $this->server->vm_id . '/firewall/aliases/' . $name,
|
||||
'delete'
|
||||
);
|
||||
|
||||
if ($response->successful()) {
|
||||
$this->notify('Alias deleted successfully', 'success');
|
||||
$this->loadAliases();
|
||||
} else {
|
||||
$message = $response->json()['errors'][0]['message'] ?? 'Unknown error';
|
||||
$this->notify('Failed to delete alias: ' . $message, 'error');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->notify('Failed to delete alias: ' . $e->getMessage(), 'error');
|
||||
} finally {
|
||||
$this->loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
public function loadIpsets(): void
|
||||
{
|
||||
try {
|
||||
$proxmox = new Proxmox;
|
||||
$response = $proxmox->request(
|
||||
'/nodes/' . $this->server->node->name . '/qemu/' . $this->server->vm_id . '/firewall/ipset'
|
||||
);
|
||||
$data = $response->json()['data'] ?? [];
|
||||
$this->ipsets = is_array($data) ? $data : [];
|
||||
} catch (\Exception $e) {
|
||||
$this->ipsets = [];
|
||||
}
|
||||
}
|
||||
|
||||
public function createIpset(): void
|
||||
{
|
||||
if (empty($this->ipsetName)) {
|
||||
$this->notify('Name is required', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
$this->loading = true;
|
||||
try {
|
||||
$proxmox = new Proxmox;
|
||||
$payload = [
|
||||
'name' => $this->ipsetName,
|
||||
];
|
||||
if ($this->ipsetComment) {
|
||||
$payload['comment'] = $this->ipsetComment;
|
||||
}
|
||||
|
||||
$response = $proxmox->request(
|
||||
'/nodes/' . $this->server->node->name . '/qemu/' . $this->server->vm_id . '/firewall/ipset',
|
||||
'post',
|
||||
$payload
|
||||
);
|
||||
|
||||
if ($response->successful()) {
|
||||
$this->notify('IP set created successfully', 'success');
|
||||
$this->ipsetName = '';
|
||||
$this->ipsetComment = '';
|
||||
$this->loadIpsets();
|
||||
} else {
|
||||
$message = $response->json()['errors'][0]['message'] ?? 'Unknown error';
|
||||
$this->notify('Failed to create IP set: ' . $message, 'error');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->notify('Failed to create IP set: ' . $e->getMessage(), 'error');
|
||||
} finally {
|
||||
$this->loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
public function deleteIpset(string $name): void
|
||||
{
|
||||
$this->loading = true;
|
||||
try {
|
||||
$proxmox = new Proxmox;
|
||||
$response = $proxmox->request(
|
||||
'/nodes/' . $this->server->node->name . '/qemu/' . $this->server->vm_id . '/firewall/ipset/' . $name,
|
||||
'delete'
|
||||
);
|
||||
|
||||
if ($response->successful()) {
|
||||
$this->notify('IP set deleted successfully', 'success');
|
||||
$this->loadIpsets();
|
||||
} else {
|
||||
$message = $response->json()['errors'][0]['message'] ?? 'Unknown error';
|
||||
$this->notify('Failed to delete IP set: ' . $message, 'error');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->notify('Failed to delete IP set: ' . $e->getMessage(), 'error');
|
||||
} finally {
|
||||
$this->loading = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user