--- a/src/cmds/secrets/mod.rs +++ b/src/cmds/secrets/mod.rs @@ -1,9 +1,10 @@ -use std::io::Write; - -use anyhow::Result; +use crate::{fleetdata::FleetSecret, host::Config}; +use anyhow::{bail, Result}; use clap::Clap; - -use crate::host::Config; +use std::{ + collections::BTreeMap, + io::{Cursor, Read}, +}; #[derive(Clap)] pub enum Secrets { @@ -11,10 +12,13 @@ ForceKeys, /// Add secret, data should be provided in stdin Add { - /// Secret owner - machine: String, /// Secret name name: String, + /// Secret owners + machines: Vec, + /// Override secret if already present + #[clap(long)] + force: bool, }, } @@ -29,21 +33,57 @@ config.key(&host)?; } } - Secrets::Add { machine, name } => { - let recipient = config.recipient(&machine)?; - let encryptor = age::Encryptor::with_recipients(vec![Box::new(recipient)]); + Secrets::Add { + machines, + name, + force, + } => { + let recipients = machines + .iter() + .map(|m| config.recipient(&m)) + .collect::>>()?; + + let secret_data = { + let mut input = vec![]; + std::io::stdin().read_to_end(&mut input)?; + + let data: BTreeMap = serde_json::from_slice(&input)?; + let mut transformed_data: BTreeMap = BTreeMap::new(); + for (k, v) in data { + if k.ends_with("_pub") { + transformed_data.insert(k, v); + } else if k.ends_with("_secret") { + let mut encrypted = vec![]; + let recipients = recipients + .iter() + .cloned() + .map(|r| Box::new(r) as Box) + .collect(); + let mut encryptor = age::Encryptor::with_recipients(recipients) + .wrap_output(&mut encrypted)?; + std::io::copy(&mut Cursor::new(v.as_bytes()), &mut encryptor)?; + drop(encryptor); - let mut encrypted = vec![]; - { - let mut w = encryptor.wrap_output(&mut encrypted)?; + transformed_data.insert(k, ascii85::encode(&encrypted)); + } else { + bail!("unknown key type: {:?}", k); + } + } + transformed_data + }; - let stdin = std::io::stdin(); - let mut lock = stdin.lock(); - std::io::copy(&mut lock, &mut w)?; - w.flush()?; + let mut data = config.data_mut(); + if data.secrets.contains_key(&name) && !force { + bail!("secret already defined"); } - - config.update_secret(&machine, &name, &encrypted) + data.secrets.insert( + name, + FleetSecret { + owners: machines.clone(), + expire_at: None, + data: secret_data, + }, + ); } } Ok(())