git.delta.rocks / jrsonnet / refs/commits / 33f3601e24db

difftreelog

fix shared generator condition

ywyowvrkYaroslav Bolyukin2026-01-08parent: #45c49ea.patch.diff
in: trunk

10 files changed

modifiedcmds/fleet/src/cmds/build_systems.rsdiffbeforeafterboth
--- a/cmds/fleet/src/cmds/build_systems.rs
+++ b/cmds/fleet/src/cmds/build_systems.rs
@@ -114,7 +114,8 @@
 
 			set.spawn_local(
 				(async move {
-					let built = match build_task(config.clone(), hostname.clone(), "toplevel-fleet").await
+					let built = match build_task(config.clone(), hostname.clone(), "toplevel-fleet")
+						.await
 					{
 						Ok(path) => path,
 						Err(e) => {
modifiedcmds/fleet/src/cmds/secrets/mod.rsdiffbeforeafterboth
--- a/cmds/fleet/src/cmds/secrets/mod.rs
+++ b/cmds/fleet/src/cmds/secrets/mod.rs
@@ -632,23 +632,23 @@
 					let config = config.clone();
 					let data = config.shared_secret(&name).expect("exists");
 					/*
-					let definition = config.shared_secret_definition(&name)?;
-					let expectations = definition.expectations()?;
-					let owners = data
-						.owners()
-						.map(|o| {
-							if expectations.owners.contains(o) {
-								o.green().to_string()
-							} else {
-								o.red().to_string()
-							}
-						})
-						.collect::<Vec<_>>();
-					table.push(SecretDisplay {
-						owners: owners.join(", "),
-						name,
-					})
-*/
+										let definition = config.shared_secret_definition(&name)?;
+										let expectations = definition.expectations()?;
+										let owners = data
+											.owners()
+											.map(|o| {
+												if expectations.owners.contains(o) {
+													o.green().to_string()
+												} else {
+													o.red().to_string()
+												}
+											})
+											.collect::<Vec<_>>();
+										table.push(SecretDisplay {
+											owners: owners.join(", "),
+											name,
+										})
+					*/
 				}
 				// info!("loaded\n{}", Table::new(table).to_string())
 			}
modifiedcrates/fleet-base/src/fleetdata.rsdiffbeforeafterboth
--- a/crates/fleet-base/src/fleetdata.rs
+++ b/crates/fleet-base/src/fleetdata.rs
@@ -153,7 +153,7 @@
 	#[serde(flatten)]
 	pub secret: FleetSecretData,
 
-	#[serde(default, skip_serializing, alias="managed")]
+	#[serde(default, skip_serializing, alias = "managed")]
 	pub _deprecated_managed: bool,
 }
 
modifiedcrates/nix-eval/src/util.rsdiffbeforeafterboth
--- a/crates/nix-eval/src/util.rs
+++ b/crates/nix-eval/src/util.rs
@@ -1,15 +1,23 @@
 use std::time::Instant;
 
 use anyhow::bail;
+use serde::Deserialize;
 use tracing::{debug, warn};
 
 use crate::{Value, nix_go_json};
 
+#[derive(Deserialize, Debug)]
+struct Assertion {
+	assertion: bool,
+	message: String,
+}
+
 #[tracing::instrument(level = "info", skip(val))]
 pub async fn assert_warn(action: &str, val: &Value) -> anyhow::Result<()> {
 	let before_errors = Instant::now();
 	let errors: Vec<String> = nix_go_json!(val.errors);
-	debug!("errors evaluation took {:?}", before_errors.elapsed());
+	// let assertions: Vec<Assertion> = nix_go_json!(val.assertions);
+	debug!("errors evaluation took {:?} {errors:?} ", before_errors.elapsed());
 	if !errors.is_empty() {
 		bail!(
 			"failed with error{}{}",
modifiedcrates/nixlike/Cargo.tomldiffbeforeafterboth
--- a/crates/nixlike/Cargo.toml
+++ b/crates/nixlike/Cargo.toml
@@ -7,10 +7,10 @@
 [dependencies]
 thiserror.workspace = true
 
+itertools = "0.14.0"
 linked-hash-map = "0.5.6"
 peg = "0.8.5"
 ron = "0.11.0"
 serde = { version = "1.0.219", features = ["derive"] }
 serde-transcode = "1.1.1"
 serde_json = "1.0.140"
-itertools = "0.14.0"
modifiedlib/default.nixdiffbeforeafterboth
after · lib/default.nix
1# Shared functions for fleet configuration, available as `fleet` module argument2{ lib }:3let4  inherit (lib.trivial) isFunction functionArgs;5  inherit (lib.options) mkOption mergeOneOption;6  inherit (lib.modules) mkOverride;7  inherit (lib.types)8    listOf9    submodule10    attrsOf11    mkOptionType12    ;13  inherit (lib.strings) optionalString hasPrefix removePrefix;14in15rec {16  types = {17    overlay = mkOptionType {18      name = "nixpkgs-overlay";19      description = "nixpkgs overlay";20      check = {21        __functor = _self: isFunction;22        isV2MergeCoherent = true;23      };24      merge = mergeOneOption;25    };26    listOfOverlay = listOf types.overlay;2728    mkHostsType = module: attrsOf (submodule module);29    mkDataType = module: submodule module;30  };3132  options = {33    mkHostsOption =34      module:35      mkOption {36        type = types.mkHostsType module;37      };38    mkDataOption =39      module:40      mkOption {41        type = types.mkDataType module;42      };43  };4445  inherit (options) mkHostsOption;4647  modules = {48    /**49      Use in places, where fleet might know better than nixpkgs defaults to50    */51    mkFleetDefault = mkOverride 999;52    /**53      Some generators use mkDefault, but optionDefault is set by nixpkgs.54    */55    mkFleetGeneratorDefault = mkOverride 1001;56  };5758  inherit (modules) mkFleetDefault mkFleetGeneratorDefault;5960  secrets = {6162    /**63      Generate a random secret password, 32 ascii characters by default6465      Options:66        size: generated password length in ascii characters (bytes).67        noSymbols: by default, character set includes various special characters ($ , ! + * : ~), and might68                   not be accepted in some contexts, this option switches charset to just [A-Za-z0-9].6970      Output:71        Resulting secret has only part: secret, which contains encrypted password.72    */73    mkPassword =74      {75        size ? 32,76      }:77      (78        {79          coreutils,80          mkSecretGenerator,81        }:82        mkSecretGenerator {83          script = ''84            mkdir $out85            gh generate password -o $out/secret --size ${toString size}86          '';87          parts.secret.encrypted = true;88        }89      );9091    /**92      Generate a random ed25519 keypair9394      Options:95        noEmbedPublic: By default, secret key also embeds public key in itself ("extended" format, 64 bytes)96                       When noEmbedPublis is enabled - only the private scalar is included.97        encoding: Encoring of public and secret parts, can be "raw" (default), "base64" or "hex".9899      Output:100        Resulting secret has two parts: public and secret, where the secret part is encrypted.101102      This secret format is used by e.g Garage S3 server103    */104    mkEd25519 =105      {106        noEmbedPublic ? false,107        encoding ? null,108      }:109      (110        { mkSecretGenerator }:111        mkSecretGenerator {112          script = ''113            mkdir $out114            gh generate ed25519 -p $out/public -s $out/secret \115              ${optionalString noEmbedPublic "--no-embed-public"} \116              ${optionalString (encoding != null) "--encoding=${encoding}"}117          '';118          parts.secret.encrypted = true;119          parts.public.encrypted = false;120        }121      );122123    /**124      Generate a random x25519 keypair125126      Options:127        encoding: Encoring of public and secret parts, can be "raw" (default), "base64" or "hex".128129      Output:130        Resulting secret has two parts: public and secret, where the secret part is encrypted.131132      This secret format is used by e.g Wireguard VPN for peers (base64-encoded)133    */134    mkX25519 =135      {136        encoding ? null,137      }:138      (139        { mkSecretGenerator }:140        mkSecretGenerator {141          script = ''142            mkdir $out143            gh generate x25519 -p $out/public -s $out/secret \144              ${optionalString (encoding != null) "--encoding=${encoding}"}145          '';146147          parts.secret.encrypted = true;148          parts.public.encrypted = false;149        }150      );151152    mkAskPass =153      {154        prompt ? "Secret value",155        part ? "secret",156      }:157      (158        {159          kdePackages,160          mkImpureSecretGenerator,161        }:162        mkImpureSecretGenerator {163          # TODO: Escape prompt?164          script = ''165            ${kdePackages.kdialog}/bin/kdialog --inputbox "${prompt}" | gh private -o $out/${part}166          '';167168          parts.${part}.encrypted = true;169        }170      );171172    /**173      Generate a random RSA keypair174175      Options:176        size: RSA key size, 4096 by default177178      Output:179        Resulting secret has two parts: public and secret, where the secret part is encrypted.180        Both parts are PEM encoded.181    */182    mkRsa =183      {184        size ? 4096,185      }:186      (187        {188          openssl,189          mkSecretGenerator,190        }:191        mkSecretGenerator {192          script = ''193            mkdir $out194195            ${openssl}/bin/openssl genrsa -out rsa_private.key ${toString size}196            ${openssl}/bin/openssl rsa -in rsa_private.key -pubout -out rsa_public.key197198            cat rsa_private.key | gh private -o $out/secret199            cat rsa_public.key | gh public -o $out/public200          '';201202          parts.secret.encrypted = true;203          parts.public.encrypted = false;204        }205      );206207    /**208      Generate a random byte sequence209210      Options:211        size: generated password length in bytes, 32 by default.212        encoding: how the generated bytes should be encoded, "raw" (default), "hex" or "base64"213        noNuls: prevent output byte sequence from containing internal \0, useful for some C applications214                that can't handle their strings properly.215216      Output:217        Resulting secret has only part: secret, which contains encrypted bytes.218219      Might be used for e.g. Wireguard VPN PSK keys (base64-encoded)220    */221    mkBytes =222      {223        count ? 32,224        encoding,225        noNuls ? false,226      }:227      (228        { mkSecretGenerator }:229        mkSecretGenerator {230          script = ''231            mkdir $out232            gh generate bytes --count=${toString count} --encoding=${encoding} -o $out/secret \233              ${optionalString noNuls "--no-nuls"}234          '';235          parts.secret.encrypted = true;236        }237      );238    /**239      Shorthand for `mkBytes`, which defaults to "hex" encoding240    */241    mkHexBytes =242      {243        count ? 32,244      }:245      mkBytes {246        inherit count;247        encoding = "hex";248      };249    /**250      Shorthand for `mkBytes`, which defaults to "base64" encoding251    */252    mkBase64Bytes =253      {254        count ? 32,255      }:256      mkBytes {257        inherit count;258        encoding = "base64";259      };260261    # Wireguard262    # mkWireguard = {}: mkX25519 {encoding = "base64";};263    # mkWireguardPsk = {}: mkBase64Bytes {count = 32;};264  };265266  inherit (secrets)267    mkPassword268    mkEd25519269    mkX25519270    mkRsa271    mkBytes272    mkHexBytes273    mkBase64Bytes274    mkAskPass275    ;276277  strings =278    let279      plaintextPrefix = "<PLAINTEXT>";280      plaintextNewlinePrefix = "<PLAINTEXT-NL>";281    in282    {283      /**284        Decode public secret part into string285      */286      decodeRawSecret =287        raw:288        if hasPrefix plaintextPrefix raw then289          removePrefix plaintextPrefix raw290        else if hasPrefix plaintextNewlinePrefix raw then291          removePrefix plaintextNewlinePrefix raw292        else293          throw "decodeRawSecret only works with plaintext-encoded secret public parts, got ${raw}";294    };295296  inherit (strings) decodeRawSecret;297}
modifiedlib/flakePart.nixdiffbeforeafterboth
--- a/lib/flakePart.nix
+++ b/lib/flakePart.nix
@@ -34,7 +34,7 @@
           # to do that, evaluate all the modules with only needed option declared.
           bootstrapEval = lib.evalModules {
             class = "fleet";
-            prefix = ["fleetConfiguration"];
+            prefix = [ "fleetConfiguration" ];
             modules = [
               module
               {
@@ -53,7 +53,7 @@
           bootstrapNixpkgs = bootstrapEval.config.nixpkgs.buildUsing;
           normalEval = bootstrapNixpkgs.lib.evalModules {
             class = "fleet";
-            prefix = ["fleetConfiguration"];
+            prefix = [ "fleetConfiguration" ];
             modules = (import ../modules/module-list.nix) ++ [
               module
               (
modifiedmodules/nixos.nixdiffbeforeafterboth
--- a/modules/nixos.nix
+++ b/modules/nixos.nix
@@ -39,13 +39,23 @@
             in
             config.nixpkgs.buildUsing.lib.evalModules {
               class = "nixos";
-              prefix = ["fleetConfiguration" "hosts" hostArgs.config._module.args.name "nixos"];
+              prefix = [
+                "fleetConfiguration"
+                "hosts"
+                hostArgs.config._module.args.name
+                "nixos"
+              ];
               modules = (import "${modulesPath}/module-list.nix") ++ [
                 (module // { key = "attr<host.nixos>"; })
                 (config.nixos // { key = "attr<fleet.nixos>"; })
               ];
               specialArgs = {
-                inherit fleetLib inputs self modulesPath;
+                inherit
+                  fleetLib
+                  inputs
+                  self
+                  modulesPath
+                  ;
               };
             };
         };
@@ -54,32 +64,34 @@
         };
       };
       config = {
-        nixos = let 
-          inherit (hostArgs.config) system;
-        in {
-          _module.args = {
-            nixosHosts = mapAttrs (_: value: value.nixos_unchecked.config) config.hosts;
-            hosts = config.hosts;
-            host = hostArgs.config;
-            fleetConfiguration = config;
+        nixos =
+          let
+            inherit (hostArgs.config) system;
+          in
+          {
+            _module.args = {
+              nixosHosts = mapAttrs (_: value: value.nixos_unchecked.config) config.hosts;
+              hosts = config.hosts;
+              host = hostArgs.config;
+              fleetConfiguration = config;
 
-            inputs' = mapAttrs (
-              inputName: input:
-              builtins.addErrorContext
-                "while retrieving system-dependent attributes for input ${escapeNixIdentifier inputName}"
-                (
-                  if input._type or null == "flake" then
-                    _fleetFlakeRootConfig.perInput system input
-                  else
-                    "input is not a flake, perhaps flake = false was added to te input declaration?"
-                )
-            ) inputs;
-            self' = builtins.addErrorContext "while retrieving system-dependent attributes for a flake's own outputs" (
-              _fleetFlakeRootConfig.perInput system self
-            );
+              inputs' = mapAttrs (
+                inputName: input:
+                builtins.addErrorContext
+                  "while retrieving system-dependent attributes for input ${escapeNixIdentifier inputName}"
+                  (
+                    if input._type or null == "flake" then
+                      _fleetFlakeRootConfig.perInput system input
+                    else
+                      "input is not a flake, perhaps flake = false was added to te input declaration?"
+                  )
+              ) inputs;
+              self' = builtins.addErrorContext "while retrieving system-dependent attributes for a flake's own outputs" (
+                _fleetFlakeRootConfig.perInput system self
+              );
+            };
+            nixpkgs.hostPlatform = system;
           };
-          nixpkgs.hostPlatform = system;
-        };
         nixos_unchecked = hostArgs.config.nixos.extendModules {
           modules = [
             {
modifiedmodules/nixos/secrets.nixdiffbeforeafterboth
--- a/modules/nixos/secrets.nix
+++ b/modules/nixos/secrets.nix
@@ -77,7 +77,7 @@
     }:
     let
       secretName = config._module.args.name;
-      literal = l: enum [l];
+      literal = l: enum [ l ];
     in
     {
       options = {
@@ -109,17 +109,16 @@
       config = {
         # C api is broken in regard to thunks
         # https://github.com/NixOS/nix/issues/12800
-        parts = let 
-          hostName = host._module.args.name;
-          generator = config.generator;
-        in builtins.deepSeq [
-          hostName
-          secretName
-          generator
-        ] (builtins.fleetEnsureHostSecret
-          hostName
-          secretName
-          generator);
+        parts =
+          let
+            hostName = host._module.args.name;
+            generator = config.generator;
+          in
+          builtins.deepSeq [
+            hostName
+            secretName
+            generator
+          ] (builtins.fleetEnsureHostSecret hostName secretName generator);
       };
     }
   );
@@ -136,14 +135,16 @@
     secrets = mkOption {
       type = attrsOf secretType;
       default = { };
-      apply = mapAttrs (_: secret: secret.parts // {definition = secret;});
+      apply = mapAttrs (_: secret: secret.parts // { definition = secret; });
       description = "Host-local secrets";
     };
     system.secretsData = mkOption {
       type = unspecified;
-      default = mapAttrs (_: s:
-        (removeAttrs s.definition ["generator"]) // {
-          parts = mapAttrs (_: part: removeAttrs part ["data"]) s.definition.parts;
+      default = mapAttrs (
+        _: s:
+        (removeAttrs s.definition [ "generator" ])
+        // {
+          parts = mapAttrs (_: part: removeAttrs part [ "data" ]) s.definition.parts;
         }
       ) config.secrets;
       description = "secrets.json contents";
@@ -152,13 +153,25 @@
   config = {
     environment.systemPackages = [ pkgs.fleet-install-secrets ];
 
-    assertions = mapAttrsToList (name: secret: let
-      hasSharedDefinition = fleetConfiguration.secrets ? name;
-    in {
-      assertion = (secret.definition.generator == "shared") == hasSharedDefinition && hasSharedDefinition -> (elem host._module.args.name fleetConfiguration.secrets.${name}.expectedOwners);
-      message = if hasSharedDefinition then"secret ${name} has host-specific secret generator, secrets with host-specific generators can not have shared generator in fleet configuration"
-      else "secret ${name} is declared as shared, for shared secret fleet configuration should include shared secret generator, and expectedOwners should contain this host";
-    }) config.secrets;
+    assertions = mapAttrsToList (
+      name: secret:
+      let
+        hasSharedDefinition = fleetConfiguration.secrets ? ${name};
+      in
+      {
+        assertion =
+          (secret.definition.generator == "shared") == hasSharedDefinition
+          && (
+            hasSharedDefinition
+            -> (elem host._module.args.name fleetConfiguration.secrets.${name}.expectedOwners)
+          );
+        message =
+          if hasSharedDefinition then
+            "secret ${name} has host-specific secret generator, secrets with host-specific generators can not have shared generator in fleet configuration"
+          else
+            "secret ${name} is declared as shared, for shared secret fleet configuration should include shared secret generator, and expectedOwners should contain this host";
+      }
+    ) config.secrets;
 
     systemd.services.fleet-install-secrets = mkIf useSysusers {
       wantedBy = [ "sysinit.target" ];
modifiedmodules/nixos/top-level.nixdiffbeforeafterboth
--- a/modules/nixos/top-level.nix
+++ b/modules/nixos/top-level.nix
@@ -2,6 +2,7 @@
   pkgs,
   config,
   lib,
+  ...
 }:
 let
   inherit (lib.strings) optionalString;