#!/bin/bash # ============================================================================= # Aegis - Production Installation Script # ============================================================================= # This script sets up the Aegis platform for production. # # Usage: # chmod +x scripts/install.sh # ./scripts/install.sh # # Prerequisites: # - Docker and Docker Compose installed # - Port 80 (or FRONTEND_PORT) available # ============================================================================= set -e # Always run from the project root (parent of scripts/) SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" cd "$PROJECT_ROOT" # Colors GREEN='\033[0;32m' YELLOW='\033[1;33m' RED='\033[0;31m' CYAN='\033[0;36m' BOLD='\033[1m' NC='\033[0m' print_ok() { echo -e "${GREEN}[✓]${NC} $1"; } print_warn() { echo -e "${YELLOW}[!]${NC} $1"; } print_error() { echo -e "${RED}[✗]${NC} $1"; } print_info() { echo -e "${CYAN}[i]${NC} $1"; } print_header() { echo -e "\n${BOLD}── $1 ──${NC}"; } echo "" echo "╔═══════════════════════════════════════════════════════════════╗" echo "║ Aegis - Production Installation ║" echo "╚═══════════════════════════════════════════════════════════════╝" echo "" # ── 1. Check prerequisites ────────────────────────────────────────── print_header "Checking prerequisites" if ! command -v docker &> /dev/null; then print_error "Docker is not installed. Please install Docker first." echo " -> https://docs.docker.com/engine/install/" exit 1 fi print_ok "Docker found: $(docker --version | head -1)" if ! docker info > /dev/null 2>&1; then print_error "Docker daemon is not running. Please start Docker." exit 1 fi print_ok "Docker daemon is running" # Check for docker compose (v2 plugin or standalone) if docker compose version > /dev/null 2>&1; then COMPOSE_CMD="docker compose" elif command -v docker-compose &> /dev/null; then COMPOSE_CMD="docker-compose" else print_error "Docker Compose is not installed." echo " -> https://docs.docker.com/compose/install/" exit 1 fi print_ok "Docker Compose found ($COMPOSE_CMD)" # ── 2. Setup .env file ────────────────────────────────────────────── print_header "Environment configuration" ENV_FILE=".env" if [ -f "$ENV_FILE" ]; then print_warn ".env file already exists" read -p " Overwrite with new values? (y/N) " -n 1 -r echo "" if [[ ! $REPLY =~ ^[Yy]$ ]]; then print_info "Keeping existing .env file" SKIP_ENV=true fi fi if [ "${SKIP_ENV}" != "true" ]; then # Generate secure secrets SECRET_KEY=$(python3 -c "import secrets; print(secrets.token_hex(32))" 2>/dev/null || openssl rand -hex 32 2>/dev/null || head -c 64 /dev/urandom | od -An -tx1 | tr -d ' \n') DB_PASSWORD=$(python3 -c "import secrets; print(secrets.token_urlsafe(24))" 2>/dev/null || openssl rand -base64 24 2>/dev/null || head -c 24 /dev/urandom | base64) MINIO_SECRET=$(python3 -c "import secrets; print(secrets.token_urlsafe(24))" 2>/dev/null || openssl rand -base64 24 2>/dev/null || head -c 24 /dev/urandom | base64) cat > "$ENV_FILE" < /dev/null 2>&1; do RETRY=$((RETRY + 1)) if [ $RETRY -ge $MAX_RETRIES ]; then print_error "PostgreSQL failed to start after $MAX_RETRIES attempts" echo " Check logs: docker logs aegis-postgres" exit 1 fi sleep 2 done print_ok "PostgreSQL is ready" # Wait for backend (which runs migrations + seed on startup) print_info "Waiting for backend (running migrations and seeds)..." RETRY=0 until docker exec aegis-backend curl -sf http://localhost:8000/health > /dev/null 2>&1; do RETRY=$((RETRY + 1)) if [ $RETRY -ge 90 ]; then print_error "Backend failed to start after 180 seconds" echo " Check logs: docker logs aegis-backend" exit 1 fi # Show progress every 10 attempts if [ $((RETRY % 5)) -eq 0 ]; then print_info " Still waiting... ($RETRY attempts, checking logs)" docker logs aegis-backend --tail 3 2>/dev/null | while IFS= read -r line; do echo " $line"; done fi sleep 2 done print_ok "Backend is ready (migrations and seeds completed)" # Wait for frontend print_info "Waiting for frontend..." RETRY=0 FRONTEND_PORT=$(grep FRONTEND_PORT "$ENV_FILE" 2>/dev/null | cut -d= -f2 || echo "80") FRONTEND_PORT=${FRONTEND_PORT:-80} until curl -sf "http://localhost:${FRONTEND_PORT}" > /dev/null 2>&1; do RETRY=$((RETRY + 1)) if [ $RETRY -ge 30 ]; then print_error "Frontend failed to start after 60 seconds" echo " Check logs: docker logs aegis-frontend" exit 1 fi sleep 2 done print_ok "Frontend is ready" # ── 5. Trigger MITRE ATT&CK sync ──────────────────────────────────── print_header "Initial data sync" echo "" read -p "Run initial MITRE ATT&CK sync? This imports ~700 techniques. (Y/n) " -n 1 -r echo "" if [[ ! $REPLY =~ ^[Nn]$ ]]; then print_info "Authenticating..." # Get admin token (try via nginx first, then directly to backend container) API_URL="http://localhost:${FRONTEND_PORT}/api/v1" TOKEN=$(curl -sf --max-time 10 -X POST "${API_URL}/auth/login" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "username=admin&password=admin123" 2>/dev/null | \ python3 -c "import sys,json; print(json.load(sys.stdin).get('access_token',''))" 2>/dev/null || echo "") # Fallback: try directly via backend container if [ -z "$TOKEN" ] || [ "$TOKEN" = "" ]; then TOKEN=$(docker exec aegis-backend curl -sf -X POST "http://localhost:8000/api/v1/auth/login" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "username=admin&password=admin123" 2>/dev/null | \ python3 -c "import sys,json; print(json.load(sys.stdin).get('access_token',''))" 2>/dev/null || echo "") API_URL="http://localhost:8000/api/v1" API_VIA_DOCKER=true fi if [ -n "$TOKEN" ] && [ "$TOKEN" != "" ]; then print_info "Syncing MITRE ATT&CK data (this takes 1-2 minutes)..." if [ "$API_VIA_DOCKER" = true ]; then SYNC_RESULT=$(docker exec aegis-backend curl -sf --max-time 300 -X POST "${API_URL}/system/sync-mitre" \ -H "Authorization: Bearer $TOKEN" 2>/dev/null || echo "error") else SYNC_RESULT=$(curl -sf --max-time 300 -X POST "${API_URL}/system/sync-mitre" \ -H "Authorization: Bearer $TOKEN" 2>/dev/null || echo "error") fi if [ "$SYNC_RESULT" != "error" ]; then print_ok "MITRE ATT&CK sync completed" else print_warn "MITRE sync may have timed out. Check the System page in the UI." fi # Sync data sources print_info "Syncing data sources (Atomic Red Team, SigmaHQ, etc.)..." if [ "$API_VIA_DOCKER" = true ]; then CURL_PREFIX="docker exec aegis-backend curl" else CURL_PREFIX="curl" fi for source_id in $($CURL_PREFIX -sf "${API_URL}/data-sources" \ -H "Authorization: Bearer $TOKEN" 2>/dev/null | \ python3 -c "import sys,json; [print(s['id']) for s in json.load(sys.stdin)]" 2>/dev/null); do $CURL_PREFIX -sf --max-time 120 -X POST "${API_URL}/data-sources/${source_id}/sync" \ -H "Authorization: Bearer $TOKEN" > /dev/null 2>&1 || true done print_ok "Data source sync triggered" else print_warn "Could not authenticate. Run MITRE sync manually from the System page." print_info "Default credentials: admin / admin123" fi else print_info "Skipping MITRE sync. You can do this later from the System page." fi # ── 6. Summary ─────────────────────────────────────────────────────── # Get the server's IP SERVER_IP=$(hostname -I 2>/dev/null | awk '{print $1}' || echo "localhost") echo "" echo "╔═══════════════════════════════════════════════════════════════╗" echo "║ Aegis is ready! ║" echo "╠═══════════════════════════════════════════════════════════════╣" echo "║ ║" echo "║ Application: http://${SERVER_IP}:${FRONTEND_PORT} " echo "║ API Docs: http://${SERVER_IP}:${FRONTEND_PORT}/api/v1/docs " echo "║ ║" echo "║ Default login: admin / admin123 ║" echo "║ ║" echo "║ ⚠ IMPORTANT: ║" echo "║ • Change the default password immediately ║" echo "║ • Set up HTTPS/TLS for internet-facing deployments ║" echo "║ • Configure firewall rules as needed ║" echo "║ • Set up regular database backups ║" echo "║ ║" echo "╚═══════════════════════════════════════════════════════════════╝" echo "" echo "Useful commands:" echo " View logs: docker logs -f aegis-backend" echo " Stop: $COMPOSE_CMD -f docker-compose.prod.yml down" echo " Restart: $COMPOSE_CMD -f docker-compose.prod.yml restart" echo " Update: $COMPOSE_CMD -f docker-compose.prod.yml up -d --build" echo " DB backup: docker exec aegis-postgres pg_dump -U postgres attackdb > backup.sql" echo ""