From a1dcddf9b4fa2176fda23714885189152551d068 Mon Sep 17 00:00:00 2001 From: lassulus Date: Fri, 1 Mar 2024 10:25:39 +0100 Subject: [PATCH] clan-cli: add interactive secrets/fact generation --- nixosModules/clanCore/secrets/default.nix | 12 +++++++-- pkgs/clan-cli/clan_cli/secrets/generate.py | 31 +++++++++++++++++++--- pkgs/clan-cli/clan_cli/vms/run.py | 2 +- 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/nixosModules/clanCore/secrets/default.nix b/nixosModules/clanCore/secrets/default.nix index 4ab8f197..5670459f 100644 --- a/nixosModules/clanCore/secrets/default.nix +++ b/nixosModules/clanCore/secrets/default.nix @@ -54,6 +54,14 @@ Extra paths to add to the PATH environment variable when running the generator. ''; }; + prompt = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = '' + prompt text to ask for a value. + This value will be passed to the script as the environment variabel $prompt_value. + ''; + }; script = lib.mkOption { type = lib.types.str; description = '' @@ -92,14 +100,14 @@ config' = config; in lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule ({ config, ... }: { + type = lib.types.attrsOf (lib.types.submodule ({ config, name, ... }: { options = { name = lib.mkOption { type = lib.types.str; description = '' name of the secret ''; - default = config._module.args.name; + default = name; }; path = lib.mkOption { type = lib.types.str; diff --git a/pkgs/clan-cli/clan_cli/secrets/generate.py b/pkgs/clan-cli/clan_cli/secrets/generate.py index bb9733f3..e478d459 100644 --- a/pkgs/clan-cli/clan_cli/secrets/generate.py +++ b/pkgs/clan-cli/clan_cli/secrets/generate.py @@ -2,6 +2,7 @@ import argparse import importlib import logging import os +from collections.abc import Callable from pathlib import Path from tempfile import TemporaryDirectory @@ -24,6 +25,7 @@ def generate_service_secrets( secret_store: SecretStoreBase, fact_store: FactStoreBase, tmpdir: Path, + prompt: Callable[[str], str], ) -> None: service_dir = tmpdir / service # check if all secrets exist and generate them if at least one is missing @@ -41,6 +43,16 @@ def generate_service_secrets( secrets_dir = service_dir / "secrets" secrets_dir.mkdir(parents=True) env["secrets"] = str(secrets_dir) + # compatibility for old outputs.nix users + if isinstance(machine.secrets_data[service]["generator"], str): + generator = machine.secrets_data[service]["generator"] + else: + generator = machine.secrets_data[service]["generator"]["finalScript"] + if machine.secrets_data[service]["generator"]["prompt"]: + prompt_value = prompt( + machine.secrets_data[service]["generator"]["prompt"] + ) + env["prompt_value"] = prompt_value # fmt: off cmd = nix_shell( [ @@ -58,7 +70,7 @@ def generate_service_secrets( "--unshare-user", "--uid", "1000", "--", - "bash", "-c", machine.secrets_data[service]["generator"]["finalScript"] + "bash", "-c", generator ], ) # fmt: on @@ -105,17 +117,30 @@ def generate_service_secrets( ) -def generate_secrets(machine: Machine) -> None: +def generate_secrets( + machine: Machine, + prompt: None | Callable[[str], str] = None, +) -> None: secrets_module = importlib.import_module(machine.secrets_module) secret_store = secrets_module.SecretStore(machine=machine) facts_module = importlib.import_module(machine.facts_module) fact_store = facts_module.FactStore(machine=machine) + if prompt is None: + prompt = lambda text: input(f"{text}: ") + with TemporaryDirectory() as tmp: tmpdir = Path(tmp) for service in machine.secrets_data: - generate_service_secrets(machine, service, secret_store, fact_store, tmpdir) + generate_service_secrets( + machine=machine, + service=service, + secret_store=secret_store, + fact_store=fact_store, + tmpdir=tmpdir, + prompt=prompt, + ) print("successfully generated secrets") diff --git a/pkgs/clan-cli/clan_cli/vms/run.py b/pkgs/clan-cli/clan_cli/vms/run.py index c9049e12..f58db06c 100644 --- a/pkgs/clan-cli/clan_cli/vms/run.py +++ b/pkgs/clan-cli/clan_cli/vms/run.py @@ -39,6 +39,7 @@ def facts_to_nixos_config(facts: dict[str, dict[str, bytes]]) -> dict: def build_vm( machine: Machine, tmpdir: Path, nix_options: list[str] = [] ) -> dict[str, str]: + # TODO pass prompt here for the GTK gui secrets_dir = get_secrets(machine, tmpdir) facts_module = importlib.import_module(machine.facts_module) @@ -68,7 +69,6 @@ def get_secrets( secrets_module = importlib.import_module(machine.secrets_module) secret_store = secrets_module.SecretStore(machine=machine) - # TODO Only generate secrets for local clans generate_secrets(machine) secret_store.upload(secrets_dir)