diff --git a/00-migrate-root.sh b/00-migrate-root.sh new file mode 100644 index 0000000..3e8dc24 --- /dev/null +++ b/00-migrate-root.sh @@ -0,0 +1,243 @@ +#!/bin/bash -eu +# ============================================================================= +# 00-migrate-root.sh — Run as ROOT +# 1. Full backup (keys, peers, nginx, certs, ufw, env) +# 2. Shuts down legacy stack +# 3. Cleans system for fresh install +# ============================================================================= +if [ "$EUID" -ne 0 ]; then + echo "[X] Run as root: sudo bash 00-migrate-root.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, cleanup 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 +[ -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" +_save /etc/nginx/sites-available nginx/sites-available +_save /etc/nginx/sites-enabled nginx/sites-enabled +_save /etc/nginx/nginx.conf nginx/nginx.conf + +# ─── 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 ─────────────────────────────────────────────────────────── +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 "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" + +# ─── SHUT DOWN LEGACY STACK ─────────────────────────────────────────────────── +sep +warn "Shutting down legacy stack..." +cd "$LEGACY_COMPOSE_DIR" +docker compose down --remove-orphans 2>/dev/null || true + +for CNAME in $(docker ps -a --format '{{.Names}} {{.Image}}' | \ + grep -iE "linuxserver/wireguard|lscr.io/linuxserver/wireguard|wireguard-api|sp-wireguard-api" | \ + awk '{print $1}' || true); do + docker stop "$CNAME" 2>/dev/null || true + docker rm "$CNAME" 2>/dev/null || true + warn "Removed: $CNAME" +done +ok "Legacy stack stopped." + +# ─── CLEAN NGINX ────────────────────────────────────────────────────────────── +sep +info "Cleaning Nginx for fresh install..." +for SITE in /etc/nginx/sites-enabled/*; do + rm -f "$SITE" && warn "Removed from sites-enabled: $(basename $SITE)" +done +for SITE in /etc/nginx/sites-available/*; do + NAME=$(basename "$SITE") + [ "$NAME" = "default" ] && continue + rm -f "$SITE" && warn "Removed from sites-available: $NAME" +done +rm -f /etc/nginx/sites-enabled/certbot-temp /etc/nginx/sites-available/certbot-temp 2>/dev/null || true +systemctl stop nginx 2>/dev/null || true +ok "Nginx cleaned." + +# ─── VERIFY PORT 51820 FREE ─────────────────────────────────────────────────── +sep +info "Checking port 51820/UDP..." +sleep 3 +ss -ulnp 2>/dev/null | grep -q ":51820" && fail "Port 51820 still in use." || ok "Port 51820/UDP free." + +echo "[END-00] $(date '+%Y-%m-%d %H:%M:%S')" >> "$LOG" + +echo "" +echo "=========================================" +echo " Backup complete — System ready" +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 " Once SP authorizes access, run:" +echo " bash 01-migrate-user.sh" +echo "" +echo " Log: cat $LOG" +echo "========================================="