git.delta.rocks / jrsonnet / refs/commits / f31248fac9ac

difftreelog

feat use builtin for getting secret

lyunptusYaroslav Bolyukin2026-01-22parent: #c810e3a.patch.diff
in: trunk

12 files changed

modifiedCargo.lockdiffbeforeafterboth
722source = "registry+https://github.com/rust-lang/crates.io-index"722source = "registry+https://github.com/rust-lang/crates.io-index"
723checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"723checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
724
725[[package]]
726name = "convert_case"
727version = "0.7.1"
728source = "registry+https://github.com/rust-lang/crates.io-index"
729checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7"
730dependencies = [
731 "unicode-segmentation",
732]
733724
734[[package]]725[[package]]
735name = "cookie-factory"726name = "cookie-factory"
764 "cfg-if",755 "cfg-if",
765]756]
766
767[[package]]
768name = "crossterm"
769version = "0.29.0"
770source = "registry+https://github.com/rust-lang/crates.io-index"
771checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b"
772dependencies = [
773 "bitflags",
774 "crossterm_winapi",
775 "derive_more",
776 "document-features",
777 "filedescriptor",
778 "mio",
779 "parking_lot",
780 "rustix 1.1.2",
781 "signal-hook",
782 "signal-hook-mio",
783 "winapi",
784]
785
786[[package]]
787name = "crossterm_winapi"
788version = "0.9.1"
789source = "registry+https://github.com/rust-lang/crates.io-index"
790checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
791dependencies = [
792 "winapi",
793]
794757
795[[package]]758[[package]]
796name = "crypto-common"759name = "crypto-common"
934 "serde_core",897 "serde_core",
935]898]
936
937[[package]]
938name = "derive_more"
939version = "2.0.1"
940source = "registry+https://github.com/rust-lang/crates.io-index"
941checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678"
942dependencies = [
943 "derive_more-impl",
944]
945
946[[package]]
947name = "derive_more-impl"
948version = "2.0.1"
949source = "registry+https://github.com/rust-lang/crates.io-index"
950checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
951dependencies = [
952 "convert_case",
953 "proc-macro2",
954 "quote",
955 "syn",
956]
957899
958[[package]]900[[package]]
959name = "digest"901name = "digest"
978 "syn",920 "syn",
979]921]
980
981[[package]]
982name = "document-features"
983version = "0.2.11"
984source = "registry+https://github.com/rust-lang/crates.io-index"
985checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d"
986dependencies = [
987 "litrs",
988]
989922
990[[package]]923[[package]]
991name = "ed25519"924name = "ed25519"
1072source = "registry+https://github.com/rust-lang/crates.io-index"1005source = "registry+https://github.com/rust-lang/crates.io-index"
1073checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"1006checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
1074
1075[[package]]
1076name = "filedescriptor"
1077version = "0.8.3"
1078source = "registry+https://github.com/rust-lang/crates.io-index"
1079checksum = "e40758ed24c9b2eeb76c35fb0aebc66c626084edd827e07e1552279814c6682d"
1080dependencies = [
1081 "libc",
1082 "thiserror 1.0.69",
1083 "winapi",
1084]
10851007
1086[[package]]1008[[package]]
1087name = "find-crate"1009name = "find-crate"
1128 "chrono",1050 "chrono",
1129 "clap",1051 "clap",
1130 "clap_complete",1052 "clap_complete",
1131 "crossterm",
1132 "fleet-base",1053 "fleet-base",
1133 "fleet-shared",1054 "fleet-shared",
1134 "futures",1055 "futures",
1142 "openssh",1063 "openssh",
1143 "opentelemetry",1064 "opentelemetry",
1144 "opentelemetry_sdk",1065 "opentelemetry_sdk",
1145 "owo-colors",
1146 "peg",1066 "peg",
1147 "regex",1067 "regex",
1148 "serde",1068 "serde",
1502source = "registry+https://github.com/rust-lang/crates.io-index"1422source = "registry+https://github.com/rust-lang/crates.io-index"
1503checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"1423checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
1504
1505[[package]]
1506name = "hermit-abi"
1507version = "0.5.2"
1508source = "registry+https://github.com/rust-lang/crates.io-index"
1509checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
15101424
1511[[package]]1425[[package]]
1512name = "hex"1426name = "hex"
1963 "serde",1877 "serde",
1964]1878]
1965
1966[[package]]
1967name = "is-terminal"
1968version = "0.4.16"
1969source = "registry+https://github.com/rust-lang/crates.io-index"
1970checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9"
1971dependencies = [
1972 "hermit-abi",
1973 "libc",
1974 "windows-sys 0.59.0",
1975]
1976
1977[[package]]
1978name = "is_ci"
1979version = "1.2.0"
1980source = "registry+https://github.com/rust-lang/crates.io-index"
1981checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45"
19821879
1983[[package]]1880[[package]]
1984name = "is_terminal_polyfill"1881name = "is_terminal_polyfill"
2084source = "registry+https://github.com/rust-lang/crates.io-index"1981source = "registry+https://github.com/rust-lang/crates.io-index"
2085checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"1982checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
2086
2087[[package]]
2088name = "litrs"
2089version = "0.4.2"
2090source = "registry+https://github.com/rust-lang/crates.io-index"
2091checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed"
20921983
2093[[package]]1984[[package]]
2094name = "lock_api"1985name = "lock_api"
2161checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"2052checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
2162dependencies = [2053dependencies = [
2163 "libc",2054 "libc",
2164 "log",
2165 "wasi 0.11.1+wasi-snapshot-preview1",2055 "wasi 0.11.1+wasi-snapshot-preview1",
2166 "windows-sys 0.59.0",2056 "windows-sys 0.59.0",
2167]2057]
2427 "thiserror 2.0.17",2317 "thiserror 2.0.17",
2428]2318]
2429
2430[[package]]
2431name = "owo-colors"
2432version = "4.2.3"
2433source = "registry+https://github.com/rust-lang/crates.io-index"
2434checksum = "9c6901729fa79e91a0913333229e9ca5dc725089d1c363b2f4b4760709dc4a52"
2435dependencies = [
2436 "supports-color 2.1.0",
2437 "supports-color 3.0.2",
2438]
24392319
2440[[package]]2320[[package]]
2441name = "papergrid"2321name = "papergrid"
3336source = "registry+https://github.com/rust-lang/crates.io-index"3216source = "registry+https://github.com/rust-lang/crates.io-index"
3337checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"3217checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
3338
3339[[package]]
3340name = "signal-hook"
3341version = "0.3.18"
3342source = "registry+https://github.com/rust-lang/crates.io-index"
3343checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
3344dependencies = [
3345 "libc",
3346 "signal-hook-registry",
3347]
3348
3349[[package]]
3350name = "signal-hook-mio"
3351version = "0.2.4"
3352source = "registry+https://github.com/rust-lang/crates.io-index"
3353checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd"
3354dependencies = [
3355 "libc",
3356 "mio",
3357 "signal-hook",
3358]
33593218
3360[[package]]3219[[package]]
3361name = "signal-hook-registry"3220name = "signal-hook-registry"
3448source = "registry+https://github.com/rust-lang/crates.io-index"3307source = "registry+https://github.com/rust-lang/crates.io-index"
3449checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"3308checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
3450
3451[[package]]
3452name = "supports-color"
3453version = "2.1.0"
3454source = "registry+https://github.com/rust-lang/crates.io-index"
3455checksum = "d6398cde53adc3c4557306a96ce67b302968513830a77a95b2b17305d9719a89"
3456dependencies = [
3457 "is-terminal",
3458 "is_ci",
3459]
3460
3461[[package]]
3462name = "supports-color"
3463version = "3.0.2"
3464source = "registry+https://github.com/rust-lang/crates.io-index"
3465checksum = "c64fc7232dd8d2e4ac5ce4ef302b1d81e0b80d055b9d77c7c4f51f6aa4c867d6"
3466dependencies = [
3467 "is_ci",
3468]
34693309
3470[[package]]3310[[package]]
3471name = "syn"3311name = "syn"
4169source = "registry+https://github.com/rust-lang/crates.io-index"4009source = "registry+https://github.com/rust-lang/crates.io-index"
4170checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"4010checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
4171
4172[[package]]
4173name = "unicode-segmentation"
4174version = "1.12.0"
4175source = "registry+https://github.com/rust-lang/crates.io-index"
4176checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
41774011
4178[[package]]4012[[package]]
4179name = "unicode-width"4013name = "unicode-width"
4450 "rustix 0.38.44",4284 "rustix 0.38.44",
4451]4285]
4452
4453[[package]]
4454name = "winapi"
4455version = "0.3.9"
4456source = "registry+https://github.com/rust-lang/crates.io-index"
4457checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
4458dependencies = [
4459 "winapi-i686-pc-windows-gnu",
4460 "winapi-x86_64-pc-windows-gnu",
4461]
4462
4463[[package]]
4464name = "winapi-i686-pc-windows-gnu"
4465version = "0.4.0"
4466source = "registry+https://github.com/rust-lang/crates.io-index"
4467checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
44684286
4469[[package]]4287[[package]]
4470name = "winapi-util"4288name = "winapi-util"
4475 "windows-sys 0.61.2",4293 "windows-sys 0.61.2",
4476]4294]
4477
4478[[package]]
4479name = "winapi-x86_64-pc-windows-gnu"
4480version = "0.4.0"
4481source = "registry+https://github.com/rust-lang/crates.io-index"
4482checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
44834295
4484[[package]]4296[[package]]
4485name = "windows-core"4297name = "windows-core"
modifiedREADME.adocdiffbeforeafterboth
211 ];211 ];
212 # And finally, I have secrets, which are shared between machines.212 # And finally, I have secrets, which are shared between machines.
213 # Note that this example is somewhat wrong, as this goes not into the machine configuration, but to fleet configuration.213 # Note that this example is somewhat wrong, as this goes not into the machine configuration, but to fleet configuration.
214 sharedSecrets = {214 secrets = {
215 "ca.pem" = {215 "ca.pem" = {
216 # This is just the public key, no need to regenerate it to change owner list216 # This is just the public key, no need to regenerate it to change owner list
217 regenerateOnOwnerAdded = false;217 regenerateOnOwnerAdded = false;
modifiedcmds/fleet/Cargo.tomldiffbeforeafterboth
28async-trait = "0.1"28async-trait = "0.1"
29base64 = "0.22.1"29base64 = "0.22.1"
30chrono = { version = "0.4", features = ["serde"] }30chrono = { version = "0.4", features = ["serde"] }
31crossterm = { version = "0.29.0", features = ["use-dev-tty"] }
32futures = "0.3"31futures = "0.3"
33hostname = "0.4.1"32hostname = "0.4.1"
34itertools = "0.14"33itertools = "0.14"
35openssh = "0.11"34openssh = "0.11"
36owo-colors = { version = "4.2", features = ["supports-color", "supports-colors"] }
37peg = "0.8"35peg = "0.8"
38regex = "1.11"36regex = "1.11"
39shlex = "1.3"37shlex = "1.3"
modifiedcmds/fleet/src/cmds/secrets/mod.rsdiffbeforeafterboth
11 fleetdata::{FleetSecretData, FleetSecretDistribution, FleetSecretPart, encrypt_secret_data},11 fleetdata::{FleetSecretData, FleetSecretDistribution, FleetSecretPart, encrypt_secret_data},
12 host::Config,12 host::Config,
13 opts::FleetOpts,13 opts::FleetOpts,
14 secret::{Expectations, RegenerationReason, SharedSecretDefinition, secret_needs_regeneration},14 secret::{Expectations, RegenerationReason, secret_needs_regeneration},
15};15};
16use fleet_shared::SecretData;16use fleet_shared::SecretData;
17use nix_eval::{NixType, Value, nix_go, nix_go_json};17use nix_eval::{NixType, Value, nix_go, nix_go_json};
18use owo_colors::OwoColorize;
19use serde::Deserialize;18use serde::Deserialize;
20use tabled::{Table, Tabled};19use tabled::{Table, Tabled};
21use tokio::{fs::read, task::spawn_blocking};20use tokio::{fs::read, task::spawn_blocking};
69 },68 },
70}69}
7170
72#[allow(clippy::too_many_arguments)]71/*
73#[tracing::instrument(skip(config, secret, definition, prefer_identities))]72#[allow(clippy::too_many_arguments)]
74async fn maybe_regenerate_shared_secret(73#[tracing::instrument(skip(config, secret, definition, prefer_identities))]
75 secret_name: &str,74async fn maybe_regenerate_shared_secret(
76 config: &Config,75 secret_name: &str,
77 mut secret: FleetSecretDistribution,76 config: &Config,
78 definition: SharedSecretDefinition,77 mut secret: FleetSecretDistribution,
79 prefer_identities: &[String],78 definition: SharedSecretDefinition,
80 expectations: &Expectations,79 prefer_identities: &[String],
81) -> Result<FleetSecretDistribution> {80 expectations: &Expectations,
82 let reason = secret_needs_regeneration(&secret.secret, &secret.owners, expectations);81) -> Result<FleetSecretDistribution> {
83 let value = definition.definition_value();82 let reason = secret_needs_regeneration(&secret.secret, &secret.owners, expectations);
8483 let value = definition.definition_value();
85 let (should_reencrypt, reason) = match reason {84
86 Some(RegenerationReason::OwnersAdded(_)) => {85 let (should_reencrypt, reason) = match reason {
87 // Secret always needs to be reencrypted for new owners to be able to read it86 Some(RegenerationReason::OwnersAdded(_)) => {
88 (87 // Secret always needs to be reencrypted for new owners to be able to read it
89 true,88 (
90 if nix_go_json!(value.regenerateOnOwnerAdded) {89 true,
91 reason90 if nix_go_json!(value.regenerateOnOwnerAdded) {
92 } else {91 reason
93 None92 } else {
94 },93 None
95 )94 },
96 }95 )
97 Some(RegenerationReason::OwnersRemoved(_)) => {96 }
98 // No need to reencrypt, we can just leave stanzas in place.97 Some(RegenerationReason::OwnersRemoved(_)) => {
99 if nix_go_json!(value.regenerateOnOwnerRemoved) {98 // No need to reencrypt, we can just leave stanzas in place.
100 (true, reason)99 if nix_go_json!(value.regenerateOnOwnerRemoved) {
101 } else {100 (true, reason)
102 (false, None)101 } else {
103 }102 (false, None)
104 }103 }
105 Some(_) => (true, reason),104 }
106 None => (false, None),105 Some(_) => (true, reason),
107 };106 None => (false, None),
108107 };
109 if let Some(reason) = reason {108
110 info!("secret needs to be regenerated: {reason}");109 if let Some(reason) = reason {
111 let generated = generate_shared(config, secret_name, definition, expectations).await?;110 info!("secret needs to be regenerated: {reason}");
112 Ok(generated)111 let generated = generate_shared(config, secret_name, definition, expectations).await?;
113 } else if should_reencrypt {112 Ok(generated)
114 info!("secret needs to be reencrypted");113 } else if should_reencrypt {
115 let identity_holder = if !prefer_identities.is_empty() {114 info!("secret needs to be reencrypted");
116 prefer_identities115 let identity_holder = if !prefer_identities.is_empty() {
117 .iter()116 prefer_identities
118 .find(|i| secret.owners.iter().any(|s| s == *i))117 .iter()
119 } else {118 .find(|i| secret.owners.iter().any(|s| s == *i))
120 secret.owners.first()119 } else {
121 };120 secret.owners.first()
122 let Some(identity_holder) = identity_holder else {121 };
123 bail!("no available holder found");122 let Some(identity_holder) = identity_holder else {
124 };123 bail!("no available holder found");
125124 };
126 for (part_name, part) in secret.secret.parts.iter_mut() {125
127 let _span = info_span!("part reencryption", part_name);126 for (part_name, part) in secret.secret.parts.iter_mut() {
128 if !part.raw.encrypted {127 let _span = info_span!("part reencryption", part_name);
129 continue;128 if !part.raw.encrypted {
130 }129 continue;
131 let host = config.host(identity_holder).await?;130 }
132 let encrypted = host131 let host = config.host(identity_holder).await?;
133 .reencrypt(132 let encrypted = host
134 part.raw.clone(),133 .reencrypt(
135 expectations.owners.iter().cloned().collect(),134 part.raw.clone(),
136 )135 expectations.owners.iter().cloned().collect(),
137 .await?;136 )
138 part.raw = encrypted;137 .await?;
139 }138 part.raw = encrypted;
140 secret.owners = expectations.owners.clone();139 }
141 Ok(secret)140 secret.owners = expectations.owners.clone();
142 } else {141 Ok(secret)
143 Ok(secret)142 } else {
144 }143 Ok(secret)
145}144 }
145}
146*/
146147
147#[derive(Deserialize)]148#[derive(Deserialize)]
148#[serde(rename_all = "camelCase")]149#[serde(rename_all = "camelCase")]
314 }315 }
315 }316 }
316}317}
317async fn generate_shared(318/*
318 config: &Config,319async fn generate_shared(
319 display_name: &str,320 config: &Config,
320 secret: SharedSecretDefinition,321 display_name: &str,
321 expectations: &Expectations,322 secret: SharedSecretDefinition,
322) -> Result<FleetSecretDistribution> {323 expectations: &Expectations,
323 // let owners: Vec<String> = nix_go_json!(secret.expectedOwners);324) -> Result<FleetSecretDistribution> {
324 Ok(FleetSecretDistribution {325 // let owners: Vec<String> = nix_go_json!(secret.expectedOwners);
325 managed: Some(true),326 Ok(FleetSecretDistribution {
326 secret: generate(327 managed: Some(true),
327 config,328 secret: generate(
328 display_name,329 config,
329 secret.definition_value(),330 display_name,
330 expectations,331 secret.definition_value(),
331 )332 expectations,
332 .await?,333 )
333 owners: expectations.owners.clone(),334 .await?,
334 })335 owners: expectations.owners.clone(),
335}336 })
337}*/
336338
337async fn parse_public(339async fn parse_public(
338 public: Option<String>,340 public: Option<String>,
625 #[tabled(rename = "Owners")]627 #[tabled(rename = "Owners")]
626 owners: String,628 owners: String,
627 }629 }
628 let mut table = vec![];630 // let mut table = vec![];
629 for name in configured.iter().cloned() {631 for name in configured.iter().cloned() {
630 let config = config.clone();632 let config = config.clone();
631 let data = config.shared_secret(&name).expect("exists");633 let data = config.shared_secret(&name).expect("exists");
632 let definition = config.shared_secret_definition(&name)?;634 /*
633 let expectations = definition.expectations()?;635 let definition = config.shared_secret_definition(&name)?;
634 let owners = data636 let expectations = definition.expectations()?;
635 .owners()637 let owners = data
636 .map(|o| {638 .owners()
637 if expectations.owners.contains(o) {639 .map(|o| {
638 o.green().to_string()640 if expectations.owners.contains(o) {
639 } else {641 o.green().to_string()
640 o.red().to_string()642 } else {
641 }643 o.red().to_string()
642 })644 }
643 .collect::<Vec<_>>();645 })
644 table.push(SecretDisplay {646 .collect::<Vec<_>>();
645 owners: owners.join(", "),647 table.push(SecretDisplay {
646 name,648 owners: owners.join(", "),
647 })649 name,
650 })
651*/
648 }652 }
649 info!("loaded\n{}", Table::new(table).to_string())653 // info!("loaded\n{}", Table::new(table).to_string())
650 }654 }
651 Secret::Edit {655 Secret::Edit {
652 name,656 name,
modifiedcrates/fleet-base/src/host.rsdiffbeforeafterboth
23use crate::{23use crate::{
24 command::MyCommand,24 command::MyCommand,
25 fleetdata::{FleetData, FleetSecretData, FleetSecretDistribution, FleetSecretDistributions},25 fleetdata::{FleetData, FleetSecretData, FleetSecretDistribution, FleetSecretDistributions},
26 secret::{HostSecretDefinition, SharedSecretDefinition},
27};26};
2827
29pub struct FleetConfigInternals {28pub struct FleetConfigInternals {
30 /// Fleet project directory, containing fleet.nix file.29 /// Fleet project directory, containing fleet.nix file.
31 pub directory: PathBuf,30 pub directory: PathBuf,
32 /// builtins.currentSystem31 /// builtins.currentSystem
33 pub local_system: String,32 pub local_system: String,
34 pub data: Mutex<FleetData>,33 pub data: Arc<Mutex<FleetData>>,
35 pub nix_args: Vec<OsString>,34 pub nix_args: Vec<OsString>,
36 /// fleet_config.config35 /// fleet_config.config
37 pub config_field: Value,36 pub config_field: Value,
521 let secrets = nix_go!(nixos.secrets);520 let secrets = nix_go!(nixos.secrets);
522 secrets.list_fields()521 secrets.list_fields()
523 }522 }
524 pub fn secret_definition(&self, name: &str) -> Result<HostSecretDefinition> {
525 let nixos = self.nixos_unchecked_config()?;
526 Ok(HostSecretDefinition(
527 self.name.clone(),
528 nix_go!(nixos.secrets[{ name }]),
529 ))
530 }
531523
532 /// Packages for this host, resolved with nixpkgs overlays524 /// Packages for this host, resolved with nixpkgs overlays
533 pub async fn pkgs(&self) -> Result<Value> {525 pub async fn pkgs(&self) -> Result<Value> {
666 let data = self.data();658 let data = self.data();
667 data.secrets.get(secret).cloned()659 data.secrets.get(secret).cloned()
668 }660 }
669 pub fn shared_secret_definition(&self, secret: &str) -> Result<SharedSecretDefinition> {
670 let config_field = &self.config_field;
671 Ok(SharedSecretDefinition(nix_go!(
672 config_field.sharedSecrets[{ secret }]
673 )))
674 }
675661
676 // TODO: Should this be something modifiable from other processes?662 // TODO: Should this be something modifiable from other processes?
677 // E.g terraform provider might want to update FleetData (e.g secrets),663 // E.g terraform provider might want to update FleetData (e.g secrets),
modifiedcrates/fleet-base/src/opts.rsdiffbeforeafterboth
211 }211 }
212 let bytes =212 let bytes =
213 std::fs::read_to_string(&fleet_data_path).context("reading fleet state (fleet.nix)")?;213 std::fs::read_to_string(&fleet_data_path).context("reading fleet state (fleet.nix)")?;
214 let data = Mutex::new(FleetData::from_str(&bytes)?);214 let data = Arc::new(Mutex::new(FleetData::from_str(&bytes)?));
215215
216 let mut fetch_settings = FetchSettings::new();216 let mut fetch_settings = FetchSettings::new();
217 fetch_settings.set(c"warn-dirty", c"false");217 fetch_settings.set(c"warn-dirty", c"false");
239 let builtins_field = Value::eval("builtins")?;239 let builtins_field = Value::eval("builtins")?;
240240
241 let fleet_root = flake.get_field("fleetConfigurations")?;241 let fleet_root = flake.get_field("fleetConfigurations")?;
242 let data_val = Value::serialized(&data)?;
243 let fleet_field = nix_go!(fleet_root.default(data_val));242 let fleet_field = nix_go!(fleet_root.default(Obj {}));
244243
245 let config_field = nix_go!(fleet_field.config);244 let config_field = nix_go!(fleet_field.config);
246245
modifiedcrates/fleet-base/src/primops.rsdiffbeforeafterboth
1use std::collections::HashMap;
2use std::sync::{Arc, Mutex};
3
1use nix_eval::NativeFn;4use nix_eval::{NativeFn, Value};
5
6use crate::fleetdata::{FleetData, FleetSecrets};
27
3#[derive(thiserror::Error, Debug)]8#[derive(thiserror::Error, Debug)]
4enum Error {}9enum Error {}
19
20}
2124
22pub fn init_primops() {25pub fn init_primops(secrets: Arc<Mutex<FleetData>>) {
23 NativeFn::new(26 NativeFn::new(
24 c"fleet_ensure_secret",27 c"fleet_ensure_host_secret",
25 c"Ensure secret existence for a host, regenerating it in case of some mismatch",28 c"Ensure secret existence for a host, regenerating it in case of some mismatch",
26 [29 [c"host", c"secret", c"generator"],
27 c"host",
28 c"secret",
29 c"expected_parts",
30 c"expected_encrypted_parts",
31 c"generator",
32 ],
33 |[30 |[host, secret, generator]| {
34 host,
35 secret,
36 expected_parts,
37 expected_encrypted_parts,
38 generator,
39 ]| {
40
41 todo!()31 todo!("ensure secret");
32 Ok(Value::new_attrs(HashMap::from_iter([(
33 "raw",
34 Value::new_str("rawData"),
35 )])))
42 },36 },
43 )37 )
44 .register();38 .register();
39 NativeFn::new(
40 c"fleet_ensure_host_secret",
41 c"Ensure secret existence for a host, regenerating it in case of some mismatch",
42 [c"host", c"secret", c"generator"],
43 |[host, secret, generator]| {
44 todo!("ensure secret");
45 Ok(Value::new_attrs(HashMap::from_iter([(
46 "raw",
47 Value::new_str("rawData"),
48 )])))
49 },
50 )
51 .register();
45}52}
4653
modifiedcrates/fleet-base/src/secret.rsdiffbeforeafterboth
1use std::collections::BTreeSet;1use std::collections::BTreeSet;
22
3use anyhow::Result;
4use chrono::{DateTime, Utc};3use chrono::{DateTime, Utc};
5use nix_eval::{Value, nix_go, nix_go_json};
64
7use crate::fleetdata::FleetSecretData;5use crate::fleetdata::FleetSecretData;
86
14 pub private_parts: BTreeSet<String>,12 pub private_parts: BTreeSet<String>,
15}13}
16
17pub struct HostSecretDefinition(pub(crate) String, pub(crate) Value);
18impl HostSecretDefinition {
19 pub fn is_managed(&self) -> Result<bool> {
20 let def = self.definition_value()?;
21 Ok(!nix_go!(def.generator).is_null())
22 }
23 pub fn is_shared(&self) -> Result<bool> {
24 let def = self.definition_value()?;
25 Ok(nix_go_json!(def.shared))
26 }
27 pub fn expectations(&self) -> Result<Expectations> {
28 let def = self.definition_value()?;
29 let parts = nix_go!(def.parts);
30
31 let mut public_parts = BTreeSet::new();
32 let mut private_parts = BTreeSet::new();
33 for part in parts.list_fields()? {
34 if nix_go_json!(parts[&part].encrypted) {
35 private_parts.insert(part.clone());
36 } else {
37 public_parts.insert(part.clone());
38 }
39 }
40
41 Ok(Expectations {
42 owners: BTreeSet::from([self.0.clone()]),
43 generation_data: nix_go_json!(def.expectedGenerationData),
44 public_parts,
45 private_parts,
46 })
47 }
48 pub fn definition_value(&self) -> Result<Value> {
49 let value = &self.1;
50 Ok(nix_go!(value.definition))
51 }
52}
53
54pub struct SharedSecretDefinition(pub(crate) Value);
55impl SharedSecretDefinition {
56 pub fn is_managed(&self) -> Result<bool> {
57 let value = &self.0;
58 Ok(!nix_go!(value.generator).is_null())
59 }
60 pub fn expectations(&self) -> Result<Expectations> {
61 let value = &self.0;
62 Ok(Expectations {
63 owners: nix_go_json!(value.expectedOwners),
64 generation_data: nix_go_json!(value.expectedGenerationData),
65 public_parts: nix_go_json!(value.expectedPublicParts),
66 private_parts: nix_go_json!(value.expectedPrivateParts),
67 })
68 }
69 pub fn definition_value(&self) -> Value {
70 self.0.clone()
71 }
72}
7314
74#[derive(thiserror::Error, Debug)]15#[derive(thiserror::Error, Debug)]
75pub enum RegenerationReason {16pub enum RegenerationReason {
modifiedmodules/module-list.nixdiffbeforeafterboth
6 ./nixos.nix6 ./nixos.nix
7 ./nixpkgs.nix7 ./nixpkgs.nix
8 ./secrets.nix8 ./secrets.nix
9 ./secrets-data.nix
10]9]
1110
modifiedmodules/nixos/secrets.nixdiffbeforeafterboth
8let8let
9 inherit (builtins)9 inherit (builtins)
10 hashString10 hashString
11 elemAt
12 length
13 toJSON11 toJSON
14 filter
15 ;12 ;
16 inherit (lib.stringsWithDeps) stringAfter;13 inherit (lib.stringsWithDeps) stringAfter;
17 inherit (lib.options) mkOption literalExpression;14 inherit (lib.options) mkOption literalExpression;
18 inherit (lib.lists) optional;15 inherit (lib.lists) optional;
19 inherit (lib.attrsets) mapAttrs mapAttrsToList;16 inherit (lib.attrsets) mapAttrs;
20 inherit (lib.modules) mkIf mkMerge;17 inherit (lib.modules) mkIf;
21 inherit (lib.types)18 inherit (lib.types)
22 submodule19 submodule
23 str20 str
24 attrsOf21 attrsOf
25 nullOr22 nullOr
26 unspecified23 unspecified
27 lazyAttrsOf
28 uniq24 uniq
29 functionTo25 functionTo
30 package26 package
31 listOf
32 bool
33 ;27 ;
34 inherit (fleetLib.strings) decodeRawSecret;28 inherit (fleetLib.strings) decodeRawSecret;
3529
36 sysConfig = config;30 sysConfig = config;
37 secretPartDataType = submodule {
38 options = {
39 raw = mkOption {
40 type = str;
41 internal = true;
42 description = "Encoded & Encrypted secret part data, passed from fleet.nix";
43 };
44 };
45 };
46 secretDataType = submodule {
47 freeformType = lazyAttrsOf secretPartDataType;
48 options = {
49 shared = mkOption {
50 description = "Is this secret owned by this machine, or propagated from shared secrets";
51 default = false;
52 };
53 };
54 };
55 secretPartType =31 secretPartType =
56 secretName:32 secretName:
57 submodule (33 submodule (
61 in37 in
62 {38 {
63 options = {39 options = {
64 encrypted = mkOption {
65 type = bool;
66 description = "Is this secret part supposed to be encrypted?";
67 };
68
69 hash = mkOption {40 hash = mkOption {
70 type = str;41 type = str;
82 type = str;53 type = str;
83 description = "Secret public data (only available for plaintext)";54 description = "Secret public data (only available for plaintext)";
84 };55 };
56 raw = mkOption {
57 type = str;
58 description = "Raw (encoded/encrypted secret part data)";
59 };
85 };60 };
86 config =61 config = {
87 let
88 raw = sysConfig.data.secrets.${secretName}.${partName}.raw;
89 in
90 {
91 hash = hashString "sha1" raw;62 hash = hashString "sha1" config.raw;
92 data = decodeRawSecret raw;63 data = decodeRawSecret config.raw;
93 path = "/run/secrets/${secretName}/${config.hash}-${partName}";64 path = "/run/secrets/${secretName}/${config.hash}-${partName}";
94 stablePath = "/run/secrets/${secretName}/${partName}";65 stablePath = "/run/secrets/${secretName}/${partName}";
95 };66 };
105 in76 in
106 {77 {
107 options = {78 options = {
108 shared = mkOption {
109 type = bool;
110 description = "Was this secret propagated from a shared secret?";
111 };
112 parts = mkOption {79 parts = mkOption {
113 type = lazyAttrsOf (secretPartType secretName);80 type = attrsOf (secretPartType secretName);
114 description = "Definition of secret parts";81 description = "Definition of secret parts";
115 default = { };
116 };82 };
117 generator = mkOption {83 generator = mkOption {
118 type = uniq (nullOr (functionTo package));84 type = uniq (nullOr (functionTo package));
135 default = sysConfig.users.users.${config.owner}.group;101 default = sysConfig.users.users.${config.owner}.group;
136 defaultText = literalExpression "config.users.users.$${owner}.group";102 defaultText = literalExpression "config.users.users.$${owner}.group";
137 };103 };
138 expectedGenerationData = mkOption {
139 type = unspecified;
140 description = "Data that gets embedded into secret part";
141 default = null;
142 };
143 };104 };
144 config = {105 config = {
145 shared = (sysConfig.data.secrets.${secretName} or { shared = false; }).shared;106 parts = builtins.fleet_ensure_host_secret sysConfig.networking.hostName secretName config.generator;
146 parts = mkMerge [
147 (mkIf (config.generator != null)
148 (
149 # Get fake derivation body, in future it should be implemented the same way as in Rust.
150 lib.callPackageWith (
151 pkgs
152 // {
153 mkSecretGenerator = pkgs.stdenv.mkDerivation;
154 mkImpureSecretGenerator = pkgs.stdenv.mkDerivation;
155 }
156 ) config.generator { }
157 ).parts
158 )
159 (mapAttrs (_: _: { }) (
160 removeAttrs (sysConfig.data.secrets.${secretName} or { }) [
161 "shared"
162 "managed"
163 ]
164 ))
165 ];
166 };107 };
167 }108 }
168 );109 );
169 processPart = secretName: partName: part: {
170 inherit (part) path stablePath;
171 raw = config.data.secrets.${secretName}.${partName}.raw;
172 };
173 processSecret = secretName: secret: {
174 inherit (secret.definition) group mode owner;
175 parts = (mapAttrs (processPart secretName) (secret.definition.parts));
176 };
177 secretsData = (mapAttrs (processSecret) config.secrets);110 secretsData = (mapAttrs (_: s: s.definition) config.secrets);
178 secretsFile = pkgs.writeTextFile {111 secretsFile = pkgs.writeTextFile {
179 name = "secrets.json";112 name = "secrets.json";
180 text = toJSON secretsData;113 text = toJSON secretsData;
185in118in
186{119{
187 options = {120 options = {
188 data.secrets = mkOption {
189 type = attrsOf secretDataType;
190 default = { };
191 description = "Host-local secret data";
192 };
193 secrets = mkOption {121 secrets = mkOption {
194 type = attrsOf secretType;122 type = attrsOf secretType;
195 default = { };123 default = { };
deletedmodules/secrets-data.nixdiffbeforeafterboth

no changes

modifiedmodules/secrets.nixdiffbeforeafterboth
5let5let
6 inherit (lib.options) mkOption literalExpression;6 inherit (lib.options) mkOption literalExpression;
7 inherit (lib.types)7 inherit (lib.types)
8 unspecified
9 nullOr8 nullOr
10 listOf9 listOf
11 str10 str
68 '';67 '';
69 default = null;68 default = null;
70 };69 };
71 expectedGenerationData = mkOption {
72 type = unspecified;
73 description = "Contextual metadata embedded within the secret part value";
74 default = null;
75 };
76 expectedPrivateParts = mkOption {
77 type = listOf str;
78 default = [ ];
79 description = "List of parts that are expected to be encrypted";
80 };
81 expectedPublicParts = mkOption {
82 type = listOf str;
83 default = [ ];
84 description = "List of parts that are expected to be public";
85 };
86 };70 };
87 };71 };
88in72in