blob: 19f01b4ddc989512c8018c13540d0d1c427aeb0b [file] [log] [blame]
# SPDX-FileCopyrightText: 2024 Clicks Codes
#
# SPDX-License-Identifier: GPL-3.0-only
{
lib,
pkgs,
config,
...
}:
let
cfg = config.clicks.security.sops;
guessFormat =
extension:
if extension == "json" then
"json"
else if extension == "yaml" || extension == "yml" then
"yaml"
else if extension == "env" then
"dotenv"
else if extension == "ini" then
"ini"
else
"binary";
getExtension =
filePath:
let
pathParts = builtins.split ''\.'' (builtins.toString filePath);
numPathParts = builtins.length pathParts;
in
builtins.elemAt pathParts (numPathParts - 1);
in
{
options.clicks.secrets =
let
generateNonBinarySopsPaths =
file: keys:
lib.lists.forEach keys (key: {
name = key;
value = config.sops.secrets."${lib.clicks.secrets.name file}:${key}".path;
});
in
lib.mkOption {
type = lib.types.attrsOf (
lib.types.submodule (
{ ... }@submodule:
{
options = {
file = lib.mkOption {
type = lib.types.pathInStore;
description = "The store path to your secrets file";
};
group = lib.mkOption {
type = lib.types.str;
description = "The user the secret should be owned by.";
default = "root";
};
keys = lib.mkOption {
type = lib.types.nullOr (lib.types.listOf lib.types.str);
description = "List of keys to pull from the structured data.";
default = null;
};
neededForUsers = lib.mkEnableOption "This secret is needed for users";
paths = lib.mkOption {
type = lib.types.nullOr (lib.types.attrsOf lib.types.str);
description = "Automatically populated with the SOPS paths to your keys, null if you are using binary secrets";
default =
if guessFormat (getExtension submodule.config.file) != "binary" then
builtins.listToAttrs (generateNonBinarySopsPaths submodule.config.file submodule.config.keys)
else
null;
};
path = lib.mkOption {
type = lib.types.nullOr lib.types.str;
description = "Populated automatically with the SOPS path of the secret, null if you are using non binary secrets";
default =
if guessFormat (getExtension submodule.config.file) == "binary" then
config.sops.secrets.${lib.clicks.secrets.name submodule.config.file}.path
else
null;
};
};
}
)
);
description = "";
default = { };
};
config =
let
generateBinarySopsSecret = secret: {
name = lib.clicks.secrets.name secret.value.file;
value = {
mode = "0400";
owner = config.users.users.root.name;
group = config.users.groups.${secret.value.group}.name;
sopsFile = secret.value.file;
format = guessFormat (getExtension secret.value.file);
inherit (secret.value) neededForUsers;
};
};
generateNonBinarySopsSecrets =
secret:
lib.lists.forEach secret.value.keys (key: {
name = "${lib.clicks.secrets.name secret.value.file}:${key}";
value = {
mode = "0040";
owner = config.users.users.root.name;
group = config.users.groups.${secret.value.group}.name;
sopsFile = secret.value.file;
format = guessFormat (getExtension secret.value.file);
inherit (secret.value) neededForUsers;
inherit key;
};
});
secretsAsList = lib.attrsets.attrsToList config.clicks.secrets;
secretsAsSops = lib.pipe secretsAsList [
(map (
secret:
if guessFormat (getExtension secret.value.file) == "binary" then
generateBinarySopsSecret secret
else
generateNonBinarySopsSecrets secret
))
lib.flatten
builtins.listToAttrs
];
in
{
sops.secrets = secretsAsSops;
};
}