difftreelog
feat non-shared secrets
in: trunk
2 files changed
cmds/fleet/src/cmds/secrets/mod.rsdiffbeforeafterboth--- a/cmds/fleet/src/cmds/secrets/mod.rs
+++ b/cmds/fleet/src/cmds/secrets/mod.rs
@@ -1,6 +1,12 @@
-use crate::{fleetdata::FleetSecret, host::Config};
+use crate::{
+ fleetdata::{FleetSecret, FleetSharedSecret},
+ host::Config,
+};
use anyhow::{bail, Result};
-use std::io::{self, Cursor, Read};
+use std::{
+ io::{self, Cursor, Read},
+ path::PathBuf,
+};
use structopt::StructOpt;
#[derive(StructOpt)]
@@ -8,7 +14,7 @@
/// Force load keys for all defined hosts
ForceKeys,
/// Add secret, data should be provided in stdin
- Add {
+ AddShared {
/// Secret name
name: String,
/// Secret owners
@@ -18,7 +24,23 @@
force: bool,
#[structopt(long)]
public: Option<String>,
+ #[structopt(long)]
+ public_file: Option<PathBuf>,
},
+ /// Add secret, data should be provided in stdin
+ Add {
+ /// Secret name
+ name: String,
+ /// Secret owners
+ machine: String,
+ /// Override secret if already present
+ #[structopt(long)]
+ force: bool,
+ #[structopt(long)]
+ public: Option<String>,
+ #[structopt(long)]
+ public_file: Option<PathBuf>,
+ },
}
impl Secrets {
@@ -32,11 +54,12 @@
config.key(&host)?;
}
}
- Secrets::Add {
+ Secrets::AddShared {
machines,
name,
force,
public,
+ public_file,
} => {
let recipients = machines
.iter()
@@ -61,16 +84,66 @@
};
let mut data = config.data_mut();
- if data.secrets.contains_key(&name) && !force {
+ if data.shared_secrets.contains_key(&name) && !force {
bail!("secret already defined");
}
- data.secrets.insert(
+ data.shared_secrets.insert(
name,
- FleetSecret {
+ FleetSharedSecret {
owners: machines,
+ secret: FleetSecret {
+ expire_at: None,
+ secret,
+ public: match (public, public_file) {
+ (Some(v), None) => Some(v),
+ (None, Some(v)) => Some(std::fs::read_to_string(&v)?),
+ (Some(_), Some(_)) => {
+ bail!("only public or public_file should be set")
+ }
+ (None, None) => None,
+ },
+ },
+ },
+ );
+ }
+ Secrets::Add {
+ machine,
+ name,
+ force,
+ public,
+ public_file,
+ } => {
+ let recipient = config.recipient(&machine)?;
+
+ let secret = {
+ let mut input = vec![];
+ io::stdin().read_to_end(&mut input)?;
+
+ let mut encrypted = vec![];
+ let recipient = Box::new(recipient) as Box<dyn age::Recipient>;
+ let mut encryptor = age::Encryptor::with_recipients(vec![recipient])
+ .wrap_output(&mut encrypted)?;
+ io::copy(&mut Cursor::new(input), &mut encryptor)?;
+ encryptor.finish()?;
+ encrypted
+ };
+
+ let mut data = config.data_mut();
+ let host_secrets = data.host_secrets.entry(machine).or_default();
+ if host_secrets.contains_key(&name) && !force {
+ bail!("secret already defined");
+ }
+ host_secrets.insert(
+ name,
+ FleetSecret {
expire_at: None,
secret,
- public,
+ public: match (public, public_file) {
+ (Some(v), None) => Some(v),
+ (None, Some(v)) => Some(std::fs::read_to_string(&v)?),
+ (Some(_), Some(_)) => bail!("only public or public_file should be set"),
+ (None, None) => None,
+ },
},
);
}
cmds/fleet/src/fleetdata.rsdiffbeforeafterboth1use chrono::{DateTime, Utc};2use serde::{Deserialize, Deserializer, Serialize, Serializer};3use std::collections::BTreeMap;45#[derive(Serialize, Deserialize, Default)]6#[serde(rename_all = "camelCase")]7pub struct HostData {8 #[serde(default)]9 #[serde(skip_serializing_if = "String::is_empty")]10 pub encryption_key: String,11}1213#[derive(Serialize, Deserialize)]14pub struct FleetData {15 #[serde(default)]16 pub hosts: BTreeMap<String, HostData>,17 #[serde(default)]18 #[serde(skip_serializing_if = "BTreeMap::is_empty")]19 pub secrets: BTreeMap<String, FleetSecret>,20}2122#[derive(Serialize, Deserialize)]23#[serde(rename_all = "camelCase")]24pub struct FleetSecret {25 pub owners: Vec<String>,26 #[serde(default)]27 #[serde(skip_serializing_if = "Option::is_none")]28 pub expire_at: Option<DateTime<Utc>>,29 #[serde(skip_serializing_if = "Option::is_none")]30 pub public: Option<String>,31 #[serde(serialize_with = "as_z85", deserialize_with = "from_z85")]32 pub secret: Vec<u8>,33}3435fn as_z85<S>(key: &[u8], serializer: S) -> Result<S::Ok, S::Error>36where37 S: Serializer,38{39 serializer.serialize_str(&z85::encode(&key))40}4142fn from_z85<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>43where44 D: Deserializer<'de>,45{46 use serde::de::Error;47 String::deserialize(deserializer)48 .and_then(|string| z85::decode(&string).map_err(|err| Error::custom(err.to_string())))49}