fix(lnd): per-node wallet password + locked-wallet self-heal on login
Replaces the fleet-wide hardcoded WALLET_PASSWORD='hellohello' that left wallets LOCKED after OTA/reboot (auto-unlock used the wrong password fleet-wide). Forward fix (both init paths unified, validated cargo check + LND REST mechanics on a scratch wallet): - Per-node random 256-bit secret in secrets/lnd-wallet-password (0600), mirroring secrets/bitcoin-rpc-password. read_wallet_password (no-gen) vs ensure_wallet_password (gen at init only). - container/lnd.rs init AND api/rpc/lnd/wallet.rs seed-derived init both use the per-node secret (wallet.rs keeps recoverable derived entropy; password unified). - Unlock tries [per-node secret, legacy 'hellohello']; single-attempt primitive distinguishes invalid-passphrase (fail fast, try next) from not-ready (retry), so a wrong password no longer hangs the boot path ~60s. Migration (candidate-unlock + rotate, best-effort at login): - change_wallet_password (WalletUnlocker.ChangePassword) + migrate_locked_wallet: if LOCKED, try candidates as current pw and ChangePassword onto the per-node secret so future boots auto-unlock. Hooked into auth.login (non-blocking) with the just-verified password as the candidate. NOT YET: seed-recovery fallback for wallets where no candidate matches (e.g. .116/.228) — destructive, needs entropy-source/funds-safety handling; next pass. NOT shipped: pending end-to-end validation on a real node. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
a9c4e54023
commit
91adc281ca
@@ -33,6 +33,19 @@ impl RpcHandler {
|
||||
|
||||
tracing::info!("[onboarding] login successful");
|
||||
|
||||
// Best-effort: heal a LOCKED LND wallet created with an unknown/legacy
|
||||
// password by rotating it onto the per-node secret, using the password
|
||||
// the user just authenticated with as a candidate. Non-blocking so login
|
||||
// is never slowed or broken when LND isn't installed / already unlocked.
|
||||
let candidate = password.to_string();
|
||||
tokio::spawn(async move {
|
||||
match crate::container::lnd::migrate_locked_wallet(&[candidate]).await {
|
||||
Ok(true) => tracing::info!("[login] LND wallet healed / auto-unlocked"),
|
||||
Ok(false) => {} // not locked, or seed-recovery required
|
||||
Err(e) => tracing::debug!("[login] LND wallet migration skipped: {e}"),
|
||||
}
|
||||
});
|
||||
|
||||
// Ensure NostrVPN config exists — covers the case where onboardingComplete
|
||||
// was never called (e.g., user took the "already set up" shortcut).
|
||||
let data_dir = self.config.data_dir.clone();
|
||||
|
||||
@@ -552,8 +552,15 @@ impl RpcHandler {
|
||||
let entropy_b64 = base64::engine::general_purpose::STANDARD.encode(entropy);
|
||||
entropy.zeroize();
|
||||
|
||||
// Use the per-node secret as the LND wallet password (NOT the
|
||||
// caller-supplied one) so the unattended boot path can auto-unlock this
|
||||
// wallet. The wallet stays recoverable from the Archipelago seed via the
|
||||
// derived entropy above. This unifies both init paths on one password
|
||||
// source — the divergence here is what left wallets locked fleet-wide.
|
||||
let _ = wallet_password; // accepted for API compat; superseded by the per-node secret
|
||||
let node_wallet_pw = crate::container::lnd::ensure_wallet_password().await?;
|
||||
let wallet_password_b64 =
|
||||
base64::engine::general_purpose::STANDARD.encode(wallet_password.as_bytes());
|
||||
base64::engine::general_purpose::STANDARD.encode(node_wallet_pw.as_bytes());
|
||||
|
||||
// Call LND REST API to initialize wallet with derived entropy.
|
||||
// LND must be running but NOT yet initialized (no existing wallet).
|
||||
|
||||
Reference in New Issue
Block a user