sops: prioritize SOPS_AGE_KEY_FILE over local key

... instead of loading both keys and raise an error

This is important for testing when one wants to override SOPS_AGE_KEY_FILE

New prio: `SOPS_AGE_KEY` > `SOPS_AGE_KEY_FILE` > `~/.config/sops/age/keys.txt`
This commit is contained in:
DavHau
2025-04-08 20:18:11 +07:00
parent 82b6a52c7c
commit ba1ad5bd43
3 changed files with 22 additions and 17 deletions

View File

@@ -20,7 +20,7 @@ log = logging.getLogger(__name__)
def generate_key() -> sops.SopsKey: def generate_key() -> sops.SopsKey:
key = maybe_get_admin_public_key() key = maybe_get_admin_public_key()
if key is not None: if key is not None:
print(f"{key.key_type.name} key {key.pubkey} is already set") print(f"{key.key_type.name} key {key.pubkey} is already set", file=sys.stderr)
return key return key
path = default_admin_private_key_path() path = default_admin_private_key_path()
@@ -34,8 +34,11 @@ def generate_key() -> sops.SopsKey:
def generate_command(args: argparse.Namespace) -> None: def generate_command(args: argparse.Namespace) -> None:
key = generate_key() key = generate_key()
key_type = key.key_type.name.lower() key_type = key.key_type.name.lower()
print(f"Add your {key_type} public key to the repository with:") print(f"Add your {key_type} public key to the repository with:", file=sys.stderr)
print(f"clan secrets users add <username> --{key_type}-key {key.pubkey}") print(
f"clan secrets users add <username> --{key_type}-key {key.pubkey}",
file=sys.stderr,
)
def show_command(args: argparse.Namespace) -> None: def show_command(args: argparse.Namespace) -> None:

View File

@@ -50,17 +50,6 @@ class KeyType(enum.Enum):
keyring: list[str] = [] keyring: list[str] = []
if self == self.AGE: if self == self.AGE:
if keys := os.environ.get("SOPS_AGE_KEY"):
# SOPS_AGE_KEY is fed into age.ParseIdentities by Sops, and
# reads identities line by line. See age/keysource.go in
# Sops, and age/parse.go in Age.
for private_key in keys.strip().splitlines():
public_key = get_public_age_key(private_key)
log.info(
f"Found age public key from a private key "
f"in the environment (SOPS_AGE_KEY): {public_key}"
)
keyring.append(public_key)
def maybe_read_from_path(key_path: Path) -> None: def maybe_read_from_path(key_path: Path) -> None:
try: try:
@@ -78,10 +67,23 @@ class KeyType(enum.Enum):
except Exception as ex: except Exception as ex:
log.warning(f"Could not read age keys from {key_path}", exc_info=ex) log.warning(f"Could not read age keys from {key_path}", exc_info=ex)
if keys := os.environ.get("SOPS_AGE_KEY"):
# SOPS_AGE_KEY is fed into age.ParseIdentities by Sops, and
# reads identities line by line. See age/keysource.go in
# Sops, and age/parse.go in Age.
for private_key in keys.strip().splitlines():
public_key = get_public_age_key(private_key)
log.info(
f"Found age public key from a private key "
f"in the environment (SOPS_AGE_KEY): {public_key}"
)
keyring.append(public_key)
# Sops will try every location, see age/keysource.go # Sops will try every location, see age/keysource.go
if key_path := os.environ.get("SOPS_AGE_KEY_FILE"): elif key_path := os.environ.get("SOPS_AGE_KEY_FILE"):
maybe_read_from_path(Path(key_path)) maybe_read_from_path(Path(key_path))
maybe_read_from_path(user_config_dir() / "sops/age/keys.txt") else:
maybe_read_from_path(user_config_dir() / "sops/age/keys.txt")
return keyring return keyring

View File

@@ -754,7 +754,7 @@ def test_secrets_key_generate_gpg(
] ]
) )
assert "age private key" not in output.out assert "age private key" not in output.out
assert re.match(r"PGP key.+is already set", output.out) is not None assert re.match(r"PGP key.+is already set", output.err) is not None
with capture_output as output: with capture_output as output:
cli.run(["secrets", "key", "show", "--flake", str(test_flake.path)]) cli.run(["secrets", "key", "show", "--flake", str(test_flake.path)])