From d3f412dc282a341d7f053fde8e794d1630edf6db Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Jan 2026 22:50:30 +0000 Subject: [PATCH] Make fail2ban setup fully automated with intelligent error handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The caddy-setup-helper.sh script now handles everything automatically (after asking for confirmation), only falling back to manual instructions if errors occur. AUTOMATED WORKFLOW: 1. ✅ Backup Caddyfile (ALWAYS FIRST - before any changes) 2. ✅ Check if fail2ban is installed 3. ✅ Install fail2ban if missing (with confirmation) 4. ✅ Create /var/log/caddy directory 5. ✅ Check if Caddy container has log volume mounted 6. ✅ Automatically add log volume to docker-compose.yml if needed 7. ✅ Create fail2ban filter at /etc/fail2ban/filter.d/caddy-auth.conf 8. ✅ Create fail2ban jail at /etc/fail2ban/jail.d/caddy.conf (with custom settings) 9. ✅ Test fail2ban configuration 10. ✅ Restart fail2ban and verify jail is active 11. ✅ Add service configurations (ActualBudget, Keycloak) to Caddyfile 12. ✅ Validate and reload Caddy configuration ERROR HANDLING: - All operations tracked with error messages array - If any step fails, script continues but tracks the failure - At the end, shows all errors encountered - Provides exact manual commands to fix issues - Backup is ALWAYS created before any changes USER EXPERIENCE: - Interactive prompts with sensible defaults - Clear colored output (INFO, SUCCESS, WARNING, ERROR) - Progress feedback at each step - Final summary with useful commands - Only shows manual instructions if automation failed SAFETY FEATURES: - Caddyfile backup before ANY modifications - docker-compose.yml backup before modifications - Validation before reloading Caddy - Test fail2ban config before restart - Restore instructions always shown after backup This matches the integrated experience of other services - fully automated unless something goes wrong, in which case it provides manual steps. --- caddy-setup-helper.sh | 540 +++++++++++++++++++++++++++++------------- 1 file changed, 370 insertions(+), 170 deletions(-) diff --git a/caddy-setup-helper.sh b/caddy-setup-helper.sh index e059b28..efc9dfc 100755 --- a/caddy-setup-helper.sh +++ b/caddy-setup-helper.sh @@ -2,7 +2,7 @@ # Caddy Setup Helper Script # This script helps manage Caddy configuration, backups, and fail2ban integration -# for dockerized Caddy setups +# for dockerized Caddy setups - FULLY AUTOMATED with error handling set -e @@ -35,6 +35,27 @@ print_error() { echo -e "${RED}[ERROR]${NC} $1" } +# Function to ask yes/no questions +ask_yn() { + local prompt="$1" + local default="${2:-n}" + local response + + if [ "$default" = "y" ]; then + read -p "$prompt [Y/n]: " response + response=${response:-y} + else + read -p "$prompt [y/N]: " response + response=${response:-n} + fi + + [[ "$response" =~ ^[Yy]$ ]] +} + +# Track if we need to show manual instructions +SHOW_MANUAL=false +ERROR_MESSAGES=() + # ============================== # 1. CHECK IF CADDY IS INSTALLED # ============================== @@ -50,20 +71,15 @@ if ! command -v docker &> /dev/null; then fi # Try to find Caddy container -if docker ps --format '{{.Names}}' | grep -q "caddy"; then - CADDY_CONTAINER=$(docker ps --format '{{.Names}}' | grep "caddy" | head -1) +if docker ps --format '{{.Names}}' | grep -iq "caddy"; then + CADDY_CONTAINER=$(docker ps --format '{{.Names}}' | grep -i "caddy" | head -1) print_success "Found running Caddy container: $CADDY_CONTAINER" else print_warning "No running Caddy container found" - read -p "Is Caddy installed? (y/n): " CADDY_INSTALLED - if [ "$CADDY_INSTALLED" != "y" ] && [ "$CADDY_INSTALLED" != "Y" ]; then + if ! ask_yn "Is Caddy installed?" "n"; then print_info "Caddy is not installed. Please install Caddy first." echo "" - echo "To install Caddy with Docker:" - echo " mkdir -p ~/docker/caddy" - echo " cd ~/docker/caddy" - echo " # Create docker-compose.yml (see example below)" - echo " docker compose up -d" + echo "To install Caddy with Docker, see CADDY-FAIL2BAN-SETUP.md" exit 0 fi fi @@ -91,7 +107,7 @@ done if [ -z "$CADDYFILE_PATH" ]; then print_warning "Caddyfile not found in common locations" - read -p "Enter path to Caddyfile (or press Enter to skip): " CUSTOM_PATH + read -p "Enter path to Caddyfile: " CUSTOM_PATH if [ -n "$CUSTOM_PATH" ] && [ -f "$CUSTOM_PATH" ]; then CADDYFILE_PATH="$CUSTOM_PATH" print_success "Using Caddyfile at: $CADDYFILE_PATH" @@ -101,12 +117,14 @@ if [ -z "$CADDYFILE_PATH" ]; then fi fi +CADDY_DIR=$(dirname "$CADDYFILE_PATH") + # ============================== -# 3. BACKUP CADDYFILE +# 3. BACKUP CADDYFILE (ALWAYS FIRST!) # ============================== print_info "Creating backup of Caddyfile..." -BACKUP_DIR=$(dirname "$CADDYFILE_PATH")/backups +BACKUP_DIR="$CADDY_DIR/backups" mkdir -p "$BACKUP_DIR" BACKUP_FILE="$BACKUP_DIR/Caddyfile.backup.$(date +%Y%m%d_%H%M%S)" @@ -118,150 +136,274 @@ echo "━━━━━━━━━━━━━━━━━━━━━━━━ echo " BACKUP RESTORE INSTRUCTIONS" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "To restore this backup:" -echo " 1. Copy backup to original location:" -echo " cp $BACKUP_FILE $CADDYFILE_PATH" -echo "" -echo " 2. Reload Caddy configuration:" +echo " cp $BACKUP_FILE $CADDYFILE_PATH" if [ -n "$CADDY_CONTAINER" ]; then - echo " docker exec -w /etc/caddy $CADDY_CONTAINER caddy reload" - echo " docker exec -w /etc/caddy $CADDY_CONTAINER caddy fmt --overwrite" -else - echo " docker exec -w /etc/caddy caddy caddy reload" - echo " docker exec -w /etc/caddy caddy caddy fmt --overwrite" + echo " docker exec -w /etc/caddy $CADDY_CONTAINER caddy reload" + echo " docker exec -w /etc/caddy $CADDY_CONTAINER caddy fmt --overwrite" fi echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" # ============================== -# 4. CHECK FOR FAIL2BAN SUPPORT +# 4. CHECK FOR FAIL2BAN INSTALLATION # ============================== -print_info "Checking Caddy configuration for fail2ban support..." +print_info "Checking for fail2ban installation..." -NEEDS_FAIL2BAN_CONFIG=false - -# Check if Caddyfile has access logging configured -if ! grep -q "log {" "$CADDYFILE_PATH" && ! grep -q "log " "$CADDYFILE_PATH"; then - print_warning "No logging configuration found in Caddyfile" - NEEDS_FAIL2BAN_CONFIG=true +FAIL2BAN_INSTALLED=false +if command -v fail2ban-client &> /dev/null; then + print_success "fail2ban is installed" + FAIL2BAN_INSTALLED=true else - print_success "Logging configuration found" -fi + print_warning "fail2ban is not installed" -# Check for common security headers -if ! grep -q "header" "$CADDYFILE_PATH"; then - print_warning "No security headers configured" -fi + if ask_yn "Would you like to install fail2ban now?" "y"; then + print_info "Installing fail2ban..." -# ============================== -# 5. OFFER TO ADD FAIL2BAN SUPPORT -# ============================== -if [ "$NEEDS_FAIL2BAN_CONFIG" = true ]; then - echo "" - read -p "Would you like to add fail2ban logging support to Caddyfile? (y/n): " ADD_FAIL2BAN - - if [ "$ADD_FAIL2BAN" = "y" ] || [ "$ADD_FAIL2BAN" = "Y" ]; then - print_info "Adding fail2ban support..." - - # Create a new Caddyfile with fail2ban support - TEMP_CADDYFILE="${CADDYFILE_PATH}.tmp" - - # Add global options if not present - if ! grep -q "{" "$CADDYFILE_PATH" | head -1 | grep -q "^{"; then - cat > "$TEMP_CADDYFILE" << 'EOF' -{ - # Global options - admin off - # Persist config - persist_config off -} - -EOF + if sudo apt update && sudo apt install -y fail2ban; then + print_success "fail2ban installed successfully" + FAIL2BAN_INSTALLED=true + else + print_error "Failed to install fail2ban" + ERROR_MESSAGES+=("Failed to install fail2ban - you may need to install it manually") + SHOW_MANUAL=true fi - - # Append original content - cat "$CADDYFILE_PATH" >> "$TEMP_CADDYFILE" - - # Move temp file to original - mv "$TEMP_CADDYFILE" "$CADDYFILE_PATH" - - print_success "Added global configuration" - print_info "Note: You'll need to add logging to individual sites" + else + print_warning "Skipping fail2ban installation" + SHOW_MANUAL=true fi fi # ============================== -# 6. PROVIDE FAIL2BAN CONFIGURATION +# 5. CREATE LOG DIRECTORY # ============================== -echo "" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo " FAIL2BAN CONFIGURATION FOR CADDY" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" -echo "To enable fail2ban protection for your Caddy services:" -echo "" -echo "1. Add logging to each site in your Caddyfile:" -echo "" -cat << 'EOF' -yourdomain.com { - # Enable structured logging for fail2ban - log { - output file /var/log/caddy/access.log - format json - level INFO - } +if [ "$FAIL2BAN_INSTALLED" = true ]; then + print_info "Checking Caddy log directory..." - # Reverse proxy or other directives - reverse_proxy localhost:8080 -} -EOF + LOG_DIR="/var/log/caddy" + if [ ! -d "$LOG_DIR" ]; then + if ask_yn "Create $LOG_DIR for Caddy logs?" "y"; then + if sudo mkdir -p "$LOG_DIR" && sudo chmod 755 "$LOG_DIR"; then + print_success "Log directory created: $LOG_DIR" + else + print_error "Failed to create log directory" + ERROR_MESSAGES+=("Failed to create $LOG_DIR - create it manually with: sudo mkdir -p $LOG_DIR && sudo chmod 755 $LOG_DIR") + SHOW_MANUAL=true + fi + fi + else + print_success "Log directory exists: $LOG_DIR" + fi +fi -echo "" -echo "2. Create fail2ban filter at /etc/fail2ban/filter.d/caddy-auth.conf:" -echo "" -cat << 'EOF' -[Definition] +# ============================== +# 6. CHECK CADDY DOCKER COMPOSE FOR LOG VOLUME +# ============================== +if [ "$FAIL2BAN_INSTALLED" = true ] && [ -n "$CADDY_CONTAINER" ]; then + print_info "Checking if Caddy container has log volume mounted..." + + # Check if the container has /var/log/caddy mounted + if docker inspect "$CADDY_CONTAINER" 2>/dev/null | grep -q "/var/log/caddy"; then + print_success "Caddy container has log volume mounted" + else + print_warning "Caddy container does not have /var/log/caddy volume mounted" + + # Check if there's a docker-compose.yml + COMPOSE_FILE="" + for file in "$CADDY_DIR/docker-compose.yml" "$CADDY_DIR/docker-compose.yaml"; do + if [ -f "$file" ]; then + COMPOSE_FILE="$file" + break + fi + done + + if [ -n "$COMPOSE_FILE" ]; then + if ask_yn "Add /var/log/caddy volume to docker-compose.yml?" "y"; then + # Backup docker-compose.yml + cp "$COMPOSE_FILE" "$COMPOSE_FILE.backup.$(date +%Y%m%d_%H%M%S)" + + # Check if volumes section exists + if grep -q "volumes:" "$COMPOSE_FILE"; then + # Add to existing volumes + if ! grep -q "/var/log/caddy" "$COMPOSE_FILE"; then + # Find the volumes section and add our volume + sed -i '/volumes:/a\ - /var/log/caddy:/var/log/caddy' "$COMPOSE_FILE" + print_success "Added log volume to docker-compose.yml" + print_warning "You'll need to restart the Caddy container for this to take effect" + + if ask_yn "Restart Caddy container now?" "n"; then + cd "$CADDY_DIR" + if docker compose down && docker compose up -d; then + print_success "Caddy container restarted" + # Update CADDY_CONTAINER name in case it changed + CADDY_CONTAINER=$(docker ps --format '{{.Names}}' | grep -i "caddy" | head -1) + else + print_error "Failed to restart Caddy container" + ERROR_MESSAGES+=("Failed to restart Caddy - restart manually with: cd $CADDY_DIR && docker compose restart") + fi + fi + fi + else + print_warning "Could not automatically add volume - docker-compose.yml format is unexpected" + ERROR_MESSAGES+=("Add this volume manually to your Caddy service: /var/log/caddy:/var/log/caddy") + SHOW_MANUAL=true + fi + fi + else + print_warning "No docker-compose.yml found - you may need to add the volume manually" + ERROR_MESSAGES+=("Add log volume to Caddy container: /var/log/caddy:/var/log/caddy") + SHOW_MANUAL=true + fi + fi +fi + +# ============================== +# 7. CREATE FAIL2BAN FILTER +# ============================== +if [ "$FAIL2BAN_INSTALLED" = true ]; then + print_info "Checking fail2ban filter configuration..." + + FILTER_FILE="/etc/fail2ban/filter.d/caddy-auth.conf" + if [ -f "$FILTER_FILE" ]; then + print_success "fail2ban filter already exists: $FILTER_FILE" + else + if ask_yn "Create fail2ban filter for Caddy?" "y"; then + print_info "Creating fail2ban filter..." + + FILTER_CONTENT='[Definition] failregex = ^.*"remote_ip":"".*"status":(?:401|403|429).*$ ^.*"remote_addr":".*"status":(?:401|403|429).*$ -ignoreregex = -EOF +ignoreregex = ^.*"remote_ip":"(?:127\.0\.0\.1|::1)".*$ +datepattern = "ts":%%s' -echo "" -echo "3. Create fail2ban jail at /etc/fail2ban/jail.d/caddy.conf:" -echo "" -cat << 'EOF' -[caddy-auth] + if echo "$FILTER_CONTENT" | sudo tee "$FILTER_FILE" > /dev/null; then + print_success "Created fail2ban filter: $FILTER_FILE" + else + print_error "Failed to create fail2ban filter" + ERROR_MESSAGES+=("Failed to create $FILTER_FILE - create it manually (see CADDY-FAIL2BAN-SETUP.md)") + SHOW_MANUAL=true + fi + fi + fi +fi + +# ============================== +# 8. CREATE FAIL2BAN JAIL +# ============================== +if [ "$FAIL2BAN_INSTALLED" = true ]; then + print_info "Checking fail2ban jail configuration..." + + JAIL_FILE="/etc/fail2ban/jail.d/caddy.conf" + if [ -f "$JAIL_FILE" ]; then + print_success "fail2ban jail already exists: $JAIL_FILE" + else + if ask_yn "Create fail2ban jail for Caddy?" "y"; then + print_info "Creating fail2ban jail..." + + # Ask for custom settings + echo "" + print_info "Fail2ban jail settings (press Enter for defaults):" + read -p " Max retries before ban [5]: " MAXRETRY + MAXRETRY=${MAXRETRY:-5} + + read -p " Find time window in seconds [600]: " FINDTIME + FINDTIME=${FINDTIME:-600} + + read -p " Ban duration in seconds [3600]: " BANTIME + BANTIME=${BANTIME:-3600} + + JAIL_CONTENT="[caddy-auth] enabled = true port = http,https filter = caddy-auth logpath = /var/log/caddy/access.log -maxretry = 5 -findtime = 600 -bantime = 3600 -action = iptables-multiport[name=CaddyAuth, port="http,https", protocol=tcp] -EOF + /var/log/caddy/*-access.log +maxretry = $MAXRETRY +findtime = $FINDTIME +bantime = $BANTIME +action = iptables-multiport[name=CaddyAuth, port=\"http,https\", protocol=tcp] +backend = auto" -echo "" -echo "4. Restart fail2ban:" -echo " sudo systemctl restart fail2ban" -echo " sudo fail2ban-client status caddy-auth" -echo "" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + if echo "$JAIL_CONTENT" | sudo tee "$JAIL_FILE" > /dev/null; then + print_success "Created fail2ban jail: $JAIL_FILE" + else + print_error "Failed to create fail2ban jail" + ERROR_MESSAGES+=("Failed to create $JAIL_FILE - create it manually (see CADDY-FAIL2BAN-SETUP.md)") + SHOW_MANUAL=true + fi + fi + fi +fi # ============================== -# 7. EXAMPLE SITE CONFIGURATIONS +# 9. TEST FAIL2BAN CONFIGURATION +# ============================== +if [ "$FAIL2BAN_INSTALLED" = true ]; then + print_info "Testing fail2ban configuration..." + + if sudo fail2ban-client -t &> /dev/null; then + print_success "fail2ban configuration is valid" + else + print_error "fail2ban configuration has errors" + ERROR_MESSAGES+=("fail2ban configuration is invalid - check with: sudo fail2ban-client -t") + SHOW_MANUAL=true + fi +fi + +# ============================== +# 10. RESTART FAIL2BAN +# ============================== +if [ "$FAIL2BAN_INSTALLED" = true ]; then + if ask_yn "Restart fail2ban to apply changes?" "y"; then + print_info "Restarting fail2ban..." + + if sudo systemctl restart fail2ban; then + print_success "fail2ban restarted successfully" + + # Wait a moment for fail2ban to start + sleep 2 + + # Check if caddy-auth jail is running + if sudo fail2ban-client status caddy-auth &> /dev/null; then + print_success "caddy-auth jail is active" + echo "" + print_info "Jail status:" + sudo fail2ban-client status caddy-auth + else + print_warning "caddy-auth jail is not active" + ERROR_MESSAGES+=("caddy-auth jail failed to start - check with: sudo fail2ban-client status") + SHOW_MANUAL=true + fi + else + print_error "Failed to restart fail2ban" + ERROR_MESSAGES+=("Failed to restart fail2ban - check logs with: sudo journalctl -u fail2ban -n 50") + SHOW_MANUAL=true + fi + fi +fi + +# ============================== +# 11. ADD SERVICE CONFIGURATIONS TO CADDYFILE # ============================== echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo " ADDING NEW SERVICES TO CADDY" +echo " ADDING NEW SERVICES TO CADDYFILE" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" -echo "Add these blocks to your Caddyfile for your new services:" + +print_info "You can now add your services to the Caddyfile" +echo "" +echo "Available services to add:" +echo " - ActualBudget (Personal Finance) - Port 5006" +echo " - Keycloak (Identity & Access Management) - Port 8180" echo "" -# ActualBudget example -echo "# ActualBudget (Personal Finance)" -cat << 'EOF' -budget.yourdomain.com { +if ask_yn "Would you like to add ActualBudget to Caddyfile?" "n"; then + read -p "Enter domain for ActualBudget (e.g., budget.yourdomain.com): " AB_DOMAIN + + if [ -n "$AB_DOMAIN" ]; then + AB_CONFIG=" +# ActualBudget - Personal Finance +$AB_DOMAIN { log { output file /var/log/caddy/actualbudget-access.log format json @@ -272,24 +414,31 @@ budget.yourdomain.com { # Security headers header { - # Enable HSTS - Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" - # Prevent clickjacking - X-Frame-Options "SAMEORIGIN" - # Prevent MIME type sniffing - X-Content-Type-Options "nosniff" - # XSS protection - X-XSS-Protection "1; mode=block" - # Referrer policy - Referrer-Policy "strict-origin-when-cross-origin" + Strict-Transport-Security \"max-age=31536000; includeSubDomains; preload\" + X-Frame-Options \"SAMEORIGIN\" + X-Content-Type-Options \"nosniff\" + X-XSS-Protection \"1; mode=block\" + Referrer-Policy \"strict-origin-when-cross-origin\" } } -EOF +" -echo "" -echo "# Keycloak (Identity & Access Management)" -cat << 'EOF' -auth.yourdomain.com { + if echo "$AB_CONFIG" >> "$CADDYFILE_PATH"; then + print_success "Added ActualBudget configuration to Caddyfile" + else + print_error "Failed to add ActualBudget configuration" + ERROR_MESSAGES+=("Add ActualBudget manually - see CADDY-FAIL2BAN-SETUP.md") + fi + fi +fi + +if ask_yn "Would you like to add Keycloak to Caddyfile?" "n"; then + read -p "Enter domain for Keycloak (e.g., auth.yourdomain.com): " KC_DOMAIN + + if [ -n "$KC_DOMAIN" ]; then + KC_CONFIG=" +# Keycloak - Identity & Access Management +$KC_DOMAIN { log { output file /var/log/caddy/keycloak-access.log format json @@ -300,48 +449,99 @@ auth.yourdomain.com { # Security headers header { - Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" - X-Frame-Options "SAMEORIGIN" - X-Content-Type-Options "nosniff" - X-XSS-Protection "1; mode=block" - Referrer-Policy "strict-origin-when-cross-origin" + Strict-Transport-Security \"max-age=31536000; includeSubDomains; preload\" + X-Frame-Options \"SAMEORIGIN\" + X-Content-Type-Options \"nosniff\" + X-XSS-Protection \"1; mode=block\" + Referrer-Policy \"strict-origin-when-cross-origin\" } } -EOF +" -echo "" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -# ============================== -# 8. RELOAD CADDY -# ============================== -if [ -n "$CADDY_CONTAINER" ]; then - read -p "Would you like to reload Caddy configuration now? (y/n): " RELOAD_NOW - - if [ "$RELOAD_NOW" = "y" ] || [ "$RELOAD_NOW" = "Y" ]; then - print_info "Reloading Caddy configuration..." - - if docker exec -w /etc/caddy "$CADDY_CONTAINER" caddy fmt --overwrite 2>/dev/null; then - print_success "Caddyfile formatted" - fi - - if docker exec -w /etc/caddy "$CADDY_CONTAINER" caddy reload 2>/dev/null; then - print_success "Caddy configuration reloaded successfully" + if echo "$KC_CONFIG" >> "$CADDYFILE_PATH"; then + print_success "Added Keycloak configuration to Caddyfile" else - print_error "Failed to reload Caddy configuration" - print_info "Check Caddy logs: docker logs $CADDY_CONTAINER" + print_error "Failed to add Keycloak configuration" + ERROR_MESSAGES+=("Add Keycloak manually - see CADDY-FAIL2BAN-SETUP.md") fi fi fi +# ============================== +# 12. VALIDATE AND RELOAD CADDY +# ============================== +if [ -n "$CADDY_CONTAINER" ]; then + echo "" + if ask_yn "Validate and reload Caddy configuration?" "y"; then + print_info "Validating Caddyfile..." + + # Format first + if docker exec -w /etc/caddy "$CADDY_CONTAINER" caddy fmt --overwrite 2>/dev/null; then + print_success "Caddyfile formatted" + fi + + # Validate + if docker exec "$CADDY_CONTAINER" caddy validate --config /etc/caddy/Caddyfile 2>/dev/null; then + print_success "Caddyfile is valid" + + # Reload + print_info "Reloading Caddy configuration..." + if docker exec -w /etc/caddy "$CADDY_CONTAINER" caddy reload 2>/dev/null; then + print_success "Caddy configuration reloaded successfully" + else + print_error "Failed to reload Caddy configuration" + ERROR_MESSAGES+=("Failed to reload Caddy - check logs with: docker logs $CADDY_CONTAINER") + SHOW_MANUAL=true + fi + else + print_error "Caddyfile validation failed" + ERROR_MESSAGES+=("Caddyfile has syntax errors - check with: docker exec $CADDY_CONTAINER caddy validate --config /etc/caddy/Caddyfile") + SHOW_MANUAL=true + fi + fi +fi + +# ============================== +# 13. FINAL SUMMARY +# ============================== echo "" -print_success "Caddy setup helper completed!" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo " SETUP COMPLETE" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" -echo "Next steps:" -echo " 1. Review and edit Caddyfile: $CADDYFILE_PATH" -echo " 2. Add site configurations for ActualBudget and Keycloak (see examples above)" -echo " 3. Set up fail2ban filters and jails (see instructions above)" -echo " 4. Test Caddy configuration: docker exec $CADDY_CONTAINER caddy validate --config /etc/caddy/Caddyfile" -echo " 5. Reload Caddy: docker exec -w /etc/caddy $CADDY_CONTAINER caddy reload" + +if [ "$SHOW_MANUAL" = true ]; then + print_warning "Some steps could not be completed automatically" + echo "" + echo "Issues encountered:" + for msg in "${ERROR_MESSAGES[@]}"; do + echo " - $msg" + done + echo "" + print_info "See CADDY-FAIL2BAN-SETUP.md for manual setup instructions" + echo "" +fi + +print_success "Caddyfile backed up to: $BACKUP_FILE" + +if [ "$FAIL2BAN_INSTALLED" = true ]; then + print_success "fail2ban is installed and configured" + echo "" + echo "Useful commands:" + echo " Check jail status: sudo fail2ban-client status caddy-auth" + echo " View banned IPs: sudo fail2ban-client get caddy-auth banip" + echo " Unban IP: sudo fail2ban-client set caddy-auth unbanip 1.2.3.4" + echo " Test filter: sudo fail2ban-regex /var/log/caddy/access.log /etc/fail2ban/filter.d/caddy-auth.conf" +fi + +echo "" +echo "Caddyfile location: $CADDYFILE_PATH" +echo "Backup location: $BACKUP_FILE" +if [ -n "$CADDY_CONTAINER" ]; then + echo "Caddy container: $CADDY_CONTAINER" + echo "Reload Caddy: docker exec -w /etc/caddy $CADDY_CONTAINER caddy reload" + echo "View Caddy logs: docker logs $CADDY_CONTAINER --tail 50" +fi +echo "" +print_success "All done!" echo ""