feat: add cross-host SSH trust, state-aware teardown, and configurable migration polling

- Add setup/cross_host_ssh.sh to establish ed25519 SSH trust between
  Unraid and Fedora (required by backup/restore scripts for direct SCP)
- Add ssh_key and authorized_key cleanup handlers to setup/cleanup.sh
- Rewrite phase8 cutover to mark GitHub repos as mirrors instead of
  archiving them (archived repos reject push mirror writes), with a
  JSON state snapshot of pre-cutover settings (description, homepage,
  wiki, projects, Pages) for exact restoration on teardown
- Rewrite phase8 teardown to restore from state snapshot with fallback
  to legacy "— was:" description parsing
- Make migration polling configurable via MIGRATION_POLL_INTERVAL_SEC
  and MIGRATION_POLL_TIMEOUT_SEC in .env (was hardcoded 120s/3s)
- Fix preflight SSL validation: check SSL_MODE instead of always
  requiring SSL_EMAIL, add conditional checks per SSL_MODE
- Add preflight checks 23-24: cross-host SSH connectivity
- Add --start-from range validation and cross_host_ssh.sh to run_all.sh

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
S
2026-02-28 20:50:41 -05:00
parent dc08375ad0
commit 316d318b5e
8 changed files with 509 additions and 31 deletions

View File

@@ -166,6 +166,27 @@ cleanup_xcode_cli() {
sudo rm -rf /Library/Developer/CommandLineTools 2>/dev/null || log_warn "Failed to remove Xcode CLI Tools (may need sudo)"
}
cleanup_ssh_key() {
local host_key="$1" path="$2"
if [[ "$DRY_RUN" == "true" ]]; then
log_info "[dry-run] Would remove SSH key pair on ${host_key}: ${path}"
return 0
fi
log_info "Removing SSH key pair on ${host_key}: ${path}"
# No single quotes around path — tilde must expand on the remote shell
ssh_exec "$host_key" "rm -f ${path} ${path}.pub" 2>/dev/null || log_warn "Failed to remove SSH key on ${host_key}"
}
cleanup_authorized_key() {
local host_key="$1" marker="$2"
if [[ "$DRY_RUN" == "true" ]]; then
log_info "[dry-run] Would remove authorized_key entry '${marker}' on ${host_key}"
return 0
fi
log_info "Removing authorized_key entry '${marker}' on ${host_key}"
ssh_exec "$host_key" "sed -i '/# ${marker}/d' ~/.ssh/authorized_keys" 2>/dev/null || log_warn "Failed to remove authorized_key '${marker}' on ${host_key}"
}
# ---------------------------------------------------------------------------
# Map host names to SSH host keys for remote operations
# ---------------------------------------------------------------------------
@@ -269,6 +290,30 @@ for host in "${HOSTS[@]}"; do
FAILED=$((FAILED + 1))
fi
;;
ssh_key)
if [[ -z "$ssh_key" ]]; then
log_warn "Cannot clean up ssh_key '$target' — no SSH key for host '$host'"
FAILED=$((FAILED + 1))
continue
fi
if cleanup_ssh_key "$ssh_key" "$target"; then
CLEANED=$((CLEANED + 1))
else
FAILED=$((FAILED + 1))
fi
;;
authorized_key)
if [[ -z "$ssh_key" ]]; then
log_warn "Cannot clean up authorized_key '$target' — no SSH key for host '$host'"
FAILED=$((FAILED + 1))
continue
fi
if cleanup_authorized_key "$ssh_key" "$target"; then
CLEANED=$((CLEANED + 1))
else
FAILED=$((FAILED + 1))
fi
;;
*)
log_warn "Unknown action type '${action_type}' for target '${target}' — skipping"
FAILED=$((FAILED + 1))

148
setup/cross_host_ssh.sh Executable file
View File

@@ -0,0 +1,148 @@
#!/usr/bin/env bash
set -euo pipefail
# =============================================================================
# setup/cross_host_ssh.sh — Establish SSH trust between Unraid and Fedora
#
# The backup/restore scripts do direct SCP between these hosts. This script
# generates ed25519 key pairs on each remote (if missing) and distributes
# public keys to the other host's authorized_keys.
#
# Runs from MacBook — orchestrates via SSH to both remotes.
# Idempotent: skips key generation if keys exist, skips authorized_keys
# entries if already present.
# =============================================================================
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
source "${SCRIPT_DIR}/../lib/common.sh"
load_env
require_vars UNRAID_IP UNRAID_SSH_USER UNRAID_SSH_PORT \
FEDORA_IP FEDORA_SSH_USER FEDORA_SSH_PORT
log_info "=== Cross-Host SSH Trust Setup ==="
# ---------------------------------------------------------------------------
# Verify MacBook can reach both hosts first
# ---------------------------------------------------------------------------
log_info "Verifying MacBook can reach both hosts..."
if ! ssh_check UNRAID; then
log_error "Cannot SSH to Unraid — run setup/unraid.sh first"
exit 1
fi
if ! ssh_check FEDORA; then
log_error "Cannot SSH to Fedora — run setup/fedora.sh first"
exit 1
fi
log_success "MacBook can reach both hosts"
# ---------------------------------------------------------------------------
# Manifest — track key generation and authorized_keys entries for rollback.
# We record to both hosts' manifests since each gets a key pair AND an
# authorized_keys entry (from the other host).
# ---------------------------------------------------------------------------
manifest_init "unraid"
manifest_init "fedora"
# Comment markers appended to authorized_keys entries so cleanup can
# identify and remove exactly the lines this script added.
UNRAID_KEY_MARKER="gitea-migration-from-unraid"
FEDORA_KEY_MARKER="gitea-migration-from-fedora"
# ---------------------------------------------------------------------------
# Step 1: Generate SSH key pair on Unraid (if not present)
# Uses ed25519 — modern, fast, small keys. No passphrase (automation key).
# ---------------------------------------------------------------------------
log_info "Checking SSH key on Unraid..."
if ssh_exec UNRAID "test -f ~/.ssh/id_ed25519" 2>/dev/null; then
log_success "Unraid already has an ed25519 key — skipping generation"
else
log_info "Generating ed25519 key pair on Unraid..."
ssh_exec UNRAID "mkdir -p ~/.ssh && chmod 700 ~/.ssh && ssh-keygen -t ed25519 -N '' -f ~/.ssh/id_ed25519 -C '${UNRAID_SSH_USER}@unraid-gitea-migration'"
# shellcheck disable=SC2088 # tilde is intentionally literal — expanded by remote shell
manifest_record "unraid" "ssh_key" "~/.ssh/id_ed25519"
log_success "Unraid SSH key generated"
fi
# ---------------------------------------------------------------------------
# Step 2: Generate SSH key pair on Fedora (if not present)
# ---------------------------------------------------------------------------
log_info "Checking SSH key on Fedora..."
if ssh_exec FEDORA "test -f ~/.ssh/id_ed25519" 2>/dev/null; then
log_success "Fedora already has an ed25519 key — skipping generation"
else
log_info "Generating ed25519 key pair on Fedora..."
ssh_exec FEDORA "mkdir -p ~/.ssh && chmod 700 ~/.ssh && ssh-keygen -t ed25519 -N '' -f ~/.ssh/id_ed25519 -C '${FEDORA_SSH_USER}@fedora-gitea-migration'"
# shellcheck disable=SC2088 # tilde is intentionally literal — expanded by remote shell
manifest_record "fedora" "ssh_key" "~/.ssh/id_ed25519"
log_success "Fedora SSH key generated"
fi
# ---------------------------------------------------------------------------
# Step 3: Distribute Unraid's public key to Fedora's authorized_keys
# The key line gets a comment marker so cleanup.sh can identify and remove it.
# ---------------------------------------------------------------------------
log_info "Distributing Unraid's public key to Fedora..."
UNRAID_PUBKEY=$(ssh_exec UNRAID "cat ~/.ssh/id_ed25519.pub")
if [[ -z "$UNRAID_PUBKEY" ]]; then
log_error "Failed to read Unraid's public key"
exit 1
fi
if ssh_exec FEDORA "grep -qF '${UNRAID_KEY_MARKER}' ~/.ssh/authorized_keys 2>/dev/null"; then
log_success "Unraid's key already in Fedora's authorized_keys — skipping"
else
ssh_exec FEDORA "mkdir -p ~/.ssh && chmod 700 ~/.ssh && printf '%s %s\n' '${UNRAID_PUBKEY}' '# ${UNRAID_KEY_MARKER}' >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
manifest_record "fedora" "authorized_key" "${UNRAID_KEY_MARKER}"
log_success "Unraid's public key added to Fedora's authorized_keys"
fi
# ---------------------------------------------------------------------------
# Step 4: Distribute Fedora's public key to Unraid's authorized_keys
# ---------------------------------------------------------------------------
log_info "Distributing Fedora's public key to Unraid..."
FEDORA_PUBKEY=$(ssh_exec FEDORA "cat ~/.ssh/id_ed25519.pub")
if [[ -z "$FEDORA_PUBKEY" ]]; then
log_error "Failed to read Fedora's public key"
exit 1
fi
if ssh_exec UNRAID "grep -qF '${FEDORA_KEY_MARKER}' ~/.ssh/authorized_keys 2>/dev/null"; then
log_success "Fedora's key already in Unraid's authorized_keys — skipping"
else
ssh_exec UNRAID "mkdir -p ~/.ssh && chmod 700 ~/.ssh && printf '%s %s\n' '${FEDORA_PUBKEY}' '# ${FEDORA_KEY_MARKER}' >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
manifest_record "unraid" "authorized_key" "${FEDORA_KEY_MARKER}"
log_success "Fedora's public key added to Unraid's authorized_keys"
fi
# ---------------------------------------------------------------------------
# Step 5: Verify cross-host SSH connectivity
# Uses the same test commands as preflight checks 23-24 to ensure the
# exact path the backup/restore scripts will use actually works.
# ---------------------------------------------------------------------------
log_info "Verifying Unraid → Fedora SSH..."
if ssh_exec UNRAID "ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new -o BatchMode=yes -p '${FEDORA_SSH_PORT:-22}' '${FEDORA_SSH_USER}@${FEDORA_IP}' true" &>/dev/null; then
log_success "Unraid → Fedora SSH works"
else
log_error "Unraid → Fedora SSH failed after key distribution"
log_error "Check that ${FEDORA_SSH_USER}@${FEDORA_IP}:${FEDORA_SSH_PORT:-22} accepts key auth"
exit 1
fi
log_info "Verifying Fedora → Unraid SSH..."
if ssh_exec FEDORA "ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new -o BatchMode=yes -p '${UNRAID_SSH_PORT:-22}' '${UNRAID_SSH_USER}@${UNRAID_IP}' true" &>/dev/null; then
log_success "Fedora → Unraid SSH works"
else
log_error "Fedora → Unraid SSH failed after key distribution"
log_error "Check that ${UNRAID_SSH_USER}@${UNRAID_IP}:${UNRAID_SSH_PORT:-22} accepts key auth"
exit 1
fi
# ---------------------------------------------------------------------------
# Summary
# ---------------------------------------------------------------------------
log_success "Cross-host SSH trust established"
log_info " Unraid → Fedora: backup/backup_primary.sh can SCP dumps"
log_info " Fedora → Unraid: backup/restore_to_primary.sh can SCP archives"