1use crate::{2 fleetdata::{FleetSecret, FleetSharedSecret},3 host::Config,4};5use anyhow::{bail, Result};6use std::{7 io::{self, Cursor, Read},8 path::PathBuf,9};10use structopt::StructOpt;1112#[derive(StructOpt)]13pub enum Secrets {14 15 ForceKeys,16 17 AddShared {18 19 name: String,20 21 machines: Vec<String>,22 23 #[structopt(long)]24 force: bool,25 #[structopt(long)]26 public: Option<String>,27 #[structopt(long)]28 public_file: Option<PathBuf>,29 },30 31 Add {32 33 name: String,34 35 machine: String,36 37 #[structopt(long)]38 force: bool,39 #[structopt(long)]40 public: Option<String>,41 #[structopt(long)]42 public_file: Option<PathBuf>,43 },44}4546impl Secrets {47 pub fn run(self, config: &Config) -> Result<()> {48 match self {49 Secrets::ForceKeys => {50 for host in config.list_hosts()? {51 if config.should_skip(&host) {52 continue;53 }54 config.key(&host)?;55 }56 }57 Secrets::AddShared {58 machines,59 name,60 force,61 public,62 public_file,63 } => {64 let recipients = machines65 .iter()66 .map(|m| config.recipient(m))67 .collect::<Result<Vec<_>>>()?;6869 let secret = {70 let mut input = vec![];71 io::stdin().read_to_end(&mut input)?;7273 let mut encrypted = vec![];74 let recipients = recipients75 .iter()76 .cloned()77 .map(|r| Box::new(r) as Box<dyn age::Recipient>)78 .collect();79 let mut encryptor =80 age::Encryptor::with_recipients(recipients).wrap_output(&mut encrypted)?;81 io::copy(&mut Cursor::new(input), &mut encryptor)?;82 encryptor.finish()?;83 encrypted84 };8586 let mut data = config.data_mut();87 if data.shared_secrets.contains_key(&name) && !force {88 bail!("secret already defined");89 }90 data.shared_secrets.insert(91 name,92 FleetSharedSecret {93 owners: machines,94 secret: FleetSecret {95 expire_at: None,96 secret,97 public: match (public, public_file) {98 (Some(v), None) => Some(v),99 (None, Some(v)) => Some(std::fs::read_to_string(&v)?),100 (Some(_), Some(_)) => {101 bail!("only public or public_file should be set")102 }103 (None, None) => None,104 },105 },106 },107 );108 }109 Secrets::Add {110 machine,111 name,112 force,113 public,114 public_file,115 } => {116 let recipient = config.recipient(&machine)?;117118 let secret = {119 let mut input = vec![];120 io::stdin().read_to_end(&mut input)?;121122 let mut encrypted = vec![];123 let recipient = Box::new(recipient) as Box<dyn age::Recipient>;124 let mut encryptor = age::Encryptor::with_recipients(vec![recipient])125 .wrap_output(&mut encrypted)?;126 io::copy(&mut Cursor::new(input), &mut encryptor)?;127 encryptor.finish()?;128 encrypted129 };130131 let mut data = config.data_mut();132 let host_secrets = data.host_secrets.entry(machine).or_default();133 if host_secrets.contains_key(&name) && !force {134 bail!("secret already defined");135 }136 host_secrets.insert(137 name,138 FleetSecret {139 expire_at: None,140 secret,141 public: match (public, public_file) {142 (Some(v), None) => Some(v),143 (None, Some(v)) => Some(std::fs::read_to_string(&v)?),144 (Some(_), Some(_)) => bail!("only public or public_file should be set"),145 (None, None) => None,146 },147 },148 );149 }150 }151 Ok(())152 }153}