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:
@@ -134,13 +134,14 @@ class Storage extends Component
|
||||
public string $reattachTargetBus = 'scsi';
|
||||
|
||||
/**
|
||||
* Storage name for cephpool.
|
||||
* Active storage pool used for new drive allocations.
|
||||
*/
|
||||
protected string $storagePool = 'cephpool';
|
||||
|
||||
public function mount(Server $server): void
|
||||
{
|
||||
parent::mount($server);
|
||||
$this->storagePool = trim((string) ($this->server->node->storage_location ?? '')) ?: 'cephpool';
|
||||
|
||||
$this->loadMaxAllowedStorage();
|
||||
$this->loadStorageConfiguration(false);
|
||||
@@ -181,7 +182,8 @@ class Storage extends Component
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
if ($notify) {
|
||||
$this->notify('Failed to load storage configuration: ' . $e->getMessage(), 'error');
|
||||
report($e);
|
||||
$this->notify('Failed to load storage configuration. Please refresh and try again.', 'error');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -193,11 +195,27 @@ class Storage extends Component
|
||||
{
|
||||
$drives = [];
|
||||
$totalSize = 0;
|
||||
$isLxc = (bool) ($this->server->node->is_lxc ?? false);
|
||||
|
||||
foreach ($config as $key => $value) {
|
||||
// Skip non-disk entries
|
||||
if (!preg_match('/^(ide|sata|scsi|virtio)(\d+)$/', $key, $matches)) {
|
||||
continue;
|
||||
if ($isLxc) {
|
||||
if (preg_match('/^rootfs$/', (string) $key)) {
|
||||
$busType = 'rootfs';
|
||||
$deviceIndex = 0;
|
||||
} elseif (preg_match('/^mp(\d+)$/', (string) $key, $matches)) {
|
||||
$busType = 'mp';
|
||||
$deviceIndex = (int) $matches[1];
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// Skip non-disk entries
|
||||
if (!preg_match('/^(ide|sata|scsi|virtio)(\d+)$/', (string) $key, $matches)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$busType = $matches[1];
|
||||
$deviceIndex = (int) $matches[2];
|
||||
}
|
||||
|
||||
// Skip cloud-init, EFI, and TPM disks
|
||||
@@ -210,9 +228,6 @@ class Storage extends Component
|
||||
continue;
|
||||
}
|
||||
|
||||
$busType = $matches[1];
|
||||
$deviceIndex = (int) $matches[2];
|
||||
|
||||
$diskInfo = $this->parseDiskString($value, $busType, $deviceIndex);
|
||||
if ($diskInfo) {
|
||||
$drives[$key] = $diskInfo;
|
||||
@@ -222,10 +237,17 @@ class Storage extends Component
|
||||
|
||||
// Sort drives by bus type and index
|
||||
uksort($drives, function ($a, $b) {
|
||||
preg_match('/^(ide|sata|scsi|virtio)(\d+)$/', $a, $matchA);
|
||||
preg_match('/^(ide|sata|scsi|virtio)(\d+)$/', $b, $matchB);
|
||||
|
||||
$busOrder = ['ide' => 0, 'sata' => 1, 'scsi' => 2, 'virtio' => 3];
|
||||
preg_match('/^(ide|sata|scsi|virtio|mp)(\d+)$/', (string) $a, $matchA);
|
||||
preg_match('/^(ide|sata|scsi|virtio|mp)(\d+)$/', (string) $b, $matchB);
|
||||
|
||||
if ((string) $a === 'rootfs') {
|
||||
$matchA = ['rootfs', 'rootfs', '0'];
|
||||
}
|
||||
if ((string) $b === 'rootfs') {
|
||||
$matchB = ['rootfs', 'rootfs', '0'];
|
||||
}
|
||||
|
||||
$busOrder = ['rootfs' => 0, 'ide' => 1, 'sata' => 2, 'scsi' => 3, 'virtio' => 4, 'mp' => 5];
|
||||
$busCompare = ($busOrder[$matchA[1]] ?? 4) <=> ($busOrder[$matchB[1]] ?? 4);
|
||||
|
||||
if ($busCompare !== 0) {
|
||||
@@ -281,7 +303,7 @@ class Storage extends Component
|
||||
$volume = trim($value);
|
||||
|
||||
// Extract storage name from volume (format: storage:volume-name)
|
||||
$storageName = 'cephpool';
|
||||
$storageName = '';
|
||||
if (str_contains($volume, ':')) {
|
||||
[$storageName, ] = explode(':', $volume, 2);
|
||||
}
|
||||
@@ -289,35 +311,37 @@ class Storage extends Component
|
||||
// Try to get the volume size from the storage API
|
||||
$sizeGb = 0;
|
||||
$sizeFormatted = 'Unknown';
|
||||
try {
|
||||
$proxmox = new Proxmox;
|
||||
$storageContent = $proxmox->request(
|
||||
'/nodes/' . $this->server->node->name . '/storage/' . $storageName . '/content',
|
||||
data: ['vmid' => $this->server->vm_id]
|
||||
);
|
||||
if ($storageName !== '') {
|
||||
try {
|
||||
$proxmox = new Proxmox;
|
||||
$storageContent = $proxmox->request(
|
||||
'/nodes/' . $this->server->node->name . '/storage/' . $storageName . '/content',
|
||||
data: ['vmid' => $this->server->vm_id]
|
||||
);
|
||||
|
||||
$data = $storageContent->json()['data'] ?? [];
|
||||
if (is_array($data)) {
|
||||
foreach ($data as $item) {
|
||||
if (isset($item['volid']) && $item['volid'] === $volume) {
|
||||
// Size is in bytes
|
||||
if (isset($item['size'])) {
|
||||
$sizeGb = round($item['size'] / (1024 * 1024 * 1024), 2);
|
||||
$sizeFormatted = $sizeGb . ' GB';
|
||||
$data = $storageContent->json()['data'] ?? [];
|
||||
if (is_array($data)) {
|
||||
foreach ($data as $item) {
|
||||
if (isset($item['volid']) && $item['volid'] === $volume) {
|
||||
// Size is in bytes
|
||||
if (isset($item['size'])) {
|
||||
$sizeGb = round($item['size'] / (1024 * 1024 * 1024), 2);
|
||||
$sizeFormatted = $sizeGb . ' GB';
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// If we can't fetch size, leave as unknown
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// If we can't fetch size, leave as unknown
|
||||
}
|
||||
|
||||
return [
|
||||
'key' => 'unused' . $unusedIndex,
|
||||
'index' => $unusedIndex,
|
||||
'volume' => $volume,
|
||||
'storage' => $storageName,
|
||||
'storage' => $storageName !== '' ? $storageName : 'Unknown',
|
||||
'size' => $sizeGb,
|
||||
'size_formatted' => $sizeFormatted,
|
||||
'label' => 'Detached Disk ' . $unusedIndex,
|
||||
@@ -445,6 +469,8 @@ class Storage extends Component
|
||||
protected function getDiskLabel(string $busType, int $index): string
|
||||
{
|
||||
$busLabels = [
|
||||
'rootfs' => 'Root Filesystem',
|
||||
'mp' => 'Mount Point',
|
||||
'ide' => 'IDE',
|
||||
'sata' => 'SATA',
|
||||
'scsi' => 'SCSI',
|
||||
@@ -584,7 +610,8 @@ class Storage extends Component
|
||||
$this->closeAddDriveModal();
|
||||
$this->loadStorageConfiguration(false);
|
||||
} catch (\Exception $e) {
|
||||
$this->notify('Failed to add drive: ' . $e->getMessage(), 'error');
|
||||
report($e);
|
||||
$this->notify('Failed to add drive. Please try again.', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -735,7 +762,8 @@ class Storage extends Component
|
||||
$this->closeEditDriveModal();
|
||||
$this->loadStorageConfiguration(false);
|
||||
} catch (\Exception $e) {
|
||||
$this->notify('Failed to update drive settings: ' . $e->getMessage(), 'error');
|
||||
report($e);
|
||||
$this->notify('Failed to update drive settings. Please try again.', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -853,7 +881,8 @@ class Storage extends Component
|
||||
// Dispatch browser event to refresh the page
|
||||
$this->dispatch('refresh-page');
|
||||
} catch (\Exception $e) {
|
||||
$this->notify('Failed to detach drive: ' . $e->getMessage(), 'error');
|
||||
report($e);
|
||||
$this->notify('Failed to detach drive. Please try again.', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user