diff --git a/nixosModules/clanCore/vars/interface.nix b/nixosModules/clanCore/vars/interface.nix index 7204f9a67..77a60b2f0 100644 --- a/nixosModules/clanCore/vars/interface.nix +++ b/nixosModules/clanCore/vars/interface.nix @@ -5,7 +5,6 @@ ... }: let - inherit (lib) mkOption; inherit (builtins) hashString toJSON @@ -198,9 +197,11 @@ in }; neededFor = lib.mkOption { description = '' - Enabling this option causes the secret to be decrypted/installed before users and groups are created. - This can be used to retrieve user's passwords. - Setting this option moves the secret to /run/secrets-for-users and disallows setting owner and group to anything else than root. + This option determines when the secret will be decrypted and deployed to the target machine. + + By setting this to `activation`, the secret will be deployed prior to running `nixos-rebuild` or `nixos-install`. + By setting this to `user`, the secret will be deployed prior to users and groups are created, allowing + users' passwords to be managed by vars. The secret will be stored in `/run/secrets-for-users` and `owner` and `group` must be `root`. ''; type = lib.types.enum [ "activation" diff --git a/pkgs/clan-cli/clan_cli/machines/install.py b/pkgs/clan-cli/clan_cli/machines/install.py index 9a625f118..dc051de61 100644 --- a/pkgs/clan-cli/clan_cli/machines/install.py +++ b/pkgs/clan-cli/clan_cli/machines/install.py @@ -1,5 +1,4 @@ import argparse -import importlib import logging import os import sys @@ -44,9 +43,7 @@ def install_machine(opts: InstallOptions) -> None: machine = opts.machine machine.override_target_host = opts.target_host - secret_facts_module = importlib.import_module(machine.secret_facts_module) machine.info(f"installing {machine.name}") - secret_facts_store = secret_facts_module.SecretStore(machine=machine) h = machine.target_host target_host = f"{h.user or 'root'}@{h.host}" @@ -63,7 +60,8 @@ def install_machine(opts: InstallOptions) -> None: upload_dir_ = upload_dir_[1:] upload_dir = tmpdir / upload_dir_ upload_dir.mkdir(parents=True) - secret_facts_store.upload(upload_dir) + machine.secret_facts_store.upload(upload_dir) + machine.secret_vars_store.populate_dir(upload_dir) if opts.password: os.environ["SSHPASS"] = opts.password diff --git a/pkgs/clan-cli/clan_cli/vars/_types.py b/pkgs/clan-cli/clan_cli/vars/_types.py index cabefbf10..949865b8f 100644 --- a/pkgs/clan-cli/clan_cli/vars/_types.py +++ b/pkgs/clan-cli/clan_cli/vars/_types.py @@ -152,3 +152,7 @@ class StoreBase(ABC): if target_hash is None and stored_hash is None: return True return stored_hash == target_hash + + @abstractmethod + def populate_dir(self, output_dir: Path) -> None: + pass diff --git a/pkgs/clan-cli/clan_cli/vars/public_modules/in_repo.py b/pkgs/clan-cli/clan_cli/vars/public_modules/in_repo.py index 05b594fc6..80e0292a5 100644 --- a/pkgs/clan-cli/clan_cli/vars/public_modules/in_repo.py +++ b/pkgs/clan-cli/clan_cli/vars/public_modules/in_repo.py @@ -49,3 +49,7 @@ class FactStore(StoreBase): def exists(self, generator: Generator, name: str) -> bool: return (self.directory(generator, name) / "value").exists() + + def populate_dir(self, output_dir: Path) -> None: + msg = "populate_dir is not implemented for public vars stores" + raise NotImplementedError(msg) diff --git a/pkgs/clan-cli/clan_cli/vars/public_modules/vm.py b/pkgs/clan-cli/clan_cli/vars/public_modules/vm.py index d693da8dd..536cbc0ab 100644 --- a/pkgs/clan-cli/clan_cli/vars/public_modules/vm.py +++ b/pkgs/clan-cli/clan_cli/vars/public_modules/vm.py @@ -47,3 +47,7 @@ class FactStore(StoreBase): return fact_path.read_bytes() msg = f"Fact {name} for service {generator.name} not found" raise ClanError(msg) + + def populate_dir(self, output_dir: Path) -> None: + msg = "populate_dir is not implemented for public vars stores" + raise NotImplementedError(msg) diff --git a/pkgs/clan-cli/clan_cli/vars/secret_modules/sops.py b/pkgs/clan-cli/clan_cli/vars/secret_modules/sops.py index ba43cbd31..a4f7c239e 100644 --- a/pkgs/clan-cli/clan_cli/vars/secret_modules/sops.py +++ b/pkgs/clan-cli/clan_cli/vars/secret_modules/sops.py @@ -172,17 +172,20 @@ class SecretStore(StoreBase): self.machine.flake_dir, sops_secrets_folder(self.machine.flake_dir) / key_name, ) + (output_dir / "key.txt").touch(mode=0o600) (output_dir / "key.txt").write_text(key) for generator in self.machine.vars_generators: for file in generator.files: if file.needed_for == "activation": - (output_dir / generator.name / file.name).parent.mkdir( + target_path = output_dir / generator.name / file.name + target_path.parent.mkdir( parents=True, exist_ok=True, ) - (output_dir / generator.name / file.name).write_bytes( - self.get(generator, file.name) - ) + # chmod after in case it doesn't have u+w + target_path.touch(mode=0o600) + target_path.write_bytes(self.get(generator, file.name)) + target_path.chmod(file.mode) def upload(self) -> None: with TemporaryDirectory(prefix="sops-upload-") as tempdir: