fix(proxmox): harden console sessions and firewall rollout safety
Reduce security and reliability risk across the Proxmox extension by removing password exposure from Livewire state, hardening console auth/cookie handling, and improving sanitized error paths. Improve firewall and router performance by adding safer lazy loading, bounded pagination, cross-server compatibility guards, and lower-overhead API workflows. Made-with: Cursor
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
namespace Paymenter\Extensions\Servers\Proxmox\Http\Controllers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Client\RequestException;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Routing\Controller;
|
||||
use Paymenter\Extensions\Servers\Proxmox\Models\Server;
|
||||
@@ -25,16 +26,17 @@ class LiveTrafficController extends Controller
|
||||
);
|
||||
$config = $configResponse->json()['data'] ?? [];
|
||||
|
||||
// Extract network interfaces from config - only show net0
|
||||
// Extract network interfaces from config
|
||||
$interfaces = [];
|
||||
// Only include net0 interface
|
||||
$key = "net0";
|
||||
if (isset($config[$key])) {
|
||||
// Parse the network config string
|
||||
$netConfig = $config[$key];
|
||||
foreach ($config as $key => $value) {
|
||||
if (!preg_match('/^net\d+$/', (string) $key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$netConfig = (string) $value;
|
||||
preg_match('/bridge=([^,]+)/', $netConfig, $bridgeMatch);
|
||||
preg_match('/^([^=]+)/', $netConfig, $modelMatch);
|
||||
|
||||
|
||||
$interfaces[] = [
|
||||
'id' => $key,
|
||||
'name' => $key,
|
||||
@@ -43,30 +45,6 @@ class LiveTrafficController extends Controller
|
||||
];
|
||||
}
|
||||
|
||||
// Try to get guest agent network data for per-interface statistics
|
||||
// This provides actual per-interface data when RRD doesn't have it
|
||||
$guestAgentInterfaces = [];
|
||||
try {
|
||||
$guestResponse = $proxmox->request(
|
||||
'/nodes/' . $server->node->name . '/' . $server->node->vmType() . '/' . $server->vm_id . '/agent/network-get-interfaces'
|
||||
);
|
||||
$guestJson = $guestResponse->json();
|
||||
$guestAgentInterfaces = $guestJson['data']['result'] ?? [];
|
||||
} catch (\Throwable $e) {
|
||||
// Guest agent not available - continue without it
|
||||
$guestAgentInterfaces = [];
|
||||
}
|
||||
|
||||
// Map guest agent interfaces by name for easier lookup
|
||||
// Guest agent interfaces are typically named eth0, eth1, ens3, etc.
|
||||
$guestInterfaceMap = [];
|
||||
foreach ($guestAgentInterfaces as $guestIf) {
|
||||
$name = $guestIf['name'] ?? '';
|
||||
if ($name) {
|
||||
$guestInterfaceMap[$name] = $guestIf;
|
||||
}
|
||||
}
|
||||
|
||||
// Get RRD data once for all interfaces (more efficient)
|
||||
$rrdResponse = null;
|
||||
$rrdRows = null;
|
||||
@@ -90,6 +68,9 @@ class LiveTrafficController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
usort($interfaces, static fn (array $a, array $b) => strcmp($a['id'], $b['id']));
|
||||
$primaryInterfaceId = $interfaces[0]['id'] ?? 'net0';
|
||||
|
||||
// Get RRD data for each interface
|
||||
$interfaceData = [];
|
||||
$totalInRate = 0;
|
||||
@@ -139,7 +120,7 @@ class LiveTrafficController extends Controller
|
||||
// If interface-specific data not found, use aggregate data as fallback for net0 only
|
||||
// For other interfaces, use zeros to ensure each interface has unique data
|
||||
if (!$currentRow) {
|
||||
if ($ifNum === '0') {
|
||||
if ($interface['id'] === $primaryInterfaceId) {
|
||||
// For net0, use aggregate data as fallback (since aggregate typically = net0)
|
||||
foreach (array_reverse($rows) as $row) {
|
||||
if (isset($row['netin'], $row['netout'])) {
|
||||
@@ -192,7 +173,7 @@ class LiveTrafficController extends Controller
|
||||
} else {
|
||||
// For net0, use aggregate data as fallback (since aggregate typically = net0)
|
||||
// For other interfaces, use zeros to ensure unique graphs per interface
|
||||
if ($ifNum === '0') {
|
||||
if ($interface['id'] === $primaryInterfaceId) {
|
||||
$history['download'][] = (float) ($sample['netin'] ?? 0);
|
||||
$history['upload'][] = (float) ($sample['netout'] ?? 0);
|
||||
} else {
|
||||
@@ -211,7 +192,7 @@ class LiveTrafficController extends Controller
|
||||
|
||||
// For net0, we'll use aggregate totals (set later)
|
||||
// For other interfaces, use zeros to ensure uniqueness
|
||||
if ($ifNum !== '0') {
|
||||
if ($interface['id'] !== $primaryInterfaceId) {
|
||||
$totalIn = 0;
|
||||
$totalOut = 0;
|
||||
}
|
||||
@@ -238,17 +219,7 @@ class LiveTrafficController extends Controller
|
||||
|
||||
// If no interface-specific data, fall back to general RRD data
|
||||
if (empty($interfaceData)) {
|
||||
$response = $proxmox->request(
|
||||
'/nodes/' . $server->node->name . '/' . $server->node->vmType() . '/' . $server->vm_id . '/rrddata',
|
||||
'get',
|
||||
[
|
||||
'timeframe' => 'hour',
|
||||
'cf' => 'AVERAGE',
|
||||
]
|
||||
);
|
||||
|
||||
$json = $response->json();
|
||||
$rows = $json['data'] ?? [];
|
||||
$rows = is_array($rrdRows) ? $rrdRows : [];
|
||||
|
||||
if (is_array($rows) && count($rows) > 0) {
|
||||
$currentRow = null;
|
||||
@@ -324,10 +295,10 @@ class LiveTrafficController extends Controller
|
||||
$totalIn = (float) ($status['netin'] ?? 0);
|
||||
$totalOut = (float) ($status['netout'] ?? 0);
|
||||
|
||||
// Update net0 totals with aggregate data (since aggregate = net0 typically)
|
||||
// Update the first configured interface totals with aggregate data
|
||||
// Other interfaces keep zeros to ensure uniqueness
|
||||
foreach ($interfaceData as &$interface) {
|
||||
if ($interface['id'] === 'net0') {
|
||||
if ($interface['id'] === $primaryInterfaceId) {
|
||||
$interface['totalIn'] = $totalIn;
|
||||
$interface['totalOut'] = $totalOut;
|
||||
}
|
||||
@@ -349,8 +320,24 @@ class LiveTrafficController extends Controller
|
||||
'timestamp' => $timestamp->timestamp,
|
||||
]);
|
||||
|
||||
} catch (RequestException $e) {
|
||||
return response()->json([
|
||||
'interfaces' => [],
|
||||
'total' => [
|
||||
'downloadRate' => 0,
|
||||
'uploadRate' => 0,
|
||||
'combinedRate' => 0,
|
||||
'downloadTotal' => 0,
|
||||
'uploadTotal' => 0,
|
||||
'combinedTotal' => 0,
|
||||
],
|
||||
'scale' => self::RATE_SCALE,
|
||||
'updatedAt' => now()->toIso8601String(),
|
||||
'timestamp' => now()->timestamp,
|
||||
'error' => 'Unable to load live traffic from hypervisor.',
|
||||
'code' => 'proxmox_request_failed',
|
||||
], 502);
|
||||
} catch (\Throwable $e) {
|
||||
// Fallback: return empty data
|
||||
return response()->json([
|
||||
'interfaces' => [],
|
||||
'total' => [
|
||||
@@ -365,6 +352,7 @@ class LiveTrafficController extends Controller
|
||||
'updatedAt' => now()->toIso8601String(),
|
||||
'timestamp' => now()->timestamp,
|
||||
'error' => 'An internal error occurred.',
|
||||
'code' => 'internal_error',
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user