lnd, joinmarket: don't write to secrets dir
Keeping the secrets dir read-only is more simple and robust. - lnd seed mnemonic creation and joinmarket wallet creation can be run as the regular service user instead of root. - It is easier to switch to a third-party secrets deployment method in the future. Don't create a seed mnemonic for lnd when a wallet exists. This avoids creating unused mnemonics and helps simplifying the migration command in `versioning.nix`.
This commit is contained in:
parent
55d87490ec
commit
03db1a61b1
@ -71,11 +71,13 @@
|
||||
## WARNING
|
||||
# If you use lnd, you should manually backup your wallet mnemonic
|
||||
# seed. This will allow you to recover on-chain funds. You can run the
|
||||
# following command after the lnd service starts:
|
||||
# scp bitcoin-node:/secrets/lnd-seed-mnemonic ./secrets/lnd-seed-mnemonic
|
||||
# following commands after the lnd service starts:
|
||||
# mkdir -p ./backups/lnd/
|
||||
# scp bitcoin-node:/var/lib/lnd/lnd-seed-mnemonic ./backups/lnd/
|
||||
#
|
||||
# You should also backup your channel state after opening new channels.
|
||||
# This will allow you to recover off-chain funds, by force-closing channels.
|
||||
# scp bitcoin-node:/var/lib/lnd/chain/bitcoin/mainnet/channel.backup /my-backup-path/channel.backup
|
||||
# scp bitcoin-node:/var/lib/lnd/chain/bitcoin/mainnet/channel.backup ./backups/lnd/
|
||||
|
||||
### SPARK WALLET
|
||||
# Enable this module to use spark-wallet, a minimalistic wallet GUI for
|
||||
@ -229,5 +231,6 @@
|
||||
# The nix-bitcoin release version that your config is compatible with.
|
||||
# When upgrading to a backwards-incompatible release, nix-bitcoin will display an
|
||||
# an error and provide hints for migrating your config to the new release.
|
||||
nix-bitcoin.configVersion = "0.0.30";
|
||||
nix-bitcoin.configVersion = "0.0.41";
|
||||
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ with lib;
|
||||
|
||||
let
|
||||
cfg = config.services.backups;
|
||||
secretsDir = config.nix-bitcoin.secretsDir;
|
||||
|
||||
filelist = pkgs.writeText "filelist.txt" ''
|
||||
${optionalString (!cfg.with-bulk-data) "- ${config.services.bitcoind.dataDir}/blocks"}
|
||||
@ -12,7 +11,6 @@ let
|
||||
${config.services.bitcoind.dataDir}
|
||||
${config.services.clightning.dataDir}
|
||||
${config.services.lnd.dataDir}
|
||||
${secretsDir}/lnd-seed-mnemonic
|
||||
${optionalString (!cfg.with-bulk-data) "- ${config.services.liquidd.dataDir}/*/blocks"}
|
||||
${optionalString (!cfg.with-bulk-data) "- ${config.services.liquidd.dataDir}/*/chainstate"}
|
||||
${config.services.liquidd.dataDir}
|
||||
@ -20,8 +18,8 @@ let
|
||||
${config.services.nbxplorer.dataDir}
|
||||
${config.services.btcpayserver.dataDir}
|
||||
${config.services.joinmarket.dataDir}
|
||||
${secretsDir}/jm-wallet-seed
|
||||
${config.services.postgresqlBackup.location}/btcpaydb.sql.gz
|
||||
${optionalString config.nix-bitcoin.generateSecrets "${config.nix-bitcoin.secretsDir}"}
|
||||
/var/lib/tor
|
||||
# Extra files
|
||||
${cfg.extraFiles}
|
||||
|
@ -240,20 +240,19 @@ in {
|
||||
'';
|
||||
# Generating wallets (jmclient/wallet.py) is only supported for mainnet or testnet
|
||||
ExecStartPost = mkIf (bitcoind.network == "mainnet")
|
||||
(nbLib.privileged "joinmarket-create-wallet" ''
|
||||
(nbLib.script "joinmarket-create-wallet" ''
|
||||
walletname=wallet.jmdat
|
||||
wallet=${cfg.dataDir}/wallets/$walletname
|
||||
if [[ ! -f $wallet ]]; then
|
||||
echo "Create wallet"
|
||||
pw=$(cat "${secretsDir}"/jm-wallet-password)
|
||||
cd ${cfg.dataDir}
|
||||
if ! ${pkgs.utillinux}/bin/runuser -u ${cfg.user} -- \
|
||||
${nbPkgs.joinmarket}/bin/jm-genwallet --datadir=${cfg.dataDir} $walletname $pw \
|
||||
if ! ${nbPkgs.joinmarket}/bin/jm-genwallet --datadir=${cfg.dataDir} $walletname $pw \
|
||||
| grep 'recovery_seed' \
|
||||
| cut -d ':' -f2 \
|
||||
| (umask u=r,go=; cat > "${secretsDir}/jm-wallet-seed"); then
|
||||
| (umask u=r,go=; cat > jm-wallet-seed); then
|
||||
echo "wallet creation failed"
|
||||
rm -f "$wallet" "${secretsDir}/jm-wallet-seed"
|
||||
rm -f "$wallet" jm-wallet-seed
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
@ -200,32 +200,28 @@ in {
|
||||
ExecStartPost = let
|
||||
restUrl = "https://${cfg.restAddress}:${toString cfg.restPort}/v1";
|
||||
in [
|
||||
# Run fully privileged for secrets dir write access
|
||||
(nbLib.privileged "lnd-create-mnemonic" ''
|
||||
(nbLib.script "lnd-create-wallet" ''
|
||||
attempts=250
|
||||
while ! { exec 3>/dev/tcp/${cfg.restAddress}/${toString cfg.restPort} && exec 3>&-; } &>/dev/null; do
|
||||
((attempts-- == 0)) && { echo "lnd REST service unreachable"; exit 1; }
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
mnemonic=${secretsDir}/lnd-seed-mnemonic
|
||||
if [[ ! -f $mnemonic ]]; then
|
||||
echo Create lnd seed
|
||||
umask u=r,go=
|
||||
${pkgs.curl}/bin/curl -s \
|
||||
--cacert ${secretsDir}/lnd-cert \
|
||||
-X GET ${restUrl}/genseed | ${pkgs.jq}/bin/jq -c '.cipher_seed_mnemonic' > "$mnemonic"
|
||||
fi
|
||||
chown ${cfg.user}: "$mnemonic"
|
||||
'')
|
||||
(nbLib.script "lnd-create-wallet" ''
|
||||
if [[ ! -f ${networkDir}/wallet.db ]]; then
|
||||
echo Create lnd wallet
|
||||
mnemonic="${cfg.dataDir}/lnd-seed-mnemonic"
|
||||
if [[ ! -f "$mnemonic" ]]; then
|
||||
echo Create lnd seed
|
||||
umask u=r,go=
|
||||
${pkgs.curl}/bin/curl -s \
|
||||
--cacert ${secretsDir}/lnd-cert \
|
||||
-X GET ${restUrl}/genseed | ${pkgs.jq}/bin/jq -c '.cipher_seed_mnemonic' > "$mnemonic"
|
||||
fi
|
||||
|
||||
echo Create lnd wallet
|
||||
${pkgs.curl}/bin/curl -s --output /dev/null --show-error \
|
||||
--cacert ${secretsDir}/lnd-cert \
|
||||
-X POST -d "{\"wallet_password\": \"$(cat ${secretsDir}/lnd-wallet-password | tr -d '\n' | base64 -w0)\", \
|
||||
\"cipher_seed_mnemonic\": $(cat ${secretsDir}/lnd-seed-mnemonic | tr -d '\n')}" \
|
||||
\"cipher_seed_mnemonic\": $(cat "$mnemonic" | tr -d '\n')}" \
|
||||
${restUrl}/initwallet
|
||||
|
||||
# Guarantees that RPC calls with cfg.cli succeed after the service is started
|
||||
@ -248,9 +244,8 @@ in {
|
||||
while ! { exec 3>/dev/tcp/${cfg.rpcAddress}/${toString cfg.rpcPort}; } &>/dev/null; do
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
'')
|
||||
# Run fully privileged for chown
|
||||
# Setting macaroon permission for other users needs root permissions
|
||||
(nbLib.privileged "lnd-create-macaroons" ''
|
||||
umask ug=r,o=
|
||||
${lib.concatMapStrings (macaroon: ''
|
||||
|
@ -69,6 +69,28 @@ let
|
||||
(mkOnionServiceChange "clightning")
|
||||
(mkOnionServiceChange "lnd")
|
||||
(mkOnionServiceChange "btcpayserver")
|
||||
{
|
||||
version = "0.0.41";
|
||||
condition = config.services.lnd.enable || config.services.joinmarket.enable;
|
||||
message = let
|
||||
secretsDir = config.nix-bitcoin.secretsDir;
|
||||
lnd = config.services.lnd;
|
||||
jm = config.services.joinmarket;
|
||||
in ''
|
||||
Secret files generated by services at runtime are now stored in the service
|
||||
data dirs instead of the global secrets dir.
|
||||
|
||||
To migrate, run the following Bash script as root on your nix-bitcoin node:
|
||||
|
||||
if [[ -e ${secretsDir}/lnd-seed-mnemonic ]]; then
|
||||
install -o ${lnd.user} -g ${lnd.group} -m400 "${secretsDir}/lnd-seed-mnemonic" "${lnd.dataDir}"
|
||||
fi
|
||||
if [[ -e ${secretsDir}/jm-wallet-seed ]]; then
|
||||
install -o ${jm.user} -g ${jm.group} -m400 "${secretsDir}/jm-wallet-seed" "${jm.dataDir}"
|
||||
fi
|
||||
rm -f "${secretsDir}"/{lnd-seed-mnemonic,jm-wallet-seed}
|
||||
'';
|
||||
}
|
||||
];
|
||||
|
||||
incompatibleChanges = optionals
|
||||
|
@ -326,16 +326,22 @@ def _():
|
||||
files = {
|
||||
"bitcoind": "var/lib/bitcoind/test/wallet.dat",
|
||||
"clightning": "var/lib/clightning/bitcoin/hsm_secret",
|
||||
"lnd": "secrets/lnd-seed-mnemonic",
|
||||
"joinmarket": "secrets/jm-wallet-seed",
|
||||
"lnd": "var/lib/lnd/lnd-seed-mnemonic",
|
||||
"joinmarket": "var/lib/joinmarket/jm-wallet-seed",
|
||||
"btcpayserver": "var/backup/postgresql/btcpaydb.sql.gz",
|
||||
}
|
||||
actual_files = succeed(f"{run_duplicity} list-current-files file:///var/lib/localBackups")
|
||||
|
||||
for test, file in files.items():
|
||||
if test in enabled_tests and file not in actual_files:
|
||||
def assert_file_exists(file):
|
||||
if file not in actual_files:
|
||||
raise Exception(f"Backup file '{file}' is missing.")
|
||||
|
||||
for test, file in files.items():
|
||||
if test in enabled_tests:
|
||||
assert_file_exists(file)
|
||||
|
||||
assert_file_exists("secrets/lnd-wallet-password")
|
||||
|
||||
|
||||
# Impure: restarts services
|
||||
@test("banlist-and-restart")
|
||||
|
Loading…
Reference in New Issue
Block a user