Highlights
🔒 Full-DB SQLCipher Encryption (Linux x86_64)
- AES-256-CBC + HMAC-SHA512, SQLCipher format v4 — every page on disk encrypted at rest
- Auto-migrates plain DBs on first boot post-update: copy →
sqlcipher_export→ per-table row-count verify → atomic rename. Timestamped.plain.bak.<ts>retained so operators can verify before manually shredding. - Graceful fallback to plain SQLite + per-field Fernet encryption where the
sqlcipher3-binarywheel isn't available (ARM / macOS / Windows). Sensitive fields (cluster passwords, TOTP secrets, OIDC client secrets, API tokens, LDAP binds) stay encrypted on every platform. - Opt-out:
PEGAPROX_DISABLE_AUTO_ENCRYPT=1to skip the auto-encrypt boot step (take your own pre-backup first).
🔑 Multi-Tier Master-Key Loader
- Tier ladder:
PEGAPROX_DB_KEYenv → systemdLoadCredentialEncrypted(TPM2-bound, strongest) →PEGAPROX_KEY_FILEenv →/etc/pegaprox/secret.key→~/.config/pegaprox/secret.key→ legacyconfig/(deprecation-warned) - Loose-permission files (chmod with group/other read) are skipped, never silently used — accidental key exposure can't become the active key
- Fresh installs via
deploy.shnow generate the master key at/etc/pegaprox/secret.key(chmod 0600 root:pegaprox) outside the config directory, so aconfig/backup no longer carries the decryption key alongside the encrypted data - New CLI:
pegaprox_multi_cluster.py --keystore-statusfor the active tier;--migrate-dbfor explicit/scripted migration;--print-keyfor handoff into secret managers
🖥️ LXC Dedicated Text Terminal
pct enteraccess for containers, routed through PVE's built-in/termproxyAPI — no SSH key required for LXC shell access- Server-side ticket mint via the cluster's stored credentials, no shell-exec on our side.
vm.consolepermission gated, audit-logged undervm.terminal - Surfaced in both the Corporate-layout VM modal and the Modern dashboard. Translation in DE / EN / FR / ES / PT / KO
📋 Cluster-Scoped Syslog Viewer (#387, PR #399)
- New Settings → Syslog Server tab with a toggle: when enabled and a cluster is selected in the dashboard, the Syslog event list filters to log rows whose hostname matches one of that cluster's hosts/nodes
- Robust hostname matching: handles URL forms, IPv4/IPv6 with or without port, FQDN short-name matching, graceful fallback when a manager can't enumerate its nodes
- Default off — pre-existing "all syslog rows" behaviour preserved
- Contributed by @gyptazy, sponsored by credativ GmbH 🙌
🎨 Corporate Layout Enhancements
- Refined surface tokens, density tuning and information hierarchy across the chrome Style (≈1.4k lines of new UI sources)
- Backwards-compatible with the Modern (Proxmox-orange) layout — switch via user prefs
- Affects: dashboard, VM modals, configure tabs, settings modals, node detail views
🛡️ Security Hardening
- Dependency floor bumps to clear pip-audit / Snyk / Aikido findings: Flask 3.0.3, flask-cors 6.0.0 (CWE-178 case-sensitivity Origin bypass + 2 mediums), gevent 25.4.1 (CVSS 9.3 trusting HTTP permission methods + CVSS 8.3 race condition + CVSS 6.9 request smuggling), werkzeug 3.0.3 (CVE-2024-34069 debugger pin RCE), urllib3 2.5.0 (CVE-2025-50181 open redirect), paramiko 4.0.0 (drops legacy DSSKey, broken-crypto class), cryptography 46.0.7, h11 0.16.0 (CVSS 9.3 request smuggling), pyasn1 0.6.3, setuptools 78.1.1, zipp 3.19.1
- 42-site reflected-content sanitizer on Proxmox-passthrough error paths across
datacenter/vms/storage/static_files— everyjsonify({'error': response.text})now routes throughparse_pve_error()which extracts only the structuredmessagefield from PVE JSON, truncates to 500 chars, html-escapes the output, and falls back to a generic error string when the upstream body looks HTML-shaped. Closes Snyk Code's CWE-79 reflected-XSS cluster on SDN endpoints. - HMAC-signed audit log entries now cover every key event (terminal open, settings toggle, migration)
🐳 Docker Operational Tuning
- HEALTHCHECK
start_periodbumped 15s → 120s,retries3 → 5 to give the in-process DB migration room on the upgrade boot. Subsequent boots short-circuit (state == 'encrypted'), so the longstart_periodonly costs on the upgrade run. requirements.txtsqlcipher3-binarycarries a platform marker (linux + x86_64) so the arm64 image build leg skips it cleanly and falls back to the plain-SQLite + Fernet path
🧪 Post-Release Hardening (v0.9.10.1)
Same-day follow-up addressing Aikido findings:
- Two critical SAST findings in
migrate_db.py— table names fromsqlite_masterwere embedded intoSELECT COUNT(*)via f-string. Replaced with an identifier whitelist + escaped double-quoting helper. Practical exploitability was nil (no HTTP input path) but f-string SQL is an unconditional defense-in-depth no-go. urllib3floor 2.5.0 → 2.7.0 (CVE-2026-44431, critical)werkzeugfloor 3.0.3 → 3.1.6 (CVE-2024-49767 DOS, CVE-2024-49766 path traversal, CVE-2026-27199, AIKIDO-2024-10410 weak encryption strength)
💎 Platinum Sponsors
Massive thanks to our Platinum Sponsors 🏆:
- netwolk GmbH — Swiss managed-services partner backing PegaProx since the early days
- Expertize — Dutch Proxmox specialists keeping the lights on
Your support directly funds ongoing PegaProx development — server costs, code-signing certificates, and the developer hours behind every release.
Interested in sponsoring? → pegaprox.com/#sponsor | sponsor@pegaprox.com | opencollective.com/pegaprox
Upgrade path: in-app updater (Settings → Updates), bash update.sh from a working install, or docker compose pull && docker compose up -d for Docker deployments.
Docker image: ghcr.io/pegaprox/pegaprox:v0.9.10 (or :latest) — multi-arch linux/amd64 + linux/arm64. The arm64 image gracefully falls back to plain SQLite + Fernet field encryption since sqlcipher3-binary ships wheels for x86_64 only.
First boot post-update: expect a one-time ~5–15 s pause on a 17 MB DB while SQLCipher migration runs. Larger DBs scale linearly (≈0.5 s/MB). The encrypted DB and the timestamped .plain.bak.<ts> backup are both retained in config/ — verify the encrypted DB works for a day or two before manually shredding the backup.