From 23ba5a767f536a7ffe6def25d1ea494ec720c145 Mon Sep 17 00:00:00 2001 From: Martin Nikov Date: Thu, 25 Apr 2024 18:31:56 +0300 Subject: [PATCH] feat(modules): Add `secrets` as nixosModules --- modules/default.nix | 1 + modules/secrets.nix | 117 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 modules/secrets.nix diff --git a/modules/default.nix b/modules/default.nix index 64bf2bf2..be71d4d4 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -5,5 +5,6 @@ ./grafana-agent-flow ./pyroscope ./host-info.nix + ./secrets.nix ]; } diff --git a/modules/secrets.nix b/modules/secrets.nix new file mode 100644 index 00000000..89167226 --- /dev/null +++ b/modules/secrets.nix @@ -0,0 +1,117 @@ +{withSystem, ...}: { + flake.nixosModules.mcl-secrets = { + config, + options, + lib, + dirs, + ... + }: let + eachServiceCfg = config.mcl.secrets.services; + isDebugVM = config.mcl.host-info.isDebugVM; + + sshKey = config.mcl.host-info.sshKey; + + ageSecretOpts = + builtins.head + (builtins.head + options.age.secrets.type.nestedTypes.elemType.getSubModules) + .imports; + + secretDir = let + machineConfigPath = config.mcl.host-info.configPath; + machineSecretDir = machineConfigPath + "/secrets"; + vmConfig = dirs.modules + "/default-vm-config"; + vmSecretDir = vmConfig + "/secrets"; + in + if isDebugVM + then vmSecretDir + else machineSecretDir; + in { + options.mcl.secrets = with lib; { + services = mkOption { + type = types.attrsOf (types.submodule ({config, ...}: let + serviceName = config._module.args.name; + in { + options = { + encryptedSecretDir = mkOption { + type = types.path; + default = secretDir; + }; + secrets = mkOption { + default = {}; + type = types.attrsOf (types.submoduleWith { + modules = [ + ageSecretOpts + ({name, ...}: let + secretName = name; + in { + config = { + name = "${serviceName}/${secretName}"; + file = + lib.mkDefault (config.encryptedSecretDir + "/${serviceName}/${secretName}.age"); + }; + }) + ]; + }); + }; + extraGroups = mkOption { + type = types.listOf types.str; + default = []; + example = ["devops" "secretsAccess"]; + description = "Groups which have access to decrypt the secrets."; + }; + extraKeys = mkOption { + type = types.listOf types.str; + default = []; + example = ["ssh-ed25519 AAAAC3Nza" "ssh-ed25519 AAAACSNss"]; + description = "Groups which have access to decrypt the secrets."; + }; + nix-file = mkOption { + default = + if (pathIsRegularFile (config.encryptedSecretDir + "/${serviceName}/secrets.nix")) + then config.encryptedSecretDir + "/${serviceName}/secrets.nix" + else + builtins.toFile "${serviceName}-secrets.nix" '' + let + hostKey = ["${sshKey}"]; + extraKeys = ["${concatStringsSep "\"\"" config.extraKeys}"]; + in { + ${concatMapStringsSep "\n" + (n: "\"${n}.age\".publicKeys = hostKey ++ extraKeys;") + (builtins.attrNames config.secrets)} + } + ''; + + # groupsKeys = ["${concatStringsSep "\"\"" (mcl-modules.libs.utils.allUserKeysForGroup config.extraGroups)}"]; + + type = types.path; + }; + }; + })); + default = {}; + example = { + service1.secrets.secretA = {}; + service1.secrets.secretB = {}; + service2.secrets.secretC = {}; + cachix-deploy.secrets.token = { + path = "/etc/cachix-agent.token"; + }; + }; + description = mdDoc "Per-service attrset of encryptedSecretDir and secrets"; + }; + }; + + config = lib.mkIf (eachServiceCfg != {}) { + age.secrets = lib.pipe eachServiceCfg [ + (lib.mapAttrsToList (serviceName: service: + lib.mapAttrsToList ( + secretName: config: + lib.nameValuePair "${serviceName}/${secretName}" config + ) + service.secrets)) + lib.concatLists + lib.listToAttrs + ]; + }; + }; +}