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:
@@ -23,14 +23,8 @@ class CloudInit extends Component
|
||||
'l26',
|
||||
'solaris',
|
||||
];
|
||||
private const MANAGEMENT_KEY = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCBeSz45D52fvy4EKPUvI4Upq2t1Ae+MJxB4Lla17f7kHFmoDCJvHfXUCxNv+Tr7FtbCWCdZ2yj8jvS8TNuJ+dzUORbRMgJcokmMLj486RYT7luRHPi+kU5CCv+yIOTNl308wTNmY12rmj+9/81gJA+ipll6UlNTh4T//koSwtcu0mw8ElyV8vjz+s+2vTbPRP+TMPGsFATyJjcjvFvtISW/TT0MuUkbk/bLpv8iurMjFSORa2/lnnMfnznSEL3LQC7CrWvpiCV7mhPn4ZLR/MksvzNnjjKzJHw2u5b8NmiztCfrPUTE/LDNwXh6WtIgqXt7BcjRUT1lR+MMLkhb1lf rsa-key-20240918';
|
||||
|
||||
public string $ciUser = '';
|
||||
|
||||
public string $password = '';
|
||||
|
||||
public string $password_confirmation = '';
|
||||
|
||||
public string $dnsDomain = '';
|
||||
|
||||
public array $dnsServers = [];
|
||||
@@ -128,8 +122,11 @@ class CloudInit extends Component
|
||||
|
||||
$normalizedKeys = array_filter(array_map('trim', $this->sshKeys));
|
||||
$encodedKeys = array_map('rawurlencode', $normalizedKeys);
|
||||
$encodedManagementKey = rawurlencode(self::MANAGEMENT_KEY);
|
||||
$allKeys = array_merge($encodedKeys, [$encodedManagementKey]);
|
||||
$allKeys = $encodedKeys;
|
||||
$managementKey = $this->managementSshKey();
|
||||
if ($managementKey !== '' && !in_array($managementKey, $normalizedKeys, true)) {
|
||||
$allKeys[] = rawurlencode($managementKey);
|
||||
}
|
||||
$payload['sshkeys'] = implode('%0A', $allKeys);
|
||||
|
||||
foreach ($this->networks as $network) {
|
||||
@@ -190,7 +187,8 @@ class CloudInit extends Component
|
||||
|
||||
$this->notify(implode(' ', $messageParts), $messageLevel);
|
||||
} catch (\Exception $e) {
|
||||
$this->notify('Failed to save cloud-init settings: ' . $e->getMessage(), 'info');
|
||||
report($e);
|
||||
$this->notify('Failed to save cloud-init settings. Please try again.', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,7 +233,8 @@ class CloudInit extends Component
|
||||
];
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->notify('Unable to load cloud-init data: ' . $e->getMessage(), 'error');
|
||||
report($e);
|
||||
$this->notify('Unable to load cloud-init data. Please refresh and try again.', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,10 +259,25 @@ class CloudInit extends Component
|
||||
|
||||
return array_values(array_filter($keys, function ($key) {
|
||||
$normalized = preg_replace('/\s+/', ' ', trim($key));
|
||||
return $normalized !== self::MANAGEMENT_KEY;
|
||||
$managementKey = $this->managementSshKey();
|
||||
if ($managementKey === '') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $normalized !== $managementKey;
|
||||
}));
|
||||
}
|
||||
|
||||
protected function managementSshKey(): string
|
||||
{
|
||||
$configured = config('services.proxmox.management_ssh_key');
|
||||
if (is_string($configured) && trim($configured) !== '') {
|
||||
return trim($configured);
|
||||
}
|
||||
|
||||
return trim((string) env('PROXMOX_MANAGEMENT_SSH_KEY', ''));
|
||||
}
|
||||
|
||||
protected function extractNetworkConfigs(array $config): array
|
||||
{
|
||||
$networks = [];
|
||||
@@ -380,16 +394,12 @@ class CloudInit extends Component
|
||||
public function startEditingPassword(): void
|
||||
{
|
||||
$this->isEditingPassword = true;
|
||||
$this->password = '';
|
||||
$this->password_confirmation = '';
|
||||
$this->passwordError = '';
|
||||
}
|
||||
|
||||
public function cancelEditingPassword(): void
|
||||
{
|
||||
$this->isEditingPassword = false;
|
||||
$this->password = '';
|
||||
$this->password_confirmation = '';
|
||||
$this->passwordError = '';
|
||||
}
|
||||
|
||||
@@ -438,12 +448,12 @@ class CloudInit extends Component
|
||||
$this->cancelEditingNetwork();
|
||||
}
|
||||
|
||||
protected function validatePasswordFields(): bool
|
||||
protected function validatePasswordFields(string $password, string $passwordConfirmation): bool
|
||||
{
|
||||
$validator = Validator::make(
|
||||
[
|
||||
'password' => $this->password,
|
||||
'password_confirmation' => $this->password_confirmation,
|
||||
'password' => $password,
|
||||
'password_confirmation' => $passwordConfirmation,
|
||||
],
|
||||
[
|
||||
'password' => 'required|string|min:8|confirmed',
|
||||
@@ -459,33 +469,40 @@ class CloudInit extends Component
|
||||
return true;
|
||||
}
|
||||
|
||||
public function updatePassword(): void
|
||||
protected function closePasswordEditor(): void
|
||||
{
|
||||
if (!$this->validatePasswordFields()) {
|
||||
$this->isEditingPassword = false;
|
||||
$this->passwordError = '';
|
||||
}
|
||||
|
||||
public function updatePassword(string $password = '', string $passwordConfirmation = ''): void
|
||||
{
|
||||
if (!$this->validatePasswordFields($password, $passwordConfirmation)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->server->password = $this->password;
|
||||
$this->server->password = $password;
|
||||
$this->server->save();
|
||||
$this->server->refresh();
|
||||
|
||||
$this->notify('Password saved to database successfully.', 'success');
|
||||
$this->cancelEditingPassword();
|
||||
$this->closePasswordEditor();
|
||||
} catch (\Exception $e) {
|
||||
$this->passwordError = 'Failed to save password: ' . $e->getMessage();
|
||||
report($e);
|
||||
$this->passwordError = 'Failed to save password. Please try again.';
|
||||
$this->notify('Failed to save password', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
public function applyPassword(): void
|
||||
public function applyPassword(string $password = '', string $passwordConfirmation = ''): void
|
||||
{
|
||||
if (!$this->validatePasswordFields()) {
|
||||
if (!$this->validatePasswordFields($password, $passwordConfirmation)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->server->password = $this->password;
|
||||
$this->server->password = $password;
|
||||
$this->server->save();
|
||||
$this->server->refresh();
|
||||
|
||||
@@ -494,14 +511,15 @@ class CloudInit extends Component
|
||||
'/nodes/' . $this->server->node->name . '/' . $this->server->node->vmType() . '/' . $this->server->vm_id . '/config',
|
||||
'put',
|
||||
[
|
||||
'cipassword' => $this->password,
|
||||
'cipassword' => $password,
|
||||
]
|
||||
);
|
||||
|
||||
$this->notify('Password applied. Reboot the server for cloud-init to set the new password.', 'success');
|
||||
$this->cancelEditingPassword();
|
||||
$this->closePasswordEditor();
|
||||
} catch (\Exception $e) {
|
||||
$this->passwordError = 'Failed to apply password: ' . $e->getMessage();
|
||||
report($e);
|
||||
$this->passwordError = 'Failed to apply password. Please try again.';
|
||||
$this->notify('Failed to apply password to Proxmox', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user