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:
+370
-170
@@ -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 ""
|
||||
|
||||
Reference in New Issue
Block a user