feat: try rework
This commit is contained in:
parent
1ddbd3b8b6
commit
ecf10628c3
51 changed files with 1941 additions and 445 deletions
163
intermediate/secrets.nix
Normal file
163
intermediate/secrets.nix
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
let
|
||||
lib = import <nixpkgs/lib>;
|
||||
secretsConfig = (import ../validation/secrets.nix).getSecretsConfig (import ../config/secrets.nix);
|
||||
|
||||
isSopsEncrypted = filePath:
|
||||
let
|
||||
content = builtins.readFile filePath;
|
||||
in
|
||||
lib.hasInfix "\nsops:" content
|
||||
|| lib.hasInfix "\n\"sops\":" content
|
||||
|| lib.hasInfix "\"sops\":" content
|
||||
|| builtins.substring 0 5 content == "sops:";
|
||||
|
||||
extractJsonKeys = filePath:
|
||||
let
|
||||
content = builtins.readFile (builtins.toString filePath);
|
||||
parsed = builtins.fromJSON content;
|
||||
|
||||
recurseKeys = prefix: obj:
|
||||
if builtins.isAttrs obj then
|
||||
lib.concatMap (name:
|
||||
if name == "sops" then
|
||||
[]
|
||||
else
|
||||
let
|
||||
newPrefix = if prefix == "" then name else "${prefix}.${name}";
|
||||
in
|
||||
recurseKeys newPrefix obj.${name}
|
||||
) (builtins.attrNames obj)
|
||||
else
|
||||
[ prefix ];
|
||||
in
|
||||
recurseKeys "" parsed;
|
||||
|
||||
getRuntimePath = path:
|
||||
"/run/secrets/${builtins.concatStringsSep "_" path}";
|
||||
|
||||
defaultMetadata = {
|
||||
path = null;
|
||||
owner = null;
|
||||
group = null;
|
||||
mode = null;
|
||||
};
|
||||
|
||||
# Step 1: Convert path/string leaves to a normalized attrset shape.
|
||||
normalizeLeaf = path: node:
|
||||
if builtins.isString node || builtins.isPath node then
|
||||
{
|
||||
file = node;
|
||||
keys = null;
|
||||
metadata = defaultMetadata;
|
||||
}
|
||||
else if builtins.isAttrs node && node ? file then
|
||||
{
|
||||
file = node.file;
|
||||
keys = node.keys or null;
|
||||
metadata = {
|
||||
path = node.path or null;
|
||||
owner = node.owner or null;
|
||||
group = node.group or null;
|
||||
mode = node.mode or null;
|
||||
};
|
||||
}
|
||||
else
|
||||
throw "Invalid secret leaf at ${builtins.concatStringsSep "." path}: must be string, path, or attrset with 'file'";
|
||||
|
||||
# Step 2: Flatten normalized leaf structure into entries.
|
||||
flattenTree = path: node:
|
||||
if builtins.isAttrs node && !(node ? file) then
|
||||
lib.concatMap (name:
|
||||
flattenTree (path ++ [ name ]) node.${name}
|
||||
) (builtins.attrNames node)
|
||||
else
|
||||
let
|
||||
normalized = normalizeLeaf path node;
|
||||
in
|
||||
[ {
|
||||
inherit path;
|
||||
file = normalized.file;
|
||||
keys = normalized.keys;
|
||||
metadata = normalized.metadata;
|
||||
} ];
|
||||
|
||||
entries = flattenTree [] secretsConfig;
|
||||
|
||||
# Step 3a: Look for keys when type is JSON and keys are not explicitly provided.
|
||||
enrichedEntries = map (entry:
|
||||
let
|
||||
isJson = builtins.match ".*\\.json$" (builtins.toString entry.file) != null;
|
||||
extractedKeys = if isJson && entry.keys == null then extractJsonKeys entry.file else null;
|
||||
in
|
||||
entry // { keys = if extractedKeys != null then extractedKeys else entry.keys; }
|
||||
) entries;
|
||||
|
||||
isReady = entry:
|
||||
if !builtins.pathExists entry.file then
|
||||
false
|
||||
else if builtins.match ".*\\.(ya?ml|json)$" (builtins.toString entry.file) != null then
|
||||
true
|
||||
else
|
||||
isSopsEncrypted entry.file;
|
||||
|
||||
readyEntries = builtins.filter isReady enrichedEntries;
|
||||
missingEntries = builtins.filter (entry: !(isReady entry)) enrichedEntries;
|
||||
|
||||
# Step 3b: Expand key lists to per-secret entries and build sops.secrets attrset.
|
||||
mkSopsSecrets = sourceEntries:
|
||||
let
|
||||
expanded = lib.concatMap (entry:
|
||||
if entry.keys != null && entry.keys != [] then
|
||||
map (key: entry // { singleKey = key; }) entry.keys
|
||||
else
|
||||
[ (entry // { singleKey = null; }) ]
|
||||
) sourceEntries;
|
||||
|
||||
mkEntry = entry:
|
||||
let
|
||||
normalizedKeyName = if entry.singleKey != null then
|
||||
builtins.replaceStrings [ "." ] [ "_" ] entry.singleKey
|
||||
else
|
||||
null;
|
||||
|
||||
secretName = if entry.singleKey != null then
|
||||
builtins.concatStringsSep "_" (entry.path ++ [ normalizedKeyName ])
|
||||
else
|
||||
builtins.concatStringsSep "_" entry.path;
|
||||
|
||||
isJson = builtins.match ".*\\.json$" (builtins.toString entry.file) != null;
|
||||
isYaml = builtins.match ".*\\.ya?ml$" (builtins.toString entry.file) != null;
|
||||
|
||||
sopsKey =
|
||||
if entry.singleKey == null then
|
||||
null
|
||||
else
|
||||
builtins.replaceStrings [ "." ] [ "/" ] entry.singleKey;
|
||||
in
|
||||
{
|
||||
name = secretName;
|
||||
value = {
|
||||
sopsFile = entry.file;
|
||||
# Compatibility: current sops-install-secrets key traversal expects
|
||||
# YAML map shape for nested key extraction. JSON documents are valid
|
||||
# YAML, so parse .json via "yaml" to support nested keys.
|
||||
format = if isJson || isYaml then "yaml" else "binary";
|
||||
path = if entry.metadata.path != null then
|
||||
entry.metadata.path
|
||||
else
|
||||
getRuntimePath (entry.path ++ lib.optional (normalizedKeyName != null) normalizedKeyName);
|
||||
owner = if entry.metadata.owner != null then entry.metadata.owner else "root";
|
||||
group = if entry.metadata.group != null then entry.metadata.group else "root";
|
||||
mode = if entry.metadata.mode != null then entry.metadata.mode else "0400";
|
||||
} // lib.optionalAttrs (sopsKey != null) {
|
||||
key = sopsKey;
|
||||
};
|
||||
};
|
||||
in
|
||||
builtins.listToAttrs (map mkEntry expanded);
|
||||
in
|
||||
{
|
||||
source = secretsConfig;
|
||||
byName = mkSopsSecrets readyEntries;
|
||||
missing = missingEntries;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue