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:
2026-03-28 19:34:46 -04:00
parent 9f996035db
commit 68043f0416
39 changed files with 1106 additions and 5013 deletions

View File

@@ -1,4 +1,4 @@
<x-proxmox::layout :server="$server" view="cloud-init" :status="$status" :status-polling="true" :status-polling-interval="'2s'">
<x-proxmox::layout :server="$server" view="cloud-init" :status="$status" :status-polling="true" :status-polling-interval="'30s'">
<style>
.formRight select {
background-color: hsl(var(--color-background));
@@ -200,25 +200,26 @@
</div>
</div>
<div class="space-y-2" x-data="{ showPasswordViewModal: false, showPasswordEditModal: @entangle('isEditingPassword') }">
<div class="space-y-2"
x-data="{
showPasswordEditModal: @entangle('isEditingPassword'),
passwordInput: '',
passwordConfirmationInput: '',
resetPasswordFields() {
this.passwordInput = ''
this.passwordConfirmationInput = ''
}
}">
<label class="text-sm font-semibold text-base/80">Root Password</label>
<div class="space-y-2">
<div class="flex items-center gap-2 border border-neutral rounded-md bg-background px-3 py-2 text-sm text-base/80">
<span class="truncate flex-1">
@if(isset($server->password) && $server->password)
@if($server->hasStoredPassword())
••••••••••••
@else
Password is sent via email on install.
@endif
</span>
@if(isset($server->password) && $server->password)
<button type="button"
class="text-base/70 hover:text-base/60 ml-2"
@click="showPasswordViewModal = true"
aria-label="View Root Password">
<x-ri-eye-line class="inline size-4" />
</button>
@endif
<button type="button"
class="text-base/70 hover:text-base/60 ml-2"
wire:click="startEditingPassword"
@@ -228,54 +229,15 @@
</div>
</div>
<p class="text-xs text-base/60">Matches the Overview tab control. Use Save for database only, Save &amp; Apply to push via cloud-init.</p>
@if(isset($server->password) && $server->password)
<div x-cloak x-show="showPasswordViewModal" x-transition.opacity class="fixed inset-0 z-50 flex items-center justify-center">
<div class="fixed inset-0 bg-black/40 backdrop-blur-sm" @click="showPasswordViewModal = false"></div>
<div class="relative bg-background-secondary border border-neutral rounded-lg shadow-xl w-full max-w-xl mx-4 p-5 space-y-4"
x-on:keydown.escape.window="showPasswordViewModal = false"
x-data="{
copied: false,
copyText: {{ json_encode($server->password) }},
copyToClipboard() {
navigator.clipboard.writeText(this.copyText);
this.copied = true;
setTimeout(() => this.copied = false, 2000);
}
}">
<div class="flex items-center justify-between">
<h4 class="text-base font-semibold text-base/90">Root Password</h4>
<button type="button"
class="bg-background border border-neutral p-1.5 rounded-md hover:bg-background/80"
@click="showPasswordViewModal = false"
aria-label="Close password view modal">
<x-ri-close-line class="size-4" />
</button>
</div>
<div class="bg-background border border-neutral rounded-md p-3 text-sm text-base/80 overflow-auto">
<code class="whitespace-pre-wrap break-all block">{{ $server->password }}</code>
</div>
<div class="flex justify-end">
<button type="button"
class="inline-flex items-center gap-2 px-4 py-2 rounded-md bg-primary text-white text-xs font-semibold hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary"
@click="copyToClipboard()">
<span x-show="!copied">Copy</span>
<span x-show="copied">Copied!</span>
</button>
</div>
</div>
</div>
@endif
<div x-cloak x-show="showPasswordEditModal" x-transition.opacity class="fixed inset-0 z-50 flex items-center justify-center">
<div class="fixed inset-0 bg-black/40 backdrop-blur-sm" @click="showPasswordEditModal = false; $wire.cancelEditingPassword()"></div>
<div class="fixed inset-0 bg-black/40 backdrop-blur-sm" @click="showPasswordEditModal = false; resetPasswordFields(); $wire.cancelEditingPassword()"></div>
<div class="relative bg-background-secondary border border-neutral rounded-lg shadow-xl w-full max-w-xl mx-4 p-5 space-y-4"
x-on:keydown.escape.window="showPasswordEditModal = false; $wire.cancelEditingPassword()">
x-on:keydown.escape.window="showPasswordEditModal = false; resetPasswordFields(); $wire.cancelEditingPassword()">
<div class="flex items-center justify-between">
<h4 class="text-base font-semibold text-base/90">Edit Root Password</h4>
<button type="button"
class="bg-background border border-neutral p-1.5 rounded-md hover:bg-background/80"
@click="showPasswordEditModal = false; $wire.cancelEditingPassword()"
@click="showPasswordEditModal = false; resetPasswordFields(); $wire.cancelEditingPassword()"
aria-label="Close password edit modal">
<x-ri-close-line class="size-4" />
</button>
@@ -284,14 +246,14 @@
<div>
<label class="text-xs font-semibold text-base/70">New Password</label>
<input type="password"
wire:model="password"
x-model="passwordInput"
class="block w-full mt-1 px-2.5 py-2.5 text-sm text-base bg-background-secondary/50 border border-neutral rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-primary/30 focus:border-primary transition-all duration-300 ease-in-out"
placeholder="Enter new password" />
</div>
<div>
<label class="text-xs font-semibold text-base/70">Confirm Password</label>
<input type="password"
wire:model="password_confirmation"
x-model="passwordConfirmationInput"
class="block w-full mt-1 px-2.5 py-2.5 text-sm text-base bg-background-secondary/50 border border-neutral rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-primary/30 focus:border-primary transition-all duration-300 ease-in-out"
placeholder="Confirm password" />
</div>
@@ -303,13 +265,15 @@
</p>
</div>
<div class="flex justify-end gap-2">
<x-button.secondary type="button" @click="showPasswordEditModal = false; $wire.cancelEditingPassword()">
<x-button.secondary type="button" @click="showPasswordEditModal = false; resetPasswordFields(); $wire.cancelEditingPassword()">
Cancel
</x-button.secondary>
<x-button.primary type="button" wire:click="updatePassword">
<x-button.primary type="button"
@click="$wire.updatePassword(passwordInput, passwordConfirmationInput).then(() => { if (!$wire.isEditingPassword) { showPasswordEditModal = false; resetPasswordFields() } })">
Save
</x-button.primary>
<x-button.primary type="button" wire:click="applyPassword">
<x-button.primary type="button"
@click="$wire.applyPassword(passwordInput, passwordConfirmationInput).then(() => { if (!$wire.isEditingPassword) { showPasswordEditModal = false; resetPasswordFields() } })">
Save &amp; Apply
</x-button.primary>
</div>