difftreelog
feat fleet secret read command reimplementation
in: trunk
2 files changed
cmds/fleet/src/cmds/secrets.rsdiffbeforeafterboth--- a/cmds/fleet/src/cmds/secrets.rs
+++ b/cmds/fleet/src/cmds/secrets.rs
@@ -1,16 +1,10 @@
-use std::{
- collections::{BTreeSet, HashSet},
- io::{Read, stdin},
- path::PathBuf,
-};
+use std::io::{Write as _, stdout};
-use anyhow::{Context as _, Result, anyhow, bail, ensure};
+use anyhow::{Context as _, Result, anyhow, bail};
use clap::Parser;
use fleet_base::{fleetdata::SecretOwner, host::Config, opts::FleetOpts};
-use fleet_shared::SecretData;
-use itertools::{ExactlyOneError, Itertools as _};
-use tokio::fs::read;
-use tracing::{info, warn};
+use itertools::Itertools as _;
+use tracing::warn;
#[derive(Parser)]
pub enum Secret {
@@ -27,13 +21,9 @@
machine: Option<String>,
/// Which private secret part to read
- #[clap(short = 'p', long, default_value = "secret")]
+ /// If not specified - only one existing part is read
+ #[clap(short = 'p', long)]
part: Option<String>,
-
- /// Which host should we use to decrypt, in case if reencryption is required, without
- /// regeneration
- #[clap(long)]
- prefer_identities: Vec<String>,
},
/// Prune (remove, mark for regeneration) secrets
Prune {
@@ -54,18 +44,6 @@
machine: Vec<String>,
},
List {},
- Edit {
- name: String,
- #[clap(short = 'm', long)]
- machine: String,
-
- #[clap(long)]
- add: bool,
-
- /// Which private secret part to read
- #[clap(short = 'p', long, default_value = "secret")]
- part: String,
- },
}
impl Secret {
@@ -83,76 +61,65 @@
name,
machine,
part: part_name,
- mut prefer_identities,
} => {
- let secret = config.data.secrets.read().expect("not poisoned");
-
- let Some(dist) = secret.get("name") else {
- bail!("secret doesn't exists");
- };
-
- let dist = if let Some(machine) = &machine {
- dist.get(&SecretOwner::host(machine))
- .ok_or_else(|| anyhow!("machine {machine} has no secret generated"))?
- } else {
- dist.distributions()
- .exactly_one()
- .map_err(|e| anyhow!("{e}"))
- .context(
- "with no machine specified, there should be exactly one distribution",
- )?
- };
-
- let part_name = part_name.unwrap_or_else(|| "secret".to_string());
- let Some(part) = dist.secret.parts.get(&part_name) else {
- bail!("secret part {part_name:?} is not defined");
- };
+ let (owners, secret_data) = {
+ let secret = config.data.secrets.read().expect("not poisoned");
- // dist.get(SecretOwner(name));
+ let Some(dist) = secret.get(&name) else {
+ bail!("secret doesn't exists");
+ };
- todo!();
- /*
- let Some(secret) = config.shared_secret(&name) else {
- bail!("secret doesn't exists");
- };
+ let dist = if let Some(machine) = &machine {
+ dist.get(&SecretOwner::host(machine))
+ .ok_or_else(|| anyhow!("machine {machine} has no secret generated"))?
+ } else {
+ dist.distributions()
+ .exactly_one()
+ .map_err(|e| anyhow!("{e}"))
+ .context(
+ "with no machine specified, there should be exactly one distribution",
+ )?
+ };
- let dist = if secret.len() == 1 {
- &secret[0]
- } else if let Some(machine) = machine {
- let dist = secret.get(&machine);
- let Some(dist) = dist else {
- bail!("machine {machine} has no distribution of secret {name}");
+ let part = if let Some(part_name) = &part_name {
+ dist.secret.parts.get(part_name).ok_or_else(|| {
+ anyhow!("secret {name} does not have part named {part_name}")
+ })?
+ } else {
+ dist.secret
+ .parts
+ .iter()
+ .exactly_one()
+ .map_err(|e| anyhow!("{e}"))
+ .context("with no part specified, there should be exactly one part")?
+ .1
};
- prefer_identities.push(machine);
- dist
- } else {
- bail!(
- "secret {name} has shares, but no --machine specified for specifing which do you need"
- )
+ let owners = dist.owners().cloned().collect::<Vec<_>>();
+ let secret_data = part.raw.clone();
+ (owners, secret_data)
};
- let Some(part) = dist.secret.parts.get(&part_name) else {
- bail!("no part {part_name} in secret {name}");
- };
- let data = if part.raw.encrypted {
- let identity_holder = if !prefer_identities.is_empty() {
- prefer_identities
- .iter()
- .find(|i| dist.owners.iter().any(|s| s == *i))
- } else {
- dist.owners.first()
+ for host in config
+ .preferred_hosts(|h| owners.iter().any(|o| o.as_host() == Some(h)))
+ .context("failed to list hosts")?
+ {
+ let host = match host {
+ Ok(h) => h,
+ Err(e) => {
+ warn!("failed to use host: {e}");
+ continue;
+ }
};
- let Some(identity_holder) = identity_holder else {
- bail!("no available holder found");
+ match host.decrypt(secret_data.clone()).await {
+ Ok(data) => {
+ let mut w = stdout();
+ w.write_all(&data)?;
+ return Ok(());
+ }
+ Err(e) => warn!("failed to decrypt on {}: {e}", host.name),
};
- let host = config.host(identity_holder)?;
- host.decrypt(part.raw.clone()).await?
- } else {
- part.raw.data.clone()
- };
- stdout().write_all(&data)?;
- */
- todo!()
+ }
+ bail!("failed to find suitable decrypting host");
}
Secret::List {} => {
/*
@@ -190,26 +157,6 @@
}
// info!("loaded\n{}", Table::new(table).to_string())
*/
- todo!()
- }
- Secret::Edit {
- name,
- machine,
- part,
- add,
- } => {
- /*let secret = config
- .host_secret(&machine, &name)
- .context("secret not found")?;
- if let Some(data) = secret.secret.parts.get(&part) {
- let host = config.host(&machine)?;
- let secret = host.decrypt(data.raw.clone()).await?;
- String::from_utf8(secret).context("secret is not utf8")?
- } else if add {
- String::new()
- } else {
- bail!("part {part} not found in secret {name}. Did you mean to `--add` it?");
- };*/
todo!()
}
Secret::Prune { name, machine } => todo!(),
crates/fleet-base/src/host.rsdiffbeforeafterboth1use std::{1use std::{2 collections::{BTreeMap, BTreeSet},2 collections::{BTreeMap, BTreeSet, HashSet},3 ffi::{OsStr, OsString},3 ffi::{OsStr, OsString},4 fmt::Display,4 fmt::Display,5 io::Write,5 io::Write,652 }652 }653 }653 }654655 pub fn preferred_hosts(656 &self,657 filter: impl Fn(&str) -> bool,658 ) -> Result<impl Iterator<Item = Result<ConfigHost>>> {659 let prefer = self660 .prefer_identities661 .iter()662 .filter_map(|v| v.as_host())663 .collect::<HashSet<_>>();664 let config = &self.config_field;665 let mut names = nix_go!(config.hosts).list_fields()?;666 names.retain(|s| filter(s));667 names.sort_by_key(|h| prefer.contains(h.as_str()));668669 Ok(names.into_iter().map(|h| self.host(&h)))670 }654671655 pub fn host(&self, name: &str) -> Result<ConfigHost> {672 pub fn host(&self, name: &str) -> Result<ConfigHost> {656 let config = &self.config_field;673 let config = &self.config_field;