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

difftreelog

feat mkAskEnv, mkAskFile

qzwzqzlnYaroslav Bolyukin2026-04-18parent: #186c645.patch.diff
in: trunk

1 file changed

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/part (preferrably just use env) to prevent shell injection164          script = ''165            mkdir $out166            ${kdePackages.kdialog}/bin/kdialog --inputbox "${prompt}" | gh private -o $out/${part}167          '';168169          parts.${part}.encrypted = true;170        }171      );172173    mkAskFile =174      {175        header ? "",176        part ? "secret",177      }:178      (179        {180          kdePackages,181          coreutils,182          mkImpureSecretGenerator,183        }:184        mkImpureSecretGenerator {185          script = ''186            mkdir $out187            tmpfile=$(${coreutils}/bin/mktemp)188            trap "${coreutils}/bin/rm -f $tmpfile" EXIT189            cat > "$tmpfile" <<'HEADER'190            ${header}191            HEADER192            ${kdePackages.kate}/bin/kate --startanon --new --block "$tmpfile"193            gh private -o $out/${part} < "$tmpfile"194          '';195196          parts.${part}.encrypted = true;197        }198      );199200    mkAskEnv =201      {202        header ? "",203        variables ? [ ],204        part ? "secret",205      }:206      mkAskFile {207        inherit part;208        header = builtins.concatStringsSep "\n" (209          (map (l: "# ${l}") (lib.splitString "\n" header))210          ++ (map (v: "${v}=") variables)211        );212      };213214    /**215      Generate a random RSA keypair216217      Options:218        size: RSA key size, 4096 by default219220      Output:221        Resulting secret has two parts: public and secret, where the secret part is encrypted.222        Both parts are PEM encoded.223    */224    mkRsa =225      {226        size ? 4096,227      }:228      (229        {230          openssl,231          mkSecretGenerator,232        }:233        mkSecretGenerator {234          script = ''235            mkdir $out236237            ${openssl}/bin/openssl genrsa -out rsa_private.key ${toString size}238            ${openssl}/bin/openssl rsa -in rsa_private.key -pubout -out rsa_public.key239240            cat rsa_private.key | gh private -o $out/secret241            cat rsa_public.key | gh public -o $out/public242          '';243244          parts.secret.encrypted = true;245          parts.public.encrypted = false;246        }247      );248249    /**250      Generate a random byte sequence251252      Options:253        size: generated password length in bytes, 32 by default.254        encoding: how the generated bytes should be encoded, "raw" (default), "hex" or "base64"255        noNuls: prevent output byte sequence from containing internal \0, useful for some C applications256                that can't handle their strings properly.257258      Output:259        Resulting secret has only part: secret, which contains encrypted bytes.260261      Might be used for e.g. Wireguard VPN PSK keys (base64-encoded)262    */263    mkBytes =264      {265        count ? 32,266        encoding,267        noNuls ? false,268      }:269      (270        { mkSecretGenerator }:271        mkSecretGenerator {272          script = ''273            mkdir $out274            gh generate bytes --count=${toString count} --encoding=${encoding} -o $out/secret \275              ${optionalString noNuls "--no-nuls"}276          '';277          parts.secret.encrypted = true;278        }279      );280    /**281      Shorthand for `mkBytes`, which defaults to "hex" encoding282    */283    mkHexBytes =284      {285        count ? 32,286      }:287      mkBytes {288        inherit count;289        encoding = "hex";290      };291    /**292      Shorthand for `mkBytes`, which defaults to "base64" encoding293    */294    mkBase64Bytes =295      {296        count ? 32,297      }:298      mkBytes {299        inherit count;300        encoding = "base64";301      };302303    # Wireguard304    # mkWireguard = {}: mkX25519 {encoding = "base64";};305    # mkWireguardPsk = {}: mkBase64Bytes {count = 32;};306  };307308  inherit (secrets)309    mkPassword310    mkEd25519311    mkX25519312    mkRsa313    mkBytes314    mkHexBytes315    mkBase64Bytes316    mkAskPass317    mkAskFile318    mkAskEnv319    ;320321  strings =322    let323      plaintextPrefix = "<PLAINTEXT>";324      plaintextNewlinePrefix = "<PLAINTEXT-NL>";325    in326    {327      /**328        Decode public secret part into string329      */330      decodeRawSecret =331        raw:332        if hasPrefix plaintextPrefix raw then333          removePrefix plaintextPrefix raw334        else if hasPrefix plaintextNewlinePrefix raw then335          removePrefix plaintextNewlinePrefix raw336        else337          throw "decodeRawSecret only works with plaintext-encoded secret public parts, got ${raw}";338    };339340  inherit (strings) decodeRawSecret;341}