summary refs log tree commit diff
path: root/nixos/lib/utils.nix
diff options
context:
space:
mode:
authortalyz <kim.lindberger@gmail.com>2019-09-06 16:38:41 +0200
committertalyz <kim.lindberger@gmail.com>2019-09-06 16:56:25 +0200
commit64358cb0e9dce1d2032e4feaae02f8f59d892b9d (patch)
treee236bde270afad05813affc46d745f1c4766063a /nixos/lib/utils.nix
parentb351454cac1eca1dc76e54e880e86d7c2bb21ffa (diff)
downloadnixpkgs-64358cb0e9dce1d2032e4feaae02f8f59d892b9d.tar
nixpkgs-64358cb0e9dce1d2032e4feaae02f8f59d892b9d.tar.gz
nixpkgs-64358cb0e9dce1d2032e4feaae02f8f59d892b9d.tar.bz2
nixpkgs-64358cb0e9dce1d2032e4feaae02f8f59d892b9d.tar.lz
nixpkgs-64358cb0e9dce1d2032e4feaae02f8f59d892b9d.tar.xz
nixpkgs-64358cb0e9dce1d2032e4feaae02f8f59d892b9d.tar.zst
nixpkgs-64358cb0e9dce1d2032e4feaae02f8f59d892b9d.zip
nixos/utils: Handle arbitrary secrets in JSON output files
Introduce new functions which allows modules to define options where,
if the input is an attrset and the output is JSON, the user can define
arbitrary secrets.
Diffstat (limited to 'nixos/lib/utils.nix')
-rw-r--r--nixos/lib/utils.nix112
1 files changed, 112 insertions, 0 deletions
diff --git a/nixos/lib/utils.nix b/nixos/lib/utils.nix
index b68e55a40b9..a522834e429 100644
--- a/nixos/lib/utils.nix
+++ b/nixos/lib/utils.nix
@@ -24,4 +24,116 @@ rec {
       throw "${shell} is not a shell package"
     else
       shell;
+
+  /* Recurse into a list or an attrset, searching for attrs named like
+     the value of the "attr" parameter, and return an attrset where the
+     names are the corresponding jq path where the attrs were found and
+     the values are the values of the attrs.
+
+     Example:
+       recursiveGetAttrWithJqPrefix {
+         example = [
+           {
+             irrelevant = "not interesting";
+           }
+           {
+             ignored = "ignored attr";
+             relevant = {
+               secret = {
+                 _secret = "/path/to/secret";
+               };
+             };
+           }
+         ];
+       } "_secret" -> { ".example[1].relevant.secret" = "/path/to/secret"; }
+  */
+  recursiveGetAttrWithJqPrefix = item: attr:
+    let
+      recurse = prefix: item:
+        if item ? ${attr} then
+          nameValuePair prefix item.${attr}
+        else if isAttrs item then
+          map (name: recurse (prefix + "." + name) item.${name}) (attrNames item)
+        else if isList item then
+          imap0 (index: item: recurse (prefix + "[${toString index}]") item) item
+        else
+          [];
+    in listToAttrs (flatten (recurse "" item));
+
+  /* Takes an attrset and a file path and generates a bash snippet that
+     outputs a JSON file at the file path with all instances of
+
+     { _secret = "/path/to/secret" }
+
+     in the attrset replaced with the contents of the file
+     "/path/to/secret" in the output JSON.
+
+     When a configuration option accepts an attrset that is finally
+     converted to JSON, this makes it possible to let the user define
+     arbitrary secret values.
+
+     Example:
+       If the file "/path/to/secret" contains the string
+       "topsecretpassword1234",
+
+       genJqSecretsReplacementSnippet {
+         example = [
+           {
+             irrelevant = "not interesting";
+           }
+           {
+             ignored = "ignored attr";
+             relevant = {
+               secret = {
+                 _secret = "/path/to/secret";
+               };
+             };
+           }
+         ];
+       } "/path/to/output.json"
+
+       would generate a snippet that, when run, outputs the following
+       JSON file at "/path/to/output.json":
+
+       {
+         "example": [
+           {
+             "irrelevant": "not interesting"
+           },
+           {
+             "ignored": "ignored attr",
+             "relevant": {
+               "secret": "topsecretpassword1234"
+             }
+           }
+         ]
+       }
+  */
+  genJqSecretsReplacementSnippet = genJqSecretsReplacementSnippet' "_secret";
+
+  # Like genJqSecretsReplacementSnippet, but allows the name of the
+  # attr which identifies the secret to be changed.
+  genJqSecretsReplacementSnippet' = attr: set: output:
+    let
+      secrets = recursiveGetAttrWithJqPrefix set attr;
+    in ''
+      if [[ -h '${output}' ]]; then
+        rm '${output}'
+      fi
+    ''
+    + concatStringsSep
+        "\n"
+        (imap1 (index: name: "export secret${toString index}=$(<'${secrets.${name}}')")
+               (attrNames secrets))
+    + "\n"
+    + "${pkgs.jq}/bin/jq >'${output}' '"
+    + concatStringsSep
+      " | "
+      (imap1 (index: name: ''${name} = $ENV.secret${toString index}'')
+             (attrNames secrets))
+    + ''
+      ' <<'EOF'
+      ${builtins.toJSON set}
+      EOF
+    '';
 }