operator-migration/01-migrate-user.sh

244 lines
9.7 KiB
Bash

#!/bin/bash -eu
# =============================================================================
# 01-migrate-user.sh — Run as Linux user (not root)
# Installs operator-docker and prepares for peer migration
# Auto-rollback: restores nginx, certs and legacy stack on failure
# =============================================================================
if [ "$EUID" -eq 0 ]; then
echo "[X] Run as your Linux user, not root"
exit 1
fi
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; RESET='\033[0m'
BACKUP_DIR="$HOME/.wg-migration"
LOG="$BACKUP_DIR/migration.log"
REPO_URL="git@git.simplifiedprivacy.com:zenaku/operator-docker.git"
OPERATOR_DIR="$HOME/operator-docker"
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}"; }
rollback_01() {
echo ""
warn "Installation failed — starting rollback..."
echo "[ROLLBACK-01] $(date '+%Y-%m-%d %H:%M:%S')" >> "$LOG"
[ -d "$OPERATOR_DIR" ] && rm -rf "$OPERATOR_DIR" && warn "operator-docker removed."
if [ -d "$BACKUP_DIR/nginx/sites-available" ]; then
sudo cp -r "$BACKUP_DIR/nginx/sites-available/." /etc/nginx/sites-available/ 2>/dev/null || true
sudo cp -r "$BACKUP_DIR/nginx/sites-enabled/." /etc/nginx/sites-enabled/ 2>/dev/null || true
warn "Nginx restored."
fi
if [ -d "$BACKUP_DIR/letsencrypt" ]; then
sudo cp -r "$BACKUP_DIR/letsencrypt/." /etc/letsencrypt/ 2>/dev/null || true
warn "Certificates restored."
fi
LEGACY_COMPOSE_DIR=$(cat "$BACKUP_DIR/legacy_compose_dir" 2>/dev/null || true)
if [ -n "$LEGACY_COMPOSE_DIR" ] && [ -d "$LEGACY_COMPOSE_DIR" ]; then
sudo modprobe ip6table_filter 2>/dev/null || true
cd "$LEGACY_COMPOSE_DIR"
docker compose up -d 2>/dev/null || true
sleep 5
LEGACY_UP=$(docker ps --format '{{.Names}}' | grep -i wireguard | head -1 || true)
[ -n "$LEGACY_UP" ] && ok "Legacy restored: $LEGACY_UP" || \
warn "Legacy failed to start. Run manually: cd $LEGACY_COMPOSE_DIR && docker compose up -d"
fi
sudo systemctl start nginx 2>/dev/null || true
warn "Rollback complete. Check: cat $LOG"
}
trap rollback_01 ERR
echo ""
echo "========================================="
echo " 01 - operator-docker installation"
echo " $(date '+%Y-%m-%d %H:%M:%S')"
echo "========================================="
echo "[START-01] $(date '+%Y-%m-%d %H:%M:%S')" >> "$LOG"
# ─── PREREQUISITES ────────────────────────────────────────────────────────────
sep
info "Checking prerequisites..."
if ! docker info &>/dev/null; then
if groups | grep -q docker; then
fail "Docker socket not accessible. Try logging out and back in."
else
warn "No Docker access — re-launching with docker group..."
exec newgrp docker "$0" "$@"
fi
fi
[ -f "$BACKUP_DIR/private_key" ] || fail "No backup found. Run: sudo bash 00-migrate-root.sh"
[ -f "$BACKUP_DIR/wg0-legacy.conf" ] || fail "No peer backup found. Run: sudo bash 00-migrate-root.sh"
[ -f "$BACKUP_DIR/legacy_compose_dir" ] || fail "No legacy snapshot found. Run: sudo bash 00-migrate-root.sh"
ok "Backups found."
# ─── CLEAN FAILED INSTALL ─────────────────────────────────────────────────────
sep
info "Checking for previous installation..."
_env_is_corrupt() {
local env_file="$1"
[ ! -f "$env_file" ] && return 1
local base_domain
base_domain=$(grep -E '^BASE_DOMAIN=' "$env_file" 2>/dev/null \
| head -1 | cut -d= -f2- | tr -d '[:space:]' || true)
[ -z "$base_domain" ] && { warn ".env: BASE_DOMAIN is empty."; return 0; }
echo "$base_domain" | grep -qE '[/;|&<> ]' && {
warn ".env: BASE_DOMAIN contains invalid characters: '$base_domain'"; return 0; }
echo "$base_domain" | grep -qE '^[0-9]+-|^ash' && {
warn ".env: BASE_DOMAIN looks like a script name: '$base_domain'"; return 0; }
[ "${#base_domain}" -gt 253 ] && {
warn ".env: BASE_DOMAIN too long (${#base_domain} chars)."; return 0; }
return 1
}
_purge_failed_install() {
warn "Cleaning up failed installation..."
echo "[PURGE] $(date '+%Y-%m-%d %H:%M:%S')" >> "$LOG"
if [ -f "$OPERATOR_DIR/docker-compose.yml" ]; then
cd "$OPERATOR_DIR"
docker compose down --remove-orphans 2>/dev/null && warn "Previous stack stopped." || true
cd "$HOME"
fi
local project
project=$(basename "$OPERATOR_DIR" | tr '[:upper:]' '[:lower:]' | tr -cs 'a-z0-9' '-')
while IFS= read -r cname; do
docker rm -f "$cname" 2>/dev/null && warn "Container removed: $cname" || true
done < <(docker ps -a --format '{{.Names}}' | grep -i "^${project}" 2>/dev/null || true)
rm -rf "$OPERATOR_DIR"
ok "Directory $OPERATOR_DIR removed."
}
if [ -d "$OPERATOR_DIR" ]; then
if _env_is_corrupt "$OPERATOR_DIR/.env"; then
warn "Corrupt .env detected from previous attempt."
_purge_failed_install
elif [ ! -f "$OPERATOR_DIR/docker-compose.yml" ]; then
warn "Incomplete repo (missing docker-compose.yml). Cleaning..."
_purge_failed_install
else
warn "Existing directory found. Removing for fresh install..."
_purge_failed_install
fi
else
info "No previous installation found."
fi
# ─── SSH KEY ──────────────────────────────────────────────────────────────────
sep
info "Checking SSH key..."
[ -f "$HOME/.ssh/id_ed25519" ] || fail "SSH key not found. Run: sudo bash 00-migrate-root.sh"
ok "SSH key found."
info "Checking access to private repo..."
SSH_TEST=$(GIT_SSH_COMMAND='ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10' \
ssh -T git@git.simplifiedprivacy.com 2>&1 || true)
if echo "$SSH_TEST" | grep -qiE "Welcome|successfully authenticated"; then
ok "Repo access authorized."
else
echo ""
warn "Could not authenticate with private repo."
echo ""
echo " Send this SSH key to Simplified Privacy:"
echo ""
cat "$HOME/.ssh/id_ed25519.pub"
echo ""
fail "Once authorized, run again: bash 01-migrate-user.sh"
fi
# ─── CLONE REPO ───────────────────────────────────────────────────────────────
sep
info "Cloning operator-docker repository..."
GIT_SSH_COMMAND='ssh -o StrictHostKeyChecking=no' \
git clone "$REPO_URL" "$OPERATOR_DIR" || fail "Could not clone repository."
ok "Repository cloned to $OPERATOR_DIR"
[ -f "$OPERATOR_DIR/install.sh" ] || fail "install.sh not found after clone. Invalid repo."
ok "install.sh found."
# ─── INSTALL ──────────────────────────────────────────────────────────────────
sep
info "Running installer..."
info "WireGuard will NOT start — 02 will start it with the legacy identity."
echo ""
cd "$OPERATOR_DIR"
sudo systemctl start nginx 2>/dev/null || true
sudo MIGRATION_MODE=1 bash install.sh || fail "Installer failed. Check output above."
if _env_is_corrupt "$OPERATOR_DIR/.env"; then
fail "install.sh finished but generated an invalid .env. Check install.sh."
fi
ok ".env generated with valid values."
# ─── WG0.CONF PLACEHOLDER ─────────────────────────────────────────────────────
sep
info "Checking wg0.conf..."
WG_CONF_DIR="$OPERATOR_DIR/services/wireguard/config/wg_confs"
WG_CONF_FILE="$WG_CONF_DIR/wg0.conf"
if [ ! -f "$WG_CONF_FILE" ]; then
warn "wg0.conf missing (MIGRATION_MODE skips WireGuard). Creating placeholder..."
mkdir -p "$WG_CONF_DIR"
cat > "$WG_CONF_FILE" << 'WGEOF'
[Interface]
Address = 10.13.13.1/16
ListenPort = 51820
PrivateKey = PLACEHOLDER
PostUp = /config/scripts/post-up.sh
PreDown = /config/scripts/pre-down.sh
WGEOF
ok "Placeholder created. 02 will inject the legacy private key."
else
ok "wg0.conf already exists."
fi
# ─── RESTORE EXTERNAL NGINX CONFIGS ───────────────────────────────────────────
sep
info "Restoring external Nginx configs..."
for SITE in "$BACKUP_DIR/nginx/sites-available/"*; do
NAME=$(basename "$SITE")
if ! grep -q "$NAME" /etc/nginx/sites-available/* 2>/dev/null; then
sudo cp "$SITE" "/etc/nginx/sites-available/$NAME" 2>/dev/null || true
if [ -L "$BACKUP_DIR/nginx/sites-enabled/$NAME" ] || \
[ -f "$BACKUP_DIR/nginx/sites-enabled/$NAME" ]; then
sudo ln -sf "/etc/nginx/sites-available/$NAME" \
"/etc/nginx/sites-enabled/$NAME" 2>/dev/null || true
ok "Config restored: $NAME"
fi
fi
done
sudo nginx -t && sudo systemctl reload nginx 2>/dev/null \
|| warn "Nginx reload failed — check manually."
trap - ERR
echo "[END-01] $(date '+%Y-%m-%d %H:%M:%S')" >> "$LOG"
echo ""
echo "========================================="
echo " Installation complete"
echo " Next: bash 02-migrate-peers.sh"
echo "========================================="
echo ""