feat(utils): new function setupSecrets

wrapper around sops-nix and genSecrets, providing various helper methods
for less boilerplates.

Changed files
+73
docs
global
+46
docs/utils.md
···
See https://github.com/soopyc/nix-on-koumakan/blob/b7983776143c15c91df69ef34ba4264a22047ec6/systems/koumakan/services/fedivese/akkoma.nix#L8-L34 for a more extensive example
+
## `_utils.setupSecrets`
+
`attrset<nixos config attr> -> {namespace<str> ? "", secrets[list<str>], config ? freeformAttrset} -> secretHelpers`
+
This is a more convoluted setup that wraps around `_utils.genSecrets` to provide some additional helper functions.
+
Usage of this function should make more sense than just using `genSecrets`.
+
+
NOTE: `<ReturnValue>.generate` is not actually a function. The attrset is "already" "rendered" should it be actually
+
resolved by not being ignored by lazy eval.
+
+
NOTE: does not support overriding config for only 1 path. might implement when demand arises.
+
+
The definition of `secretHelpers` is defined as follows:
+
```nix
+
secretHelpers = {
+
generate = {}; # => {sops.secrets.* = <sopsConfig>}
+
get = path: ""; # => actual path of the secret, usually /run/secrets/the/secret
+
+
placeholder = path: ""; # => placeholder string generated by sops-nix, for that secret path to be used in templates.
+
getTemplate = file: ""; # => actual path of the template, realized at activation time, similar to the get function.
+
mkTemplate = file: content: {}; # => {sops.templates.* = ...;}
+
# ^ => filename of the template. just an arbitrary string.
+
}
+
```
+
+
### Example
+
```nix
+
{ _utils, config, ... }: let
+
secrets = _utils.setupSecrets config {
+
namespace = "balls"; # for us, the namespace is just the top level element in our secrets yaml file.
+
config = {
+
owner = "jane";
+
};
+
secrets = [ "my/definitions/gock" "my/sizes/gock" ];
+
};
+
in {
+
imports = [
+
secrets.generate
+
(secrets.mkTemplate "my-secret.env" ''
+
MY_GOCK_SIZE=${secrets.placeholder "my/sizes/gock"}
+
'')
+
];
+
+
some.service.settings.gock.file = secrets.get "my/definitions/gock"; # resolves to the path of balls/my/definitions/gock.
+
some.service.settings.envFile = secrets.getTemplate "my-secret.env";
+
}
+
```
+
## `_utils.mkNginxFile`
`{filename<str> ? "index.html", content<str>, status<int> ? 200} -> {alias<str>, tryFiles<str>}`
Simple helper function to generate an attrset compatible with a nginx vhost `locations` attribute that serves a single file.
+27
global/utils.nix
···
}
]);
+
setupSecrets = _config: {
+
namespace ? (lib.warn "secret namespace left as default, which is empty. it is encouraged to set a namespace for easier secret management. to override, explicitly set this to an empty value." ""),
+
secrets,
+
config ? {},
+
}: let
+
_r_ns = namespace + lib.optionalString (lib.stringLength namespace != 0) "/";
+
check = path:
+
if lib.elem path secrets
+
then path
+
else throw "secret path `${path}` is not defined in namespace `${namespace}`. (resolved: ${_r_ns namespace}/${path})";
+
getRealPath = path: _r_ns + check path;
+
in
+
builtins.addErrorContext "while setting up secrets with namespace ${namespace}" {
+
generate = {sops.secrets = genSecrets namespace secrets config;}; # i love trolling
+
get = path: _config.sops.secrets.${getRealPath path}.path;
+
+
placeholder = path: _config.sops.placeholder.${getRealPath path};
+
getTemplate = file: _config.sops.templates.${file}.path;
+
mkTemplate = file: content:
+
builtins.addErrorContext "while generating sops template ${file}" {
+
sops.templates.${file} =
+
{inherit content;}
+
// (lib.optionalAttrs (builtins.hasAttr "owner" config) {inherit (config) owner;})
+
// (lib.optionalAttrs (builtins.hasAttr "group" config) {inherit (config) group;});
+
};
+
};
+
genSecrets = namespace: files: value:
lib.genAttrs (
map (x: namespace + lib.optionalString (lib.stringLength namespace != 0) "/" + x) files