#!/bin/bash # # Depl0y - Automated VM Deployment Panel Installer # Complete automated installation - no manual steps required! # Install: curl -fsSL https://raw.githubusercontent.com/agit8or1/Depl0y/main/install.sh -o install.sh # less install.sh # read it before running it as root # sudo bash install.sh # set -e # Installer version for tracking. This is the single source of truth — the # banner below renders it, so there is no second copy to forget to bump. INSTALLER_VERSION="latest" ENV_FILE=/etc/depl0y/config.env # Release signing key (minisign); same as minisign.pub in the repository. MINISIGN_PUBKEY="RWSr/WW0BJrhEcOJU3TiPs935jc6Qqy7yjLeuu5l+jaG0jlZ0CaHHRjH" INSTALLER_BUILD="$(date +%Y%m%d%H%M%S)" echo "╔══════════════════════════════════════════════════════════╗" echo "║ ║" echo "║ ██████╗ ███████╗██████╗ ██╗ ██████╗ ██╗ ██╗ ║" echo "║ ██╔══██╗██╔════╝██╔══██╗██║ ██╔═████╗╚██╗ ██╔╝ ║" echo "║ ██║ ██║█████╗ ██████╔╝██║ ██║██╔██║ ╚████╔╝ ║" echo "║ ██║ ██║██╔══╝ ██╔═══╝ ██║ ████╔╝██║ ╚██╔╝ ║" echo "║ ██████╔╝███████╗██║ ███████╗╚██████╔╝ ██║ ║" echo "║ ╚═════╝ ╚══════╝╚═╝ ╚══════╝ ╚═════╝ ╚═╝ ║" echo "║ ║" echo "║ Automated VM Deployment Panel for Proxmox VE ║" echo "║ https://deploy.agit8or.net ║" printf "\xe2\x95\x91%*s%*s\xe2\x95\x91\n" $(( (57 + ${#INSTALLER_VERSION} + 8) / 2 )) "Version $INSTALLER_VERSION" $(( (57 - ${#INSTALLER_VERSION} - 8 + 1) / 2 )) "" echo "║ ║" echo "╚══════════════════════════════════════════════════════════╝" echo "" echo "This installer will:" echo " ✓ Install all dependencies" echo " ✓ Set up Depl0y application" echo " ✓ Configure cloud images (optional)" echo " ✓ Configure inter-node SSH (optional)" echo " ✓ No manual steps required!" echo "" # Check if running as root if [ "$EUID" -ne 0 ]; then echo "ERROR: Please run as root (use sudo)" exit 1 fi echo "🚀 Starting Depl0y installation..." echo "" # Check if Depl0y is already installed UPGRADE_MODE=false RESET_PASSWORD=false if [ -d "/opt/depl0y" ] && [ -f "/etc/systemd/system/depl0y-backend.service" ]; then UPGRADE_MODE=true echo "📦 Existing Depl0y installation detected" echo " This will upgrade your installation while preserving your database" echo "" # Ask if user wants to reset admin password echo "⚠️ Do you want to reset the admin password to 'admin'?" echo " This will reset the 'admin' user credentials to default (admin/admin)" echo " and disable 2FA for the admin account." echo "" echo -n " Type YES (all caps) to reset password, or press Enter to skip: " # Read from the terminal, not stdin: with `curl ... | sudo bash` stdin is this # script, and in unattended runs there is nothing to read. No terminal: skip. if ! read -r RESET_CONFIRM 2>/dev/null < /dev/tty; then RESET_CONFIRM="" echo "(no terminal)" fi echo "" if [ "$RESET_CONFIRM" = "YES" ]; then RESET_PASSWORD=true echo "✓ Password reset scheduled for after installation" echo "" else echo "✓ Skipping password reset" echo "" fi fi # Detect OS if [ -f /etc/os-release ]; then . /etc/os-release OS=$ID VER=$VERSION_ID else echo "ERROR: Cannot detect OS" exit 1 fi echo "✓ Detected OS: $OS $VER" # Check if Ubuntu/Debian if [[ "$OS" != "ubuntu" && "$OS" != "debian" ]]; then echo "ERROR: This installer currently only supports Ubuntu and Debian" exit 1 fi # Function to validate dependencies after installation validate_dependencies() { echo "" echo "🔍 Validating installed dependencies..." local MISSING_DEPS=() local REQUIRED_PACKAGES=( "python3" "python3-pip" "python3-venv" "python3-cryptography" "sudo" "nginx" "nodejs" "npm" "sqlite3" "curl" "wget" "git" "sshpass" "at" ) for pkg in "${REQUIRED_PACKAGES[@]}"; do if ! dpkg -l | grep -q "^ii.*$pkg"; then MISSING_DEPS+=("$pkg") fi done # Also check for critical commands if ! command -v python3 &> /dev/null; then MISSING_DEPS+=("python3-binary") fi if ! command -v sudo &> /dev/null; then MISSING_DEPS+=("sudo-binary") fi if [ ${#MISSING_DEPS[@]} -gt 0 ]; then echo "❌ ERROR: Missing required dependencies:" for dep in "${MISSING_DEPS[@]}"; do echo " • $dep" done echo "" echo "Please install missing dependencies manually or check apt-get logs above." return 1 fi echo "✓ All dependencies validated successfully" return 0 } # Update system echo "" echo "📦 Updating system packages..." apt-get update -qq # Install ALL dependencies echo "📦 Installing dependencies..." echo " This includes: Python, Node.js, nginx, SQLite, PDF libraries, and more" apt-get install -y -qq \ sudo \ python3 \ python3-pip \ python3-venv \ python3-dev \ python3-cryptography \ build-essential \ libssl-dev \ nginx \ nodejs \ npm \ sqlite3 \ curl \ wget \ git \ sshpass \ openssh-client \ at \ ca-certificates \ minisign \ gnupg \ lsb-release \ libpango-1.0-0 \ libpangocairo-1.0-0 \ libgdk-pixbuf2.0-0 \ libffi-dev \ shared-mime-info echo "✓ Dependencies installed" # Validate all dependencies are present if ! validate_dependencies; then echo "" echo "❌ Installation failed due to missing dependencies" exit 1 fi # Create depl0y user echo "" echo "👤 Creating depl0y system user..." if ! id -u depl0y > /dev/null 2>&1; then useradd -r -m -d /opt/depl0y -s /bin/bash depl0y echo "✓ User 'depl0y' created" else echo "✓ User 'depl0y' already exists" fi # If upgrading, stop the backend service and backup encryption keys if [ "$UPGRADE_MODE" = true ]; then echo "🔄 Preparing for upgrade..." # Clear any stale Python cache first echo "🗑️ Clearing old Python cache..." find /opt/depl0y/backend -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true find /opt/depl0y/backend -type f -name "*.pyc" -delete 2>/dev/null || true echo "✓ Cache cleared" if systemctl is-active --quiet depl0y-backend; then systemctl stop depl0y-backend echo "✓ Stopped backend service" fi # Find the existing keys. /etc/depl0y/config.env is where they belong; older # installs kept them in the (world-readable) service unit instead. KEYS_VALID=false UNIT_FILE=/etc/systemd/system/depl0y-backend.service EXISTING_SECRET="" EXISTING_ENCRYPTION="" # Same precedence as systemd: config.env counts only if the unit loads it (or there # is no unit), and then it overrides any Environment= line. if [ -f "$ENV_FILE" ] && { [ ! -f "$UNIT_FILE" ] || grep -q "^EnvironmentFile=-\{0,1\}$ENV_FILE" "$UNIT_FILE"; }; then EXISTING_SECRET=$(sed -n 's/^SECRET_KEY=["'"'"']\{0,1\}\([^"'"'"']*\).*/\1/p' "$ENV_FILE" | tail -1) EXISTING_ENCRYPTION=$(sed -n 's/^ENCRYPTION_KEY=["'"'"']\{0,1\}\([^"'"'"']*\).*/\1/p' "$ENV_FILE" | tail -1) fi if [ -z "$EXISTING_ENCRYPTION" ] && [ -f "$UNIT_FILE" ]; then EXISTING_ENCRYPTION=$(sed -n 's/^Environment="\{0,1\}ENCRYPTION_KEY=\([^"]*\)"\{0,1\}$/\1/p' "$UNIT_FILE" | tail -1) # A SECRET_KEY found only in the unit file was readable by every local user, # so it is not reused: a new one is generated (everyone signs in again). EXISTING_SECRET="" fi if [ -n "$EXISTING_ENCRYPTION" ]; then echo "🔍 Found the existing encryption key, validating..." if python3 -c "import sys; from cryptography.fernet import Fernet; Fernet(sys.argv[1].encode())" "$EXISTING_ENCRYPTION" 2>/dev/null; then echo "✓ Existing encryption key is valid and will be preserved" KEYS_VALID=true else echo "⚠️ Existing encryption key is INVALID (wrong format)" echo " Will generate new keys - you'll need to re-enter Proxmox credentials" fi else echo "⚠️ Could not find the existing encryption key" echo " Will generate new keys" fi fi # Download latest Depl0y echo "" echo "⬇️ Downloading the latest Depl0y release from GitHub..." cd /tmp rm -f depl0y-latest.tar.gz depl0y-SHA256SUMS depl0y-SHA256SUMS.minisig # Pin the release we expect *before* downloading, and require the signature to be # for exactly that tag. Without this, any older signed release served in place of # "latest" would verify (a downgrade to a version with known bugs). # Set DEPL0Y_TAG=vX.Y.Z to install a specific release. EXPECTED_TAG="${DEPL0Y_TAG:-}" if [ -z "$EXPECTED_TAG" ]; then EXPECTED_TAG=$(curl -fsSL -H "Accept: application/vnd.github+json" \ "https://api.github.com/repos/agit8or1/Depl0y/releases/latest" \ | sed -n 's/^[[:space:]]*"tag_name":[[:space:]]*"\([^"]*\)".*/\1/p' | head -1) fi if ! [[ "$EXPECTED_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then echo "❌ Could not determine the release to install (got '${EXPECTED_TAG}'); set DEPL0Y_TAG=vX.Y.Z." >&2 exit 1 fi echo " Release: $EXPECTED_TAG" RELEASE_BASE="https://github.com/agit8or1/Depl0y/releases/download/$EXPECTED_TAG" curl -fsSL "$RELEASE_BASE/depl0y.tar.gz" -o depl0y-latest.tar.gz curl -fsSL "$RELEASE_BASE/SHA256SUMS" -o depl0y-SHA256SUMS if ! curl -fsSL "$RELEASE_BASE/SHA256SUMS.minisig" -o depl0y-SHA256SUMS.minisig; then echo "❌ The latest release is not signed (no SHA256SUMS.minisig); aborting." >&2 exit 1 fi if ! SIG_OUT=$(minisign -V -P "$MINISIGN_PUBKEY" -m depl0y-SHA256SUMS -x depl0y-SHA256SUMS.minisig 2>&1); then echo "❌ SHA256SUMS is not signed by the Depl0y release key; aborting." >&2 exit 1 fi # The signed trusted comment names the release ("depl0y vX.Y.Z"); checked against the # extracted version below. SIGNED_FOR=$(sed -n 's/^Trusted comment: depl0y v\([0-9]\+\.[0-9]\+\.[0-9]\+\)$/\1/p' <<<"$SIG_OUT") if [ -z "$SIGNED_FOR" ]; then echo "❌ Unexpected signature comment on SHA256SUMS; aborting." >&2 exit 1 fi if [ "v$SIGNED_FOR" != "$EXPECTED_TAG" ]; then echo "❌ The signature is for depl0y v$SIGNED_FOR, but $EXPECTED_TAG was expected; aborting." >&2 exit 1 fi echo "✓ Signature verified (depl0y v$SIGNED_FOR)" EXPECTED_SHA=$(awk '$2 == "depl0y.tar.gz" {print $1}' depl0y-SHA256SUMS) ACTUAL_SHA=$(sha256sum depl0y-latest.tar.gz | awk '{print $1}') if [ -z "$EXPECTED_SHA" ] || [ "$EXPECTED_SHA" != "$ACTUAL_SHA" ]; then echo "❌ Checksum mismatch for the downloaded release; aborting." >&2 exit 1 fi echo "✓ Checksum verified" echo "📦 Extracting Depl0y..." rm -rf /tmp/depl0y-install mkdir -p /tmp/depl0y-install cd /tmp/depl0y-install tar -xzf /tmp/depl0y-latest.tar.gz DEPL0Y_VERSION=$(grep -o 'return "[0-9]\+\.[0-9]\+\.[0-9]\+"' /tmp/depl0y-install/backend/app/core/config.py | grep -o '[0-9.]\+' | head -1) if [ "$DEPL0Y_VERSION" != "$SIGNED_FOR" ]; then echo "❌ The package is version ${DEPL0Y_VERSION:-unknown}, but the signature is for $SIGNED_FOR; aborting." >&2 exit 1 fi echo "✓ Depl0y ${DEPL0Y_VERSION}" # Refuse to downgrade an existing install unless asked to. INSTALLED_VERSION=$(grep -o 'return "[0-9]\+\.[0-9]\+\.[0-9]\+"' /opt/depl0y/backend/app/core/config.py 2>/dev/null | grep -o '[0-9.]\+' | head -1 || true) if [ -n "$INSTALLED_VERSION" ] && [ "$INSTALLED_VERSION" != "$DEPL0Y_VERSION" ] \ && [ "$(printf '%s\n%s\n' "$INSTALLED_VERSION" "$DEPL0Y_VERSION" | sort -V | tail -1)" = "$INSTALLED_VERSION" ] \ && [ "${DEPL0Y_ALLOW_DOWNGRADE:-0}" != "1" ]; then echo "❌ v$INSTALLED_VERSION is installed; refusing to downgrade to v$DEPL0Y_VERSION (set DEPL0Y_ALLOW_DOWNGRADE=1 to force)." >&2 exit 1 fi # Install backend echo "" echo "🔧 Installing backend..." mkdir -p /opt/depl0y chmod 755 /opt/depl0y # Allow nginx to traverse cp -r backend /opt/depl0y/ chown -R depl0y:depl0y /opt/depl0y/backend chmod -R 755 /opt/depl0y/backend # Create Python virtual environment echo "🐍 Setting up Python environment..." cd /opt/depl0y/backend sudo -u depl0y python3 -m venv venv sudo -u depl0y venv/bin/pip install --upgrade pip -q sudo -u depl0y venv/bin/pip install -r requirements.txt -q # Install additional Python packages for features echo " Installing additional Python packages..." sudo -u depl0y venv/bin/pip install -q weasyprint markdown requests echo "✓ Backend installed" # Install scripts echo "" echo "📜 Installing scripts..." if [ -d "/tmp/depl0y-install/scripts" ]; then mkdir -p /opt/depl0y/scripts cp -r /tmp/depl0y-install/scripts/* /opt/depl0y/scripts/ chmod -R 755 /opt/depl0y/scripts echo "✓ Scripts installed" else echo "⚠️ No scripts directory found in package" fi # Install frontend echo "" echo "🎨 Installing frontend..." if [ "$UPGRADE_MODE" = true ]; then # During upgrade, use pre-built frontend from package (much faster!) echo " Using pre-built frontend from package..." if [ -d "/tmp/depl0y-install/frontend/dist" ]; then mkdir -p /opt/depl0y/frontend/dist chmod 755 /opt/depl0y/frontend cp -r /tmp/depl0y-install/frontend/dist/* /opt/depl0y/frontend/dist/ chown -R www-data:www-data /opt/depl0y/frontend/dist chmod -R 755 /opt/depl0y/frontend/dist echo "✓ Frontend installed (pre-built)" else echo "⚠️ Pre-built frontend not found, building from source..." cd /tmp/depl0y-install/frontend npm install --silent npm run build mkdir -p /opt/depl0y/frontend/dist chmod 755 /opt/depl0y/frontend cp -r dist/* /opt/depl0y/frontend/dist/ chown -R www-data:www-data /opt/depl0y/frontend/dist chmod -R 755 /opt/depl0y/frontend/dist echo "✓ Frontend installed (built from source)" fi else # Fresh install - try to use pre-built frontend first if [ -d "/tmp/depl0y-install/frontend/dist" ]; then echo " Using pre-built frontend from package..." mkdir -p /opt/depl0y/frontend/dist chmod 755 /opt/depl0y/frontend cp -r /tmp/depl0y-install/frontend/dist/* /opt/depl0y/frontend/dist/ chown -R www-data:www-data /opt/depl0y/frontend/dist chmod -R 755 /opt/depl0y/frontend/dist echo "✓ Frontend installed (pre-built)" else echo " Building frontend from source (this may take 3-5 minutes)..." cd /tmp/depl0y-install/frontend npm install npm run build mkdir -p /opt/depl0y/frontend/dist chmod 755 /opt/depl0y/frontend cp -r dist/* /opt/depl0y/frontend/dist/ chown -R www-data:www-data /opt/depl0y/frontend/dist chmod -R 755 /opt/depl0y/frontend/dist echo "✓ Frontend installed (built from source)" fi fi # Create database directory echo "" echo "💾 Setting up database..." mkdir -p /var/lib/depl0y/db mkdir -p /var/lib/depl0y/cloud-images mkdir -p /var/lib/depl0y/isos mkdir -p /var/lib/depl0y/ssh_keys mkdir -p /var/log/depl0y chown -R depl0y:depl0y /var/lib/depl0y chown -R depl0y:depl0y /var/log/depl0y chmod -R 755 /var/lib/depl0y echo "✓ Database directories created" # Copy documentation echo "" echo "📚 Installing documentation..." if [ -d "/tmp/depl0y-install/docs" ]; then mkdir -p /opt/depl0y/docs cp -r /tmp/depl0y-install/docs/* /opt/depl0y/docs/ chown -R depl0y:depl0y /opt/depl0y/docs echo "✓ Documentation installed" fi # Generate or reuse encryption keys echo "" if [ "$UPGRADE_MODE" = true ] && [ "$KEYS_VALID" = true ]; then echo "🔐 Reusing existing encryption keys..." SECRET_KEY="${EXISTING_SECRET:-$(python3 -c 'import secrets; print(secrets.token_urlsafe(32))')}" ENCRYPTION_KEY="$EXISTING_ENCRYPTION" echo "✓ Existing encryption key preserved" else echo "🔐 Generating new encryption keys..." SECRET_KEY=$(python3 -c 'import secrets; print(secrets.token_urlsafe(32))') ENCRYPTION_KEY=$(python3 -c 'from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())') echo "✓ New encryption keys generated" if [ "$UPGRADE_MODE" = true ]; then echo "" echo "⚠️ IMPORTANT: New encryption keys have been generated" echo " This means you will need to:" echo " • Log in again (previous sessions are now invalid)" echo " • Re-enter all Proxmox host credentials" echo " • Your database and settings are preserved" fi fi # Secrets live in /etc/depl0y/config.env (depl0y:depl0y 0600), never in the # world-readable unit file. Other settings already in config.env are kept. echo "" echo "🔐 Writing keys to $ENV_FILE..." install -d -m 0755 -o root -g root /etc/depl0y ENV_TMP=$(mktemp /etc/depl0y/.config.env.XXXXXX) if [ -f "$ENV_FILE" ] && [ ! -L "$ENV_FILE" ]; then grep -vE '^(SECRET_KEY|ENCRYPTION_KEY)=' "$ENV_FILE" > "$ENV_TMP" || true fi printf 'SECRET_KEY=%s\nENCRYPTION_KEY=%s\n' "$SECRET_KEY" "$ENCRYPTION_KEY" >> "$ENV_TMP" chown depl0y:depl0y "$ENV_TMP" chmod 0600 "$ENV_TMP" mv -f "$ENV_TMP" "$ENV_FILE" echo "✓ Keys stored in $ENV_FILE" # Create systemd service echo "" echo "⚙️ Creating systemd service..." cat > /etc/systemd/system/depl0y-backend.service << SVCEOF [Unit] Description=Depl0y Backend API After=network.target [Service] Type=simple User=depl0y Group=depl0y WorkingDirectory=/opt/depl0y/backend Environment="PATH=/opt/depl0y/backend/venv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" EnvironmentFile=${ENV_FILE} ExecStart=/opt/depl0y/backend/venv/bin/uvicorn app.main:app --host 127.0.0.1 --port 8000 Restart=always RestartSec=10 [Install] WantedBy=multi-user.target SVCEOF # Clear Python cache to ensure new code is loaded if [ "$UPGRADE_MODE" = true ]; then echo "🗑️ Clearing Python cache again (ensuring fresh code)..." else echo "🗑️ Clearing Python cache..." fi find /opt/depl0y/backend -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true find /opt/depl0y/backend -type f -name "*.pyc" -delete 2>/dev/null || true systemctl daemon-reload systemctl enable depl0y-backend # Use restart to ensure service loads new code (important for upgrades) if [ "$UPGRADE_MODE" = true ]; then echo "🔄 Restarting backend service with new code..." systemctl restart depl0y-backend else echo "🚀 Starting backend service..." systemctl start depl0y-backend fi echo "✓ Backend service started" # Configure nginx echo "" echo "🌐 Configuring nginx..." cat > /etc/nginx/sites-available/depl0y << 'NGEOF' server { listen 80; server_name _; client_max_body_size 10G; # Frontend location / { root /opt/depl0y/frontend/dist; try_files $uri $uri/ /index.html; location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { expires 1y; add_header Cache-Control "public, immutable"; } } # Backend API location /api { proxy_pass http://127.0.0.1:8000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_connect_timeout 300s; proxy_send_timeout 300s; proxy_read_timeout 300s; } access_log /var/log/nginx/depl0y_access.log; error_log /var/log/nginx/depl0y_error.log; } NGEOF ln -sf /etc/nginx/sites-available/depl0y /etc/nginx/sites-enabled/depl0y rm -f /etc/nginx/sites-enabled/default nginx -t systemctl restart nginx echo "✓ Nginx configured" # Sudo rules and the root-owned updater (/usr/local/sbin/depl0y-update) echo "" echo "🔐 Configuring permissions..." bash /tmp/depl0y-install/scripts/install-updater.sh echo "✓ Permissions configured" # Wait for backend to start echo "" echo "⏳ Waiting for backend to start..." sleep 5 # Check if backend is running if systemctl is-active --quiet depl0y-backend; then echo "✓ Backend is running" else echo "" echo "❌ ERROR: Backend failed to start!" echo "" echo "Last 30 lines of backend logs:" journalctl -u depl0y-backend -n 30 --no-pager echo "" echo "Installation failed. Please check the logs above for errors." exit 1 fi # Create default admin user echo "" echo "👤 Creating default admin user..." cd /opt/depl0y/backend if sudo -u depl0y /opt/depl0y/backend/venv/bin/python3 create_admin.py; then echo "✓ Default credentials: admin / admin" else echo "❌ ERROR: Failed to create admin user!" echo "Check permissions and database connection." exit 1 fi # Initialize system settings in database echo "" echo "⚙️ Initializing system settings..." sudo -u depl0y sqlite3 /var/lib/depl0y/db/depl0y.db "CREATE TABLE IF NOT EXISTS system_settings ( id INTEGER PRIMARY KEY AUTOINCREMENT, key VARCHAR(100) UNIQUE NOT NULL, value TEXT NOT NULL, description TEXT, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP );" 2>/dev/null || true sudo -u depl0y sqlite3 /var/lib/depl0y/db/depl0y.db "INSERT OR REPLACE INTO system_settings (key, value, description) VALUES ('app_version', '${DEPL0Y_VERSION}', 'Current application version'), ('app_name', 'Depl0y', 'Application name');" 2>/dev/null || true echo "✓ System settings initialized" # Optional Proxmox setup echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo " Optional: Proxmox Integration Setup" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" echo "Would you like to configure Proxmox integration now?" echo "This will:" echo " • Set up cloud images for fast VM deployment" echo " • Configure inter-node SSH for multi-node clusters" echo "" read -p "Configure Proxmox now? (y/N): " -n 1 -r SETUP_PROXMOX < /dev/tty || SETUP_PROXMOX="N" echo "" if [[ $SETUP_PROXMOX =~ ^[Yy]$ ]]; then echo "" echo "📋 Proxmox Configuration" echo "" # Get Proxmox details read -p "Enter Proxmox hostname or IP: " PROXMOX_HOST < /dev/tty read -p "Enter Proxmox root username [root]: " PROXMOX_USER < /dev/tty PROXMOX_USER=${PROXMOX_USER:-root} echo "" echo "⚠️ Note: Password will be used to set up SSH keys only" read -s -p "Enter Proxmox root password: " PROXMOX_PASSWORD < /dev/tty echo "" echo "" # Setup SSH keys for depl0y user echo "🔑 Setting up SSH keys..." if [ ! -f "/opt/depl0y/.ssh/id_rsa" ]; then sudo -u depl0y mkdir -p /opt/depl0y/.ssh sudo -u depl0y ssh-keygen -t rsa -b 4096 -f /opt/depl0y/.ssh/id_rsa -N "" -q echo "✓ SSH keys generated" else echo "✓ SSH keys already exist" fi # Copy SSH key to Proxmox echo "📤 Copying SSH key to Proxmox..." sudo -u depl0y sshpass -p "$PROXMOX_PASSWORD" ssh-copy-id -o StrictHostKeyChecking=no "${PROXMOX_USER}@${PROXMOX_HOST}" 2>/dev/null || { echo "⚠️ Failed to copy SSH key. You may need to do this manually later." } # Test SSH connection if sudo -u depl0y ssh -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no "${PROXMOX_USER}@${PROXMOX_HOST}" "echo SSH_OK" 2>/dev/null | grep -q "SSH_OK"; then echo "✓ SSH connection verified" # Setup cloud images echo "" echo "☁️ Setting up cloud images..." sudo -u depl0y ssh -o StrictHostKeyChecking=no "${PROXMOX_USER}@${PROXMOX_HOST}" 'bash -s' << 'CLOUDEOF' # Check if cloud-init is available if ! which cloud-init >/dev/null 2>&1; then apt-get update -qq apt-get install -y -qq cloud-init fi # Create cloud-init snippet storage if needed mkdir -p /var/lib/vz/snippets chmod 755 /var/lib/vz/snippets echo "✓ Cloud images configured" CLOUDEOF # Setup inter-node SSH for cluster echo "" echo "🔗 Setting up inter-node SSH for cluster..." sudo -u depl0y ssh -o StrictHostKeyChecking=no "${PROXMOX_USER}@${PROXMOX_HOST}" 'bash -s' << 'SSHEOF' # Get cluster nodes NODES=$(pvesh get /cluster/status --output-format json 2>/dev/null | grep -o '"name":"[^"]*"' | cut -d'"' -f4 | grep -v "^$" || echo "") if [ -n "$NODES" ]; then echo " Found cluster nodes: $NODES" # Generate SSH key on this node if needed if [ ! -f ~/.ssh/id_rsa ]; then ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa -N "" -q fi # Copy key to other nodes for node in $NODES; do if [ "$node" != "$(hostname)" ]; then echo " Setting up SSH to $node..." ssh-copy-id -o StrictHostKeyChecking=no "root@$node" 2>/dev/null || true fi done echo "✓ Inter-node SSH configured" else echo " Single node or cluster not configured, skipping" fi SSHEOF echo "" echo "✓ Proxmox integration complete!" else echo "⚠️ SSH connection failed. You can set this up later via Settings in the web interface." fi else echo "" echo "⏭️ Skipping Proxmox setup. You can configure this later via:" echo " Settings → Cloud Images" echo " Settings → Proxmox Cluster SSH" fi # Cleanup echo "" echo "🧹 Cleaning up..." cd / rm -rf /tmp/depl0y-install rm -f /tmp/depl0y-latest.tar.gz # Reset admin password if requested if [ "$RESET_PASSWORD" = true ]; then echo "" echo "🔐 Resetting admin password..." # Hash for 'admin' password using bcrypt # Generated with: python3 -c "from passlib.hash import bcrypt; print(bcrypt.hash('admin'))" ADMIN_HASH='$2b$12$LQKvFz5K5w5Y5Y5Y5Y5Y5uKX8yN0gZGZGZGZGZGZGZGZGZGZGZGZG' # Use Python from venv to properly reset the password sudo -u depl0y /opt/depl0y/backend/venv/bin/python3 << 'EOF' import sqlite3 import sys import bcrypt try: # Connect to database db_path = '/var/lib/depl0y/db/depl0y.db' conn = sqlite3.connect(db_path) cursor = conn.cursor() # Hash the password using bcrypt (same as security.py) password = 'admin' salt = bcrypt.gensalt(rounds=12) hashed = bcrypt.hashpw(password.encode('utf-8'), salt) hashed_password = hashed.decode('utf-8') # Update admin user - reset password and disable 2FA cursor.execute(""" UPDATE users SET hashed_password = ?, totp_enabled = 0, totp_secret = NULL WHERE username = 'admin' """, (hashed_password,)) if cursor.rowcount > 0: conn.commit() print("✓ Admin password reset to 'admin'") print("✓ 2FA disabled for admin account") else: print("⚠️ Admin user not found in database") sys.exit(1) conn.close() except Exception as e: print(f"❌ Failed to reset password: {e}") sys.exit(1) EOF if [ $? -eq 0 ]; then echo "✓ Password reset complete" else echo "❌ Password reset failed - please check the error above" fi echo "" fi # Final restart for upgrades to ensure new code is loaded if [ "$UPGRADE_MODE" = true ]; then echo "" echo "🔄 Final restart to load new code..." find /opt/depl0y/backend -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true find /opt/depl0y/backend -type f -name "*.pyc" -delete 2>/dev/null || true systemctl restart depl0y-backend sleep 3 if systemctl is-active --quiet depl0y-backend; then echo "✓ Backend restarted successfully" else echo "⚠️ Backend restart may have issues, check logs" fi fi # Get IP address IP=$(hostname -I | awk '{print $1}') echo "" if [ "$UPGRADE_MODE" = true ]; then echo "╔══════════════════════════════════════════════════════════╗" echo "║ ║" echo "║ ✅ UPGRADE COMPLETE ║" echo "║ ║" echo "╚══════════════════════════════════════════════════════════╝" echo "" echo "🎉 Depl0y v${DEPL0Y_VERSION} has been successfully upgraded!" echo "" echo "📍 Access Depl0y at:" echo " http://$IP" echo "" if [ "$RESET_PASSWORD" = true ]; then echo "🔐 Password Reset:" echo " • Admin password has been reset to: admin" echo " • 2FA has been disabled for admin account" echo " • ⚠️ CHANGE PASSWORD IMMEDIATELY!" echo "" fi echo "📚 Note:" echo " • Your database has been preserved" echo " • Your encryption keys have been preserved" echo " • All users and settings retained" else echo "╔══════════════════════════════════════════════════════════╗" echo "║ ║" echo "║ ✅ INSTALLATION COMPLETE ║" echo "║ ║" echo "╚══════════════════════════════════════════════════════════╝" echo "" echo "🎉 Depl0y v${DEPL0Y_VERSION} has been successfully installed!" echo "" echo "📍 Access Depl0y at:" echo " http://$IP" echo "" echo "👤 Default Credentials:" echo " Username: admin" echo " Password: admin" echo " 🔐 2FA: Disabled (enable in Settings after login)" echo " ⚠️ CHANGE PASSWORD IMMEDIATELY AFTER FIRST LOGIN!" echo "" echo "✨ Features:" echo " • High Availability wizard" echo " • Cloud image management" echo " • Automated VM deployment" echo "" echo "📚 Next Steps:" echo " 1. Access the web interface" echo " 2. Change the default password" echo " 3. Add your Proxmox host in Settings (if not done)" echo " 4. Try the HA Setup Wizard!" fi echo "" echo "📖 Documentation: http://$IP → Documentation" echo "🆘 Support: Check /opt/depl0y/docs/" echo "" echo "Thank you for using Depl0y! 🚀" echo ""