#!/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 <