1use crate::{fleetdata::FleetSecret, host::Config};2use anyhow::{bail, Result};3use clap::Clap;4use std::{5 collections::BTreeMap,6 io::{Cursor, Read},7};89#[derive(Clap)]10pub enum Secrets {11 12 ForceKeys,13 14 Add {15 16 name: String,17 18 machines: Vec<String>,19 20 #[clap(long)]21 force: bool,22 },23}2425impl Secrets {26 pub fn run(self, config: &Config) -> Result<()> {27 match self {28 Secrets::ForceKeys => {29 for host in config.list_hosts()? {30 if config.should_skip(&host) {31 continue;32 }33 config.key(&host)?;34 }35 }36 Secrets::Add {37 machines,38 name,39 force,40 } => {41 let recipients = machines42 .iter()43 .map(|m| config.recipient(&m))44 .collect::<Result<Vec<_>>>()?;4546 let secret_data = {47 let mut input = vec![];48 std::io::stdin().read_to_end(&mut input)?;4950 let data: BTreeMap<String, String> = serde_json::from_slice(&input)?;51 let mut transformed_data: BTreeMap<String, String> = BTreeMap::new();52 for (k, v) in data {53 if k.ends_with("_pub") {54 transformed_data.insert(k, v);55 } else if k.ends_with("_secret") {56 let mut encrypted = vec![];57 let recipients = recipients58 .iter()59 .cloned()60 .map(|r| Box::new(r) as Box<dyn age::Recipient>)61 .collect();62 let mut encryptor = age::Encryptor::with_recipients(recipients)63 .wrap_output(&mut encrypted)?;64 std::io::copy(&mut Cursor::new(v.as_bytes()), &mut encryptor)?;65 drop(encryptor);6667 transformed_data.insert(k, ascii85::encode(&encrypted));68 } else {69 bail!("unknown key type: {:?}", k);70 }71 }72 transformed_data73 };7475 let mut data = config.data_mut();76 if data.secrets.contains_key(&name) && !force {77 bail!("secret already defined");78 }79 data.secrets.insert(80 name,81 FleetSecret {82 owners: machines.clone(),83 expire_at: None,84 data: secret_data,85 },86 );87 }88 }89 Ok(())90 }91}