diff --git a/authentik-install.sh b/authentik-install.sh new file mode 100644 index 0000000..91bc0e6 --- /dev/null +++ b/authentik-install.sh @@ -0,0 +1,388 @@ +#!/bin/bash + +# Script to install Docker and Authentik with verification and redeployment capabilities +# Must be run with root privileges + +# Error handling +set -e +trap 'echo "Error on line $LINENO. Exit code: $?"' ERR + +# Configuration +INSTALL_DIR="/docker/authentik" +COMPOSE_FILE="${INSTALL_DIR}/docker-compose.yml" +LOG_FILE="/var/log/authentik-install.log" +CONTAINER_NAME="authentik" +POSTGRES_PASSWORD=$(openssl rand -base64 32) +SECRET_KEY=$(openssl rand -base64 32) +ADMIN_PASSWORD=$(openssl rand -base64 12) +ADMIN_TOKEN=$(openssl rand -hex 32) +ADMIN_EMAIL="admin@example.com" # Default value, can be overridden + +# Utility functions +check_port_availability() { + local port=$1 + if lsof -i ":$port" >/dev/null 2>&1; then + local process_info=$(lsof -i ":$port" | tail -n 1) + local pid=$(echo "$process_info" | awk '{print $2}') + local process_name=$(echo "$process_info" | awk '{print $1}') + log_message "⚠ Error: Port $port is already in use by process $process_name (PID: $pid)" + return 1 + fi + return 0 +} + +check_required_ports() { + local required_ports=(9000 9443) + local ports_in_use=() + + for port in "${required_ports[@]}"; do + if ! check_port_availability "$port"; then + ports_in_use+=($port) + fi + done + + if [ ${#ports_in_use[@]} -ne 0 ]; then + log_message "The following required ports are already in use: ${ports_in_use[*]}" + log_message "Please free these ports before proceeding with the installation." + return 1 + fi + + log_message "✓ All required ports are available" + return 0 +} + +log_message() { + local message="[$(date '+%Y-%m-%d %H:%M:%S')] $1" + echo "$message" + echo "$message" >> "$LOG_FILE" +} + +show_container_status() { + log_message "Current container status:" + echo "----------------------------------------" + docker compose ps --format "table {{.Name}}\t{{.Status}}\t{{.Ports}}" + echo "----------------------------------------" +} + +check_service_status() { + local max_attempts=10 + local attempt=1 + local delay=30 + + while [ $attempt -le $max_attempts ]; do + log_message "Checking services (attempt $attempt/$max_attempts)..." + + local all_services_running=true + local services=("server" "worker" "postgresql" "redis") + local failed_services=() + + # Check each service + for service in "${services[@]}"; do + local container_name="authentik-${service}-1" + + # Check if container exists and is running + if ! docker ps -q -f name="$container_name" >/dev/null 2>&1; then + failed_services+=("$service") + all_services_running=false + continue + fi + + # Check container status + local status=$(docker inspect -f '{{.State.Status}}' "$container_name" 2>/dev/null) + if [ "$status" != "running" ]; then + failed_services+=("$service") + all_services_running=false + fi + done + + if [ "$all_services_running" = true ]; then + # All containers are running, now check if server is responding + log_message "All containers are running, checking web interface..." + + # Give the web interface a few attempts to respond + for i in {1..3}; do + if curl -sf http://localhost:9000 >/dev/null 2>&1; then + log_message "Web interface is responding" + return 0 + fi + sleep 10 + done + fi + + if [ $attempt -lt $max_attempts ]; then + if [ ${#failed_services[@]} -ne 0 ]; then + log_message "Services still initializing: ${failed_services[*]}" + else + log_message "Waiting for web interface to become available..." + fi + sleep $delay + fi + + attempt=$((attempt + 1)) + done + + if [ ${#failed_services[@]} -ne 0 ]; then + log_message "The following services failed to start: ${failed_services[*]}" + else + log_message "Web interface failed to respond" + fi + return 1 +} + +verify_installation() { + log_message "Verifying Authentik installation..." + + if ! check_service_status; then + log_message "Authentik services are not running properly" + return 1 + fi + + log_message "Authentik services are running properly" + return 0 +} + +check_docker_version() { + local current_version=$(docker --version | cut -d ' ' -f3 | cut -d ',' -f1) + log_message "Current Docker version: $current_version" + + apt-get update >/dev/null 2>&1 + local available_version=$(apt-cache policy docker-ce | grep Candidate | cut -d ' ' -f4) + + if [ "$current_version" != "$available_version" ]; then + log_message "New Docker version available: $available_version" + read -p "Do you want to upgrade Docker? (y/N) " -n 1 -r + echo + if [[ $REPLY =~ ^[Yy]$ ]]; then + apt-get install -y docker-ce docker-ce-cli containerd.io + fi + fi +} + +redeploy_service() { + log_message "Redeploying Authentik..." + + cd "$INSTALL_DIR" + docker compose down + docker compose up -d + + local retries=0 + while ! check_service_status && [ $retries -lt 5 ]; do + log_message "Waiting for Authentik to become healthy..." + sleep 30 + retries=$((retries + 1)) + done + + if [ $retries -eq 5 ]; then + log_message "Failed to redeploy Authentik after 5 attempts" + return 1 + fi + + log_message "Authentik redeployed successfully" + return 0 +} + +backup_existing_data() { + if [ -d "$INSTALL_DIR" ]; then + local backup_dir="${INSTALL_DIR}_backup_$(date +%Y%m%d_%H%M%S)" + log_message "Creating backup of existing installation at $backup_dir" + cp -r "$INSTALL_DIR" "$backup_dir" + fi +} + +check_existing_installation() { + log_message "Checking existing Authentik installation..." + + if [ -f "$COMPOSE_FILE" ]; then + log_message "Existing installation found" + show_container_status + + if verify_installation; then + log_message "✓ Authentik is running correctly" + log_message "✓ Authentik is accessible at http://localhost:9000" + return 0 + else + log_message "⚠ Service issues detected. Starting recovery process..." + return 1 + fi + fi + return 1 +} + +# Main script execution starts here + +# Check if script is run as root +if [ "$EUID" -ne 0 ]; then + echo "Please run as root" + exit 1 +fi + +# Initialize log file +mkdir -p "$(dirname "$LOG_FILE")" +touch "$LOG_FILE" +log_message "Starting Authentik installation/verification script" + +# Check if Docker is installed +if ! command -v docker &> /dev/null; then + log_message "Docker not found. Installing Docker..." + curl -fsSL https://get.docker.com -o get-docker.sh + sh get-docker.sh + apt-get update + apt-get install -y docker-compose-plugin + log_message "Docker installed successfully" +else + log_message "Docker already installed" + check_docker_version +fi + +# Check existing installation or proceed with new installation +if check_existing_installation; then + exit 0 +else + if [ -f "$COMPOSE_FILE" ]; then + # Attempt to recover existing installation + backup_existing_data + + if ! redeploy_service; then + log_message "Failed to redeploy Authentik. Manual intervention may be required." + exit 1 + fi + else + # New installation + log_message "Starting new Authentik installation" + + # Create installation directory + mkdir -p "$INSTALL_DIR" + cd "$INSTALL_DIR" + + # Check if required ports are available + if ! check_required_ports; then + log_message "⚠ Port conflict detected. Please resolve the conflicts and run the script again." + exit 1 + fi + + # Create docker-compose.yml + cat > docker-compose.yml <