Private
Public Access
1
0

feat: enhance Proxmox Firewall functionality by increasing log limit, adding DHCP management during firewall state changes, and implementing IP geolocation features for improved user experience

This commit is contained in:
2025-12-02 07:17:20 -05:00
parent d8db472915
commit b100625a2b
2 changed files with 503 additions and 5 deletions

View File

@@ -21,7 +21,7 @@ class Firewall extends Component
public array $networkAdapters = [];
public array $adapterFirewallStates = [];
public array $firewallLogs = [];
public int $logLimit = 100;
public int $logLimit = 5000;
public array $availableIps = [];
public bool $showDeleteModal = false;
public ?int $deletingRulePos = null;
@@ -737,6 +737,7 @@ class Firewall extends Component
$segments = array_filter(array_map('trim', explode(',', $currentConfig)));
$newSegments = [];
$firewallFound = false;
$dhcpFound = false;
$currentFirewallState = $this->adapterFirewallStates[$adapterName] ?? false;
$newFirewallState = !$currentFirewallState;
@@ -754,6 +755,15 @@ class Firewall extends Component
$newSegments[] = 'firewall=1';
}
// If disabling, we skip adding firewall=1, effectively removing it
} elseif (strtolower($segmentKey) === 'dhcp') {
$dhcpFound = true;
// If enabling firewall, disable DHCP
if ($newFirewallState) {
$newSegments[] = 'dhcp=0';
} else {
// Keep existing DHCP setting when disabling firewall
$newSegments[] = $segment;
}
} else {
$newSegments[] = $segment;
}
@@ -764,6 +774,11 @@ class Firewall extends Component
$newSegments[] = 'firewall=1';
}
// If DHCP wasn't in the config and we're enabling firewall, explicitly disable DHCP
if (!$dhcpFound && $newFirewallState) {
$newSegments[] = 'dhcp=0';
}
$newConfig = implode(',', $newSegments);
$proxmox->request(
@@ -802,7 +817,10 @@ class Firewall extends Component
$response = $this->firewallRequest(
'/nodes/' . $this->server->node->name . '/qemu/' . $this->server->vm_id . '/firewall/log',
'get',
['limit' => $this->logLimit]
[
'limit' => $this->logLimit,
'start' => 0
]
);
if (! $response->successful()) {
@@ -1001,6 +1019,101 @@ class Firewall extends Component
return $result;
}
/**
* Check if an IP address is public (callable from frontend)
*/
public function checkIsPublicIP(string $ip): bool
{
return $this->isPublicIP($ip);
}
/**
* Check if an IP address is public (not private/localhost)
*/
protected function isPublicIP(string $ip): bool
{
$ip = $this->extractIPFromString($ip);
if (empty($ip)) {
return false;
}
// Check for localhost
if (in_array(strtolower($ip), ['localhost', '127.0.0.1', '::1'])) {
return false;
}
// Validate IP format
if (!filter_var($ip, FILTER_VALIDATE_IP)) {
return false;
}
// Check if it's a private IP
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
return true;
}
return false;
}
/**
* Extract IP address from a string (removes port if present)
*/
protected function extractIPFromString(string $input): string
{
$input = trim($input);
// Handle IPv6 with port: [2001:db8::1]:8080
if (preg_match('/^\[([^\]]+)\](?::\d+)?$/', $input, $matches)) {
return $matches[1];
}
// Handle IPv4 with port: 192.168.1.1:8080
if (preg_match('/^([^:]+):\d+$/', $input, $matches)) {
// Make sure it's not IPv6 without brackets
if (!str_contains($matches[1], ':')) {
return $matches[1];
}
}
// Return as-is if no port detected
return $input;
}
/**
* Fetch IP geolocation data
*/
public function fetchIPLocation($ip): array
{
if (empty($ip)) {
return ['status' => 'fail', 'message' => 'IP address is required'];
}
// Extract and validate IP address
$cleanIP = $this->extractIPFromString((string)$ip);
if (!$this->isPublicIP($cleanIP)) {
return ['status' => 'fail', 'message' => 'Not a valid public IP address'];
}
try {
// Use HTTP protocol to fetch geolocation data
$response = \Illuminate\Support\Facades\Http::timeout(5)->get("http://ip-api.com/json/{$cleanIP}");
if ($response->successful()) {
return $response->json();
}
return ['status' => 'fail', 'message' => 'Failed to fetch location data'];
} catch (\Exception $e) {
\Log::error('Error fetching IP location', [
'ip' => $cleanIP,
'error' => $e->getMessage(),
]);
return ['status' => 'fail', 'message' => 'Error: ' . $e->getMessage()];
}
}
public function render()
{
return view('proxmox::firewall')->layoutData([