pub mod config_builder;
pub mod networks;
use self::{config_builder::ConfigBuilder, networks::Network};
use crate::{config::CONFIG_FILE_NAME, key_utils, prelude::*};
use abscissa_core::Command;
use clap::Parser;
use std::{
fs,
os::unix::fs::PermissionsExt,
path::{Path, PathBuf},
process,
};
pub const SUBDIRECTORIES: &[&str] = &["schema", "secrets", "state"];
pub const SECRETS_DIR_PERMISSIONS: u32 = 0o700;
pub const SECRET_CONNECTION_KEY: &str = "kms-identity.key";
macro_rules! abort {
($fmt:expr, $($arg:tt)+) => {
status_err!(format!($fmt, $($arg)+));
process::exit(1);
};
}
#[derive(Command, Debug, Parser)]
pub struct InitCommand {
#[clap(short = 'n', long = "networks")]
networks: Option<String>,
output_paths: Vec<PathBuf>,
}
impl Runnable for InitCommand {
fn run(&self) {
if self.output_paths.len() != 1 {
eprintln!("Usage: tmkms init [-f] KMS_HOME_PATH");
process::exit(1);
}
let mut networks = vec![];
match &self.networks {
Some(chain_ids) => {
for chain_id in chain_ids.split(',') {
networks.push(Network::parse(chain_id));
}
}
None => {
networks.push(Network::CosmosHub);
}
}
let kms_home = {
let output_path = &self.output_paths[0];
if !output_path.exists() {
status_ok!("Creating", "{}", output_path.display());
fs::create_dir_all(output_path).unwrap_or_else(|e| {
abort!("couldn't create `{}`: {}", output_path.display(), e);
});
}
fs::canonicalize(output_path).unwrap_or_else(|e| {
abort!("couldn't canonicalize `{}`: {}", output_path.display(), e);
})
};
for subdir in SUBDIRECTORIES {
let subdir_path = kms_home.join(subdir);
fs::create_dir_all(&subdir_path).unwrap_or_else(|e| {
abort!("couldn't create `{}`: {}", subdir_path.display(), e);
});
}
let secrets_dir = kms_home.join("secrets");
set_permissions(&secrets_dir, SECRETS_DIR_PERMISSIONS);
let config_path = kms_home.join(CONFIG_FILE_NAME);
let config_toml = ConfigBuilder::new(&kms_home, &networks).generate();
fs::write(&config_path, config_toml).unwrap_or_else(|e| {
abort!("couldn't write `{}`: {}", config_path.display(), e);
});
status_ok!("Generated", "KMS configuration: {}", config_path.display());
let secret_connection_key = secrets_dir.join(SECRET_CONNECTION_KEY);
key_utils::generate_key(&secret_connection_key).unwrap_or_else(|e| {
abort!(
"couldn't generate `{}`: {}",
secret_connection_key.display(),
e
);
});
status_ok!(
"Generated",
"Secret Connection key: {}",
secret_connection_key.display()
);
}
}
fn set_permissions(path: impl AsRef<Path>, mode: u32) {
fs::set_permissions(path.as_ref(), fs::Permissions::from_mode(mode)).unwrap_or_else(|e| {
abort!(
"couldn't set permissions on `{}`: {}",
path.as_ref().display(),
e
);
});
}