Files
sops-manager/nix/flakeModule.nix
2026-02-03 14:59:16 +04:00

150 lines
4.1 KiB
Nix

{
config,
lib,
flake-parts-lib,
inputs,
den,
...
}:
let
inherit (lib) mkOption types;
cfg = config.secrets;
in
{
options.secrets = {
masterKeys = mkOption {
type = types.listOf types.str;
default = [ ];
description = "A list of master keys for encrypting secrets.";
};
secretsDir = mkOption {
type = types.str;
default = null;
description = "Path to the directory containing secrets relative to flake root.";
};
formatter = mkOption {
type = lib.types.functionTo lib.types.unspecified;
default = pkgs: pkgs.prettier;
description = "The formatter function to use for formatting sops file";
};
nixosModule = mkOption {
type = types.deferredModule;
readOnly = true;
description = "NixOS module configuration for managing sops secrets.";
};
};
config =
let
secrets = builtins.fromJSON (builtins.readFile "${inputs.self}/${cfg.secretsDir}/secrets.json");
all_keys = lib.flatten (lib.concatAttrValues per_host_keys);
per_host_keys = lib.mergeAttrsList (
lib.flatten (
map (
x:
builtins.mapAttrs (
name: value:
let
v = value.sopsPublic or [ ];
type = builtins.typeOf v;
vv =
if type == "string" then
[ v ]
else if type == "list" then
v
else
throw "Unexpected type ${type} for sopsPublic in host ${name}";
in
vv
) x
) (builtins.attrValues den.hosts)
)
);
secret_map = lib.mapAttrs (
name: value:
let
keys = lib.unique (
lib.flatten (map (k: per_host_keys.${k}) value.hosts)
++ (lib.optionals value.global all_keys)
++ cfg.masterKeys
);
in
{
inherit (value)
format
neededForUsers
hosts
global
;
inherit keys;
}
) secrets;
rules = lib.mapAttrsToList (name: value: {
path_regex = "${cfg.secretsDir}/${name}$";
key_groups = [ { age = value.keys; } ];
}) secret_map;
sops_secrets_map = lib.concatMapAttrs (
name: value:
let
hasHost = (lib.elem "wawa" value.hosts) || value.global;
in
if hasHost then
{
${name} = {
inherit (value) format neededForUsers;
sopsFile = inputs.self + "/${cfg.secretsDir}/${name}";
};
}
else
{ }
) secret_map;
in
{
secrets.nixosModule =
{ ... }:
{
config.sops.secrets = sops_secrets_map;
};
perSystem =
{ pkgs, self', ... }:
let
formatted =
let
unformatted = (pkgs.formats.yaml { }).generate ".sops.yaml" {
creation_rules = rules;
};
in
pkgs.stdenvNoCC.mkDerivation {
name = ".sops-yaml-formatted";
src = unformatted;
phases = [ "format" ];
format = ''
cp $src .sops.yaml
chmod +w .sops.yaml
${lib.getExe (cfg.formatter pkgs)} .sops.yaml
cp .sops.yaml $out
'';
};
in
{
packages.write-sops-config = pkgs.writeShellApplication {
name = "write-sops-config";
text = ''
cp ${formatted} .sops.yaml
find ${cfg.secretsDir} -type f ! -name "secrets.json" -exec ${pkgs.lib.getExe pkgs.sops} updatekeys -y {} \;
'';
};
checks.check-sops-config =
pkgs.runCommand "check-sops-config"
{
nativeBuildInputs = [ pkgs.diffutils ];
}
''
set -e
diff -u ${inputs.self}/.sops.yaml ${formatted}
touch $out
'';
};
};
}