Skip to content

Commit

Permalink
Implemented custom cookie file
Browse files Browse the repository at this point in the history
This change allows the user to specify a custom cookie file, which is then
used instead of `~/.bitcoin/.cookie`. This resolves situations when the
user wants to have the cookie file in non-standard path.

Aside from that, the code now pre-computes the default path, improving
the performance by avoiding allocation (and copying). Unfortunately, due
to limitations of Rust, the code doesn't print out cookie configuration
anymore. This however might be safer, since the cookie isn't printed,
and thus doesn't end up in some readable logs by accident.

Closes #176
Closes #189
  • Loading branch information
Kixunil authored and romanz committed Nov 13, 2019
1 parent ef3c387 commit 5549287
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 17 deletions.
4 changes: 4 additions & 0 deletions RELEASE-NOTES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# 0.8.1 (TBD)

* Allow setting `--cookie-file` path via configuration (@Kixunil)

# 0.8.0 (28 Oct 2019)

* Use `configure_me` instead of `clap` to support config files, environment variables and man pages (@Kixunil)
Expand Down
8 changes: 7 additions & 1 deletion config_spec.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,17 @@ default = "crate::config::default_daemon_dir()"
[[param]]
name = "cookie"
type = "String"
doc = "JSONRPC authentication cookie ('USER:PASSWORD', default: read from ~/.bitcoin/.cookie)"
doc = "JSONRPC authentication cookie ('USER:PASSWORD', default: read from cookie file)"
# Force the user to use config file in order to avoid password leaks
argument = false
env_var = false

[[param]]
name = "cookie_file"
type = "std::path::PathBuf"
doc = "JSONRPC authentication cookie file (default: ~/.bitcoin/.cookie)"
# This is safe to configure on command line.

[[param]]
name = "network"
type = "crate::config::BitcoinNetwork"
Expand Down
96 changes: 80 additions & 16 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::fmt;
use std::fs;
use std::net::SocketAddr;
use std::net::ToSocketAddrs;
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::sync::Arc;
use stderrlog;
Expand Down Expand Up @@ -121,15 +121,13 @@ impl Into<Network> for BitcoinNetwork {
}

/// Parsed and post-processed configuration
#[derive(Debug)]
pub struct Config {
// See below for the documentation of each field:
pub log: stderrlog::StdErrLog,
pub network_type: Network,
pub db_path: PathBuf,
pub daemon_dir: PathBuf,
pub daemon_rpc_addr: SocketAddr,
pub cookie: Option<String>,
pub electrum_rpc_addr: SocketAddr,
pub monitoring_addr: SocketAddr,
pub jsonrpc_import: bool,
Expand All @@ -139,6 +137,7 @@ pub struct Config {
pub txid_limit: usize,
pub server_banner: String,
pub blocktxids_cache_size: usize,
pub cookie_getter: Arc<dyn CookieGetter>,
}

/// Returns default daemon directory
Expand All @@ -151,6 +150,22 @@ fn default_daemon_dir() -> PathBuf {
home
}

fn create_cookie_getter(
cookie: Option<String>,
cookie_file: Option<PathBuf>,
daemon_dir: &Path,
) -> Arc<dyn CookieGetter> {
match (cookie, cookie_file) {
(None, None) => Arc::new(CookieFile::from_daemon_dir(daemon_dir)),
(None, Some(file)) => Arc::new(CookieFile::from_file(file)),
(Some(cookie), None) => Arc::new(StaticCookie::from_string(cookie)),
(Some(_), Some(_)) => {
eprintln!("Error: ambigous configuration - cookie and cookie_file can't be specified at the same time");
std::process::exit(1);
}
}
}

impl Config {
/// Parses args, env vars, config files and post-processes them
pub fn from_args() -> Config {
Expand All @@ -169,6 +184,9 @@ impl Config {
let (mut config, _) =
internal::Config::including_optional_config_files(configs).unwrap_or_exit();

let cookie_getter =
create_cookie_getter(config.cookie, config.cookie_file, &config.daemon_dir);

let db_subdir = match config.network {
// We must keep the name "mainnet" due to backwards compatibility
Network::Bitcoin => "mainnet",
Expand Down Expand Up @@ -241,7 +259,6 @@ impl Config {
db_path: config.db_dir,
daemon_dir: config.daemon_dir,
daemon_rpc_addr,
cookie: config.cookie,
electrum_rpc_addr,
monitoring_addr,
jsonrpc_import: config.jsonrpc_import,
Expand All @@ -251,43 +268,90 @@ impl Config {
blocktxids_cache_size: (config.blocktxids_cache_size_mb * MB) as usize,
txid_limit: config.txid_limit,
server_banner: config.server_banner,
cookie_getter,
};
eprintln!("{:?}", config);
config
}

pub fn cookie_getter(&self) -> Arc<dyn CookieGetter> {
if let Some(ref value) = self.cookie {
Arc::new(StaticCookie {
value: value.as_bytes().to_vec(),
})
} else {
Arc::new(CookieFile {
daemon_dir: self.daemon_dir.clone(),
})
Arc::clone(&self.cookie_getter)
}
}

// CookieGetter + Debug isn't implemented in Rust, so we have to skip cookie_getter
macro_rules! debug_struct {
($name:ty, $($field:ident,)*) => {
impl fmt::Debug for $name {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct(stringify!($name))
$(
.field(stringify!($field), &self.$field)
)*
.finish()
}
}
}
}

debug_struct! { Config,
log,
network_type,
db_path,
daemon_dir,
daemon_rpc_addr,
electrum_rpc_addr,
monitoring_addr,
jsonrpc_import,
index_batch_size,
bulk_index_threads,
tx_cache_size,
txid_limit,
server_banner,
blocktxids_cache_size,
}

struct StaticCookie {
value: Vec<u8>,
}

impl StaticCookie {
fn from_string(value: String) -> Self {
StaticCookie {
value: value.into(),
}
}
}

impl CookieGetter for StaticCookie {
fn get(&self) -> Result<Vec<u8>> {
Ok(self.value.clone())
}
}

struct CookieFile {
daemon_dir: PathBuf,
cookie_file: PathBuf,
}

impl CookieFile {
fn from_daemon_dir(daemon_dir: &Path) -> Self {
CookieFile {
cookie_file: daemon_dir.join(".cookie"),
}
}

fn from_file(cookie_file: PathBuf) -> Self {
CookieFile { cookie_file }
}
}

impl CookieGetter for CookieFile {
fn get(&self) -> Result<Vec<u8>> {
let path = self.daemon_dir.join(".cookie");
let contents = fs::read(&path).chain_err(|| {
ErrorKind::Connection(format!("failed to read cookie from {:?}", path))
let contents = fs::read(&self.cookie_file).chain_err(|| {
ErrorKind::Connection(format!(
"failed to read cookie from {}",
self.cookie_file.display()
))
})?;
Ok(contents)
}
Expand Down

0 comments on commit 5549287

Please sign in to comment.