#!/bin/bash -eu # ============================================================================= # 00-backup.sh — Run as ROOT via: sudo bash steps/00-backup.sh # 1. Full backup (keys, peers, nginx, certs, ufw, env) # NOTE: does NOT shut down legacy stack — that happens in 02-peers.sh # only after the new WireGuard is verified and running. # ============================================================================= if [ "$EUID" -ne 0 ]; then echo "[X] Run as root: sudo bash steps/00-backup.sh" exit 1 fi RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; RESET='\033[0m' LINUX_USER="${SUDO_USER:-}" if [ -z "$LINUX_USER" ] || [ "$LINUX_USER" = "root" ]; then LINUX_USER=$(getent passwd | awk -F: '$3 >= 1000 && $3 < 65534 {print $1}' | head -1 || true) fi [ -z "$LINUX_USER" ] && { echo -e "${RED}[X]${RESET} Could not detect Linux user."; exit 1; } LINUX_HOME=$(getent passwd "$LINUX_USER" | cut -d: -f6) BACKUP_DIR="$LINUX_HOME/.wg-migration" LOG="$BACKUP_DIR/migration.log" mkdir -p "$BACKUP_DIR" chmod 700 "$BACKUP_DIR" chown "$LINUX_USER":"$LINUX_USER" "$BACKUP_DIR" : > "$LOG" chown "$LINUX_USER":"$LINUX_USER" "$LOG" ok() { echo -e "${GREEN}[OK]${RESET} $1"; echo "[OK] $1" >> "$LOG"; } info() { echo -e "${CYAN}[i]${RESET} $1"; echo "[i] $1" >> "$LOG"; } warn() { echo -e "${YELLOW}[!]${RESET} $1"; echo "[!] $1" >> "$LOG"; } fail() { echo -e "${RED}[X]${RESET} $1"; echo "[X] $1" >> "$LOG"; exit 1; } sep() { echo -e "\n${CYAN}------------------------------------${RESET}"; } _save() { if [ -e "$1" ]; then cp -r "$1" "$BACKUP_DIR/$2" 2>/dev/null || true chown -R "$LINUX_USER":"$LINUX_USER" "$BACKUP_DIR/$2" 2>/dev/null || true ok "Saved: $1 → $2" else warn "Not found (skipped): $1" fi } echo "" echo "=========================================" echo " 00 - Backup and preparation" echo " $(date '+%Y-%m-%d %H:%M:%S')" echo " User : $LINUX_USER" echo " Backup : $BACKUP_DIR" echo "=========================================" echo "[START-00] $(date '+%Y-%m-%d %H:%M:%S')" >> "$LOG" # ─── DOCKER ─────────────────────────────────────────────────────────────────── sep info "Checking Docker..." command -v docker &>/dev/null || fail "Docker not installed." ok "Docker: $(docker --version)" # ─── DETECT LEGACY WIREGUARD ────────────────────────────────────────────────── sep info "Detecting legacy WireGuard container..." LEGACY_WG=$(docker ps --format '{{.Names}} {{.Image}}' | \ grep -i "linuxserver/wireguard\|lscr.io/linuxserver/wireguard" | \ awk '{print $1}' | head -1 || true) if [ -z "$LEGACY_WG" ]; then LEGACY_WG=$(docker ps --format '{{.Names}}' | \ grep -iE "^wireguard$|^wg$|^sp-wireguard$" | head -1 || true) fi [ -z "$LEGACY_WG" ] && fail "No legacy WireGuard container found. Check: docker ps" ok "Legacy WireGuard: $LEGACY_WG" # ─── DETECT LEGACY STACK ────────────────────────────────────────────────────── sep info "Detecting legacy stack location..." LEGACY_COMPOSE_DIR="" for DIR in "$LINUX_HOME/sp-env-wireguard" \ "$LINUX_HOME/sp-env-wireguard-ipv6" \ "$LINUX_HOME/wireguard" \ /root/sp-env-wireguard \ /root/sp-env-wireguard-ipv6 \ /opt/sp-env-wireguard \ /srv/wireguard; do if [ -f "$DIR/docker-compose.yml" ] && grep -q "wireguard" "$DIR/docker-compose.yml" 2>/dev/null; then LEGACY_COMPOSE_DIR="$DIR" break fi done if [ -z "$LEGACY_COMPOSE_DIR" ]; then LEGACY_COMPOSE_DIR=$(find "$LINUX_HOME" /root -maxdepth 6 -name "docker-compose.yml" 2>/dev/null | \ while read -r f; do grep -q "wireguard" "$f" 2>/dev/null && dirname "$f" && break done | head -1) fi [ -z "$LEGACY_COMPOSE_DIR" ] && fail "Legacy docker-compose.yml not found." ok "Legacy stack: $LEGACY_COMPOSE_DIR" echo "$LEGACY_COMPOSE_DIR" > "$BACKUP_DIR/legacy_compose_dir" chown "$LINUX_USER":"$LINUX_USER" "$BACKUP_DIR/legacy_compose_dir" # ─── BACKUP WIREGUARD KEYS AND PEERS ────────────────────────────────────────── sep info "Backing up WireGuard keys and peers..." WG_CONF_CONTENT=$(docker exec "$LEGACY_WG" cat /config/wg_confs/wg0.conf 2>/dev/null || true) [ -z "$WG_CONF_CONTENT" ] && fail "Could not read wg0.conf from container $LEGACY_WG" LEGACY_PRIVATE_KEY=$(echo "$WG_CONF_CONTENT" | grep "^PrivateKey" | awk '{print $3}' | tr -d '[:space:]') [ -z "$LEGACY_PRIVATE_KEY" ] && fail "PrivateKey not found in wg0.conf" echo "$LEGACY_PRIVATE_KEY" > "$BACKUP_DIR/private_key" chmod 600 "$BACKUP_DIR/private_key" chown "$LINUX_USER":"$LINUX_USER" "$BACKUP_DIR/private_key" ok "Private key saved." LEGACY_PUBLIC_KEY=$(docker exec "$LEGACY_WG" cat /config/server/publickey-server 2>/dev/null | tr -d '[:space:]' || true) if [ -z "$LEGACY_PUBLIC_KEY" ]; then warn "publickey-server not found — deriving from private key..." LEGACY_PUBLIC_KEY=$(echo "$LEGACY_PRIVATE_KEY" | wg pubkey 2>/dev/null || true) fi [ -z "$LEGACY_PUBLIC_KEY" ] && fail "Could not obtain public key." echo "$LEGACY_PUBLIC_KEY" > "$BACKUP_DIR/public_key" chmod 600 "$BACKUP_DIR/public_key" chown "$LINUX_USER":"$LINUX_USER" "$BACKUP_DIR/public_key" ok "Public key: $LEGACY_PUBLIC_KEY" PEER_CONF=$(docker exec "$LEGACY_WG" wg showconf wg0 2>/dev/null || echo "$WG_CONF_CONTENT") echo "$PEER_CONF" > "$BACKUP_DIR/wg0-legacy.conf" cp "$BACKUP_DIR/wg0-legacy.conf" "$BACKUP_DIR/wg0-legacy.conf.original" chmod 600 "$BACKUP_DIR/wg0-legacy.conf" "$BACKUP_DIR/wg0-legacy.conf.original" chown "$LINUX_USER":"$LINUX_USER" "$BACKUP_DIR/wg0-legacy.conf" "$BACKUP_DIR/wg0-legacy.conf.original" PEER_COUNT=$(grep -c "^\[Peer\]" "$BACKUP_DIR/wg0-legacy.conf" || echo 0) ok "$PEER_COUNT peers saved." # ─── BACKUP NGINX ───────────────────────────────────────────────────────────── sep info "Backing up Nginx..." mkdir -p "$BACKUP_DIR/nginx/sites-available" "$BACKUP_DIR/nginx/sites-enabled" cp -r /etc/nginx/sites-available/. "$BACKUP_DIR/nginx/sites-available/" 2>/dev/null || true cp -r /etc/nginx/sites-enabled/. "$BACKUP_DIR/nginx/sites-enabled/" 2>/dev/null || true _save /etc/nginx/nginx.conf nginx/nginx.conf chown -R "$LINUX_USER":"$LINUX_USER" "$BACKUP_DIR/nginx/" ok "Nginx configs backed up." # ─── BACKUP SSL CERTIFICATES ────────────────────────────────────────────────── sep info "Backing up SSL certificates..." mkdir -p "$BACKUP_DIR/letsencrypt" if [ -d /etc/letsencrypt ]; then cp -r /etc/letsencrypt/. "$BACKUP_DIR/letsencrypt/" chown -R "$LINUX_USER":"$LINUX_USER" "$BACKUP_DIR/letsencrypt/" ok "Certificates saved." else warn "/etc/letsencrypt not found." fi # ─── BACKUP UFW ─────────────────────────────────────────────────────────────── sep info "Backing up UFW rules..." ufw status verbose > "$BACKUP_DIR/ufw-snapshot.txt" 2>/dev/null || true chown "$LINUX_USER":"$LINUX_USER" "$BACKUP_DIR/ufw-snapshot.txt" ok "UFW snapshot saved." # ─── BACKUP LEGACY ENV ──────────────────────────────────────────────────────── sep info "Backing up legacy .env..." _save "$LEGACY_COMPOSE_DIR/.env" legacy.env # ─── DETECT NETWORK ─────────────────────────────────────────────────────────── sep IPV6_ADDRS=$(ip -6 addr show | grep -oP 'inet6 \K[0-9a-fA-F:]+' | grep -v '^::1$' || true) if [ -n "$IPV6_ADDRS" ]; then echo "ipv6=true" > "$BACKUP_DIR/network_info" ok "IPv6 detected." else echo "ipv6=false" > "$BACKUP_DIR/network_info" info "IPv4 only." fi chown "$LINUX_USER":"$LINUX_USER" "$BACKUP_DIR/network_info" # ─── SSH KEY ────────────────────────────────────────────────────────────────── sep info "Adding $LINUX_USER to docker group..." usermod -aG docker "$LINUX_USER" 2>/dev/null || true ok "$LINUX_USER added to docker group. Changes take effect on next login." info "Checking SSH key for private repo..." mkdir -p "$LINUX_HOME/.ssh" chmod 700 "$LINUX_HOME/.ssh" chown "$LINUX_USER":"$LINUX_USER" "$LINUX_HOME/.ssh" if [ ! -f "$LINUX_HOME/.ssh/id_ed25519" ]; then sudo -u "$LINUX_USER" ssh-keygen -t ed25519 -f "$LINUX_HOME/.ssh/id_ed25519" -N "" -q ok "SSH key generated." else ok "SSH key already exists." fi SSH_PUBKEY=$(cat "$LINUX_HOME/.ssh/id_ed25519.pub") echo "$SSH_PUBKEY" > "$BACKUP_DIR/ssh_pubkey" chown "$LINUX_USER":"$LINUX_USER" "$BACKUP_DIR/ssh_pubkey" # ─── NOTE: legacy stack NOT shut down here ──────────────────────────────────── # go-legacy continues running until 02-peers.sh confirms the new # WireGuard is up and identity is preserved. This avoids any VPN downtime # during the install phase. echo "[END-00] $(date '+%Y-%m-%d %H:%M:%S')" >> "$LOG" echo "" echo "=========================================" echo " Backup complete — legacy still running" echo "=========================================" echo "" echo " Backup dir : $BACKUP_DIR" echo " Legacy stack : $LEGACY_COMPOSE_DIR" echo " Public key : $LEGACY_PUBLIC_KEY" echo " Peers saved : $PEER_COUNT" echo "" echo " SSH key for Simplified Privacy:" echo " $SSH_PUBKEY" echo "" echo " go-legacy is still running." echo " It will be stopped by 02-peers.sh" echo " only after the new WireGuard is verified." echo "" echo " Log: cat $LOG" echo "========================================="