diff --git a/modules/core/ssh-bootstrap.nix b/modules/core/ssh-bootstrap.nix index e88dcd8..9a0c7ba 100644 --- a/modules/core/ssh-bootstrap.nix +++ b/modules/core/ssh-bootstrap.nix @@ -31,7 +31,7 @@ lib.mkIf userExists { }; systemd.services.factory-ssh-keygen = { - description = "Generate factory SSH key for ${userName} if missing"; + description = "Generate or repair factory SSH key for ${userName}"; wantedBy = [ "multi-user.target" ]; after = [ "ssh-passphrase-setup.service" ]; requires = [ "ssh-passphrase-setup.service" ]; @@ -39,14 +39,47 @@ lib.mkIf userExists { Type = "oneshot"; RemainAfterExit = true; }; - path = [ pkgs.openssh pkgs.coreutils ]; + path = [ pkgs.openssh pkgs.coreutils pkgs.util-linux ]; script = '' - if [ ! -f "${keyPath}" ]; then - PASSPHRASE=$(cat /var/lib/secrets/ssh-passphrase) + set -eu + + PASSPHRASE=$(cat /var/lib/secrets/ssh-passphrase) + lock_file="${keyPath}.lock" + + exec 9>"$lock_file" + + if ! flock -n 9; then + echo "Factory SSH key setup is already running." >&2 + exit 1 + fi + + generate_factory_key() { ssh-keygen -q -N "$PASSPHRASE" -t ed25519 -f "${keyPath}" chown ${userName}:users "${keyPath}" "${keyPath}.pub" chmod 600 "${keyPath}" chmod 644 "${keyPath}.pub" + } + + if [ ! -f "${keyPath}" ]; then + generate_factory_key + elif ! ssh-keygen -y -P "$PASSPHRASE" -f "${keyPath}" >/dev/null 2>&1; then + backup_suffix="$(date -u +%Y%m%d_%H%M%S)-$$" + backup_path="${keyPath}.bak-$backup_suffix" + backup_index=0 + + while [ -e "$backup_path" ] || [ -e "$backup_path.pub" ]; do + backup_index=$((backup_index + 1)) + backup_path="${keyPath}.bak-$backup_suffix-$backup_index" + done + + echo "Existing factory SSH key does not match current passphrase; backing it up to $backup_path and generating a replacement." + mv "${keyPath}" "$backup_path" + + if [ -f "${keyPath}.pub" ]; then + mv "${keyPath}.pub" "$backup_path.pub" + fi + + generate_factory_key fi ''; };