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
|
## WARNING
|
||||||
# If you use lnd, you should manually backup your wallet mnemonic
|
# 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
|
# seed. This will allow you to recover on-chain funds. You can run the
|
||||||
# following command after the lnd service starts:
|
# following commands after the lnd service starts:
|
||||||
# scp bitcoin-node:/secrets/lnd-seed-mnemonic ./secrets/lnd-seed-mnemonic
|
# 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.
|
# You should also backup your channel state after opening new channels.
|
||||||
# This will allow you to recover off-chain funds, by force-closing 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
|
### SPARK WALLET
|
||||||
# Enable this module to use spark-wallet, a minimalistic wallet GUI for
|
# 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.
|
# The nix-bitcoin release version that your config is compatible with.
|
||||||
# When upgrading to a backwards-incompatible release, nix-bitcoin will display an
|
# 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.
|
# 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
|
let
|
||||||
cfg = config.services.backups;
|
cfg = config.services.backups;
|
||||||
secretsDir = config.nix-bitcoin.secretsDir;
|
|
||||||
|
|
||||||
filelist = pkgs.writeText "filelist.txt" ''
|
filelist = pkgs.writeText "filelist.txt" ''
|
||||||
${optionalString (!cfg.with-bulk-data) "- ${config.services.bitcoind.dataDir}/blocks"}
|
${optionalString (!cfg.with-bulk-data) "- ${config.services.bitcoind.dataDir}/blocks"}
|
||||||
@ -12,7 +11,6 @@ let
|
|||||||
${config.services.bitcoind.dataDir}
|
${config.services.bitcoind.dataDir}
|
||||||
${config.services.clightning.dataDir}
|
${config.services.clightning.dataDir}
|
||||||
${config.services.lnd.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}/*/blocks"}
|
||||||
${optionalString (!cfg.with-bulk-data) "- ${config.services.liquidd.dataDir}/*/chainstate"}
|
${optionalString (!cfg.with-bulk-data) "- ${config.services.liquidd.dataDir}/*/chainstate"}
|
||||||
${config.services.liquidd.dataDir}
|
${config.services.liquidd.dataDir}
|
||||||
@ -20,8 +18,8 @@ let
|
|||||||
${config.services.nbxplorer.dataDir}
|
${config.services.nbxplorer.dataDir}
|
||||||
${config.services.btcpayserver.dataDir}
|
${config.services.btcpayserver.dataDir}
|
||||||
${config.services.joinmarket.dataDir}
|
${config.services.joinmarket.dataDir}
|
||||||
${secretsDir}/jm-wallet-seed
|
|
||||||
${config.services.postgresqlBackup.location}/btcpaydb.sql.gz
|
${config.services.postgresqlBackup.location}/btcpaydb.sql.gz
|
||||||
|
${optionalString config.nix-bitcoin.generateSecrets "${config.nix-bitcoin.secretsDir}"}
|
||||||
/var/lib/tor
|
/var/lib/tor
|
||||||
# Extra files
|
# Extra files
|
||||||
${cfg.extraFiles}
|
${cfg.extraFiles}
|
||||||
|
@ -240,20 +240,19 @@ in {
|
|||||||
'';
|
'';
|
||||||
# Generating wallets (jmclient/wallet.py) is only supported for mainnet or testnet
|
# Generating wallets (jmclient/wallet.py) is only supported for mainnet or testnet
|
||||||
ExecStartPost = mkIf (bitcoind.network == "mainnet")
|
ExecStartPost = mkIf (bitcoind.network == "mainnet")
|
||||||
(nbLib.privileged "joinmarket-create-wallet" ''
|
(nbLib.script "joinmarket-create-wallet" ''
|
||||||
walletname=wallet.jmdat
|
walletname=wallet.jmdat
|
||||||
wallet=${cfg.dataDir}/wallets/$walletname
|
wallet=${cfg.dataDir}/wallets/$walletname
|
||||||
if [[ ! -f $wallet ]]; then
|
if [[ ! -f $wallet ]]; then
|
||||||
echo "Create wallet"
|
echo "Create wallet"
|
||||||
pw=$(cat "${secretsDir}"/jm-wallet-password)
|
pw=$(cat "${secretsDir}"/jm-wallet-password)
|
||||||
cd ${cfg.dataDir}
|
cd ${cfg.dataDir}
|
||||||
if ! ${pkgs.utillinux}/bin/runuser -u ${cfg.user} -- \
|
if ! ${nbPkgs.joinmarket}/bin/jm-genwallet --datadir=${cfg.dataDir} $walletname $pw \
|
||||||
${nbPkgs.joinmarket}/bin/jm-genwallet --datadir=${cfg.dataDir} $walletname $pw \
|
|
||||||
| grep 'recovery_seed' \
|
| grep 'recovery_seed' \
|
||||||
| cut -d ':' -f2 \
|
| 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"
|
echo "wallet creation failed"
|
||||||
rm -f "$wallet" "${secretsDir}/jm-wallet-seed"
|
rm -f "$wallet" jm-wallet-seed
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
@ -200,32 +200,28 @@ in {
|
|||||||
ExecStartPost = let
|
ExecStartPost = let
|
||||||
restUrl = "https://${cfg.restAddress}:${toString cfg.restPort}/v1";
|
restUrl = "https://${cfg.restAddress}:${toString cfg.restPort}/v1";
|
||||||
in [
|
in [
|
||||||
# Run fully privileged for secrets dir write access
|
(nbLib.script "lnd-create-wallet" ''
|
||||||
(nbLib.privileged "lnd-create-mnemonic" ''
|
|
||||||
attempts=250
|
attempts=250
|
||||||
while ! { exec 3>/dev/tcp/${cfg.restAddress}/${toString cfg.restPort} && exec 3>&-; } &>/dev/null; do
|
while ! { exec 3>/dev/tcp/${cfg.restAddress}/${toString cfg.restPort} && exec 3>&-; } &>/dev/null; do
|
||||||
((attempts-- == 0)) && { echo "lnd REST service unreachable"; exit 1; }
|
((attempts-- == 0)) && { echo "lnd REST service unreachable"; exit 1; }
|
||||||
sleep 0.1
|
sleep 0.1
|
||||||
done
|
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
|
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 \
|
${pkgs.curl}/bin/curl -s --output /dev/null --show-error \
|
||||||
--cacert ${secretsDir}/lnd-cert \
|
--cacert ${secretsDir}/lnd-cert \
|
||||||
-X POST -d "{\"wallet_password\": \"$(cat ${secretsDir}/lnd-wallet-password | tr -d '\n' | base64 -w0)\", \
|
-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
|
${restUrl}/initwallet
|
||||||
|
|
||||||
# Guarantees that RPC calls with cfg.cli succeed after the service is started
|
# 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
|
while ! { exec 3>/dev/tcp/${cfg.rpcAddress}/${toString cfg.rpcPort}; } &>/dev/null; do
|
||||||
sleep 0.1
|
sleep 0.1
|
||||||
done
|
done
|
||||||
|
|
||||||
'')
|
'')
|
||||||
# Run fully privileged for chown
|
# Setting macaroon permission for other users needs root permissions
|
||||||
(nbLib.privileged "lnd-create-macaroons" ''
|
(nbLib.privileged "lnd-create-macaroons" ''
|
||||||
umask ug=r,o=
|
umask ug=r,o=
|
||||||
${lib.concatMapStrings (macaroon: ''
|
${lib.concatMapStrings (macaroon: ''
|
||||||
|
@ -69,6 +69,28 @@ let
|
|||||||
(mkOnionServiceChange "clightning")
|
(mkOnionServiceChange "clightning")
|
||||||
(mkOnionServiceChange "lnd")
|
(mkOnionServiceChange "lnd")
|
||||||
(mkOnionServiceChange "btcpayserver")
|
(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
|
incompatibleChanges = optionals
|
||||||
|
@ -326,16 +326,22 @@ def _():
|
|||||||
files = {
|
files = {
|
||||||
"bitcoind": "var/lib/bitcoind/test/wallet.dat",
|
"bitcoind": "var/lib/bitcoind/test/wallet.dat",
|
||||||
"clightning": "var/lib/clightning/bitcoin/hsm_secret",
|
"clightning": "var/lib/clightning/bitcoin/hsm_secret",
|
||||||
"lnd": "secrets/lnd-seed-mnemonic",
|
"lnd": "var/lib/lnd/lnd-seed-mnemonic",
|
||||||
"joinmarket": "secrets/jm-wallet-seed",
|
"joinmarket": "var/lib/joinmarket/jm-wallet-seed",
|
||||||
"btcpayserver": "var/backup/postgresql/btcpaydb.sql.gz",
|
"btcpayserver": "var/backup/postgresql/btcpaydb.sql.gz",
|
||||||
}
|
}
|
||||||
actual_files = succeed(f"{run_duplicity} list-current-files file:///var/lib/localBackups")
|
actual_files = succeed(f"{run_duplicity} list-current-files file:///var/lib/localBackups")
|
||||||
|
|
||||||
for test, file in files.items():
|
def assert_file_exists(file):
|
||||||
if test in enabled_tests and file not in actual_files:
|
if file not in actual_files:
|
||||||
raise Exception(f"Backup file '{file}' is missing.")
|
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
|
# Impure: restarts services
|
||||||
@test("banlist-and-restart")
|
@test("banlist-and-restart")
|
||||||
|
Loading…
Reference in New Issue
Block a user