diff --git a/Partnership/onboard_cancel.sh b/Partnership/onboard_cancel.sh index e8d3d7d..eecf7e4 100755 --- a/Partnership/onboard_cancel.sh +++ b/Partnership/onboard_cancel.sh @@ -1,36 +1,48 @@ #!/bin/bash # ============================================================================================== -# ============================= Onboard Cancel ================================================= +# ============================= Delete Keys ==================================================== # ============================================================================================== # # PURPOSE # ───────────────────────────────────────────────────────────────────────────── -# Undoes Phase 1 of the onboard process: -# 1. Remove HOST1's public key from HOST2's authorized_keys (while key still works) -# 2. Remove phase flags from HOST2's varaverk_setup.db -# 3. Delete local SSH key pair -# 4. Clear phase flags from local varaverk_setup.db -# -# Run on the OWNER. If HOST2 is unreachable the local side is still cleaned up. +# Removes SSH keys between HOST1 and HOST2 in the specified direction. +# Safe to run at any phase. Clears related setup.db flags. # # ============================================================================================== # RUNTIME MODES # ============================================================================================== # -# Partnership/onboard_cancel.sh -# Cancel Phase 1 — remove keys and reset state +# Partnership/onboard_cancel.sh --direction=h1 (default) +# HOST1 → HOST2: remove HOST1's public key from HOST2's authorized_keys, +# delete local HOST1 key pair, clear HOST2 phase flags from setup.db. +# +# Partnership/onboard_cancel.sh --direction=h2 +# HOST2 → HOST1: remove HOST2's public key from HOST1's authorized_keys. +# Identifies the key by HOST2's hostname in the key comment. +# +# Partnership/onboard_cancel.sh --direction=both +# Both directions. # # Partnership/onboard_cancel.sh --dry-run -# Preview all steps without making changes +# Preview without making changes. # # ============================================================================================== SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SCRIPTS_ROOT="$SCRIPT_DIR/.." SSH_TIMEOUT=15 +DIRECTION="h1" +FILTERED_ARGS=() + +for arg in "$@"; do + case "$arg" in + --direction=*) DIRECTION="${arg#--direction=}" ;; + *) FILTERED_ARGS+=("$arg") ;; + esac +done source "$SCRIPTS_ROOT/load_config.sh" -parse_args "$@" +parse_args "${FILTERED_ARGS[@]}" [[ "$EUID" -ne 0 ]] && { error "Must be run as root"; exit 1; } @@ -43,99 +55,98 @@ MIRROR_ID=$( [[ "$OWNER_ID" == "HOST1" ]] && echo "HOST2" || echo "HOST1" ) MIRROR="${!MIRROR_ID}" SSH_KEY_PUB="${SSH_KEY}.pub" STATE_FILE="/boot/config/varaverk_setup.db" +AUTH_KEYS="/root/.ssh/authorized_keys" +MIRROR_SHORT="${MIRROR%%.*}" START=$(date +%s) echo "" -echo "━━━ Cancel Phase 1 — $MY_ID → $MIRROR_ID ($MIRROR) — $(date '+%Y-%m-%d %H:%M:%S') ━━━" -echo "" +echo "━━━ Delete Keys — direction:${DIRECTION} — $(date '+%Y-%m-%d %H:%M:%S') ━━━" [[ "$DRY_RUN" == true ]] && warn "DRY RUN — no changes will be made" echo "" -REMOTE_CLEANED=false -LOCAL_KEY_GONE=false -STATE_CLEARED=false +H1_DONE=false +H2_DONE=false -# ── Step 1: Remove HOST1's public key from HOST2 ────────────────────────────── -echo "━━━ Step 1 — Remove Public Key from $MIRROR_ID ━━━" +# ── HOST1 → HOST2: remove HOST1's key from HOST2 + delete local pair ────────── +if [[ "$DIRECTION" == "h1" || "$DIRECTION" == "both" ]]; then + echo "━━━ HOST1 → HOST2: Remove HOST1 key from $MIRROR ━━━" -if [[ ! -f "$SSH_KEY_PUB" ]]; then - log "No local public key at $SSH_KEY_PUB — nothing to remove from $MIRROR_ID" - REMOTE_CLEANED=true -else - # Extract the key blob (middle field of pub key) — used as a unique identifier. - # Use | as sed delimiter to avoid clashing with base64 / characters in the blob. - KEY_BLOB=$(awk '{print $2}' "$SSH_KEY_PUB") - MIRROR_IP=$(resolve_tailscale_ip "$MIRROR" 2>/dev/null || true) - - if [[ -z "$MIRROR_IP" ]]; then - warn "Cannot resolve $MIRROR Tailscale IP — skipping remote cleanup" - warn "Remove HOST1's public key from $MIRROR:/root/.ssh/authorized_keys manually" - elif [[ "$DRY_RUN" == true ]]; then - warn "DRY RUN — would remove key blob from $MIRROR:/root/.ssh/authorized_keys" - warn "DRY RUN — would clear ${MIRROR_ID}_PHASE* from $MIRROR:/boot/config/varaverk_setup.db" - REMOTE_CLEANED=true + if [[ ! -f "$SSH_KEY_PUB" ]]; then + log "No local public key — nothing to remove from $MIRROR" + H1_DONE=true else - timeout "$SSH_TIMEOUT" ssh -i "$SSH_KEY" \ - -o ConnectTimeout="$SSH_TIMEOUT" -o BatchMode=yes root@"$MIRROR_IP" \ - "sed -i \"|${KEY_BLOB}|d\" /root/.ssh/authorized_keys 2>/dev/null - sed -i \"/^${MIRROR_ID}_PHASE/d\" /boot/config/varaverk_setup.db 2>/dev/null - echo ok" 2>/dev/null | grep -q ok && { - log "HOST1 key removed from $MIRROR authorized_keys ✅" - log "Phase flags cleared on $MIRROR ✅" - REMOTE_CLEANED=true - } || warn "Could not SSH to $MIRROR — remove HOST1 key and phase flags there manually" + KEY_BLOB=$(awk '{print $2}' "$SSH_KEY_PUB") + MIRROR_IP=$(resolve_tailscale_ip "$MIRROR" 2>/dev/null || true) + + if [[ -z "$MIRROR_IP" ]]; then + warn "Cannot resolve $MIRROR Tailscale IP — remove HOST1 key from $MIRROR manually" + elif [[ "$DRY_RUN" == true ]]; then + warn "DRY RUN — would remove HOST1 key from $MIRROR:/root/.ssh/authorized_keys" + H1_DONE=true + else + timeout "$SSH_TIMEOUT" ssh -i "$SSH_KEY" \ + -o ConnectTimeout="$SSH_TIMEOUT" -o BatchMode=yes root@"$MIRROR_IP" \ + "sed -i \"|${KEY_BLOB}|d\" /root/.ssh/authorized_keys 2>/dev/null + sed -i \"/^${MIRROR_ID}_PHASE\|^${MIRROR_ID}_KEY_READY/d\" /boot/config/varaverk_setup.db 2>/dev/null + echo ok" 2>/dev/null | grep -q ok && { + log "HOST1 key removed from $MIRROR authorized_keys ✅" + H1_DONE=true + } || warn "Could not SSH to $MIRROR — remove HOST1 key there manually" + fi fi + + # Delete local key pair + if [[ ! -f "$SSH_KEY" && ! -f "$SSH_KEY_PUB" ]]; then + log "Local key already gone" + elif [[ "$DRY_RUN" == true ]]; then + warn "DRY RUN — would delete: $SSH_KEY and ${SSH_KEY}.pub" + else + rm -f "$SSH_KEY" "$SSH_KEY_PUB" && log "Local key pair deleted ✅" || \ + warn "Failed to delete local key — check permissions" + fi + + # Clear HOST2 phase flags from local setup.db + if [[ -f "$STATE_FILE" ]]; then + if [[ "$DRY_RUN" == false ]]; then + sed -i "/^${MIRROR_ID}_PHASE/d; /^${MIRROR_ID}_KEY_READY/d" "$STATE_FILE" + log "Phase flags cleared from local setup.db ✅" + else + warn "DRY RUN — would clear ${MIRROR_ID}_PHASE* from setup.db" + fi + fi + echo "" fi -# ── Step 2: Delete local SSH key pair ───────────────────────────────────────── -echo "" -echo "━━━ Step 2 — Delete Local SSH Key ━━━" +# ── HOST2 → HOST1: remove HOST2's key from HOST1's authorized_keys ──────────── +if [[ "$DIRECTION" == "h2" || "$DIRECTION" == "both" ]]; then + echo "━━━ HOST2 → HOST1: Remove $MIRROR key from HOST1 ━━━" -if [[ ! -f "$SSH_KEY" && ! -f "$SSH_KEY_PUB" ]]; then - log "Local key already gone" - LOCAL_KEY_GONE=true -elif [[ "$DRY_RUN" == true ]]; then - warn "DRY RUN — would delete: $SSH_KEY" - warn "DRY RUN — would delete: $SSH_KEY_PUB" - LOCAL_KEY_GONE=true -else - rm -f "$SSH_KEY" "$SSH_KEY_PUB" && { - log "Local key pair deleted ✅" - LOCAL_KEY_GONE=true - } || warn "Failed to delete $SSH_KEY — check permissions" -fi - -# ── Step 3: Clear phase flags from local setup.db ───────────────────────────── -echo "" -echo "━━━ Step 3 — Clear Phase State ━━━" - -if [[ ! -f "$STATE_FILE" ]]; then - log "No varaverk_setup.db — nothing to clear" - STATE_CLEARED=true -elif [[ "$DRY_RUN" == true ]]; then - warn "DRY RUN — would remove ${MIRROR_ID}_PHASE* from $STATE_FILE" - STATE_CLEARED=true -else - sed -i "/^${MIRROR_ID}_PHASE/d" "$STATE_FILE" && { - log "Phase flags cleared from local setup.db ✅" - STATE_CLEARED=true - } + if [[ ! -f "$AUTH_KEYS" ]]; then + log "No authorized_keys on HOST1 — nothing to remove" + H2_DONE=true + elif ! grep -qi "$MIRROR_SHORT" "$AUTH_KEYS" 2>/dev/null; then + log "$MIRROR key not found in HOST1 authorized_keys (already removed or never added)" + H2_DONE=true + elif [[ "$DRY_RUN" == true ]]; then + warn "DRY RUN — would remove $MIRROR_SHORT key from $AUTH_KEYS" + H2_DONE=true + else + sed -i "/${MIRROR_SHORT}/Id" "$AUTH_KEYS" && { + log "$MIRROR key removed from HOST1 authorized_keys ✅" + H2_DONE=true + } || warn "Failed to remove $MIRROR key from HOST1 authorized_keys" + fi + echo "" fi # ── Summary ─────────────────────────────────────────────────────────────────── END=$(date +%s) -echo "" -echo "━━━━━ CANCEL SUMMARY ━━━━━" -echo " $MIRROR_ID key removed: $( [[ "$REMOTE_CLEANED" == true ]] && echo "✅" || echo "⚠ manual cleanup needed" )" -echo " Local key deleted: $( [[ "$LOCAL_KEY_GONE" == true ]] && echo "✅" || echo "⚠ still present" )" -echo " Phase state cleared: $( [[ "$STATE_CLEARED" == true ]] && echo "✅" || echo "⚠" )" +echo "━━━━━ DONE ━━━━━" +[[ "$DIRECTION" == "h1" || "$DIRECTION" == "both" ]] && \ + echo " HOST1 → HOST2: $( [[ "$H1_DONE" == true ]] && echo "✅" || echo "⚠ manual step may be needed" )" +[[ "$DIRECTION" == "h2" || "$DIRECTION" == "both" ]] && \ + echo " HOST2 → HOST1: $( [[ "$H2_DONE" == true ]] && echo "✅" || echo "⚠" )" echo " Duration: $(format_duration $(( END - START )))" -echo "" -if [[ "$REMOTE_CLEANED" == false ]]; then - echo " Manual cleanup on $MIRROR:" - echo " sed -i '/$(awk "{print \$3}" "$SSH_KEY_PUB" 2>/dev/null || echo "HOST1_key_comment")//d' /root/.ssh/authorized_keys" -fi -echo " Run Phase 1 again to restart the onboard process." echo "━━━━━━━━━━━━━━━━━━━━━━━" exit 0 diff --git a/Plugin/unraid/pages/partnership.php b/Plugin/unraid/pages/partnership.php index f5697ef..643ef9f 100644 --- a/Plugin/unraid/pages/partnership.php +++ b/Plugin/unraid/pages/partnership.php @@ -56,9 +56,10 @@