feat: add legacy security warning API and UI modal for pre-factory-seal machines
Agent-Logs-Url: https://github.com/naturallaw777/staging_alpha/sessions/f7c8f11b-873b-403f-ac55-8b5b7cd9f1fb Co-authored-by: naturallaw777 <99053422+naturallaw777@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
a40ea61415
commit
13c686a8a1
@@ -59,6 +59,11 @@ REBOOT_COMMAND = ["reboot"]
|
|||||||
ONBOARDING_FLAG = "/var/lib/sovran/onboarding-complete"
|
ONBOARDING_FLAG = "/var/lib/sovran/onboarding-complete"
|
||||||
AUTOLAUNCH_DISABLE_FLAG = "/var/lib/sovran/hub-autolaunch-disabled"
|
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 ────────────────────────────────────────
|
# ── Tech Support constants ────────────────────────────────────────
|
||||||
|
|
||||||
SUPPORT_KEY_FILE = "/root/.ssh/sovran_support_authorized"
|
SUPPORT_KEY_FILE = "/root/.ssh/sovran_support_authorized"
|
||||||
@@ -2916,6 +2921,37 @@ async def api_domains_check(req: DomainCheckRequest):
|
|||||||
return {"domains": list(check_results)}
|
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": "<message>"} 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 user management ────────────────────────────────────────
|
||||||
|
|
||||||
MATRIX_USERS_FILE = "/var/lib/secrets/matrix-users"
|
MATRIX_USERS_FILE = "/var/lib/secrets/matrix-users"
|
||||||
|
|||||||
55
app/sovran_systemsos_web/static/css/security.css
Normal file
55
app/sovran_systemsos_web/static/css/security.css
Normal file
@@ -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;
|
||||||
|
}
|
||||||
@@ -38,6 +38,9 @@ if ($upgradeCloseBtn) $upgradeCloseBtn.addEventListener("click", closeUpgradeMod
|
|||||||
if ($upgradeCancelBtn) $upgradeCancelBtn.addEventListener("click", closeUpgradeModal);
|
if ($upgradeCancelBtn) $upgradeCancelBtn.addEventListener("click", closeUpgradeModal);
|
||||||
if ($upgradeModal) $upgradeModal.addEventListener("click", function(e) { if (e.target === $upgradeModal) 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 ───────────────────────────────────────
|
// ── Upgrade modal functions ───────────────────────────────────────
|
||||||
|
|
||||||
function openUpgradeModal() {
|
function openUpgradeModal() {
|
||||||
@@ -84,6 +87,9 @@ async function init() {
|
|||||||
// If we can't reach the endpoint, continue to normal dashboard
|
// If we can't reach the endpoint, continue to normal dashboard
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for legacy machine security warning
|
||||||
|
await checkLegacySecurity();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var cfg = await apiFetch("/api/config");
|
var cfg = await apiFetch("/api/config");
|
||||||
_currentRole = cfg.role || "server_plus_desktop";
|
_currentRole = cfg.role || "server_plus_desktop";
|
||||||
|
|||||||
23
app/sovran_systemsos_web/static/js/security.js
Normal file
23
app/sovran_systemsos_web/static/js/security.js
Normal file
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -99,5 +99,10 @@ const $upgradeConfirmBtn = document.getElementById("upgrade-confirm-btn");
|
|||||||
const $upgradeCancelBtn = document.getElementById("upgrade-cancel-btn");
|
const $upgradeCancelBtn = document.getElementById("upgrade-cancel-btn");
|
||||||
const $upgradeCloseBtn = document.getElementById("upgrade-close-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
|
// System status banner
|
||||||
// (removed — health is now shown per-tile via the composite health field)
|
// (removed — health is now shown per-tile via the composite health field)
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
<link rel="stylesheet" href="/static/css/onboarding.css" />
|
<link rel="stylesheet" href="/static/css/onboarding.css" />
|
||||||
<link rel="stylesheet" href="/static/css/support.css" />
|
<link rel="stylesheet" href="/static/css/support.css" />
|
||||||
<link rel="stylesheet" href="/static/css/domain-setup.css" />
|
<link rel="stylesheet" href="/static/css/domain-setup.css" />
|
||||||
|
<link rel="stylesheet" href="/static/css/security.css" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
@@ -209,6 +210,26 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Legacy Security Warning Modal -->
|
||||||
|
<div class="modal-overlay" id="security-warning-modal" role="alertdialog" aria-modal="true" aria-labelledby="security-warning-title">
|
||||||
|
<div class="creds-dialog security-warning-dialog">
|
||||||
|
<div class="creds-header security-warning-header">
|
||||||
|
<span class="creds-title" id="security-warning-title">⚠️ Security Warning</span>
|
||||||
|
</div>
|
||||||
|
<div class="creds-body security-warning-body">
|
||||||
|
<div class="security-warning-icon">🔒</div>
|
||||||
|
<p class="security-warning-message" id="security-warning-message"></p>
|
||||||
|
<div class="security-warning-actions">
|
||||||
|
<p class="security-warning-hint">Change your passwords now to secure your system:</p>
|
||||||
|
<div class="security-warning-links">
|
||||||
|
<a class="btn btn-primary security-warning-link" href="/onboarding?step=passwords" id="security-warning-change-btn">Change Passwords</a>
|
||||||
|
<button class="btn btn-close-modal" id="security-warning-dismiss-btn">I Understand — Dismiss</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Reboot overlay -->
|
<!-- Reboot overlay -->
|
||||||
<div class="reboot-overlay" id="reboot-overlay">
|
<div class="reboot-overlay" id="reboot-overlay">
|
||||||
<div class="reboot-card">
|
<div class="reboot-card">
|
||||||
@@ -236,6 +257,7 @@
|
|||||||
<script src="/static/js/update.js"></script>
|
<script src="/static/js/update.js"></script>
|
||||||
<script src="/static/js/rebuild.js"></script>
|
<script src="/static/js/rebuild.js"></script>
|
||||||
<script src="/static/js/features.js"></script>
|
<script src="/static/js/features.js"></script>
|
||||||
|
<script src="/static/js/security.js"></script>
|
||||||
<script src="/static/js/events.js"></script>
|
<script src="/static/js/events.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Reference in New Issue
Block a user