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:
132
scripts/init.ps1
132
scripts/init.ps1
@@ -1,132 +0,0 @@
|
||||
# =============================================================================
|
||||
# Aegis Initialization Script (PowerShell)
|
||||
# =============================================================================
|
||||
# This script initializes the Aegis platform after starting Docker containers.
|
||||
#
|
||||
# Usage:
|
||||
# .\scripts\init.ps1
|
||||
# =============================================================================
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
Write-Host "╔═══════════════════════════════════════════════════════════════════════╗" -ForegroundColor Cyan
|
||||
Write-Host "║ Aegis - Platform Initialization ║" -ForegroundColor Cyan
|
||||
Write-Host "╚═══════════════════════════════════════════════════════════════════════╝" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
function Write-Status {
|
||||
param([string]$Message)
|
||||
Write-Host "[✓] $Message" -ForegroundColor Green
|
||||
}
|
||||
|
||||
function Write-Warning {
|
||||
param([string]$Message)
|
||||
Write-Host "[!] $Message" -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
function Write-Error {
|
||||
param([string]$Message)
|
||||
Write-Host "[✗] $Message" -ForegroundColor Red
|
||||
}
|
||||
|
||||
# Check if Docker is running
|
||||
try {
|
||||
docker info | Out-Null
|
||||
} catch {
|
||||
Write-Error "Docker is not running. Please start Docker first."
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check if containers are running
|
||||
$backendRunning = docker-compose ps | Select-String "aegis-backend"
|
||||
if (-not $backendRunning) {
|
||||
Write-Warning "Containers not running. Starting them now..."
|
||||
docker-compose up -d
|
||||
Write-Host ""
|
||||
Write-Host "Waiting for services to be healthy..."
|
||||
Start-Sleep -Seconds 10
|
||||
}
|
||||
|
||||
# Wait for backend to be ready
|
||||
Write-Host "Waiting for backend to be ready..."
|
||||
$maxRetries = 30
|
||||
$retryCount = 0
|
||||
$ready = $false
|
||||
|
||||
while (-not $ready -and $retryCount -lt $maxRetries) {
|
||||
try {
|
||||
$response = Invoke-WebRequest -Uri "http://localhost:8000/health" -UseBasicParsing -TimeoutSec 2
|
||||
if ($response.StatusCode -eq 200) {
|
||||
$ready = $true
|
||||
}
|
||||
} catch {
|
||||
$retryCount++
|
||||
Write-Host " Waiting... ($retryCount/$maxRetries)"
|
||||
Start-Sleep -Seconds 2
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $ready) {
|
||||
Write-Error "Backend failed to start after $maxRetries attempts"
|
||||
exit 1
|
||||
}
|
||||
Write-Status "Backend is healthy"
|
||||
|
||||
# Run database migrations
|
||||
Write-Host ""
|
||||
Write-Host "Running database migrations..."
|
||||
docker-compose exec -T backend alembic upgrade head
|
||||
Write-Status "Migrations completed"
|
||||
|
||||
# Seed admin user
|
||||
Write-Host ""
|
||||
Write-Host "Seeding admin user..."
|
||||
try {
|
||||
docker-compose exec -T backend python -m app.seed 2>$null
|
||||
Write-Status "Admin user ready"
|
||||
} catch {
|
||||
Write-Warning "Admin user may already exist"
|
||||
}
|
||||
|
||||
# Ask about MITRE sync
|
||||
Write-Host ""
|
||||
$runSync = Read-Host "Do you want to run initial MITRE ATT&CK sync? (y/N)"
|
||||
if ($runSync -eq "y" -or $runSync -eq "Y") {
|
||||
Write-Host "Triggering MITRE sync (this may take a minute)..."
|
||||
|
||||
try {
|
||||
# Get admin token
|
||||
$loginBody = "username=admin&password=admin123"
|
||||
$loginResponse = Invoke-RestMethod -Uri "http://localhost:8000/api/v1/auth/login" `
|
||||
-Method Post -Body $loginBody -ContentType "application/x-www-form-urlencoded"
|
||||
|
||||
$token = $loginResponse.access_token
|
||||
|
||||
if ($token) {
|
||||
$headers = @{ "Authorization" = "Bearer $token" }
|
||||
Invoke-RestMethod -Uri "http://localhost:8000/api/v1/system/sync-mitre" `
|
||||
-Method Post -Headers $headers | Out-Null
|
||||
Write-Status "MITRE sync triggered"
|
||||
}
|
||||
} catch {
|
||||
Write-Warning "Could not authenticate. Run sync manually from the System page."
|
||||
}
|
||||
}
|
||||
|
||||
# Print summary
|
||||
Write-Host ""
|
||||
Write-Host "╔═══════════════════════════════════════════════════════════════════════╗" -ForegroundColor Cyan
|
||||
Write-Host "║ Aegis is ready! ║" -ForegroundColor Cyan
|
||||
Write-Host "╠═══════════════════════════════════════════════════════════════════════╣" -ForegroundColor Cyan
|
||||
Write-Host "║ ║" -ForegroundColor Cyan
|
||||
Write-Host "║ Frontend: http://localhost:5173 ║" -ForegroundColor White
|
||||
Write-Host "║ Backend API: http://localhost:8000 ║" -ForegroundColor White
|
||||
Write-Host "║ Swagger UI: http://localhost:8000/docs ║" -ForegroundColor White
|
||||
Write-Host "║ MinIO Console: http://localhost:9001 ║" -ForegroundColor White
|
||||
Write-Host "║ ║" -ForegroundColor Cyan
|
||||
Write-Host "║ Default login: admin / admin123 ║" -ForegroundColor Yellow
|
||||
Write-Host "║ ║" -ForegroundColor Cyan
|
||||
Write-Host "║ ⚠️ Change the default password in production! ║" -ForegroundColor Yellow
|
||||
Write-Host "║ ║" -ForegroundColor Cyan
|
||||
Write-Host "╚═══════════════════════════════════════════════════════════════════════╝" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
@@ -1,9 +1,10 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# Aegis Initialization Script
|
||||
# Aegis Development Initialization Script
|
||||
# =============================================================================
|
||||
# This script initializes the Aegis platform after starting Docker containers.
|
||||
#
|
||||
# This script initializes the Aegis platform for local development.
|
||||
# For production, use: ./scripts/install.sh
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/init.sh
|
||||
# =============================================================================
|
||||
|
||||
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