0a6cb5510c
Aegis CI / lint-and-test (push) Has been cancelled
Snyk Security Scan / Python vulnerabilities (backend) (push) Has been cancelled
Snyk Security Scan / npm vulnerabilities (frontend) (push) Has been cancelled
Snyk Security Scan / Docker image vulnerabilities (backend) (push) Has been cancelled
Fresh installs failed at 'docker compose up' with 'required variable PLATFORM_URL is missing a value' — the install script computes ORIGIN_URL for CORS_ORIGINS already but never wrote PLATFORM_URL, which the backend now requires with no default. Also backfills it into an existing .env (from CORS_ORIGINS) when the user chooses to keep their current config, so upgrading an older install in place doesn't hit the same error.
559 lines
26 KiB
Bash
559 lines
26 KiB
Bash
#!/bin/bash
|
|
# =============================================================================
|
|
# Aegis - Interactive Production Installer
|
|
# =============================================================================
|
|
# Sets up the Aegis platform for production with an interactive wizard
|
|
# that configures all environment variables.
|
|
#
|
|
# Usage:
|
|
# chmod +x scripts/install.sh
|
|
# ./scripts/install.sh
|
|
#
|
|
# Prerequisites:
|
|
# - Docker and Docker Compose installed
|
|
# - Port 80 (or chosen 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 & helpers ──────────────────────────────────────────────────────
|
|
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
RED='\033[0;31m'
|
|
CYAN='\033[0;36m'
|
|
BOLD='\033[1m'
|
|
DIM='\033[2m'
|
|
NC='\033[0m'
|
|
|
|
print_ok() { echo -e "${GREEN}[OK]${NC} $1"; }
|
|
print_warn() { echo -e "${YELLOW}[!]${NC} $1"; }
|
|
print_error() { echo -e "${RED}[X]${NC} $1"; }
|
|
print_info() { echo -e "${CYAN}[i]${NC} $1"; }
|
|
print_header() { echo -e "\n${BOLD}── $1 ──${NC}\n"; }
|
|
print_prompt() { echo -en "${CYAN}>>>${NC} $1"; }
|
|
|
|
# Generate a cryptographically secure random string
|
|
gen_secret() {
|
|
python3 -c "import secrets; print(secrets.token_hex($1))" 2>/dev/null \
|
|
|| openssl rand -hex "$1" 2>/dev/null \
|
|
|| head -c "$1" /dev/urandom | od -An -tx1 | tr -d ' \n'
|
|
}
|
|
|
|
gen_password() {
|
|
python3 -c "import secrets; print(secrets.token_urlsafe($1))" 2>/dev/null \
|
|
|| openssl rand -base64 "$1" 2>/dev/null \
|
|
|| head -c "$1" /dev/urandom | base64 | tr -d '=/+' | head -c "$1"
|
|
}
|
|
|
|
# ── Banner ────────────────────────────────────────────────────────────────
|
|
|
|
clear 2>/dev/null || true
|
|
echo ""
|
|
echo -e "${BOLD}"
|
|
echo " ╔══════════════════════════════════════════════════════════╗"
|
|
echo " ║ ║"
|
|
echo " ║ Aegis - Installation Wizard ║"
|
|
echo " ║ MITRE ATT&CK Coverage Platform ║"
|
|
echo " ║ ║"
|
|
echo " ╚══════════════════════════════════════════════════════════╝"
|
|
echo -e "${NC}"
|
|
|
|
# ═════════════════════════════════════════════════════════════════════════
|
|
# STEP 1: Check prerequisites
|
|
# ═════════════════════════════════════════════════════════════════════════
|
|
|
|
print_header "Step 1/5 - 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"
|
|
|
|
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)"
|
|
|
|
# Auto-detect Docker API version
|
|
DOCKER_SERVER_API=$(docker version --format '{{.Server.APIVersion}}' 2>/dev/null || echo "")
|
|
if [ -n "$DOCKER_SERVER_API" ]; then
|
|
export DOCKER_API_VERSION="$DOCKER_SERVER_API"
|
|
fi
|
|
|
|
# ═════════════════════════════════════════════════════════════════════════
|
|
# STEP 2: Interactive configuration
|
|
# ═════════════════════════════════════════════════════════════════════════
|
|
|
|
print_header "Step 2/5 - Configuration"
|
|
|
|
ENV_FILE=".env"
|
|
SKIP_CONFIG=false
|
|
|
|
if [ -f "$ENV_FILE" ]; then
|
|
print_warn "An existing .env file was found."
|
|
echo ""
|
|
print_prompt "Do you want to reconfigure? (y/N): "
|
|
read -r REPLY
|
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
print_info "Keeping existing configuration."
|
|
SKIP_CONFIG=true
|
|
|
|
# Older installs predate PLATFORM_URL (added when email links moved
|
|
# off the dev default) — backfill it from CORS_ORIGINS so upgrading
|
|
# in place doesn't break the backend's required-env-var check.
|
|
if ! grep -q '^PLATFORM_URL=' "$ENV_FILE" 2>/dev/null; then
|
|
EXISTING_ORIGIN=$(grep '^CORS_ORIGINS=' "$ENV_FILE" 2>/dev/null | cut -d= -f2-)
|
|
if [ -n "$EXISTING_ORIGIN" ]; then
|
|
echo "PLATFORM_URL=${EXISTING_ORIGIN}" >> "$ENV_FILE"
|
|
print_info "Added missing PLATFORM_URL=${EXISTING_ORIGIN} to existing .env"
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if [ "$SKIP_CONFIG" = false ]; then
|
|
|
|
echo -e " ${DIM}Answer the following questions to configure Aegis."
|
|
echo -e " Press Enter to accept the default value shown in [brackets].${NC}"
|
|
echo ""
|
|
|
|
# ── Domain / URL ──────────────────────────────────────────────────
|
|
|
|
echo -e " ${BOLD}1. Domain Configuration${NC}"
|
|
echo -e " ${DIM}The domain where Aegis will be accessible."
|
|
echo -e " Examples: aegis.example.com, 192.168.1.100, localhost${NC}"
|
|
echo ""
|
|
print_prompt "Domain or IP [localhost]: "
|
|
read -r INPUT_DOMAIN
|
|
DOMAIN="${INPUT_DOMAIN:-localhost}"
|
|
|
|
# ── Protocol ──────────────────────────────────────────────────────
|
|
|
|
if [ "$DOMAIN" = "localhost" ] || [ "$DOMAIN" = "127.0.0.1" ]; then
|
|
PROTOCOL="http"
|
|
print_info "Using HTTP for local deployment"
|
|
else
|
|
echo ""
|
|
print_prompt "Are you using HTTPS/SSL? (Y/n): "
|
|
read -r REPLY
|
|
if [[ $REPLY =~ ^[Nn]$ ]]; then
|
|
PROTOCOL="http"
|
|
else
|
|
PROTOCOL="https"
|
|
fi
|
|
fi
|
|
|
|
# ── Port ──────────────────────────────────────────────────────────
|
|
|
|
if [ "$PROTOCOL" = "https" ]; then
|
|
DEFAULT_PORT=443
|
|
else
|
|
DEFAULT_PORT=80
|
|
fi
|
|
|
|
echo ""
|
|
echo -e " ${BOLD}2. Port${NC}"
|
|
print_prompt "Frontend port [$DEFAULT_PORT]: "
|
|
read -r INPUT_PORT
|
|
FRONTEND_PORT="${INPUT_PORT:-$DEFAULT_PORT}"
|
|
|
|
# Build the full origin URL for CORS
|
|
if { [ "$PROTOCOL" = "https" ] && [ "$FRONTEND_PORT" = "443" ]; } || \
|
|
{ [ "$PROTOCOL" = "http" ] && [ "$FRONTEND_PORT" = "80" ]; }; then
|
|
ORIGIN_URL="${PROTOCOL}://${DOMAIN}"
|
|
else
|
|
ORIGIN_URL="${PROTOCOL}://${DOMAIN}:${FRONTEND_PORT}"
|
|
fi
|
|
|
|
# ── Admin account ─────────────────────────────────────────────────
|
|
|
|
echo ""
|
|
echo -e " ${BOLD}3. Admin Account${NC}"
|
|
echo -e " ${DIM}The initial administrator account for Aegis.${NC}"
|
|
echo ""
|
|
print_prompt "Admin username [admin]: "
|
|
read -r INPUT_ADMIN_USER
|
|
ADMIN_USERNAME="${INPUT_ADMIN_USER:-admin}"
|
|
|
|
echo ""
|
|
echo -e " ${DIM}Leave empty to auto-generate a secure password."
|
|
echo -e " The password will be shown in the installation summary.${NC}"
|
|
print_prompt "Admin password [auto-generate]: "
|
|
read -rs INPUT_ADMIN_PASS
|
|
echo ""
|
|
ADMIN_PASSWORD="${INPUT_ADMIN_PASS}"
|
|
|
|
if [ -z "$ADMIN_PASSWORD" ]; then
|
|
ADMIN_PASSWORD=$(gen_password 18)
|
|
ADMIN_PW_GENERATED=true
|
|
print_info "Password will be auto-generated"
|
|
else
|
|
ADMIN_PW_GENERATED=false
|
|
print_ok "Password set"
|
|
fi
|
|
|
|
# ── Database ──────────────────────────────────────────────────────
|
|
|
|
echo ""
|
|
echo -e " ${BOLD}4. Database${NC}"
|
|
print_prompt "Database name [attackdb]: "
|
|
read -r INPUT_DB_NAME
|
|
DB_NAME="${INPUT_DB_NAME:-attackdb}"
|
|
|
|
print_prompt "Database user [postgres]: "
|
|
read -r INPUT_DB_USER
|
|
DB_USER="${INPUT_DB_USER:-postgres}"
|
|
|
|
echo -e " ${DIM}Leave empty to auto-generate a secure password.${NC}"
|
|
print_prompt "Database password [auto-generate]: "
|
|
read -rs INPUT_DB_PASS
|
|
echo ""
|
|
if [ -z "$INPUT_DB_PASS" ]; then
|
|
DB_PASSWORD=$(gen_password 24)
|
|
print_info "Database password auto-generated"
|
|
else
|
|
DB_PASSWORD="$INPUT_DB_PASS"
|
|
print_ok "Database password set"
|
|
fi
|
|
|
|
# ── Token expiry ──────────────────────────────────────────────────
|
|
|
|
echo ""
|
|
echo -e " ${BOLD}5. Session Duration${NC}"
|
|
print_prompt "Token expiry in minutes [60]: "
|
|
read -r INPUT_TOKEN_EXP
|
|
TOKEN_EXPIRE_MINUTES="${INPUT_TOKEN_EXP:-60}"
|
|
|
|
# ── MITRE sync ────────────────────────────────────────────────────
|
|
|
|
echo ""
|
|
echo -e " ${BOLD}6. Initial Data${NC}"
|
|
print_prompt "Run MITRE ATT&CK sync after install? (Y/n): "
|
|
read -r INPUT_SYNC
|
|
if [[ $INPUT_SYNC =~ ^[Nn]$ ]]; then
|
|
RUN_MITRE_SYNC=false
|
|
else
|
|
RUN_MITRE_SYNC=true
|
|
fi
|
|
|
|
# ── Generate secrets ──────────────────────────────────────────────
|
|
|
|
SECRET_KEY=$(gen_secret 32)
|
|
MINIO_ACCESS=$(gen_secret 8)
|
|
MINIO_SECRET=$(gen_password 24)
|
|
|
|
# ── Show summary before writing ──────────────────────────────────
|
|
|
|
echo ""
|
|
echo -e "${BOLD} ┌──────────────────────────────────────────────────────┐${NC}"
|
|
echo -e "${BOLD} │ Configuration Summary │${NC}"
|
|
echo -e "${BOLD} ├──────────────────────────────────────────────────────┤${NC}"
|
|
echo -e " │ URL: ${CYAN}${ORIGIN_URL}${NC}"
|
|
echo -e " │ Admin user: ${CYAN}${ADMIN_USERNAME}${NC}"
|
|
if [ "$ADMIN_PW_GENERATED" = true ]; then
|
|
echo -e " │ Admin pass: ${CYAN}(auto-generated)${NC}"
|
|
else
|
|
echo -e " │ Admin pass: ${CYAN}(custom)${NC}"
|
|
fi
|
|
echo -e " │ Database: ${CYAN}${DB_USER}@${DB_NAME}${NC}"
|
|
echo -e " │ Port: ${CYAN}${FRONTEND_PORT}${NC}"
|
|
echo -e " │ Session TTL: ${CYAN}${TOKEN_EXPIRE_MINUTES} min${NC}"
|
|
echo -e " │ MITRE sync: ${CYAN}$([ "$RUN_MITRE_SYNC" = true ] && echo "yes" || echo "no")${NC}"
|
|
echo -e "${BOLD} └──────────────────────────────────────────────────────┘${NC}"
|
|
echo ""
|
|
# Warn about data loss if containers/volumes already exist
|
|
if $COMPOSE_CMD -f docker-compose.prod.yml ps -q 2>/dev/null | grep -q . || \
|
|
docker volume ls --format '{{.Name}}' 2>/dev/null | grep -qi 'postgres'; then
|
|
echo ""
|
|
print_warn "Existing database volumes detected."
|
|
print_warn "Proceeding will RESET the database — all existing data will be lost."
|
|
fi
|
|
|
|
print_prompt "Proceed with these settings? (Y/n): "
|
|
read -r CONFIRM
|
|
if [[ $CONFIRM =~ ^[Nn]$ ]]; then
|
|
print_warn "Installation cancelled. Run the script again to reconfigure."
|
|
exit 0
|
|
fi
|
|
|
|
# ── Write .env ────────────────────────────────────────────────────
|
|
|
|
cat > "$ENV_FILE" <<ENVEOF
|
|
# =============================================================================
|
|
# Aegis Production Environment
|
|
# Generated by install.sh on $(date -u +"%Y-%m-%d %H:%M:%S UTC")
|
|
# =============================================================================
|
|
|
|
# ── Database ─────────────────────────────────────────────────────────────────
|
|
DB_USER=${DB_USER}
|
|
DB_PASSWORD=${DB_PASSWORD}
|
|
DB_NAME=${DB_NAME}
|
|
|
|
# ── Security ─────────────────────────────────────────────────────────────────
|
|
SECRET_KEY=${SECRET_KEY}
|
|
TOKEN_EXPIRE_MINUTES=${TOKEN_EXPIRE_MINUTES}
|
|
|
|
# ── Initial Admin Account ────────────────────────────────────────────────────
|
|
ADMIN_USERNAME=${ADMIN_USERNAME}
|
|
ADMIN_PASSWORD=${ADMIN_PASSWORD}
|
|
|
|
# ── MinIO Object Storage ─────────────────────────────────────────────────────
|
|
MINIO_ACCESS_KEY=${MINIO_ACCESS}
|
|
MINIO_SECRET_KEY=${MINIO_SECRET}
|
|
MINIO_BUCKET=evidence
|
|
|
|
# ── CORS (allowed frontend origins) ─────────────────────────────────────────
|
|
CORS_ORIGINS=${ORIGIN_URL}
|
|
|
|
# ── Emails ────────────────────────────────────────────────────────────────────
|
|
# Base URL used to build links in outbound emails (set-password, etc).
|
|
PLATFORM_URL=${ORIGIN_URL}
|
|
|
|
# ── Frontend ─────────────────────────────────────────────────────────────────
|
|
FRONTEND_PORT=${FRONTEND_PORT}
|
|
|
|
# ── Environment ──────────────────────────────────────────────────────────────
|
|
AEGIS_ENV=production
|
|
ENVEOF
|
|
|
|
print_ok ".env file created with secure configuration"
|
|
|
|
fi # end SKIP_CONFIG
|
|
|
|
# ═════════════════════════════════════════════════════════════════════════
|
|
# STEP 3: Build and start
|
|
# ═════════════════════════════════════════════════════════════════════════
|
|
|
|
print_header "Step 3/5 - Building and starting containers"
|
|
|
|
print_info "This may take several minutes on first run..."
|
|
|
|
# When reconfiguring, remove old volumes so Postgres re-initializes with the new password.
|
|
# Without this, Postgres ignores the new DB_PASSWORD because it only sets credentials
|
|
# on first initialization (empty volume) — leaving the backend unable to authenticate.
|
|
if [ "$SKIP_CONFIG" = false ]; then
|
|
print_info "Removing existing volumes to apply new credentials..."
|
|
$COMPOSE_CMD -f docker-compose.prod.yml down -v > /dev/null 2>&1 || true
|
|
fi
|
|
|
|
if ! $COMPOSE_CMD -f docker-compose.prod.yml up -d --build 2>&1; then
|
|
print_error "Failed to build/start containers. Check the output above."
|
|
exit 1
|
|
fi
|
|
|
|
print_ok "Containers started"
|
|
|
|
# ═════════════════════════════════════════════════════════════════════════
|
|
# STEP 4: Wait for services
|
|
# ═════════════════════════════════════════════════════════════════════════
|
|
|
|
print_header "Step 4/5 - Waiting for services to be ready"
|
|
|
|
# Wait for PostgreSQL
|
|
echo -en " 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
|
|
echo ""
|
|
print_error "PostgreSQL failed to start. Check: docker logs aegis-postgres"
|
|
exit 1
|
|
fi
|
|
echo -n "."
|
|
sleep 2
|
|
done
|
|
echo -e " ${GREEN}ready${NC}"
|
|
|
|
# Wait for backend (runs migrations + seed)
|
|
echo -en " Backend (migrations + seed) ..."
|
|
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
|
|
echo ""
|
|
print_error "Backend failed to start after 3 minutes."
|
|
echo " Check: docker logs aegis-backend"
|
|
exit 1
|
|
fi
|
|
echo -n "."
|
|
sleep 2
|
|
done
|
|
echo -e " ${GREEN}ready${NC}"
|
|
|
|
# Wait for frontend
|
|
FRONTEND_PORT=$(grep FRONTEND_PORT "$ENV_FILE" 2>/dev/null | cut -d= -f2 || echo "80")
|
|
FRONTEND_PORT=${FRONTEND_PORT:-80}
|
|
|
|
echo -en " Frontend ..."
|
|
RETRY=0
|
|
until curl -sf "http://localhost:${FRONTEND_PORT}" > /dev/null 2>&1; do
|
|
RETRY=$((RETRY + 1))
|
|
if [ $RETRY -ge 30 ]; then
|
|
echo ""
|
|
print_error "Frontend failed to start. Check: docker logs aegis-frontend"
|
|
exit 1
|
|
fi
|
|
echo -n "."
|
|
sleep 2
|
|
done
|
|
echo -e " ${GREEN}ready${NC}"
|
|
|
|
print_ok "All services are running"
|
|
|
|
# ── Extract admin credentials from backend logs ──────────────────────
|
|
|
|
ADMIN_CREDS_USER=""
|
|
ADMIN_CREDS_PASS=""
|
|
|
|
# Try to extract the credentials from the backend startup logs
|
|
LOG_OUTPUT=$(docker logs aegis-backend 2>&1 | tail -20)
|
|
|
|
if echo "$LOG_OUTPUT" | grep -q "Initial Admin User Created"; then
|
|
ADMIN_CREDS_USER=$(echo "$LOG_OUTPUT" | grep "Username :" | sed 's/.*Username : //')
|
|
ADMIN_CREDS_PASS=$(echo "$LOG_OUTPUT" | grep "Password :" | sed 's/.*Password : //')
|
|
fi
|
|
|
|
# Fallback: if we set it via env, use those values
|
|
if [ -z "$ADMIN_CREDS_USER" ]; then
|
|
ADMIN_CREDS_USER=$(grep ADMIN_USERNAME "$ENV_FILE" 2>/dev/null | cut -d= -f2 || echo "admin")
|
|
ADMIN_CREDS_USER="${ADMIN_CREDS_USER:-admin}"
|
|
fi
|
|
|
|
if [ -z "$ADMIN_CREDS_PASS" ] || [ "$ADMIN_CREDS_PASS" = "(set via ADMIN_PASSWORD env var)" ]; then
|
|
ADMIN_CREDS_PASS=$(grep ADMIN_PASSWORD "$ENV_FILE" 2>/dev/null | cut -d= -f2 || echo "")
|
|
fi
|
|
|
|
# ═════════════════════════════════════════════════════════════════════════
|
|
# STEP 5: Initial data sync (optional)
|
|
# ═════════════════════════════════════════════════════════════════════════
|
|
|
|
# Re-read RUN_MITRE_SYNC if we skipped config
|
|
if [ "$SKIP_CONFIG" = true ]; then
|
|
echo ""
|
|
print_prompt "Run initial MITRE ATT&CK sync? (~700 techniques, 1-2 min) (Y/n): "
|
|
read -r INPUT_SYNC
|
|
if [[ $INPUT_SYNC =~ ^[Nn]$ ]]; then
|
|
RUN_MITRE_SYNC=false
|
|
else
|
|
RUN_MITRE_SYNC=true
|
|
fi
|
|
fi
|
|
|
|
if [ "$RUN_MITRE_SYNC" = true ]; then
|
|
print_header "Step 5/5 - Initial data sync"
|
|
|
|
print_info "Authenticating with backend..."
|
|
|
|
# Authenticate via backend container (most reliable)
|
|
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_CREDS_USER}&password=${ADMIN_CREDS_PASS}" 2>/dev/null | \
|
|
python3 -c "import sys,json; print(json.load(sys.stdin).get('access_token',''))" 2>/dev/null || echo "")
|
|
|
|
if [ -n "$TOKEN" ] && [ "$TOKEN" != "" ]; then
|
|
|
|
# MITRE ATT&CK sync
|
|
print_info "Syncing MITRE ATT&CK techniques (1-2 minutes)..."
|
|
SYNC_RESULT=$(docker exec aegis-backend curl -sf --max-time 300 \
|
|
-X POST "http://localhost:8000/api/v1/system/sync-mitre" \
|
|
-H "Authorization: Bearer $TOKEN" 2>/dev/null || echo "error")
|
|
|
|
if [ "$SYNC_RESULT" != "error" ]; then
|
|
NEW_TECHNIQUES=$(echo "$SYNC_RESULT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('new',0))" 2>/dev/null || echo "?")
|
|
print_ok "MITRE sync completed ($NEW_TECHNIQUES techniques imported)"
|
|
else
|
|
print_warn "MITRE sync timed out. You can retry from the System page."
|
|
fi
|
|
|
|
# Data sources sync
|
|
print_info "Syncing data sources (Atomic Red Team, SigmaHQ, etc.)..."
|
|
SOURCES=$(docker exec aegis-backend curl -sf "http://localhost:8000/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 || echo "")
|
|
|
|
SYNC_COUNT=0
|
|
for source_id in $SOURCES; do
|
|
docker exec aegis-backend curl -sf --max-time 120 \
|
|
-X POST "http://localhost:8000/api/v1/data-sources/${source_id}/sync" \
|
|
-H "Authorization: Bearer $TOKEN" > /dev/null 2>&1 && SYNC_COUNT=$((SYNC_COUNT + 1)) || true
|
|
done
|
|
if [ "$SYNC_COUNT" -gt 0 ]; then
|
|
print_ok "Data sources synced ($SYNC_COUNT sources)"
|
|
fi
|
|
else
|
|
print_warn "Could not authenticate. Run MITRE sync from the System page."
|
|
fi
|
|
else
|
|
print_header "Step 5/5 - Skipping data sync"
|
|
print_info "You can import data later from the System page."
|
|
fi
|
|
|
|
# ═════════════════════════════════════════════════════════════════════════
|
|
# FINAL SUMMARY
|
|
# ═════════════════════════════════════════════════════════════════════════
|
|
|
|
# Build the access URL
|
|
ORIGIN_URL=$(grep CORS_ORIGINS "$ENV_FILE" 2>/dev/null | cut -d= -f2 || echo "http://localhost")
|
|
SERVER_IP=$(hostname -I 2>/dev/null | awk '{print $1}' || echo "localhost")
|
|
|
|
echo ""
|
|
echo ""
|
|
echo -e "${BOLD}╔══════════════════════════════════════════════════════════════╗${NC}"
|
|
echo -e "${BOLD}║ ║${NC}"
|
|
echo -e "${BOLD}║ ${GREEN}Aegis is ready!${NC}${BOLD} ║${NC}"
|
|
echo -e "${BOLD}║ ║${NC}"
|
|
echo -e "${BOLD}╠══════════════════════════════════════════════════════════════╣${NC}"
|
|
echo -e "${BOLD}║${NC} ${BOLD}║${NC}"
|
|
echo -e "${BOLD}║${NC} ${CYAN}Application${NC} ${ORIGIN_URL}"
|
|
echo -e "${BOLD}║${NC} ${CYAN}Local IP${NC} http://${SERVER_IP}:${FRONTEND_PORT}"
|
|
echo -e "${BOLD}║${NC} ${BOLD}║${NC}"
|
|
echo -e "${BOLD}╠══════════════════════════════════════════════════════════════╣${NC}"
|
|
echo -e "${BOLD}║${NC} ${BOLD}║${NC}"
|
|
echo -e "${BOLD}║${NC} ${CYAN}Admin Login${NC} ${BOLD}║${NC}"
|
|
echo -e "${BOLD}║${NC} Username: ${GREEN}${ADMIN_CREDS_USER}${NC}"
|
|
if [ -n "$ADMIN_CREDS_PASS" ]; then
|
|
echo -e "${BOLD}║${NC} Password: ${GREEN}${ADMIN_CREDS_PASS}${NC}"
|
|
else
|
|
echo -e "${BOLD}║${NC} Password: ${YELLOW}(check: docker logs aegis-backend | grep Password)${NC}"
|
|
fi
|
|
echo -e "${BOLD}║${NC} ${BOLD}║${NC}"
|
|
echo -e "${BOLD}╠══════════════════════════════════════════════════════════════╣${NC}"
|
|
echo -e "${BOLD}║${NC} ${BOLD}║${NC}"
|
|
echo -e "${BOLD}║${NC} ${YELLOW}Important:${NC} ${BOLD}║${NC}"
|
|
echo -e "${BOLD}║${NC} - Save the admin password now if auto-generated ${BOLD}║${NC}"
|
|
echo -e "${BOLD}║${NC} - Set up HTTPS/TLS for internet-facing deployments ${BOLD}║${NC}"
|
|
echo -e "${BOLD}║${NC} - Configure firewall rules as needed ${BOLD}║${NC}"
|
|
echo -e "${BOLD}║${NC} - Set up regular database backups ${BOLD}║${NC}"
|
|
echo -e "${BOLD}║${NC} ${BOLD}║${NC}"
|
|
echo -e "${BOLD}╚══════════════════════════════════════════════════════════════╝${NC}"
|
|
echo ""
|
|
echo -e "${BOLD}Useful commands:${NC}"
|
|
echo -e " ${DIM}View logs${NC} docker logs -f aegis-backend"
|
|
echo -e " ${DIM}Stop${NC} $COMPOSE_CMD -f docker-compose.prod.yml down"
|
|
echo -e " ${DIM}Restart${NC} $COMPOSE_CMD -f docker-compose.prod.yml restart"
|
|
echo -e " ${DIM}Update${NC} $COMPOSE_CMD -f docker-compose.prod.yml up -d --build"
|
|
echo -e " ${DIM}DB backup${NC} docker exec aegis-postgres pg_dump -U postgres ${DB_NAME:-attackdb} > backup.sql"
|
|
echo -e " ${DIM}Reconfigure${NC} ./scripts/install.sh"
|
|
echo ""
|