feat: production deployment setup and hardcoded URL fixes
- Fix hardcoded localhost:8000 URLs in frontend to use relative /api/v1 path (works with Nginx proxy in prod and VITE_API_URL in dev) - Create production entrypoint (entrypoint.prod.sh) that runs migrations, seeds, and starts uvicorn with 4 workers (no --reload) - Create comprehensive install.sh script for production deployment that generates secure .env, builds containers, waits for health, and optionally triggers initial MITRE sync - Update docker-compose.prod.yml to use production entrypoint - Update Dockerfile to make both entrypoints executable - Remove init.ps1 (production will always be Linux) - Update README with production deployment instructions
This commit is contained in:
249
scripts/install.sh
Normal file
249
scripts/install.sh
Normal file
@@ -0,0 +1,249 @@
|
||||
#!/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
|
||||
|
||||
# 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" <<EOF
|
||||
# ── Aegis Production Environment ─────────────────────────────────
|
||||
# Generated by install.sh on $(date -u +"%Y-%m-%d %H:%M:%S UTC")
|
||||
|
||||
# Database
|
||||
DB_USER=postgres
|
||||
DB_PASSWORD=${DB_PASSWORD}
|
||||
DB_NAME=attackdb
|
||||
|
||||
# Security
|
||||
SECRET_KEY=${SECRET_KEY}
|
||||
TOKEN_EXPIRE_MINUTES=60
|
||||
|
||||
# MinIO Object Storage
|
||||
MINIO_ACCESS_KEY=minioadmin
|
||||
MINIO_SECRET_KEY=${MINIO_SECRET}
|
||||
MINIO_BUCKET=evidence
|
||||
|
||||
# Frontend
|
||||
FRONTEND_PORT=80
|
||||
EOF
|
||||
|
||||
print_ok ".env file created with secure random secrets"
|
||||
print_info "Review and edit .env if needed before proceeding"
|
||||
fi
|
||||
|
||||
# ── 3. Build and start containers ────────────────────────────────────
|
||||
|
||||
print_header "Building and starting containers"
|
||||
|
||||
print_info "This may take a few minutes on first run..."
|
||||
$COMPOSE_CMD -f docker-compose.prod.yml up -d --build 2>&1 | while IFS= read -r line; do
|
||||
echo " $line"
|
||||
done
|
||||
|
||||
print_ok "Containers started"
|
||||
|
||||
# ── 4. Wait for services to be healthy ───────────────────────────────
|
||||
|
||||
print_header "Waiting for services"
|
||||
|
||||
# Wait for postgres
|
||||
print_info "Waiting for PostgreSQL..."
|
||||
MAX_RETRIES=30
|
||||
RETRY=0
|
||||
until docker exec aegis-postgres pg_isready -U postgres > /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 curl -sf http://localhost:8000/health > /dev/null 2>&1 || \
|
||||
docker exec aegis-backend curl -sf http://localhost:8000/health > /dev/null 2>&1; do
|
||||
RETRY=$((RETRY + 1))
|
||||
if [ $RETRY -ge 60 ]; then
|
||||
print_error "Backend failed to start after 120 seconds"
|
||||
echo " Check logs: docker logs aegis-backend"
|
||||
exit 1
|
||||
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
|
||||
TOKEN=$(curl -sf -X POST "http://localhost:${FRONTEND_PORT}/api/v1/auth/login" \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-d "username=admin&password=admin123" | python3 -c "import sys,json; print(json.load(sys.stdin).get('access_token',''))" 2>/dev/null || echo "")
|
||||
|
||||
if [ -n "$TOKEN" ] && [ "$TOKEN" != "" ]; then
|
||||
print_info "Syncing MITRE ATT&CK data (this takes 1-2 minutes)..."
|
||||
SYNC_RESULT=$(curl -sf -X POST "http://localhost:${FRONTEND_PORT}/api/v1/system/sync-mitre" \
|
||||
-H "Authorization: Bearer $TOKEN" 2>/dev/null || echo "error")
|
||||
|
||||
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.)..."
|
||||
for source_id in $(curl -sf "http://localhost:${FRONTEND_PORT}/api/v1/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 -sf -X POST "http://localhost:${FRONTEND_PORT}/api/v1/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 ""
|
||||
Reference in New Issue
Block a user