support api secrets, handle SIGQUIT, SIGINT, and SIGTERM

This commit is contained in:
scilio 2022-07-22 10:47:04 -04:00
parent 40bf97a841
commit e902f78e32
8 changed files with 132 additions and 22 deletions

1
Cargo.lock generated
View file

@ -2475,6 +2475,7 @@ dependencies = [
"bytes 0.5.6",
"chacha20",
"clap",
"dirs",
"failure",
"futures 0.3.17",
"grin_api 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)",

View file

@ -11,6 +11,7 @@ byteorder = "1"
bytes = "0.5.6"
chacha20 = "0.8.1"
clap = { version = "2.33", features = ["yaml"] }
dirs = "2.0"
failure = "0.1.8"
futures = "0.3"
hmac = { version = "0.12.0", features = ["std"]}

View file

@ -13,11 +13,19 @@ args:
short: n
long: grin_node_url
takes_value: true
- grin_node_secret_path:
help: Path to a file containing the secret for the GRIN node api
long: grin_node_secret_path
takes_value: true
- wallet_owner_url:
help: Api address of running wallet owner listener
short: l
long: wallet_owner_url
takes_value: true
- wallet_owner_secret_path:
help: Path to a file containing the secret for the wallet owner api
long: wallet_owner_secret_path
takes_value: true
- wallet_pass:
help: The wallet's password
long: wallet_pass

View file

@ -2,7 +2,7 @@ use crate::error::{self, Result};
use crate::secp::SecretKey;
use core::num::NonZeroU32;
use grin_util::{ToHex, ZeroingString};
use grin_util::{file, ToHex, ZeroingString};
use rand::{thread_rng, Rng};
use ring::{aead, pbkdf2};
use serde_derive::{Deserialize, Serialize};
@ -11,6 +11,11 @@ use std::io::prelude::*;
use std::net::SocketAddr;
use std::path::PathBuf;
const GRIN_HOME: &str = ".grin";
const CHAIN_NAME: &str = "main";
const NODE_API_SECRET_FILE_NAME: &str = ".api_secret";
const WALLET_OWNER_API_SECRET_FILE_NAME: &str = ".owner_api_secret";
/// The decrypted server config to be passed around and used by the rest of the mwixnet code
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct ServerConfig {
@ -22,8 +27,22 @@ pub struct ServerConfig {
pub addr: SocketAddr,
/// foreign api address of the grin node
pub grin_node_url: SocketAddr,
/// path to file containing api secret for the grin node
pub grin_node_secret_path: Option<String>,
/// owner api address of the grin wallet
pub wallet_owner_url: SocketAddr,
/// path to file containing secret for the grin wallet's owner api
pub wallet_owner_secret_path: Option<String>,
}
impl ServerConfig {
pub fn node_api_secret(&self) -> Option<String> {
file::get_first_line(self.grin_node_secret_path.clone())
}
pub fn wallet_owner_api_secret(&self) -> Option<String> {
file::get_first_line(self.wallet_owner_secret_path.clone())
}
}
/// Encrypted server key, for storing on disk and decrypting with a password.
@ -134,7 +153,9 @@ struct RawConfig {
interval_s: u32,
addr: SocketAddr,
grin_node_url: SocketAddr,
grin_node_secret_path: Option<String>,
wallet_owner_url: SocketAddr,
wallet_owner_secret_path: Option<String>,
}
/// Writes the server config to the config_path given, encrypting the server_key first.
@ -152,7 +173,9 @@ pub fn write_config(
interval_s: server_config.interval_s,
addr: server_config.addr,
grin_node_url: server_config.grin_node_url,
grin_node_secret_path: server_config.grin_node_secret_path.clone(),
wallet_owner_url: server_config.wallet_owner_url,
wallet_owner_secret_path: server_config.wallet_owner_secret_path.clone(),
};
let encoded: String = toml::to_string(&raw_config).map_err(|e| {
error::ErrorKind::SaveConfigError(format!("Error while encoding config as toml: {}", e))
@ -189,10 +212,34 @@ pub fn load_config(config_path: &PathBuf, password: &ZeroingString) -> Result<Se
interval_s: raw_config.interval_s,
addr: raw_config.addr,
grin_node_url: raw_config.grin_node_url,
grin_node_secret_path: raw_config.grin_node_secret_path,
wallet_owner_url: raw_config.wallet_owner_url,
wallet_owner_secret_path: raw_config.wallet_owner_secret_path,
})
}
pub fn get_grin_path() -> PathBuf {
let mut grin_path = match dirs::home_dir() {
Some(p) => p,
None => PathBuf::new(),
};
grin_path.push(GRIN_HOME);
grin_path.push(CHAIN_NAME);
grin_path
}
pub fn node_secret_path() -> PathBuf {
let mut path = get_grin_path();
path.push(NODE_API_SECRET_FILE_NAME);
path
}
pub fn wallet_owner_secret_path() -> PathBuf {
let mut path = get_grin_path();
path.push(WALLET_OWNER_API_SECRET_FILE_NAME);
path
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -1,13 +1,10 @@
use config::ServerConfig;
use error::{Error, ErrorKind};
use node::HttpGrinNode;
use wallet::HttpWallet;
use clap::App;
use grin_util::{StopState, ZeroingString};
use rpassword;
use std::env;
use std::io::{self, Write};
use std::path::PathBuf;
use std::sync::Arc;
use tokio::runtime::Runtime;
@ -41,9 +38,9 @@ fn real_main() -> Result<(), Box<dyn std::error::Error>> {
let config_path = match args.value_of("config_file") {
Some(path) => PathBuf::from(path),
None => {
let mut current_dir = env::current_dir()?;
current_dir.push("mwixnet-config.toml");
current_dir
let mut grin_path = config::get_grin_path();
grin_path.push("mwixnet-config.toml");
grin_path
}
};
@ -52,7 +49,9 @@ fn real_main() -> Result<(), Box<dyn std::error::Error>> {
.map(|t| t.parse::<u32>().unwrap());
let bind_addr = args.value_of("bind_addr");
let grin_node_url = args.value_of("grin_node_url");
let grin_node_secret_path = args.value_of("grin_node_secret_path");
let wallet_owner_url = args.value_of("wallet_owner_url");
let wallet_owner_secret_path = args.value_of("wallet_owner_secret_path");
// Write a new config file if init-config command is supplied
if let ("init-config", Some(_)) = args.subcommand() {
@ -68,7 +67,17 @@ fn real_main() -> Result<(), Box<dyn std::error::Error>> {
interval_s: round_time.unwrap_or(DEFAULT_INTERVAL),
addr: bind_addr.unwrap_or("0.0.0.0:3000").parse()?,
grin_node_url: grin_node_url.unwrap_or("127.0.0.1:3413").parse()?,
grin_node_secret_path: match grin_node_secret_path {
Some(p) => Some(p.to_owned()),
None => config::node_secret_path().to_str().map(|p| p.to_owned()),
},
wallet_owner_url: wallet_owner_url.unwrap_or("127.0.0.1:3420").parse()?,
wallet_owner_secret_path: match wallet_owner_secret_path {
Some(p) => Some(p.to_owned()),
None => config::wallet_owner_secret_path()
.to_str()
.map(|p| p.to_owned()),
},
};
let password = prompt_password_confirm();
@ -93,31 +102,41 @@ fn real_main() -> Result<(), Box<dyn std::error::Error>> {
server_config.grin_node_url = grin_node_url.parse()?;
}
// Override grin_node_secret_path, if supplied
if let Some(grin_node_secret_path) = grin_node_secret_path {
server_config.grin_node_secret_path = Some(grin_node_secret_path.to_owned());
}
// Override wallet_owner_url, if supplied
if let Some(wallet_owner_url) = wallet_owner_url {
server_config.wallet_owner_url = wallet_owner_url.parse()?;
}
// Override wallet_owner_secret_path, if supplied
if let Some(wallet_owner_secret_path) = wallet_owner_secret_path {
server_config.wallet_owner_secret_path = Some(wallet_owner_secret_path.to_owned());
}
// Open wallet
let wallet_pass = prompt_wallet_password(&args.value_of("wallet_pass"));
let wallet = HttpWallet::open_wallet(&server_config.wallet_owner_url, &wallet_pass)?;
let wallet = HttpWallet::open_wallet(
&server_config.wallet_owner_url,
&server_config.wallet_owner_api_secret(),
&wallet_pass,
)?;
// Create GrinNode
let node = HttpGrinNode::new(&server_config.grin_node_url, &None);
let node = HttpGrinNode::new(
&server_config.grin_node_url,
&server_config.node_api_secret(),
);
let stop_state = Arc::new(StopState::new());
let stop_state_clone = stop_state.clone();
let rt = Runtime::new()?;
rt.spawn(async move {
let shutdown_signal = async move {
// Wait for the CTRL+C signal
tokio::signal::ctrl_c()
.await
.expect("failed to install CTRL+C signal handler");
};
futures::executor::block_on(shutdown_signal);
futures::executor::block_on(build_signals_fut());
stop_state_clone.stop();
});
@ -130,6 +149,30 @@ fn real_main() -> Result<(), Box<dyn std::error::Error>> {
)
}
async fn build_signals_fut() {
if cfg!(unix) {
use tokio::signal::unix::{signal, SignalKind};
// Listen for SIGINT, SIGQUIT, and SIGTERM
let mut terminate_signal =
signal(SignalKind::terminate()).expect("failed to create terminate signal");
let mut quit_signal = signal(SignalKind::quit()).expect("failed to create quit signal");
let mut interrupt_signal =
signal(SignalKind::interrupt()).expect("failed to create interrupt signal");
futures::future::select_all(vec![
Box::pin(terminate_signal.recv()),
Box::pin(quit_signal.recv()),
Box::pin(interrupt_signal.recv()),
])
.await;
} else {
tokio::signal::ctrl_c()
.await
.expect("failed to install CTRL+C signal handler");
}
}
fn prompt_password() -> ZeroingString {
ZeroingString::from(rpassword::prompt_password_stdout("Server password: ").unwrap())
}

View file

@ -116,15 +116,14 @@ pub mod comsig_serde {
}
/// Creates a ComSignature from a hex string
pub fn deserialize<'de, D>(deserializer: D) -> std::result::Result<ComSignature, D::Error>
pub fn deserialize<'de, D>(deserializer: D) -> Result<ComSignature, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::Error;
let bytes = String::deserialize(deserializer)
.and_then(|string| grin_util::from_hex(&string).map_err(Error::custom))?;
let sig: ComSignature =
grin_core::ser::deserialize_default(&mut &bytes[..]).map_err(Error::custom)?;
let sig: ComSignature = ser::deserialize_default(&mut &bytes[..]).map_err(Error::custom)?;
Ok(sig)
}
}

View file

@ -319,7 +319,9 @@ mod tests {
interval_s: 1,
addr: TcpListener::bind("127.0.0.1:0")?.local_addr()?,
grin_node_url: "127.0.0.1:3413".parse()?,
grin_node_secret_path: None,
wallet_owner_url: "127.0.0.1:3420".parse()?,
wallet_owner_secret_path: None,
};
let threaded_rt = runtime::Runtime::new()?;

View file

@ -85,6 +85,7 @@ pub fn assemble_tx(
#[derive(Clone)]
pub struct HttpWallet {
wallet_owner_url: SocketAddr,
wallet_owner_secret: Option<String>,
shared_key: SecretKey,
token: Token,
}
@ -104,6 +105,7 @@ impl HttpWallet {
/// Calls the 'open_wallet' using the RPC API.
pub fn open_wallet(
wallet_owner_url: &SocketAddr,
wallet_owner_secret: &Option<String>,
wallet_pass: &ZeroingString,
) -> Result<HttpWallet> {
println!("Opening wallet at {}", wallet_owner_url);
@ -115,6 +117,7 @@ impl HttpWallet {
});
let token: Token = HttpWallet::send_enc_request(
&wallet_owner_url,
&wallet_owner_secret,
"open_wallet",
&open_wallet_params,
&shared_key,
@ -123,6 +126,7 @@ impl HttpWallet {
Ok(HttpWallet {
wallet_owner_url: wallet_owner_url.clone(),
wallet_owner_secret: wallet_owner_secret.clone(),
shared_key: shared_key.clone(),
token: token.clone(),
})
@ -152,6 +156,7 @@ impl HttpWallet {
fn send_enc_request<D: serde::de::DeserializeOwned>(
wallet_owner_url: &SocketAddr,
wallet_owner_secret: &Option<String>,
method: &str,
params: &serde_json::Value,
shared_key: &SecretKey,
@ -164,8 +169,11 @@ impl HttpWallet {
"jsonrpc": "2.0",
});
let enc_req = EncryptedRequest::from_json(&JsonId::IntId(1), &req, &shared_key)?;
let res =
client::post::<EncryptedRequest, EncryptedResponse>(url.as_str(), None, &enc_req)?;
let res = client::post::<EncryptedRequest, EncryptedResponse>(
url.as_str(),
wallet_owner_secret.clone(),
&enc_req,
)?;
let decrypted = res.decrypt(&shared_key)?;
let response: Response = serde_json::from_value(decrypted.clone())?;
let parsed = serde_json::from_value(response.result.unwrap().get("Ok").unwrap().clone())?;
@ -205,6 +213,7 @@ impl Wallet for HttpWallet {
});
let output: OutputWithBlind = HttpWallet::send_enc_request(
&self.wallet_owner_url,
&self.wallet_owner_secret,
"build_output",
&req_json,
&self.shared_key,