From 8c19ee9d72ec15389ab7e168020ed2085ddb8dcd Mon Sep 17 00:00:00 2001 From: beatzaplenty Date: Mon, 27 Jul 2026 05:42:32 +1000 Subject: [PATCH] fix(sops): hard-fail on missing admin key and expand literal ~ in key path SOPS_AGE_KEY_FILE was set in hosts/nixos/home.nix sessionVariables with a literal ~ that Home Manager injects as-is into the environment. In bash, tilde expansion does not happen inside double-quoted variable references, so DEFAULT_SOPS_AGE_KEY_FILE resolved to ~/... literally and the -s file-existence check in ensure_admin_decrypt_key silently failed. The script then generated a brand-new age key (to ~/... relative to the repo root) while the real admin key at ~/.config/sops/age/keys.txt went untouched -- making it appear the key was lost when it was actually still intact. Fix the home.nix root cause by using config.home.homeDirectory so the path is fully resolved. Add tilde expansion in ensure_admin_decrypt_key as a belt-and-suspenders guard for any caller whose environment has the same issue. Also replace the auto-generate-a-new-key fallback with a hard failure: auto- generating a new admin key is never useful (it cannot decrypt existing secrets) and created serious confusion about whether the original key was lost. Co-Authored-By: Claude Sonnet 4.6 --- hosts/nixos/home.nix | 2 +- scripts/secrets/sync-host-keys.sh | 41 ++++++++++++------------------- 2 files changed, 17 insertions(+), 26 deletions(-) diff --git a/hosts/nixos/home.nix b/hosts/nixos/home.nix index 64b8b7f..65702e6 100644 --- a/hosts/nixos/home.nix +++ b/hosts/nixos/home.nix @@ -27,7 +27,7 @@ # Optional: set environment vars sessionVariables = { EDITOR = "vim"; - SOPS_AGE_KEY_FILE = "~/.config/sops/age/keys.txt"; + SOPS_AGE_KEY_FILE = "${config.home.homeDirectory}/.config/sops/age/keys.txt"; }; file = { diff --git a/scripts/secrets/sync-host-keys.sh b/scripts/secrets/sync-host-keys.sh index 512929e..3a57373 100755 --- a/scripts/secrets/sync-host-keys.sh +++ b/scripts/secrets/sync-host-keys.sh @@ -85,6 +85,10 @@ ensure_admin_decrypt_key() { fi local key_file="$DEFAULT_SOPS_AGE_KEY_FILE" + # Expand a leading ~ that survived variable substitution without tilde + # expansion (happens when SOPS_AGE_KEY_FILE or XDG_CONFIG_HOME is set with + # a literal ~ in the caller's environment). + key_file="${key_file/#~\//$HOME/}" if [[ -s "$key_file" ]]; then echo "Found existing sops age key at ${key_file}." @@ -93,36 +97,23 @@ ensure_admin_decrypt_key() { if [[ "$dry_run" -eq 1 ]]; then echo "[dry-run] No sops age decryption key found (checked \$SOPS_AGE_KEY, \$SOPS_AGE_KEY_FILE, ${key_file})." - echo "[dry-run] Would generate a new one here -- continuing the dry run without one; any" - echo "[dry-run] 'would re-encrypt' output below couldn't actually run for real yet." + echo "[dry-run] Continuing dry run without one -- any 'would re-encrypt' output below" + echo "[dry-run] couldn't actually run for real until a key is present." return fi - echo "No sops age decryption key found (checked \$SOPS_AGE_KEY, \$SOPS_AGE_KEY_FILE, ${key_file})." - echo "Generating a new one at ${key_file}..." - mkdir -p "$(dirname "$key_file")" - nix-shell "${NIX_OPTS[@]}" -p age --run "age-keygen -o '${key_file}'" 2>&1 | grep -v "^Public key:" || true - local new_pub - new_pub="$(age_pubkey_from_identity_file "$key_file")" + cat >&2 <