A security + reliability release on top of 1.1.0. Every 1.1.0 install should update.
🔒 Security
- Cluster root ticket exposed to any authenticated user (GHSA-pwhc-r276-gp73, critical) — the WebSocket token-validation endpoint returned the managed cluster's stored
root@pamticket to any signed-in account, bypassing every PegaProx permission below it. The endpoint now only answers our own console subprocess, authenticated with a per-run secret. Clusters configured with an API token were not affected. - Snapshot name path traversal — the snapshot routes interpolated the name straight into the Proxmox API path while the gate above them checked only the VM id, so a caller who legitimately owned one guest could normalise dot-segments into a different endpoint entirely — sent with the cluster's stored credentials.
vm.snapshotis a builtin default carried by every shipped role template, so the most locked-down role an operator would build still reached it. Snapshot names are now validated centrally, before any path is built. - SSRF guard bypass via IPv6 transition addresses (GHSA-ffhp-cpm8-4mpv, high) — 6to4, NAT64, Teredo and v4-mapped addresses embed an IPv4 address that the outbound-URL guard never decoded, so an internal target could be reached through an IPv6 literal. The embedded address is now decoded and classified. Reported by @tonghuaroot.
- Object-level authorization sweep — the recurring class where reaching a cluster was treated as reaching everything on it is closed across the console, LXC terminal, PBS backups / notes / tasks, node task logs, global search, tags, reports and the per-VM read endpoints. Pool- and ACL-scoped accounts now stay inside their scope on every one of them, and a tenant-scoped custom role resolves against its own tenant instead of falling back to the viewer defaults.
- Live streams re-check the account behind them on their own clock, so a disabled or demoted account loses an open stream instead of keeping it until it disconnects; and one account can no longer hold unbounded SSE streams open.
- Encryption-key rotation was rebuilt: it committed re-encrypted rows before the new key reached disk, missed the PBS, ESXi and VAPID secrets and
clusters.ha_settings, and left the audit trail reading as tampered. It is one transaction now, and a damaged key file refuses startup rather than being regenerated over.
🖥️ Console
- Latency — the receive slice the relay used was also the delay every outbound pointer event inherited. At a realistic event rate the writer waited on every send; it now passes the full rate with p50 in the low milliseconds (#713).
- Intermittent disconnects — the greenlet pool issued more concurrent node calls than the HTTPS keep-alive pool could hold, so every sweep left fresh handshakes on the node's
pveproxy— the same one a console rides. The two are one constant now. - Scale — the IP/disk sweep scales with running guests rather than nodes and could fill the shared budget on a large estate; it has its own share, so opening a console no longer queues behind it.
✨ Features
- External window VM console — a console can be opened in its own browser window and several watched side by side (#767).
- The interface now carries its copyright, license and source references on every screen, with the full notice under Settings → About.
🐛 Fixes
- Clean systemd stop that also takes the console subprocess with it (#780, #784). PR by @si458.
- Request-pool slots are reclaimed from idle keep-alive connections instead of starving the server (#777).
- An idle tab dropped from about four REST calls a second to well under one (#782).
- A same-named cluster no longer steals another cluster's log handler (#783).
- Node shell over IPv6 (#778); deleting a cluster purges every table keyed by it (#779).
- Pool assignment is gated on
pool.assignrather thanadmin.users(#766). - Three XCP-ng and ESXi endpoints that raised instead of answering (#781).
- Container create and destroy work again in the client portal (#765); cluster settings survive a reconfigure (#762); Corporate ESXi table headers align with their rows (#753).
- The compliance dashboard now says why a node check could not run — no SSH credentials, node in backoff, or a failed connection — instead of showing
err 502next to every node (#717). - Cross-cluster migration fingerprint case (#733); a V2P disk copy that reported success after failing, and an ESXi password left world-readable.
✅ Quality
1270 automated tests (authorization, integration, SSRF, crypto rotation, i18n, frontend invariants) run on every PR. The console work was verified against a live PVE 9.2.10 cluster, including a 22-minute single-session soak with zero drops.
Thank you to everyone who filed, fixed, translated and sponsored along the way. 💚
💛 Sponsors
PegaProx is AGPL-3.0 and built in the open. Huge thanks to our Platinum sponsors who keep it moving:
💎 Platinum