diff --git a/readme.md b/readme.md index 8f5a931..10e8b9a 100644 --- a/readme.md +++ b/readme.md @@ -184,3 +184,13 @@ example: without pgp: ./.vault/private_keys/[username].pem with pgp: ./.vault/private_keys/[username].pem.pgp +# Mocking vault calls / Integrationtests + +if you want to test vault in ci environments, it is good to make sure that a certain secret would be there, even if you don't want to expose the secret. +it can also be useful to overwrite all the secrets in vault (just for ci). this is possible with this the argument `--i_know_what_i_do__enable_fetch_raw_secrets_from_env=[USER]`. +if you use the commandline argument vault ensures that the `[USER]` could decrypt the secret. You must provide the secret to vault as an environment variable `VAULT_DEBUG_SECRET_[SECRET]`. +this allows you to write integration tests for scripts that need to call vault. + +``` +export VAULT_DEBUG_SECRET_foo=some_test_value; vault --i_know_what_i_do__enable_fetch_raw_secrets_from_env=test get_multi '{"secrets": [{"secret": "foo"}]}' +``` \ No newline at end of file diff --git a/src/key/key_map.rs b/src/key/key_map.rs index 75366c5..513055f 100644 --- a/src/key/key_map.rs +++ b/src/key/key_map.rs @@ -18,6 +18,7 @@ use toml; pub struct KeyMap { pems: Vec, entries: Vec, + debug_enable_fetch_raw_secrets_from_env: Option, } #[derive(Debug)] @@ -43,6 +44,7 @@ pub struct Subscription { pub struct KeyMapConfig { pub path_private_key: String, + pub debug_enable_fetch_raw_secrets_from_env: Option, } impl Subscription { @@ -316,6 +318,7 @@ impl KeyMap { Ok(KeyMap { pems: Self::build_private_pems(&config)?, entries: buffer, + debug_enable_fetch_raw_secrets_from_env: config.debug_enable_fetch_raw_secrets_from_env.clone(), }) } @@ -340,7 +343,37 @@ impl KeyMap { Ok(String::from_utf8(decrypted.get_content().to_vec()).context(anyhow!("Invalid Utf8"))?) } + + pub fn decrypt_debug_enable_fetch_raw_secrets_from_env(&self, subscription_key: &str) -> Result { + let decrypt_user = self.debug_enable_fetch_raw_secrets_from_env.as_ref().expect("decrypt user must be given"); + let secret_path = format!("./.vault/secrets/{}/{}.crypt", subscription_key, decrypt_user); + + let crypt_file = match fs::metadata(&secret_path) { + Ok(k) => k, + Err(e) => return Err(anyhow!("could not find key - crypt file {} does not exist", &secret_path)) + }; + + if !crypt_file.is_file() { + return Err(anyhow!("could not find key - crypt file {} is not a file", &secret_path)) + } + + let env_var = format!("VAULT_DEBUG_SECRET_{}", subscription_key); + let secret = match ::std::env::var(&env_var) { + Ok(k) => k, + Err(e) => { + return Err(anyhow!("could not find key - could not read env var {}, error: {}", &env_var, e)) + } + }; + + Ok(UncryptedVaultFile::new(secret.into_bytes())) + } + pub fn decrypt(&self, subscription_key: &str) -> Result { + + if self.debug_enable_fetch_raw_secrets_from_env.is_some() { + return self.decrypt_debug_enable_fetch_raw_secrets_from_env(subscription_key); + } + let possible_files: Vec = { let mut buffer = vec![]; diff --git a/src/main.rs b/src/main.rs index 4e8fb6d..34d036c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -36,6 +36,11 @@ fn main() -> ::anyhow::Result<()> { .required(false) .help("are you using a feature that only exists in a new vault version and your coworkers are still using an old version? install --min-version to warn your coworkers =)"), ) + .arg( + Arg::new("i_know_what_i_do__enable_fetch_raw_secrets_from_env") + .long("i_know_what_i_do__enable_fetch_raw_secrets_from_env") + .required(false) + ) .version(cargo_crate_version!()) .subcommand( ::clap::Command::new("get").arg( @@ -89,6 +94,8 @@ fn main() -> ::anyhow::Result<()> { ) .get_matches(); + let debug_enable_fetch_raw_secrets_from_env = matches.get_one::("i_know_what_i_do__enable_fetch_raw_secrets_from_env").map(|x|x.clone()); + if let Some(min_version) = matches.get_one::("expect_version") { let version_requirement = VersionReq::parse(min_version).context("could not parse version requirement, expected something like >=1.2.3, <1.8.0")?; let version_current = Version::parse(cargo_crate_version!()).context("could not parse current version, should not happen")?; @@ -123,6 +130,7 @@ fn main() -> ::anyhow::Result<()> { for _ in 1..3 { let keymap = KeyMap::from_path(&KeyMapConfig { path_private_key: path_private_key.clone(), + debug_enable_fetch_raw_secrets_from_env: debug_enable_fetch_raw_secrets_from_env.clone() })?; println!("keys are fine"); @@ -138,21 +146,9 @@ fn main() -> ::anyhow::Result<()> { let mut keymap = KeyMap::from_path(&KeyMapConfig { path_private_key: path_private_key.clone(), + debug_enable_fetch_raw_secrets_from_env: debug_enable_fetch_raw_secrets_from_env.clone() })?; - if let Some(matches) = matches.subcommand_matches("update") { - let status = self_update::backends::github::Update::configure() - .repo_owner("easybill") - .repo_name("vault") - .bin_name("vault") - .show_download_progress(true) - .current_version(matches.get_one::("current_version").expect("current version has a default")) - .build()? - .update()?; - println!("Update status: `{}`!", status.version()); - return Ok(()) - } - // You can check the value provided by positional arguments, or option arguments if let Some(matches) = matches.subcommand_matches("get") { let key = matches.get_one::("key").expect("key must exists"); @@ -177,14 +173,6 @@ fn main() -> ::anyhow::Result<()> { ); } - if let Some(matches) = matches.subcommand_matches("create-openssl-key") { - let username = matches.get_one::("username").expect("username must exists"); - - create_keys(username)?; - - return Ok(()); - } - if let Some(matches) = matches.subcommand_matches("template") { let filename = matches.get_one::("filename").expect("filename must exists"); @@ -202,8 +190,37 @@ fn main() -> ::anyhow::Result<()> { return Ok(()); } + if debug_enable_fetch_raw_secrets_from_env.is_some() { + unimplemented!("command is not implemented for debug_enable_fetch_raw_secrets_from_env"); + } + + if let Some(matches) = matches.subcommand_matches("update") { + let status = self_update::backends::github::Update::configure() + .repo_owner("easybill") + .repo_name("vault") + .bin_name("vault") + .show_download_progress(true) + .current_version(matches.get_one::("current_version").expect("current version has a default")) + .build()? + .update()?; + println!("Update status: `{}`!", status.version()); + return Ok(()) + } + + if let Some(matches) = matches.subcommand_matches("create-openssl-key") { + let username = matches.get_one::("username").expect("username must exists"); + + create_keys(username)?; + + return Ok(()); + } + if let Some(_matches) = matches.subcommand_matches("rotate") { - rotate_keys(&KeyMapConfig { path_private_key }).context("rotate keys")?; + + rotate_keys(&KeyMapConfig { + path_private_key, + debug_enable_fetch_raw_secrets_from_env: debug_enable_fetch_raw_secrets_from_env.clone() + }).context("rotate keys")?; return Ok(()); } @@ -213,7 +230,10 @@ fn main() -> ::anyhow::Result<()> { if scan_for_new_secrets(&keymap)? > 0 { // refresh the keymap - keymap = KeyMap::from_path(&KeyMapConfig { path_private_key })?; + keymap = KeyMap::from_path(&KeyMapConfig { + path_private_key, + debug_enable_fetch_raw_secrets_from_env: debug_enable_fetch_raw_secrets_from_env.clone() + })?; } // check loaded keys: