diff --git a/nixosModules/clanCore/secrets/default.nix b/nixosModules/clanCore/secrets/default.nix index 16bbfb0a..3accc0b0 100644 --- a/nixosModules/clanCore/secrets/default.nix +++ b/nixosModules/clanCore/secrets/default.nix @@ -46,14 +46,36 @@ ''; }; generator = lib.mkOption { - type = lib.types.str; - description = '' - Script to generate the secret. - The script will be called with the following variables: - - facts: path to a directory where facts can be stored - - secrets: path to a directory where secrets can be stored - The script is expected to generate all secrets and facts defined in the module. - ''; + type = lib.types.submodule ({ config, ... }: { + options = { + path = lib.mkOption { + type = lib.types.listOf (lib.types.either lib.types.path lib.types.package); + default = [ ]; + description = '' + Extra paths to add to the PATH environment variable when running the generator. + ''; + }; + script = lib.mkOption { + type = lib.types.str; + description = '' + Script to generate the secret. + The script will be called with the following variables: + - facts: path to a directory where facts can be stored + - secrets: path to a directory where secrets can be stored + The script is expected to generate all secrets and facts defined in the module. + ''; + }; + finalScript = lib.mkOption { + type = lib.types.str; + readOnly = true; + internal = true; + default = '' + export PATH="${lib.makeBinPath config.path}" + ${config.script} + ''; + }; + }; + }); }; secrets = let diff --git a/nixosModules/clanCore/secrets/password-store.nix b/nixosModules/clanCore/secrets/password-store.nix index cf23ccbf..2413d7de 100644 --- a/nixosModules/clanCore/secrets/password-store.nix +++ b/nixosModules/clanCore/secrets/password-store.nix @@ -39,7 +39,7 @@ in trap "rm -rf $facts" EXIT secrets=$(mktemp -d) trap "rm -rf $secrets" EXIT - ( ${v.generator} ) + ( ${v.generator.finalScript} ) ${lib.concatMapStrings (fact: '' mkdir -p "$CLAN_DIR"/"$(dirname ${fact.path})" diff --git a/nixosModules/clanCore/secrets/sops.nix b/nixosModules/clanCore/secrets/sops.nix index b62a5f7a..0c8888ea 100644 --- a/nixosModules/clanCore/secrets/sops.nix +++ b/nixosModules/clanCore/secrets/sops.nix @@ -32,7 +32,14 @@ in import json import sys from clan_cli.secrets.sops_generate import generate_secrets_from_nix - args = json.loads(${builtins.toJSON (builtins.toJSON { machine_name = config.clanCore.machineName; secret_submodules = config.clanCore.secrets; })}) + args = json.loads(${builtins.toJSON (builtins.toJSON { + machine_name = config.clanCore.machineName; + secret_submodules = lib.mapAttrs (_name: secret: { + secrets = builtins.attrNames secret.secrets; + facts = lib.mapAttrs (_: secret: secret.path) secret.facts; + generator = secret.generator.finalScript; + }) config.clanCore.secrets; + })}) generate_secrets_from_nix(**args) ''; uploadSecrets = pkgs.writeScript "upload-secrets" '' diff --git a/nixosModules/clanCore/zerotier/default.nix b/nixosModules/clanCore/zerotier/default.nix index c263a621..20c1394f 100644 --- a/nixosModules/clanCore/zerotier/default.nix +++ b/nixosModules/clanCore/zerotier/default.nix @@ -138,9 +138,9 @@ in facts.zerotier-meshname = { }; facts.zerotier-network-id = { }; secrets.zerotier-identity-secret = { }; - generator = '' - export PATH=${lib.makeBinPath [ config.services.zerotierone.package pkgs.fakeroot ]} - ${pkgs.python3.interpreter} ${./generate.py} --mode network \ + generator.path = [ config.services.zerotierone.package pkgs.fakeroot pkgs.python3 ]; + generator.script = '' + python3 ${./generate.py} --mode network \ --ip "$facts/zerotier-ip" \ --meshname "$facts/zerotier-meshname" \ --identity-secret "$secrets/zerotier-identity-secret" \ @@ -155,10 +155,9 @@ in facts.zerotier-ip = { }; facts.zerotier-meshname = { }; secrets.zerotier-identity-secret = { }; - - generator = '' - export PATH=${lib.makeBinPath [ config.services.zerotierone.package ]} - ${pkgs.python3.interpreter} ${./generate.py} --mode identity \ + generator.path = [ config.services.zerotierone.package pkgs.python3 ]; + generator.script = '' + python3 ${./generate.py} --mode identity \ --ip "$facts/zerotier-ip" \ --meshname "$facts/zerotier-meshname" \ --identity-secret "$secrets/zerotier-identity-secret" \ diff --git a/pkgs/clan-cli/clan_cli/secrets/sops_generate.py b/pkgs/clan-cli/clan_cli/secrets/sops_generate.py index 73395837..c99a7a3b 100644 --- a/pkgs/clan-cli/clan_cli/secrets/sops_generate.py +++ b/pkgs/clan-cli/clan_cli/secrets/sops_generate.py @@ -41,11 +41,9 @@ def generate_secrets_group( clan_dir = flake_dir secrets = secret_options["secrets"] needs_regeneration = any( - not has_secret(flake_dir, f"{machine_name}-{secret['name']}") - for secret in secrets.values() + not has_secret(flake_dir, f"{machine_name}-{name}") for name in secrets ) or any( - not (flake_dir / fact["path"]).exists() - for fact in secret_options["facts"].values() + not (flake_dir / fact).exists() for fact in secret_options["facts"].values() ) generator = secret_options["generator"] @@ -56,7 +54,7 @@ def generate_secrets_group( secrets_dir = subdir / "secrets" secrets_dir.mkdir(parents=True) - text = f"""\ + text = f""" set -euo pipefail export facts={shlex.quote(str(facts_dir))} export secrets={shlex.quote(str(secrets_dir))} @@ -69,25 +67,25 @@ export secrets={shlex.quote(str(secrets_dir))} msg = "failed to the following command:\n" msg += text raise ClanError(msg) - for secret in secrets.values(): - secret_file = secrets_dir / secret["name"] + for name in secrets: + secret_file = secrets_dir / name if not secret_file.is_file(): - msg = f"did not generate a file for '{secret['name']}' when running the following command:\n" + msg = f"did not generate a file for '{name}' when running the following command:\n" msg += text raise ClanError(msg) encrypt_secret( flake_dir, - sops_secrets_folder(flake_dir) / f"{machine_name}-{secret['name']}", + sops_secrets_folder(flake_dir) / f"{machine_name}-{name}", secret_file.read_text(), add_machines=[machine_name], ) - for fact in secret_options["facts"].values(): - fact_file = facts_dir / fact["name"] + for name, fact_path in secret_options["facts"].items(): + fact_file = facts_dir / name if not fact_file.is_file(): - msg = f"did not generate a file for '{fact['name']}' when running the following command:\n" + msg = f"did not generate a file for '{name}' when running the following command:\n" msg += text raise ClanError(msg) - fact_path = clan_dir.joinpath(fact["path"]) + fact_path = clan_dir / fact_path fact_path.parent.mkdir(parents=True, exist_ok=True) shutil.copyfile(fact_file, fact_path)