difftreelog
feat manager identities
in: trunk
7 files changed
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -92,6 +92,8 @@
"scrypt",
"sha2",
"subtle",
+ "which",
+ "wsl",
"x25519-dalek",
"zeroize",
]
@@ -111,6 +113,7 @@
"rand 0.8.5",
"secrecy",
"sha2",
+ "tempfile",
]
[[package]]
@@ -1286,6 +1289,15 @@
]
[[package]]
+name = "home"
+version = "0.5.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf"
+dependencies = [
+ "windows-sys 0.59.0",
+]
+
+[[package]]
name = "hostname"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3639,6 +3651,18 @@
]
[[package]]
+name = "which"
+version = "4.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
+dependencies = [
+ "either",
+ "home",
+ "once_cell",
+ "rustix 0.38.40",
+]
+
+[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3776,6 +3800,12 @@
]
[[package]]
+name = "wsl"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8dab7ac864710bdea6594becbea5b5050333cf34fefb0dc319567eb347950d4"
+
+[[package]]
name = "x25519-dalek"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
Cargo.tomldiffbeforeafterboth--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,7 +11,7 @@
nix-eval = { path = "./crates/nix-eval" }
nixlike = { path = "./crates/nixlike" }
-age = { version = "0.11", features = ["ssh"] }
+age = { version = "0.11", features = ["ssh", "plugin"] }
anyhow = "1.0"
clap = { version = "4.5", features = ["derive", "env", "unicode", "wrap_help"] }
clap_complete = "4.5"
cmds/fleet/src/cmds/secrets/mod.rsdiffbeforeafterboth--- a/cmds/fleet/src/cmds/secrets/mod.rs
+++ b/cmds/fleet/src/cmds/secrets/mod.rs
@@ -23,6 +23,7 @@
#[derive(Parser)]
pub enum Secret {
+ AddManager,
/// Force load host keys for all defined hosts
ForceKeys,
/// Add secret, data should be provided in stdin
@@ -521,6 +522,9 @@
impl Secret {
pub async fn run(self, config: &Config, opts: &FleetOpts) -> Result<()> {
match self {
+ Secret::AddManager => {
+ todo!("part of fleet-pusher")
+ }
Secret::ForceKeys => {
for host in config.list_hosts().await? {
if opts.should_skip(&host).await? {
crates/fleet-base/src/fleetdata.rsdiffbeforeafterboth1use std::{2 collections::BTreeMap,3 io::{self, Cursor},4};56use age::Recipient;7use chrono::{DateTime, Utc};8use fleet_shared::SecretData;9use rand::{10 distr::{Alphanumeric, SampleString as _},11 rng,12};13use serde::{Deserialize, Serialize, de::Error};14use serde_json::Value;1516#[derive(Serialize, Deserialize, Default)]17#[serde(rename_all = "camelCase")]18pub struct HostData {19 #[serde(default)]20 #[serde(skip_serializing_if = "String::is_empty")]21 pub encryption_key: String,22}2324const VERSION: &str = "0.1.0";25pub struct FleetDataVersion;26impl Serialize for FleetDataVersion {27 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>28 where29 S: serde::Serializer,30 {31 VERSION.serialize(serializer)32 }33}34impl<'de> Deserialize<'de> for FleetDataVersion {35 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>36 where37 D: serde::Deserializer<'de>,38 {39 let version = String::deserialize(deserializer)?;40 if version != VERSION {41 return Err(D::Error::custom(format!(42 "fleet.nix data version mismatch, expected {VERSION}, got {version}.\nFollow the docs for migration instruction"43 )));44 }45 Ok(Self)46 }47}4849fn generate_gc_prefix() -> String {50 let id = Alphanumeric.sample_string(&mut rng(), 8);51 format!("fleet-gc-{id}")52}5354#[derive(Serialize, Deserialize)]55#[serde(rename_all = "camelCase")]56pub struct FleetData {57 pub version: FleetDataVersion,58 #[serde(default = "generate_gc_prefix")]59 pub gc_root_prefix: String,6061 #[serde(default)]62 pub hosts: BTreeMap<String, HostData>,63 #[serde(default)]64 #[serde(skip_serializing_if = "BTreeMap::is_empty")]65 pub shared_secrets: BTreeMap<String, FleetSharedSecret>,66 #[serde(default)]67 #[serde(skip_serializing_if = "BTreeMap::is_empty")]68 pub host_secrets: BTreeMap<String, BTreeMap<String, FleetSecret>>,6970 // extra_name => anything71 #[serde(default)]72 #[serde(skip_serializing_if = "BTreeMap::is_empty")]73 pub extra: BTreeMap<String, Value>,74}7576#[derive(Serialize, Deserialize, Clone)]77#[serde(rename_all = "camelCase")]78#[must_use]79pub struct FleetSharedSecret {80 pub owners: Vec<String>,81 #[serde(flatten)]82 pub secret: FleetSecret,83}8485/// Returns None if recipients.is_empty()86pub fn encrypt_secret_data<'a>(87 recipients: impl IntoIterator<Item = &'a dyn Recipient>,88 data: Vec<u8>,89) -> Option<SecretData> {90 let mut encrypted = vec![];91 let mut encryptor = age::Encryptor::with_recipients(recipients.into_iter())92 .ok()?93 .wrap_output(&mut encrypted)94 .expect("in memory write");95 io::copy(&mut Cursor::new(data), &mut encryptor).expect("in memory copy");96 encryptor.finish().expect("in memory flush");97 Some(SecretData {98 data: encrypted,99 encrypted: true,100 })101}102103#[derive(Serialize, Deserialize, Clone)]104pub struct FleetSecretPart {105 pub raw: SecretData,106}107108#[derive(Serialize, Deserialize, Clone)]109#[serde(rename_all = "camelCase")]110#[must_use]111pub struct FleetSecret {112 #[serde(default = "Utc::now")]113 pub created_at: DateTime<Utc>,114 #[serde(default)]115 #[serde(skip_serializing_if = "Option::is_none", alias = "expire_at")]116 pub expires_at: Option<DateTime<Utc>>,117118 #[serde(flatten)]119 pub parts: BTreeMap<String, FleetSecretPart>,120121 #[serde(default)]122 #[serde(skip_serializing_if = "Value::is_null")]123 pub generation_data: Value,124}1use std::{2 collections::BTreeMap,3 io::{self, Cursor},4};56use age::Recipient;7use chrono::{DateTime, Utc};8use fleet_shared::SecretData;9use rand::{10 distr::{Alphanumeric, SampleString as _},11 rng,12};13use serde::{Deserialize, Serialize, de::Error};14use serde_json::Value;1516#[derive(Serialize, Deserialize, Default)]17#[serde(rename_all = "camelCase")]18pub struct HostData {19 #[serde(default)]20 #[serde(skip_serializing_if = "String::is_empty")]21 pub encryption_key: String,22}2324const VERSION: &str = "0.1.0";25pub struct FleetDataVersion;26impl Serialize for FleetDataVersion {27 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>28 where29 S: serde::Serializer,30 {31 VERSION.serialize(serializer)32 }33}34impl<'de> Deserialize<'de> for FleetDataVersion {35 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>36 where37 D: serde::Deserializer<'de>,38 {39 let version = String::deserialize(deserializer)?;40 if version != VERSION {41 return Err(D::Error::custom(format!(42 "fleet.nix data version mismatch, expected {VERSION}, got {version}.\nFollow the docs for migration instruction"43 )));44 }45 Ok(Self)46 }47}4849fn generate_gc_prefix() -> String {50 let id = Alphanumeric.sample_string(&mut rng(), 8);51 format!("fleet-gc-{id}")52}5354#[derive(Serialize, Deserialize)]55#[serde(rename_all = "camelCase")]56pub struct ManagerKey {57 pub name: String,58 pub key: String,59}6061#[derive(Serialize, Deserialize)]62#[serde(rename_all = "camelCase")]63pub struct FleetData {64 pub version: FleetDataVersion,65 #[serde(default = "generate_gc_prefix")]66 pub gc_root_prefix: String,6768 #[serde(default)]69 pub manager_keys: Vec<ManagerKey>,7071 #[serde(default)]72 pub hosts: BTreeMap<String, HostData>,73 #[serde(default)]74 #[serde(skip_serializing_if = "BTreeMap::is_empty")]75 pub shared_secrets: BTreeMap<String, FleetSharedSecret>,76 #[serde(default)]77 #[serde(skip_serializing_if = "BTreeMap::is_empty")]78 pub host_secrets: BTreeMap<String, BTreeMap<String, FleetSecret>>,7980 // extra_name => anything81 #[serde(default)]82 #[serde(skip_serializing_if = "BTreeMap::is_empty")]83 pub extra: BTreeMap<String, Value>,84}8586#[derive(Serialize, Deserialize, Clone)]87#[serde(rename_all = "camelCase")]88#[must_use]89pub struct FleetSharedSecret {90 pub owners: Vec<String>,91 #[serde(flatten)]92 pub secret: FleetSecret,93}9495/// Returns None if recipients.is_empty()96pub fn encrypt_secret_data<'a>(97 recipients: impl IntoIterator<Item = &'a dyn Recipient>,98 data: Vec<u8>,99) -> Option<SecretData> {100 let mut encrypted = vec![];101 let mut encryptor = age::Encryptor::with_recipients(recipients.into_iter())102 .ok()?103 .wrap_output(&mut encrypted)104 .expect("in memory write");105 io::copy(&mut Cursor::new(data), &mut encryptor).expect("in memory copy");106 encryptor.finish().expect("in memory flush");107 Some(SecretData {108 data: encrypted,109 encrypted: true,110 })111}112113#[derive(Serialize, Deserialize, Clone)]114pub struct FleetSecretPart {115 pub raw: SecretData,116}117118#[derive(Serialize, Deserialize, Clone)]119#[serde(rename_all = "camelCase")]120#[must_use]121pub struct FleetSecret {122 #[serde(default = "Utc::now")]123 pub created_at: DateTime<Utc>,124 #[serde(default)]125 #[serde(skip_serializing_if = "Option::is_none", alias = "expire_at")]126 pub expires_at: Option<DateTime<Utc>>,127128 #[serde(flatten)]129 pub parts: BTreeMap<String, FleetSecretPart>,130131 #[serde(default)]132 #[serde(skip_serializing_if = "Value::is_null")]133 pub generation_data: Value,134}flake.lockdiffbeforeafterboth--- a/flake.lock
+++ b/flake.lock
@@ -2,11 +2,11 @@
"nodes": {
"crane": {
"locked": {
- "lastModified": 1750266157,
- "narHash": "sha256-tL42YoNg9y30u7zAqtoGDNdTyXTi8EALDeCB13FtbQA=",
+ "lastModified": 1753316655,
+ "narHash": "sha256-tzWa2kmTEN69OEMhxFy+J2oWSvZP5QhEgXp3TROOzl0=",
"owner": "ipetkov",
"repo": "crane",
- "rev": "e37c943371b73ed87faf33f7583860f81f1d5a48",
+ "rev": "f35a3372d070c9e9ccb63ba7ce347f0634ddf3d2",
"type": "github"
},
"original": {
@@ -22,11 +22,11 @@
]
},
"locked": {
- "lastModified": 1749398372,
- "narHash": "sha256-tYBdgS56eXYaWVW3fsnPQ/nFlgWi/Z2Ymhyu21zVM98=",
+ "lastModified": 1753121425,
+ "narHash": "sha256-TVcTNvOeWWk1DXljFxVRp+E0tzG1LhrVjOGGoMHuXio=",
"owner": "hercules-ci",
"repo": "flake-parts",
- "rev": "9305fe4e5c2a6fcf5ba6a3ff155720fbe4076569",
+ "rev": "644e0fc48951a860279da645ba77fe4a6e814c5e",
"type": "github"
},
"original": {
@@ -37,11 +37,11 @@
},
"nixpkgs": {
"locked": {
- "lastModified": 1750895632,
- "narHash": "sha256-EPZWiRmaSTxoBArK5dQyRlSNVLXiBt2hmsYIPgMf3zk=",
+ "lastModified": 1753320130,
+ "narHash": "sha256-KCuv6iYQ0XTVAEJvDLIsk99CJm7fuqIE0/KknyeYPtM=",
"owner": "nixos",
"repo": "nixpkgs",
- "rev": "6ac57ce7fee0d80226095a57ccb7519855ad7c5e",
+ "rev": "788cc7374af486168b8aab6ca49e316c03508a86",
"type": "github"
},
"original": {
@@ -68,11 +68,11 @@
]
},
"locked": {
- "lastModified": 1750819193,
- "narHash": "sha256-XvkupGPZqD54HuKhN/2WhbKjAHeTl1UEnWspzUzRFfA=",
+ "lastModified": 1753238793,
+ "narHash": "sha256-jmQeEpgX+++MEgrcikcwoSiI7vDZWLP0gci7XiWb9uQ=",
"owner": "oxalica",
"repo": "rust-overlay",
- "rev": "1ba3b9c59b68a4b00156827ad46393127b51b808",
+ "rev": "0ad7ab4ca8e83febf147197e65c006dff60623ab",
"type": "github"
},
"original": {
@@ -103,11 +103,11 @@
]
},
"locked": {
- "lastModified": 1749194973,
- "narHash": "sha256-eEy8cuS0mZ2j/r/FE0/LYBSBcIs/MKOIVakwHVuqTfk=",
+ "lastModified": 1753006367,
+ "narHash": "sha256-tzbhc4XttkyEhswByk5R38l+ztN9UDbnj0cTcP6Hp9A=",
"owner": "numtide",
"repo": "treefmt-nix",
- "rev": "a05be418a1af1198ca0f63facb13c985db4cb3c5",
+ "rev": "421b56313c65a0815a52b424777f55acf0b56ddf",
"type": "github"
},
"original": {
modules/secrets-data.nixdiffbeforeafterboth--- a/modules/secrets-data.nix
+++ b/modules/secrets-data.nix
@@ -94,12 +94,28 @@
};
config = { };
};
+ managerKey = {
+ options = {
+ name = mkOption {
+ type = str;
+ description = "Who does this manager key belongs to.";
+ };
+ key = mkOption {
+ type = str;
+ description = "Age-compatible key";
+ };
+ };
+ config = {};
+ };
in
{
options.data = mkDataOption (
{ config, ... }:
{
options = {
+ managerKeys = mkOption {
+ type = listOf (submodule managerKey);
+ };
sharedSecrets = mkOption {
type = attrsOf (submodule sharedSecretData);
default = { };
rust-toolchain.tomldiffbeforeafterboth--- a/rust-toolchain.toml
+++ b/rust-toolchain.toml
@@ -1,3 +1,3 @@
[toolchain]
-channel = "1.86.0"
+channel = "nightly-2025-06-10"
components = ["rustfmt", "clippy", "rust-analyzer", "rust-src"]