Merge pull request 'install: upload vars needed for activation for installation' (#2643) from Enzime/clan-core:push-yvpxptntlmuy into main

Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/2643
This commit is contained in:
Mic92
2024-12-22 05:53:26 +00:00
6 changed files with 26 additions and 12 deletions

View File

@@ -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"

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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: