Merge pull request #125 from naturallaw777/copilot/update-security-check-for-unsealed-state

[WIP] Update sovran-legacy-security-check to warn on unsealed state
This commit is contained in:
Sovran_Systems
2026-04-07 12:50:45 -05:00
committed by GitHub
6 changed files with 58 additions and 16 deletions

View File

@@ -2949,6 +2949,16 @@ async def api_security_status():
"The default system password may be known to the factory. "
"Please change your system and application passwords immediately."
)
elif status == "unsealed":
try:
with open(SECURITY_WARNING_FILE, "r") as f:
warning = f.read().strip()
except FileNotFoundError:
warning = (
"This machine was set up without the factory seal process. "
"Factory test data — including SSH keys, database contents, and wallet information — "
"may still be present on this system."
)
return {"status": status, "warning": warning}
@@ -3027,14 +3037,21 @@ async def api_change_password(req: ChangePasswordRequest):
except Exception as exc:
raise HTTPException(status_code=500, detail=f"Failed to write secrets file: {exc}")
# Clear legacy security status so the warning banner is removed
for path in (SECURITY_STATUS_FILE, SECURITY_WARNING_FILE):
# Clear legacy security status so the warning banner is removed — but only
# for "legacy" machines (pre-seal era). For "unsealed" machines, changing
# passwords is not enough; the factory residue (SSH keys, wallet data,
# databases) remains until a proper re-seal or re-install is performed.
try:
os.remove(path)
with open(SECURITY_STATUS_FILE, "r") as f:
current_status = f.read().strip()
if current_status == "legacy":
os.remove(SECURITY_STATUS_FILE)
try:
os.remove(SECURITY_WARNING_FILE)
except FileNotFoundError:
pass
except Exception:
pass # Non-fatal; don't block a successful password change
except (FileNotFoundError, OSError):
pass
return {"ok": True}

View File

@@ -602,11 +602,19 @@ function renderAutolaunchToggle(enabled) {
var securityBanner = "";
if (_securityIsLegacy) {
var msg = _securityWarningMessage || "Your system may have factory default passwords. Please change your passwords to secure your system.";
var linkText, linkAction;
if (_securityStatus === "unsealed") {
linkText = "Contact Support";
linkAction = "openSupportModal(); return false;";
} else {
linkText = "Change Passwords";
linkAction = "openServiceDetailModal('root-password-setup.service', 'System Passwords', 'passwords'); return false;";
}
securityBanner =
'<div class="security-inline-banner">' +
'<span class="security-inline-icon">⚠</span>' +
'<span class="security-inline-text">' + msg + '</span>' +
'<a class="security-inline-link" href="#" onclick="openServiceDetailModal(\'root-password-setup.service\', \'System Passwords\', \'passwords\'); return false;">Change Passwords</a>' +
'<a class="security-inline-link" href="#" onclick="' + linkAction + '">' + linkText + '</a>' +
'</div>';
}

View File

@@ -5,9 +5,10 @@
async function checkLegacySecurity() {
try {
var data = await apiFetch("/api/security/status");
if (data && data.status === "legacy") {
if (data && (data.status === "legacy" || data.status === "unsealed")) {
_securityIsLegacy = true;
_securityWarningMessage = data.warning || "This machine may have a known factory password. Please change your passwords immediately.";
_securityStatus = data.status;
_securityWarningMessage = data.warning || "This machine may have a security issue. Please review your system security.";
}
} catch (_) {
// Non-fatal — silently ignore if the endpoint is unreachable

View File

@@ -626,8 +626,11 @@ function openSystemChangePasswordModal(unit, name, icon) {
resultEl.textContent = "✅ System password changed successfully.";
submitBtn.textContent = "Change Password";
submitBtn.disabled = false;
// Hide the legacy security banner if it's visible
if (typeof _securityIsLegacy !== "undefined" && _securityIsLegacy) {
// Hide the legacy security banner if it's visible — but only for
// "legacy" status machines. For "unsealed" machines, changing passwords
// is not enough; the factory residue remains until a proper re-seal or re-install.
if (typeof _securityIsLegacy !== "undefined" && _securityIsLegacy &&
(typeof _securityStatus === "undefined" || _securityStatus !== "unsealed")) {
_securityIsLegacy = false;
var banner = document.querySelector(".security-inline-banner");
if (banner) banner.remove();

View File

@@ -101,6 +101,7 @@ const $upgradeCloseBtn = document.getElementById("upgrade-close-btn");
// Legacy security warning state (populated by checkLegacySecurity in security.js)
var _securityIsLegacy = false;
var _securityStatus = "ok"; // "ok", "legacy", or "unsealed"
var _securityWarningMessage = "";
// System status banner

View File

@@ -100,11 +100,23 @@ in
};
path = [ pkgs.coreutils ];
script = ''
# If already onboarded or sealed, nothing to do
[ -f /var/lib/sovran-customer-onboarded ] && exit 0
# If sealed AND onboarded fully clean, nothing to do
[ -f /var/lib/sovran-factory-sealed ] && [ -f /var/lib/sovran-customer-onboarded ] && exit 0
# If sealed but not yet onboarded seal was run, customer hasn't finished setup yet, that's fine
[ -f /var/lib/sovran-factory-sealed ] && exit 0
# If secrets exist but no sealed/onboarded flag, this is a legacy machine
# If onboarded but NOT sealed installer ran without factory seal!
if [ -f /var/lib/sovran-customer-onboarded ] && [ ! -f /var/lib/sovran-factory-sealed ]; then
mkdir -p /var/lib/sovran
echo "unsealed" > /var/lib/sovran/security-status
cat > /var/lib/sovran/security-warning << 'EOF'
This machine was set up without the factory seal process. Factory test data including SSH keys, database contents, and wallet information may still be present on this system. It is strongly recommended to back up any important data and re-install using a fresh ISO, or contact Sovran Systems support for assistance.
EOF
exit 0
fi
# No flags at all + secrets exist = legacy (pre-seal era) machine
if [ -f /var/lib/secrets/root-password ]; then
mkdir -p /var/lib/sovran
echo "legacy" > /var/lib/sovran/security-status