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 @@ + +
Enable or disable firewall for each network adapter
+{{ $adapter['mac'] }}{{ $adapter['bridge'] }}No network adapters found
+