git.delta.rocks / jrsonnet / refs/commits / 5fd92ba91a91

difftreelog

feat non-shared secrets

Yaroslav Bolyukin2021-10-01parent: #076f1fe.patch.diff
in: trunk

2 files changed

modifiedcmds/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,
+						},
 					},
 				);
 			}
modifiedcmds/fleet/src/fleetdata.rsdiffbeforeafterboth
before · cmds/fleet/src/fleetdata.rs
1use 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}