mirror of
https://github.com/Routstr/routstr-core.git
synced 2026-08-09 11:04:36 +00:00
fix(settings): fail fast when an nsec is set but ROUTSTR_SECRET_KEY is missing
Encryption of the Nostr identity at rest is mandatory. When a legacy nsec is present (env or blob) but no ROUTSTR_SECRET_KEY is set, bootstrap previously fell into vault.encrypt and surfaced its generic "key not set" error. Raise an explicit, nsec-contextual error first so the boot failure is intentional and actionable — it names the missing key and prints the generation command — rather than relying on vault throwing incidentally. No secret is dropped: the node refuses to start until the operator sets the key. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
afcb3f7cda
commit
56a67c0a86
@@ -498,6 +498,17 @@ async def bootstrap_secrets(db_session: AsyncSession) -> None:
|
||||
else:
|
||||
legacy_nsec = _legacy_plaintext(raw_blob, "NSEC", "nsec")
|
||||
if legacy_nsec:
|
||||
# The node has a Nostr identity to protect. Encryption at rest is
|
||||
# mandatory: without a key we fail fast (clear, actionable boot
|
||||
# error) rather than silently persisting the nsec in plaintext.
|
||||
if not os.environ.get("ROUTSTR_SECRET_KEY"):
|
||||
raise RuntimeError(
|
||||
"An nsec is configured but ROUTSTR_SECRET_KEY is not set. "
|
||||
"The key is required to encrypt the Nostr identity at rest. "
|
||||
"Generate one and set it in the environment:\n"
|
||||
' python -c "from cryptography.fernet import Fernet; '
|
||||
'print(Fernet.generate_key().decode())"'
|
||||
)
|
||||
secret.encrypted_nsec = vault.encrypt(legacy_nsec)
|
||||
settings.nsec = legacy_nsec
|
||||
changed = True
|
||||
|
||||
@@ -163,6 +163,27 @@ async def test_fail_fast_when_nsec_encrypted_with_different_key(
|
||||
await bootstrap_secrets(integration_session)
|
||||
|
||||
|
||||
# --- encryption is mandatory: a node with an nsec needs ROUTSTR_SECRET_KEY -----
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_legacy_nsec_without_secret_key_fails_fast(
|
||||
clean_secret_env: None,
|
||||
integration_session: AsyncSession,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
# A node with a Nostr identity must not boot without a key to encrypt it:
|
||||
# encryption at rest is mandatory, not opt-in. The failure names the missing
|
||||
# key and hands over the generation command rather than crashing opaquely or
|
||||
# silently persisting the nsec in plaintext. No env/blob copy is dropped — the
|
||||
# node refuses until the operator sets the key.
|
||||
monkeypatch.delenv("ROUTSTR_SECRET_KEY", raising=False)
|
||||
monkeypatch.setenv("NSEC", NSEC_HEX)
|
||||
|
||||
with pytest.raises(RuntimeError, match="ROUTSTR_SECRET_KEY"):
|
||||
await bootstrap_secrets(integration_session)
|
||||
|
||||
|
||||
# --- boot ordering: rescue legacy blob secrets before they are stripped ----
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user