#!/usr/bin/env bash # ============================================================================== # nx9-wg Production Rollback Tool # ============================================================================== # PURPOSE: # Rolls back the active production binary (/usr/local/bin/nx9-wg) to the most # recent (or specified) backup binary created during previous deployments. # # PREREQUISITES: # - Root privileges (or sudo) # - At least one backup binary at /usr/local/bin/nx9-wg.backup.* # # SAFETY INVARIANTS: # - Never modifies or deletes the SQLite database. # - Restores executable permissions (0755) and root ownership. # - Confirms service restoration and binary version after rollback. # # USAGE: # sudo bash scripts/rollback.sh [OPTIONS] [SPECIFIC_BACKUP_PATH] # # OPTIONS: # -y, --yes Skip confirmation prompt # -l, --list List available backup binaries and exit # -h, --help Show this help message # ============================================================================== set -euo pipefail BIN_DIR="/usr/local/bin" ACTIVE_BIN="${BIN_DIR}/nx9-wg" ASSUME_YES=0 LIST_ONLY=0 SPECIFIED_BACKUP="" usage() { cat <&2 usage ;; *) SPECIFIED_BACKUP="$1" shift ;; esac done log() { echo -e "\033[1;34m[ROLLBACK]\033[0m \033[1;37m$*\033[0m" } success() { echo -e "\033[1;32m[SUCCESS]\033[0m $*" } warn() { echo -e "\033[1;33m[WARN]\033[0m $*" } error() { echo -e "\033[1;31m[ERROR]\033[0m $*" >&2 exit 1 } # 1. Privilege Verification (unless list only) if [[ "${EUID}" -ne 0 && "${LIST_ONLY}" -eq 0 ]]; then error "Rollback must be run as root (or via sudo)." fi # 2. Discover Available Backups BACKUPS=($(ls -1t "${ACTIVE_BIN}".backup.* 2>/dev/null || true)) if [[ "${#BACKUPS[@]}" -eq 0 ]]; then error "No backup binaries found in ${BIN_DIR} matching nx9-wg.backup.*" fi if [[ "${LIST_ONLY}" -eq 1 ]]; then echo "Available production backup binaries in ${BIN_DIR}:" for b in "${BACKUPS[@]}"; do SIZE="$(du -h "$b" | cut -f1)" DATE="$(date -r "$b" '+%Y-%m-%d %H:%M:%S')" echo " $b (${SIZE}, ${DATE})" done exit 0 fi # 3. Select Target Backup TARGET_BACKUP="" if [[ -n "${SPECIFIED_BACKUP}" ]]; then if [[ -f "${SPECIFIED_BACKUP}" ]]; then TARGET_BACKUP="${SPECIFIED_BACKUP}" elif [[ -f "${BIN_DIR}/${SPECIFIED_BACKUP}" ]]; then TARGET_BACKUP="${BIN_DIR}/${SPECIFIED_BACKUP}" else error "Specified backup file not found: ${SPECIFIED_BACKUP}" fi else TARGET_BACKUP="${BACKUPS[0]}" fi log "Selected rollback target: ${TARGET_BACKUP}" BACKUP_SIZE="$(du -h "${TARGET_BACKUP}" | cut -f1)" BACKUP_DATE="$(date -r "${TARGET_BACKUP}" '+%Y-%m-%d %H:%M:%S')" echo " Size: ${BACKUP_SIZE}" echo " Timestamp: ${BACKUP_DATE}" # 4. Confirmation Prompt if [[ "${ASSUME_YES}" -eq 0 ]]; then echo -e "\n\033[1;33mAre you sure you want to replace active binary ${ACTIVE_BIN} with ${TARGET_BACKUP}?\033[0m" read -r -p "Type 'yes' to proceed with rollback: " CONFIRM if [[ "${CONFIRM}" != "yes" ]]; then error "Rollback aborted by user." fi fi # 5. Execute Rollback if command -v systemctl >/dev/null 2>&1; then if systemctl is-active --quiet nx9-wg 2>/dev/null; then log "Stopping nx9-wg service..." systemctl stop nx9-wg || true fi fi log "Restoring binary from ${TARGET_BACKUP} to ${ACTIVE_BIN}..." install -m 0755 "${TARGET_BACKUP}" "${ACTIVE_BIN}" # 6. Restart Service if command -v systemctl >/dev/null 2>&1; then log "Starting nx9-wg service..." systemctl start nx9-wg || error "Failed to restart nx9-wg service after rollback." sleep 1 if systemctl is-active --quiet nx9-wg; then success "nx9-wg.service is active and running." else warn "nx9-wg.service is not active. Checking logs:" journalctl -u nx9-wg -n 20 --no-pager || true error "Service failed to become active after rollback." fi fi # 7. Verify Restored Version RESTORED_VER="$("${ACTIVE_BIN}" version | head -n 1)" success "Rollback successful. Active binary version: ${RESTORED_VER}" echo -e "\n=================================================================" echo -e "\033[1;32m PRODUCTION ROLLBACK COMPLETED SUCCESSFULLY!\033[0m" echo -e "=================================================================\n"