Support for reloading rpc credentials and reconnecting

Fixes #393

Smarter identification of auth type (usernamePassword/cookie); if using cookie auth, watch cookie file for changes and, if seen, reload and attempt rpc reconnect
This commit is contained in:
Dan Janosik 2023-04-03 10:54:17 -04:00
parent c8301ad78b
commit c5a147ff9f
No known key found for this signature in database
GPG key ID: 70C0B166321C0AF8
3 changed files with 70 additions and 17 deletions

27
app.js
View file

@ -872,8 +872,15 @@ expressApp.onStartup = async () => {
}
}
expressApp.continueStartup = function() {
let rpcCred = config.credentials.rpc;
function connectToRpcServer() {
// reload credentials, the main "config.credentials.rpc" can be stale
// since the username/password can be sourced from the auth cookie
// which changes each startup of bitcoind
let credentialsForRpcConnect = config.credentials.loadFreshRpcCredentials();
debugLog(`RPC Credentials: ${JSON.stringify(utils.obfuscateProperties(credentialsForRpcConnect, ["password"]), null, 4)}`);
let rpcCred = credentialsForRpcConnect;
debugLog(`Connecting to RPC node at [${rpcCred.host}]:${rpcCred.port}`);
let usernamePassword = `${rpcCred.username}:${rpcCred.password}`;
@ -910,6 +917,22 @@ expressApp.continueStartup = function() {
// no timeout RPC client, for long-running commands
global.rpcClientNoTimeout = jayson.Client.http(rpcClientNoTimeoutProperties);
}
expressApp.continueStartup = function() {
connectToRpcServer();
// if using cookie auth, watch for changes to the file and reconnect
if (config.credentials.rpc.authType == "cookie") {
debugLog(`RPC authentication is cookie based; watching for changes to the auth cookie file...`);
fs.watchFile(config.credentials.rpc.authCookieFilepath, (curr, prev) => {
debugLog(`RPC auth cookie change detected; attempting reconnect...`);
connectToRpcServer();
});
}
// default values - after we connect via RPC, we update these
global.txindexAvailable = false;

View file

@ -35,15 +35,6 @@ const currentCoin = process.env.BTCEXP_COIN || "BTC";
const rpcCred = credentials.rpc;
if (rpcCred.cookie && !rpcCred.username && !rpcCred.password && fs.existsSync(rpcCred.cookie)) {
console.log(`Loading RPC cookie file: ${rpcCred.cookie}`);
[ rpcCred.username, rpcCred.password ] = fs.readFileSync(rpcCred.cookie).toString().split(':', 2);
if (!rpcCred.password) {
throw new Error(`Cookie file ${rpcCred.cookie} in unexpected format`);
}
}
const cookieSecret = process.env.BTCEXP_COOKIE_SECRET
|| (rpcCred.password && crypto.createHmac('sha256', JSON.stringify(rpcCred))

View file

@ -3,19 +3,58 @@
const os = require('os');
const path = require('path');
const url = require('url');
const fs = require("fs");
const debug = require("debug");
const debugLog = debug("btcexp:config");
const btcUri = process.env.BTCEXP_BITCOIND_URI ? url.parse(process.env.BTCEXP_BITCOIND_URI, true) : { query: { } };
const btcAuth = btcUri.auth ? btcUri.auth.split(':') : [];
module.exports = {
rpc: {
function loadFreshRpcCredentials() {
let username = btcAuth[0] || process.env.BTCEXP_BITCOIND_USER;
let password = btcAuth[1] || process.env.BTCEXP_BITCOIND_PASS;
let authCookieFilepath = btcUri.query.cookie || process.env.BTCEXP_BITCOIND_COOKIE || path.join(os.homedir(), '.bitcoin', '.cookie');
let authType = "usernamePassword";
if (!username && !password && fs.existsSync(authCookieFilepath)) {
authType = "cookie";
}
if (authType == "cookie") {
debugLog(`Loading RPC cookie file: ${authCookieFilepath}`);
[ username, password ] = fs.readFileSync(authCookieFilepath).toString().trim().split(':', 2);
if (!password) {
throw new Error(`Cookie file ${authCookieFilepath} in unexpected format`);
}
}
return {
host: btcUri.hostname || process.env.BTCEXP_BITCOIND_HOST || "127.0.0.1",
port: btcUri.port || process.env.BTCEXP_BITCOIND_PORT || 8332,
username: btcAuth[0] || process.env.BTCEXP_BITCOIND_USER,
password: btcAuth[1] || process.env.BTCEXP_BITCOIND_PASS,
cookie: btcUri.query.cookie || process.env.BTCEXP_BITCOIND_COOKIE || path.join(os.homedir(), '.bitcoin', '.cookie'),
authType: authType,
username: username,
password: password,
authCookieFilepath: authCookieFilepath,
timeout: parseInt(btcUri.query.timeout || process.env.BTCEXP_BITCOIND_RPC_TIMEOUT || 5000),
},
};
}
module.exports = {
loadFreshRpcCredentials: loadFreshRpcCredentials,
rpc: loadFreshRpcCredentials(),
// optional: enter your api access key from ipstack.com below
// to include a map of the estimated locations of your node's