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:
@@ -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([
|
||||
|
||||
Reference in New Issue
Block a user