{ config, lib, pkgs, ... }: with lib; let cfg = config.services.secrets; defaultPackages = with pkgs; [ pkgs.vault pkgs.jq ]; in { options = { services.secrets = { enable = mkOption { type = types.bool; default = false; }; debug = mkOption { type = types.bool; default = false; }; createSecretsDir = mkOption { type = types.bool; default = true; }; secretsDirUser = mkOption { type = types.str; default = "root"; }; secretsDirGroup = mkOption { type = types.str; default = "root"; }; secretsDir = mkOption { type = types.str; default = "/secrets"; }; vaultURL = mkOption { type = types.str; default = "https://vault.owo.monster"; description = "default Vault URL, can be overrided with env variables"; }; extraFunctions = mkOption { type = types.lines; default = ""; description = "extra bash functions to add to top of script"; }; extraPackages = mkOption { type = types.listOf types.package; default = [ ]; description = "extra packages for script"; }; secrets = mkOption { type = types.attrsOf (types.submodule ({ config, name, ... }: { options = { user = mkOption { type = types.str; default = "root"; }; group = mkOption { type = types.str; default = "root"; }; permissions = mkOption { type = types.str; default = "660"; }; path = mkOption { type = types.str; default = "${cfg.secretsDir}/${name}"; }; fetchScript = mkOption { type = types.lines; description = '' script used to fetch secrets, $file is secret.path ''; }; checkScript = mkOption { type = types.nullOr types.lines; default = null; description = '' script used to check contents of secret file, set LOCAL_FAIL to true on failure ''; }; manual = mkOption { type = types.bool; default = false; description = "should the secret be manually deployed"; }; }; })); }; }; }; config = mkMerge [ (mkIf (cfg.enable) (let scriptBase = '' set -e -o pipefail ${if cfg.debug then "set -x" else ""} ''; manualSecrets = filterAttrs (name: secret: secret.manual) cfg.secrets; nonManualSecrets = filterAttrs (name: secret: !secret.manual) cfg.secrets; initScript = '' ${scriptBase} VAULT_ADDR_DEFAULT="${cfg.vaultURL}" [ -n "$VAULT_ADDR" ] && export VAULT_ADDR="$VAULT_ADDR_DEFAULT" kv_get() { vault kv get -format json "$1" } simple_get() { kv_get "$1" | jq ".data.data$2" -r } ${cfg.extraFunctions} '' + (lib.concatStringsSep "\n" (lib.mapAttrsToList (name: secret: '' if [[ ! -f "${secret.path}" ]]; then echo "Initializing Secret ${secret.path}" else echo "Updating Secret ${secret.path}" fi secretFile="${secret.path}" ${secret.fetchScript} chown ${secret.user}:${secret.group} "${secret.path}" chmod ${secret.permissions} "${secret.path}" '') nonManualSecrets)) + (lib.concatStringsSep "\n" (lib.mapAttrsToList (name: secret: '' if [[ ! -f "${secret.path}" ]]; then echo "Manual Secret ${secret.path} Doesn't Exist" exit 1 fi echo "Updating Permissions on Manual Secret ${secret.path}" chown ${secret.user}:${secret.group} "${secret.path}" chmod ${secret.permissions} "${secret.path}" '') manualSecrets)) + '' echo "Secrets Deployed" ''; checkScript = '' ${scriptBase} getUser() { stat --format "%U" "$1" 2>/dev/null } getGroup() { stat --format "%U" "$1" 2>/dev/null } getPermissions() { stat --format "%a" "$1" 2>/dev/null } GLOBAL_FAIL=false '' + (lib.concatStringsSep "\n" (lib.mapAttrsToList (name: secret: '' LOCAL_FAIL=false echo "Secret: ${name}" echo "Checking ${secret.path}" # some variables which can be used by checkScript # shellcheck disable=SC2034 secretFile="${secret.path}" if [[ -f "${secret.path}" ]]; then echo "✅ File Exists" else echo "❌ File Does Not Exist" LOCAL_FAIL=true fi if getUser "${secret.path}" >/dev/null && [[ "$(getUser "${secret.path}")" == "${secret.user}" ]]; then echo "✅ File Is Owned By Correct User" else echo "❌ File Is Not Owned By Correct User (${secret.user})" LOCAL_FAIL=true fi if getGroup "${secret.path}" >/dev/null && [[ "$(getGroup "${secret.path}")" == "${secret.group}" ]]; then echo "✅ File Is Owned By Correct Group" else echo "❌ File Is Not Owned By Correct Group (${secret.user})" LOCAL_FAIL=true fi if getPermissions "${secret.path}" >/dev/null && [[ "$(getPermissions "${secret.path}")" -eq "${secret.permissions}" ]]; then echo "✅ File Has Correct Permissions" else echo "❌ File Does Not Have Correct Permissions (${secret.permissions})" LOCAL_FAIL=true fi ${if secret.checkScript != null then secret.checkScript else ""} if [[ "$LOCAL_FAIL" == "true" ]]; then echo "❌ File Did Not Pass The Vibe Check" GLOBAL_FAIL=true else echo "✅ File Passed The Vibe Check" fi echo '') cfg.secrets)) + '' if [[ "$GLOBAL_FAIL" == "true" ]]; then echo "❌ One Or More Secrets Did Not Pass The Vibe Check" exit 1 else echo "✅ All Secrets Passed The Vibe Check" fi ''; in { environment.systemPackages = [ (pkgs.writeShellApplication { name = "secrets-check"; runtimeInputs = defaultPackages ++ cfg.extraPackages; text = checkScript; }) (pkgs.writeShellApplication { name = "secrets-init"; runtimeInputs = defaultPackages ++ cfg.extraPackages; text = initScript; }) ]; })) (mkIf (cfg.enable && cfg.createSecretsDir) { systemd.tmpfiles.rules = [ "d ${cfg.secretsDir} - ${cfg.secretsDirUser} ${cfg.secretsDirGroup}" ]; }) ]; }