diff --git a/.drone.yml b/.drone.yml index 631c43a..b71068d 100644 --- a/.drone.yml +++ b/.drone.yml @@ -26,7 +26,6 @@ steps: - name: dockersock path: /var/run commands: - - apk add bash - cuddle x build_cuddle_image environment: DOCKER_BUILDKIT: 1 @@ -34,19 +33,14 @@ steps: from_secret: docker_username DOCKER_PASSWORD: from_secret: docker_password + CUDDLE_SECRETS_PROVIDER: 1password + CUDDLE_ONE_PASSWORD_DOT_ENV: ".env.ci" + OP_SERVICE_ACCOUNT_TOKEN: + from_secret: op_service_account_token + depends_on: - "load_secret" - - name: send telegram notification - image: appleboy/drone-telegram - settings: - token: - from_secret: telegram_token - to: 2129601481 - format: markdown - when: - status: [failure] - services: - name: docker image: docker:dind diff --git a/.env.ci b/.env.ci new file mode 100644 index 0000000..9e8a520 --- /dev/null +++ b/.env.ci @@ -0,0 +1,2 @@ +DOCKER_USERNAME={{ op://application/docker_hub_credentials/username }} +DOCKER_PASSWORD={{ op://application/docker_hub_credentials/password }} diff --git a/Cargo.lock b/Cargo.lock index df082a9..e7a7784 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -41,6 +41,15 @@ dependencies = [ "libc", ] +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + [[package]] name = "anstream" version = "0.3.2" @@ -116,6 +125,17 @@ dependencies = [ "syn 2.0.27", ] +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -263,6 +283,21 @@ dependencies = [ "tokio", ] +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags 1.3.2", + "strsim 0.8.0", + "textwrap", + "unicode-width", + "vec_map", +] + [[package]] name = "clap" version = "4.3.19" @@ -281,7 +316,7 @@ dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim", + "strsim 0.10.0", ] [[package]] @@ -386,8 +421,9 @@ name = "cuddle_cli" version = "0.1.0" dependencies = [ "anyhow", - "clap", + "clap 4.3.19", "dirs 5.0.1", + "dotenv", "envconfig", "git2", "inquire", @@ -469,7 +505,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim", + "strsim 0.10.0", "syn 1.0.109", ] @@ -572,6 +608,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" +dependencies = [ + "clap 2.34.0", +] + [[package]] name = "dyn-clone" version = "1.0.12" @@ -964,6 +1009,15 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "hermit-abi" version = "0.3.2" @@ -1176,7 +1230,7 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.2", "rustix", "windows-sys 0.48.0", ] @@ -1358,7 +1412,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.2", "libc", ] @@ -2035,6 +2089,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + [[package]] name = "strsim" version = "0.10.0" @@ -2109,6 +2169,15 @@ dependencies = [ "unic-segment", ] +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + [[package]] name = "thiserror" version = "1.0.44" @@ -2457,6 +2526,12 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + [[package]] name = "version_check" version = "0.9.4" diff --git a/ci/src/main.rs b/ci/src/main.rs index 950aa50..18f5e84 100644 --- a/ci/src/main.rs +++ b/ci/src/main.rs @@ -56,7 +56,7 @@ async fn dind_image( .platform(platform) .build()?, ) - .from("rust:1.70.0-slim-bullseye") + .from("rustlang/rust:nightly") .with_exec(vec![ "rustup", "target", diff --git a/cuddle_cli/Cargo.toml b/cuddle_cli/Cargo.toml index 4efc591..d26433d 100644 --- a/cuddle_cli/Cargo.toml +++ b/cuddle_cli/Cargo.toml @@ -37,3 +37,4 @@ tempfile = { version = "3.6.0" } serde_json = "1.0.97" rlua = "0.19.5" rlua-searcher = { git = "https://git.front.kjuulh.io/kjuulh/rlua-searcher.git", rev = "2b29a9d0e86ec7f91b31dd844a58168969b7b74b" } +dotenv = { version = "0.15.0", features = ["clap"] } diff --git a/cuddle_cli/src/actions/mod.rs b/cuddle_cli/src/actions/mod.rs index 73d2565..6851770 100644 --- a/cuddle_cli/src/actions/mod.rs +++ b/cuddle_cli/src/actions/mod.rs @@ -48,7 +48,12 @@ impl CuddleAction { for (k, v) in args { let var = match v { CuddleShellScriptArg::Env(e) => { - let env_var = matches.get_one::(&e.key).cloned().unwrap(); + let env_var = matches.get_one::(&k).cloned().ok_or( + anyhow::anyhow!( + "failed to find env variable with key: {}", + &e.key + ), + )?; CuddleVariable::new(k.clone(), env_var) } diff --git a/cuddle_cli/src/cli/mod.rs b/cuddle_cli/src/cli/mod.rs index d1faa7f..eaff387 100644 --- a/cuddle_cli/src/cli/mod.rs +++ b/cuddle_cli/src/cli/mod.rs @@ -41,6 +41,16 @@ impl CuddleCli { config, }; + if let Ok(provider) = std::env::var("CUDDLE_SECRETS_PROVIDER") { + let provider = provider + .split(",") + .map(|p| p.to_string()) + .collect::>(); + tracing::trace!("secrets-provider enabled, handling for each entry"); + handle_providers(provider)?; + std::thread::sleep(std::time::Duration::from_millis(100)); + } + match context { Some(_) => { tracing::debug!("build full cli"); @@ -180,7 +190,7 @@ impl CuddleCli { .subcommand_required(true) .arg_required_else_help(true) .propagate_version(true) - .arg(clap::Arg::new("secrets-provider").long("secrets-provider")); + .arg(clap::Arg::new("secrets-provider").long("secrets-provider").env("CUDDLE_SECRETS_PROVIDER")); root_cmd = subcommands::x::build_command(root_cmd, self.clone()); root_cmd = subcommands::render_template::build_command(root_cmd); @@ -211,11 +221,6 @@ impl CuddleCli { if let Some(cli) = self.command.clone() { let matches = cli.clone().get_matches(); - if let Some(provider) = matches.get_many::("secrets-provider") { - tracing::trace!("secrets-provider enabled, handling for each entry"); - handle_providers(provider.cloned().collect::>())? - } - let res = match matches.subcommand() { Some(("x", exe_submatch)) => subcommands::x::execute_x(exe_submatch, self.clone()), Some(("render_template", sub_matches)) => { @@ -254,11 +259,15 @@ impl TryFrom for SecretProvider { fn try_from(value: String) -> Result { match value.as_str() { "1password" => { - let one_password_inject = std::env::var("ONE_PASSWORD_INJECT")?; - let one_password_dot_env = std::env::var("ONE_PASSWORD_DOT_ENV")?; + let one_password_inject = std::env::var("CUDDLE_ONE_PASSWORD_INJECT") + .ok() + .filter(|f| f.as_str() != ""); + let one_password_dot_env = std::env::var("CUDDLE_ONE_PASSWORD_DOT_ENV").ok(); let injectables = one_password_inject + .unwrap_or(String::new()) .split(",") + .filter(|s| s.contains('=')) .map(|i| i.to_string()) .collect::>(); @@ -267,11 +276,12 @@ impl TryFrom for SecretProvider { // anyhow::bail!("1pass injectable path doesn't exist: {}", i); // } // } - if &one_password_dot_env != "" { + if let Some(one_password_dot_env) = &one_password_dot_env { if let Ok(dir) = std::env::current_dir() { tracing::trace!( current_dir = dir.display().to_string(), dotenv = &one_password_dot_env, + exists = PathBuf::from(&one_password_dot_env).exists(), "1password dotenv inject" ); } @@ -279,14 +289,24 @@ impl TryFrom for SecretProvider { Ok(Self::OnePassword { inject: injectables, - dotenv: if PathBuf::from(&one_password_dot_env).exists() { - Some(one_password_dot_env) + dotenv: if let Some(one_password_dot_env) = one_password_dot_env { + if PathBuf::from(&one_password_dot_env).exists() { + Some(one_password_dot_env) + } else { + None + } } else { None }, }) } - _ => Err(anyhow::anyhow!("value is not one of supported values")), + value => { + tracing::debug!( + "provided secrets manager doesn't match any allowed values {}", + value + ); + Err(anyhow::anyhow!("value is not one of supported values")) + } } } } @@ -317,23 +337,32 @@ fn handle_providers(provider: Vec) -> anyhow::Result<()> { Ok(secrets_pair) } - let res: anyhow::Result> = provider + let res = provider .into_iter() .map(|p| SecretProvider::try_from(p)) - .flatten() + .collect::>>(); + let res = res?; + + let res = res + .into_iter() .map(|p| match p { SecretProvider::OnePassword { inject, dotenv } => { + tracing::trace!( + inject = inject.join(","), + dotenv = dotenv, + "handling 1password" + ); if let Some(dotenv) = dotenv { - let pairs = execute_1password_inject(&dotenv)?; + let pairs = execute_1password_inject(&dotenv).unwrap(); for (key, value) in pairs { - tracing::debug!(env_name = &key, "set var from 1password"); + tracing::debug!(env_name = &key, value=&value, "set var from 1password"); std::env::set_var(key, value); } } for i in inject { let (env_var_name, op_lookup) = i.split_once("=").ok_or(anyhow::anyhow!( - "ONE_PASSWORD_INJECT is not a key value pair ie. key:value,key2=value2" + "CUDDLE_ONE_PASSWORD_INJECT is not a key value pair ie. key:value,key2=value2" ))?; let secret = execute_1password(&op_lookup)?; std::env::set_var(&env_var_name, secret); diff --git a/cuddle_cli/src/main.rs b/cuddle_cli/src/main.rs index f1c19bb..d0ea1b3 100644 --- a/cuddle_cli/src/main.rs +++ b/cuddle_cli/src/main.rs @@ -1,5 +1,4 @@ use config::CuddleConfig; -use tracing::Level; use tracing_subscriber::prelude::*; use tracing_subscriber::{fmt, EnvFilter}; @@ -12,6 +11,7 @@ mod util; fn main() -> anyhow::Result<()> { init_logging()?; + let _ = dotenv::dotenv(); let config = CuddleConfig::from_env()?; diff --git a/templates/build_cuddle_image.Dockerfile b/templates/build_cuddle_image.Dockerfile index c7248db..9a16835 100644 --- a/templates/build_cuddle_image.Dockerfile +++ b/templates/build_cuddle_image.Dockerfile @@ -1,4 +1,4 @@ -FROM rust:1.70-slim-bullseye as base +FROM rustlang/rust:nightly as base RUN rustup target add x86_64-unknown-linux-musl @@ -20,5 +20,9 @@ RUN cargo install --target x86_64-unknown-linux-musl --path cuddle_cli FROM docker:dind +RUN apk add bash git + +COPY --from=1password/op:2 /usr/local/bin/op /usr/local/bin/op + COPY --from=base /usr/local/cargo/bin/ /usr/local/cargo/bin/ ENV PATH="${PATH}:/usr/local/cargo/bin"