clan-cli: add interactive secrets/fact generation

This commit is contained in:
lassulus 2024-03-01 10:25:39 +01:00
parent f500aee786
commit a1dcddf9b4
3 changed files with 39 additions and 6 deletions

View File

@ -54,6 +54,14 @@
Extra paths to add to the PATH environment variable when running the generator. 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 { script = lib.mkOption {
type = lib.types.str; type = lib.types.str;
description = '' description = ''
@ -92,14 +100,14 @@
config' = config; config' = config;
in in
lib.mkOption { lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule ({ config, ... }: { type = lib.types.attrsOf (lib.types.submodule ({ config, name, ... }: {
options = { options = {
name = lib.mkOption { name = lib.mkOption {
type = lib.types.str; type = lib.types.str;
description = '' description = ''
name of the secret name of the secret
''; '';
default = config._module.args.name; default = name;
}; };
path = lib.mkOption { path = lib.mkOption {
type = lib.types.str; type = lib.types.str;

View File

@ -2,6 +2,7 @@ import argparse
import importlib import importlib
import logging import logging
import os import os
from collections.abc import Callable
from pathlib import Path from pathlib import Path
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
@ -24,6 +25,7 @@ def generate_service_secrets(
secret_store: SecretStoreBase, secret_store: SecretStoreBase,
fact_store: FactStoreBase, fact_store: FactStoreBase,
tmpdir: Path, tmpdir: Path,
prompt: Callable[[str], str],
) -> None: ) -> None:
service_dir = tmpdir / service service_dir = tmpdir / service
# check if all secrets exist and generate them if at least one is missing # 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 = service_dir / "secrets"
secrets_dir.mkdir(parents=True) secrets_dir.mkdir(parents=True)
env["secrets"] = str(secrets_dir) 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 # fmt: off
cmd = nix_shell( cmd = nix_shell(
[ [
@ -58,7 +70,7 @@ def generate_service_secrets(
"--unshare-user", "--unshare-user",
"--uid", "1000", "--uid", "1000",
"--", "--",
"bash", "-c", machine.secrets_data[service]["generator"]["finalScript"] "bash", "-c", generator
], ],
) )
# fmt: on # 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) secrets_module = importlib.import_module(machine.secrets_module)
secret_store = secrets_module.SecretStore(machine=machine) secret_store = secrets_module.SecretStore(machine=machine)
facts_module = importlib.import_module(machine.facts_module) facts_module = importlib.import_module(machine.facts_module)
fact_store = facts_module.FactStore(machine=machine) fact_store = facts_module.FactStore(machine=machine)
if prompt is None:
prompt = lambda text: input(f"{text}: ")
with TemporaryDirectory() as tmp: with TemporaryDirectory() as tmp:
tmpdir = Path(tmp) tmpdir = Path(tmp)
for service in machine.secrets_data: 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") print("successfully generated secrets")

View File

@ -39,6 +39,7 @@ def facts_to_nixos_config(facts: dict[str, dict[str, bytes]]) -> dict:
def build_vm( def build_vm(
machine: Machine, tmpdir: Path, nix_options: list[str] = [] machine: Machine, tmpdir: Path, nix_options: list[str] = []
) -> dict[str, str]: ) -> dict[str, str]:
# TODO pass prompt here for the GTK gui
secrets_dir = get_secrets(machine, tmpdir) secrets_dir = get_secrets(machine, tmpdir)
facts_module = importlib.import_module(machine.facts_module) facts_module = importlib.import_module(machine.facts_module)
@ -68,7 +69,6 @@ def get_secrets(
secrets_module = importlib.import_module(machine.secrets_module) secrets_module = importlib.import_module(machine.secrets_module)
secret_store = secrets_module.SecretStore(machine=machine) secret_store = secrets_module.SecretStore(machine=machine)
# TODO Only generate secrets for local clans
generate_secrets(machine) generate_secrets(machine)
secret_store.upload(secrets_dir) secret_store.upload(secrets_dir)