diff --git a/app/sovran_systemsos_web/server.py b/app/sovran_systemsos_web/server.py index 267e96f..2f5b355 100644 --- a/app/sovran_systemsos_web/server.py +++ b/app/sovran_systemsos_web/server.py @@ -59,6 +59,11 @@ REBOOT_COMMAND = ["reboot"] ONBOARDING_FLAG = "/var/lib/sovran/onboarding-complete" AUTOLAUNCH_DISABLE_FLAG = "/var/lib/sovran/hub-autolaunch-disabled" +# ── Legacy security check constants ────────────────────────────── + +SECURITY_STATUS_FILE = "/var/lib/sovran/security-status" +SECURITY_WARNING_FILE = "/var/lib/sovran/security-warning" + # ── Tech Support constants ──────────────────────────────────────── SUPPORT_KEY_FILE = "/root/.ssh/sovran_support_authorized" @@ -2916,6 +2921,37 @@ async def api_domains_check(req: DomainCheckRequest): return {"domains": list(check_results)} +# ── Legacy security check ───────────────────────────────────────── + +@app.get("/api/security/status") +async def api_security_status(): + """Return the legacy security status and warning message, if present. + + Reads /var/lib/sovran/security-status and /var/lib/sovran/security-warning. + Returns {"status": "legacy", "warning": ""} for legacy machines, + or {"status": "ok", "warning": ""} when the files are absent. + """ + try: + with open(SECURITY_STATUS_FILE, "r") as f: + status = f.read().strip() + except FileNotFoundError: + status = "ok" + + warning = "" + if status == "legacy": + try: + with open(SECURITY_WARNING_FILE, "r") as f: + warning = f.read().strip() + except FileNotFoundError: + warning = ( + "This machine was manufactured before the factory-seal process. " + "The default system password may be known to the factory. " + "Please change your system and application passwords immediately." + ) + + return {"status": status, "warning": warning} + + # ── Matrix user management ──────────────────────────────────────── MATRIX_USERS_FILE = "/var/lib/secrets/matrix-users" diff --git a/app/sovran_systemsos_web/static/css/security.css b/app/sovran_systemsos_web/static/css/security.css new file mode 100644 index 0000000..513fc3d --- /dev/null +++ b/app/sovran_systemsos_web/static/css/security.css @@ -0,0 +1,55 @@ +/* ── Legacy security warning modal ──────────────────────────────── */ + +.security-warning-dialog { + max-width: 520px; +} + +.security-warning-header { + background-color: #3b1212; + border-bottom-color: #7a2020; +} + +.security-warning-body { + display: flex; + flex-direction: column; + align-items: center; + gap: 16px; + padding: 24px 20px; +} + +.security-warning-icon { + font-size: 2.5rem; +} + +.security-warning-message { + text-align: center; + color: var(--text-primary); + line-height: 1.6; + margin: 0; +} + +.security-warning-actions { + width: 100%; + display: flex; + flex-direction: column; + align-items: center; + gap: 12px; +} + +.security-warning-hint { + color: var(--text-secondary); + font-size: 0.85rem; + margin: 0; + text-align: center; +} + +.security-warning-links { + display: flex; + gap: 12px; + flex-wrap: wrap; + justify-content: center; +} + +.security-warning-link { + text-decoration: none; +} diff --git a/app/sovran_systemsos_web/static/js/events.js b/app/sovran_systemsos_web/static/js/events.js index a39ef00..05e713b 100644 --- a/app/sovran_systemsos_web/static/js/events.js +++ b/app/sovran_systemsos_web/static/js/events.js @@ -38,6 +38,9 @@ if ($upgradeCloseBtn) $upgradeCloseBtn.addEventListener("click", closeUpgradeMod if ($upgradeCancelBtn) $upgradeCancelBtn.addEventListener("click", closeUpgradeModal); if ($upgradeModal) $upgradeModal.addEventListener("click", function(e) { if (e.target === $upgradeModal) closeUpgradeModal(); }); +// Legacy security warning modal — dismiss closes the modal only +if ($securityWarningDismiss) $securityWarningDismiss.addEventListener("click", closeSecurityWarningModal); + // ── Upgrade modal functions ─────────────────────────────────────── function openUpgradeModal() { @@ -84,6 +87,9 @@ async function init() { // If we can't reach the endpoint, continue to normal dashboard } + // Check for legacy machine security warning + await checkLegacySecurity(); + try { var cfg = await apiFetch("/api/config"); _currentRole = cfg.role || "server_plus_desktop"; diff --git a/app/sovran_systemsos_web/static/js/security.js b/app/sovran_systemsos_web/static/js/security.js new file mode 100644 index 0000000..096c3d4 --- /dev/null +++ b/app/sovran_systemsos_web/static/js/security.js @@ -0,0 +1,23 @@ +"use strict"; + +// ── Legacy security warning ─────────────────────────────────────── + +function openSecurityWarningModal(message) { + if ($securityWarningMessage) $securityWarningMessage.textContent = message; + if ($securityWarningModal) $securityWarningModal.classList.add("open"); +} + +function closeSecurityWarningModal() { + if ($securityWarningModal) $securityWarningModal.classList.remove("open"); +} + +async function checkLegacySecurity() { + try { + var data = await apiFetch("/api/security/status"); + if (data && data.status === "legacy") { + openSecurityWarningModal(data.warning || "This machine may have a known factory password. Please change your passwords immediately."); + } + } catch (_) { + // Non-fatal — silently ignore if the endpoint is unreachable + } +} diff --git a/app/sovran_systemsos_web/static/js/state.js b/app/sovran_systemsos_web/static/js/state.js index 896c372..597213a 100644 --- a/app/sovran_systemsos_web/static/js/state.js +++ b/app/sovran_systemsos_web/static/js/state.js @@ -99,5 +99,10 @@ const $upgradeConfirmBtn = document.getElementById("upgrade-confirm-btn"); const $upgradeCancelBtn = document.getElementById("upgrade-cancel-btn"); const $upgradeCloseBtn = document.getElementById("upgrade-close-btn"); +// Legacy security warning modal +const $securityWarningModal = document.getElementById("security-warning-modal"); +const $securityWarningMessage = document.getElementById("security-warning-message"); +const $securityWarningDismiss = document.getElementById("security-warning-dismiss-btn"); + // System status banner // (removed — health is now shown per-tile via the composite health field) \ No newline at end of file diff --git a/app/sovran_systemsos_web/templates/index.html b/app/sovran_systemsos_web/templates/index.html index 44746c3..2b014a6 100644 --- a/app/sovran_systemsos_web/templates/index.html +++ b/app/sovran_systemsos_web/templates/index.html @@ -14,6 +14,7 @@ + @@ -209,6 +210,26 @@ + + +
@@ -236,6 +257,7 @@ + \ No newline at end of file