Make fail2ban setup fully automated with intelligent error handling

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.
This commit is contained in:
Claude
2026-01-11 22:50:30 +00:00
parent 5dbb27ee8b
commit d3f412dc28
+370 -170
View File
@@ -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":"<HOST>".*"status":(?:401|403|429).*$
^.*"remote_addr":"<HOST>.*"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 ""