From aa4115732ea7366e8e483f3f7f09ead96fd9adfd Mon Sep 17 00:00:00 2001 From: "Joseph.Rawlings" Date: Mon, 1 Dec 2025 13:57:36 -0500 Subject: [PATCH] feat: implement network adapter management in Proxmox Firewall component, allowing users to enable or disable firewalls for individual adapters and enhancing overall functionality --- .../Servers/Proxmox/Livewire/Firewall.php | 134 +++++++++++++++++- .../Servers/Proxmox/views/firewall.blade.php | 134 +++++++++++++++++- 2 files changed, 262 insertions(+), 6 deletions(-) diff --git a/extensions/Servers/Proxmox/Livewire/Firewall.php b/extensions/Servers/Proxmox/Livewire/Firewall.php index f3592ce5f..fee4afbe9 100644 --- a/extensions/Servers/Proxmox/Livewire/Firewall.php +++ b/extensions/Servers/Proxmox/Livewire/Firewall.php @@ -17,13 +17,15 @@ class Firewall extends Component public string $error = ''; public bool $showErrorModal = false; public bool $showRuleForm = false; + public array $networkAdapters = []; + public array $adapterFirewallStates = []; // Rule creation/editing public ?int $editingRulePos = null; public string $ruleAction = 'ACCEPT'; public string $ruleSource = ''; public string $ruleDest = ''; - public string $ruleProto = ''; + public string $ruleProto = 'tcp'; public string $ruleDport = ''; public string $ruleSport = ''; public string $ruleComment = ''; @@ -44,10 +46,12 @@ class Firewall extends Component public function mount(Server $server): void { parent::mount($server); + $this->server->loadMissing('ipAddresses'); $this->loadFirewallOptions(); $this->loadFirewallRules(); $this->loadAliases(); $this->loadIpsets(); + $this->loadNetworkAdapters(); } /** @@ -217,7 +221,7 @@ class Firewall extends Component $this->ruleAction = 'ACCEPT'; $this->ruleSource = ''; $this->ruleDest = ''; - $this->ruleProto = ''; + $this->ruleProto = 'tcp'; $this->ruleDport = ''; $this->ruleSport = ''; $this->ruleComment = ''; @@ -557,6 +561,132 @@ class Firewall extends Component } } + public function loadNetworkAdapters(): void + { + try { + $proxmox = new Proxmox; + $config = $proxmox->request('/nodes/' . $this->server->node->name . '/qemu/' . $this->server->vm_id . '/config')->json()['data'] ?? []; + + $this->networkAdapters = []; + $this->adapterFirewallStates = []; + + foreach ($config as $key => $value) { + if (!is_string($key) || !str_starts_with($key, 'net')) { + continue; + } + + $configString = (string) $value; + $segments = array_filter(array_map('trim', explode(',', $configString))); + $model = 'virtio'; + $mac = null; + $bridge = null; + $firewallEnabled = false; + + foreach ($segments as $segment) { + if (!str_contains($segment, '=')) { + continue; + } + + [$segmentKey, $segmentValue] = array_map('trim', explode('=', $segment, 2)); + $segmentKeyLower = strtolower($segmentKey); + + if (in_array($segmentKeyLower, ['virtio', 'e1000', 'e1000e', 'rtl8139', 'vmxnet3', 'pcnet'], true)) { + $model = $segmentKeyLower; + $mac = $segmentValue; + } elseif ($segmentKeyLower === 'bridge') { + $bridge = $segmentValue; + } elseif ($segmentKeyLower === 'firewall') { + $firewallEnabled = ($segmentValue == '1' || $segmentValue === '1'); + } + } + + $this->networkAdapters[$key] = [ + 'name' => $key, + 'model' => $model, + 'mac' => $mac, + 'bridge' => $bridge, + ]; + $this->adapterFirewallStates[$key] = $firewallEnabled; + } + } catch (\Exception $e) { + $this->networkAdapters = []; + $this->adapterFirewallStates = []; + } + } + + public function toggleAdapterFirewall(string $adapterName): void + { + $this->loading = true; + try { + $proxmox = new Proxmox; + $config = $proxmox->request('/nodes/' . $this->server->node->name . '/qemu/' . $this->server->vm_id . '/config')->json()['data'] ?? []; + + if (!isset($config[$adapterName])) { + $this->error = 'Network adapter not found: ' . $adapterName; + $this->showErrorModal = true; + $this->loading = false; + return; + } + + $currentConfig = (string) $config[$adapterName]; + $segments = array_filter(array_map('trim', explode(',', $currentConfig))); + $newSegments = []; + $firewallFound = false; + $currentFirewallState = $this->adapterFirewallStates[$adapterName] ?? false; + $newFirewallState = !$currentFirewallState; + + foreach ($segments as $segment) { + if (!str_contains($segment, '=')) { + $newSegments[] = $segment; + continue; + } + + [$segmentKey, $segmentValue] = array_map('trim', explode('=', $segment, 2)); + + if (strtolower($segmentKey) === 'firewall') { + $firewallFound = true; + if ($newFirewallState) { + $newSegments[] = 'firewall=1'; + } + // If disabling, we skip adding firewall=1, effectively removing it + } else { + $newSegments[] = $segment; + } + } + + // If firewall wasn't in the config and we're enabling it, add it + if (!$firewallFound && $newFirewallState) { + $newSegments[] = 'firewall=1'; + } + + $newConfig = implode(',', $newSegments); + + $proxmox->request( + '/nodes/' . $this->server->node->name . '/qemu/' . $this->server->vm_id . '/config', + 'put', + [$adapterName => $newConfig] + ); + + $this->adapterFirewallStates[$adapterName] = $newFirewallState; + $this->notify('Firewall ' . ($newFirewallState ? 'enabled' : 'disabled') . ' for ' . $adapterName, 'success'); + $this->loadNetworkAdapters(); // Reload to ensure consistency + } catch (\Exception $e) { + $errorMessage = $e->getMessage(); + if (method_exists($e, 'response') && $e->response()) { + $errorData = $e->response()->json(); + if (isset($errorData['errors']) && is_array($errorData['errors']) && !empty($errorData['errors'])) { + $errorMessage = $errorData['errors'][0]['message'] ?? $errorMessage; + } elseif (isset($errorData['message'])) { + $errorMessage = $errorData['message']; + } + } + $this->error = 'Failed to toggle firewall for adapter: ' . $errorMessage; + $this->showErrorModal = true; + } finally { + $this->loading = false; + } + } + public function render() { return view('proxmox::firewall')->layoutData([ diff --git a/extensions/Servers/Proxmox/views/firewall.blade.php b/extensions/Servers/Proxmox/views/firewall.blade.php index 3890589d5..909722186 100644 --- a/extensions/Servers/Proxmox/views/firewall.blade.php +++ b/extensions/Servers/Proxmox/views/firewall.blade.php @@ -101,6 +101,63 @@ + +
+
+
+ +
+
+

Network Adapters

+

Enable or disable firewall for each network adapter

+
+
+ + @if(count($networkAdapters) > 0) +
+ @foreach($networkAdapters as $adapter) +
+
+
+
+

{{ $adapter['name'] }}

+ + {{ ucfirst($adapter['model'] ?? 'Unknown') }} + +
+
+ @if($adapter['mac']) +
MAC: {{ $adapter['mac'] }}
+ @endif + @if($adapter['bridge']) +
Bridge: {{ $adapter['bridge'] }}
+ @endif +
+
+
+ Disabled + + Enabled +
+
+
+ @endforeach +
+ @else +
+ +

No network adapters found

+
+ @endif +
+ @if($firewallEnabled)
@@ -143,11 +200,57 @@
@@ -156,7 +259,25 @@
- +
@@ -168,7 +289,12 @@
- +