Archived
feat(provision): Phase 2 — migrate SSH host keys to clan vars
Replaces the gitignored host-keys/ directory with clan vars as the authoritative storage for SSH host keys. Keys are now generated as sops-binary-encrypted clan var files (admin-key only) and checked into vars/per-machine/<target>/openssh/, eliminating the plaintext private key that previously had to live outside the repo. Changes: - modules/clan/ssh-host-key.nix: clan vars generator for the ed25519 SSH host key pair (neededFor="activation" — not mapped to sops.secrets, delivered via tarball baking for LXC or --pre-format-files for VMs) - flake.nix: add clanCore module + required settings to every mkTarget; deduplicate bundled disko/sops-nix via follows; all 27 hosts eval clean - flake.lock: updated to reflect the new follows constraints - scripts/lib/clan-vars.sh: new helper library with clan_ssh_key_exists / clan_ssh_pubkey_path / clan_decrypt_ssh_key / clan_generate_ssh_key for use by the provisioning and sync scripts - scripts/secrets/sync-host-keys.sh: queue_host_sync() now checks clan vars first; generates via clan_generate_ssh_key if no key exists; derives age fingerprint from clan pub key for .sops.yaml registration - scripts/proxmox/create-proxmox-resource.sh: key management simplified (sync-host-keys.sh now generates the key if missing, so the inline prepare-host-key.sh call is gone); sync_remote_host_keys() decrypts the clan key into a temp dir and scps just the two files to the node when a clan key exists, falling back to the old host-keys/ scp for any remaining legacy entries Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B2EJ4qTsM5KUqhS5c3GAwx
This commit is contained in:
@@ -59,6 +59,10 @@ source "${repo_root}/scripts/env.sh"
|
||||
source "${repo_root}/scripts/lib/nix-eval.sh"
|
||||
# shellcheck source=../lib/confirm.sh
|
||||
source "${repo_root}/scripts/lib/confirm.sh"
|
||||
# shellcheck source=../lib/sops-age.sh
|
||||
source "${repo_root}/scripts/lib/sops-age.sh"
|
||||
# shellcheck source=../lib/clan-vars.sh
|
||||
source "${repo_root}/scripts/lib/clan-vars.sh"
|
||||
|
||||
sync_keys="${repo_root}/scripts/secrets/sync-host-keys.sh"
|
||||
|
||||
@@ -485,19 +489,10 @@ echo "Target: ${flake_target} (host=${host}, type=${type}) -> Proxmox resource '
|
||||
nix_extra_opts
|
||||
|
||||
# --- make sure this target has a registered host key --------------------
|
||||
# sync-host-keys.sh is idempotent and generates the key (via clan vars) if
|
||||
# no key exists yet -- the old inline prepare-host-key.sh call is gone.
|
||||
echo
|
||||
echo "==> Ensuring host key exists and is registered..."
|
||||
_host_keyfile="${repo_root}/host-keys/${flake_target}_ssh_host_ed25519_key"
|
||||
if [[ ! -f "$_host_keyfile" ]]; then
|
||||
echo " No host key found for ${flake_target}; generating one via prepare-host-key.sh..."
|
||||
_prepare_host_key="${repo_root}/scripts/secrets/prepare-host-key.sh"
|
||||
if [[ "$dry_run" -eq 1 ]]; then
|
||||
echo "[dry-run] would run: bash ${_prepare_host_key} ${flake_target}"
|
||||
else
|
||||
bash "$_prepare_host_key" "$flake_target"
|
||||
fi
|
||||
fi
|
||||
unset _host_keyfile _prepare_host_key
|
||||
sync_args=("$flake_target")
|
||||
[[ "$dry_run" -eq 1 ]] && sync_args+=(--dry-run)
|
||||
bash "$sync_keys" "${sync_args[@]}"
|
||||
@@ -647,21 +642,37 @@ ensure_remote_repo() {
|
||||
fi
|
||||
}
|
||||
|
||||
# --- sync locally-managed host-keys/ to the node ---------------------------
|
||||
# Gitignored (see .gitignore), so `git pull` above never carries it -- both
|
||||
# build paths need it present as NIXOS_HOST_KEYS_DIR / --pre-format-files
|
||||
# input on the node itself now that the build runs there. scp (not rsync,
|
||||
# not already a dependency anywhere else in this repo) mirrors how this
|
||||
# script already transfers the --image case below.
|
||||
# --- sync host key to the node ---------------------------------------------
|
||||
# Clan-managed keys live in vars/per-machine/ (committed, sops-encrypted),
|
||||
# so they arrive on the node via `git pull`. But the build scripts expect a
|
||||
# plaintext key file in host-keys/ (NIXOS_HOST_KEYS_DIR for LXC, or
|
||||
# --pre-format-files for VM). For clan keys, decrypt locally and scp just the
|
||||
# two files for this target; for legacy host-keys/ entries, scp the whole dir.
|
||||
sync_remote_host_keys() {
|
||||
echo
|
||||
echo "==> Syncing host-keys/ to ${node}..."
|
||||
echo "==> Syncing host key for ${flake_target} to ${node}..."
|
||||
if [[ "$dry_run" -eq 1 ]]; then
|
||||
echo "[dry-run] would copy ${repo_root}/host-keys/ to ${ssh_target}:${remote_repo_dir}/host-keys/"
|
||||
if clan_ssh_key_exists "$flake_target" "$repo_root"; then
|
||||
echo "[dry-run] would decrypt clan SSH key for ${flake_target} and copy to ${ssh_target}:${remote_repo_dir}/host-keys/"
|
||||
else
|
||||
echo "[dry-run] would copy ${repo_root}/host-keys/ to ${ssh_target}:${remote_repo_dir}/host-keys/"
|
||||
fi
|
||||
return
|
||||
fi
|
||||
ssh "$ssh_target" "mkdir -p '${remote_repo_dir}/host-keys'"
|
||||
scp -pr "${repo_root}/host-keys/." "${ssh_target}:${remote_repo_dir}/host-keys/"
|
||||
if clan_ssh_key_exists "$flake_target" "$repo_root"; then
|
||||
local tmpdir
|
||||
tmpdir="$(mktemp -d)"
|
||||
# shellcheck disable=SC2064
|
||||
trap "rm -rf '${tmpdir}'" RETURN
|
||||
echo " Decrypting clan SSH key for ${flake_target}..."
|
||||
clan_decrypt_ssh_key "$flake_target" "$repo_root" "$tmpdir"
|
||||
scp -p "${tmpdir}/${flake_target}_ssh_host_ed25519_key" \
|
||||
"${tmpdir}/${flake_target}_ssh_host_ed25519_key.pub" \
|
||||
"${ssh_target}:${remote_repo_dir}/host-keys/"
|
||||
else
|
||||
scp -pr "${repo_root}/host-keys/." "${ssh_target}:${remote_repo_dir}/host-keys/"
|
||||
fi
|
||||
}
|
||||
|
||||
# --- build (or reuse an image already on the node) ------------------------
|
||||
|
||||
Reference in New Issue
Block a user