FIXES: - Fix Magic Mirror npm install to run inside Docker container instead of on host - npm (Node Package Manager) commands now execute inside the MagicMirror container where Node.js is installed, preventing errors on hosts without Node.js NEW SERVICES: - Add ActualBudget: Open-source personal finance management with bank sync (SimpleFIN) - Add Keycloak: Identity and Access Management (SSO, OAuth2, SAML, MFA) - Both services integrated into main installation script and available as standalone docker-compose files for existing servers CADDY & FAIL2BAN: - Add caddy-setup-helper.sh: Interactive script to configure Caddy and fail2ban * Detects existing Caddy installation * Automatically backs up Caddyfile with timestamp * Checks for fail2ban support * Provides service integration examples - Add fail2ban filter and jail configurations for Caddy protection - Add comprehensive setup guide (CADDY-FAIL2BAN-SETUP.md) DOCUMENTATION: - Detailed deployment instructions for each service - Reverse proxy configuration examples - Security best practices and headers - Backup/restore procedures - Troubleshooting guides This update enables secure deployment of new services on existing servers with proper Caddy reverse proxy integration and fail2ban protection against attacks.
348 lines
10 KiB
Bash
Executable File
348 lines
10 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Caddy Setup Helper Script
|
|
# This script helps manage Caddy configuration, backups, and fail2ban integration
|
|
# for dockerized Caddy setups
|
|
|
|
set -e
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m' # No Color
|
|
|
|
echo "==============================================="
|
|
echo " Caddy Configuration & Fail2ban Setup Helper"
|
|
echo "==============================================="
|
|
echo ""
|
|
|
|
# Function to print colored output
|
|
print_info() {
|
|
echo -e "${BLUE}[INFO]${NC} $1"
|
|
}
|
|
|
|
print_success() {
|
|
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
|
}
|
|
|
|
print_warning() {
|
|
echo -e "${YELLOW}[WARNING]${NC} $1"
|
|
}
|
|
|
|
print_error() {
|
|
echo -e "${RED}[ERROR]${NC} $1"
|
|
}
|
|
|
|
# ==============================
|
|
# 1. CHECK IF CADDY IS INSTALLED
|
|
# ==============================
|
|
print_info "Checking for Caddy installation..."
|
|
|
|
CADDY_CONTAINER=""
|
|
CADDYFILE_PATH=""
|
|
|
|
# Check if Docker is available
|
|
if ! command -v docker &> /dev/null; then
|
|
print_error "Docker is not installed or not in PATH"
|
|
exit 1
|
|
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)
|
|
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
|
|
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"
|
|
exit 0
|
|
fi
|
|
fi
|
|
|
|
# ==============================
|
|
# 2. LOCATE CADDYFILE
|
|
# ==============================
|
|
print_info "Locating Caddyfile..."
|
|
|
|
# Common Caddyfile locations
|
|
POSSIBLE_PATHS=(
|
|
"$HOME/docker/caddy/Caddyfile"
|
|
"$HOME/docker/caddy/caddyfile"
|
|
"$HOME/docker/caddy/config/Caddyfile"
|
|
"/etc/caddy/Caddyfile"
|
|
)
|
|
|
|
for path in "${POSSIBLE_PATHS[@]}"; do
|
|
if [ -f "$path" ]; then
|
|
CADDYFILE_PATH="$path"
|
|
print_success "Found Caddyfile at: $CADDYFILE_PATH"
|
|
break
|
|
fi
|
|
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
|
|
if [ -n "$CUSTOM_PATH" ] && [ -f "$CUSTOM_PATH" ]; then
|
|
CADDYFILE_PATH="$CUSTOM_PATH"
|
|
print_success "Using Caddyfile at: $CADDYFILE_PATH"
|
|
else
|
|
print_error "Cannot proceed without Caddyfile location"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# ==============================
|
|
# 3. BACKUP CADDYFILE
|
|
# ==============================
|
|
print_info "Creating backup of Caddyfile..."
|
|
|
|
BACKUP_DIR=$(dirname "$CADDYFILE_PATH")/backups
|
|
mkdir -p "$BACKUP_DIR"
|
|
|
|
BACKUP_FILE="$BACKUP_DIR/Caddyfile.backup.$(date +%Y%m%d_%H%M%S)"
|
|
cp "$CADDYFILE_PATH" "$BACKUP_FILE"
|
|
print_success "Backup created: $BACKUP_FILE"
|
|
|
|
echo ""
|
|
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:"
|
|
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"
|
|
fi
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo ""
|
|
|
|
# ==============================
|
|
# 4. CHECK FOR FAIL2BAN SUPPORT
|
|
# ==============================
|
|
print_info "Checking Caddy configuration for fail2ban support..."
|
|
|
|
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
|
|
else
|
|
print_success "Logging configuration found"
|
|
fi
|
|
|
|
# Check for common security headers
|
|
if ! grep -q "header" "$CADDYFILE_PATH"; then
|
|
print_warning "No security headers configured"
|
|
fi
|
|
|
|
# ==============================
|
|
# 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
|
|
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"
|
|
fi
|
|
fi
|
|
|
|
# ==============================
|
|
# 6. PROVIDE FAIL2BAN CONFIGURATION
|
|
# ==============================
|
|
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
|
|
}
|
|
|
|
# Reverse proxy or other directives
|
|
reverse_proxy localhost:8080
|
|
}
|
|
EOF
|
|
|
|
echo ""
|
|
echo "2. Create fail2ban filter at /etc/fail2ban/filter.d/caddy-auth.conf:"
|
|
echo ""
|
|
cat << 'EOF'
|
|
[Definition]
|
|
failregex = ^.*"remote_ip":"<HOST>".*"status":(?:401|403|429).*$
|
|
^.*"remote_addr":"<HOST>.*"status":(?:401|403|429).*$
|
|
ignoreregex =
|
|
EOF
|
|
|
|
echo ""
|
|
echo "3. Create fail2ban jail at /etc/fail2ban/jail.d/caddy.conf:"
|
|
echo ""
|
|
cat << 'EOF'
|
|
[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
|
|
|
|
echo ""
|
|
echo "4. Restart fail2ban:"
|
|
echo " sudo systemctl restart fail2ban"
|
|
echo " sudo fail2ban-client status caddy-auth"
|
|
echo ""
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
|
|
# ==============================
|
|
# 7. EXAMPLE SITE CONFIGURATIONS
|
|
# ==============================
|
|
echo ""
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo " ADDING NEW SERVICES TO CADDY"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo ""
|
|
echo "Add these blocks to your Caddyfile for your new services:"
|
|
echo ""
|
|
|
|
# ActualBudget example
|
|
echo "# ActualBudget (Personal Finance)"
|
|
cat << 'EOF'
|
|
budget.yourdomain.com {
|
|
log {
|
|
output file /var/log/caddy/actualbudget-access.log
|
|
format json
|
|
level INFO
|
|
}
|
|
|
|
reverse_proxy localhost:5006
|
|
|
|
# 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"
|
|
}
|
|
}
|
|
EOF
|
|
|
|
echo ""
|
|
echo "# Keycloak (Identity & Access Management)"
|
|
cat << 'EOF'
|
|
auth.yourdomain.com {
|
|
log {
|
|
output file /var/log/caddy/keycloak-access.log
|
|
format json
|
|
level INFO
|
|
}
|
|
|
|
reverse_proxy localhost:8180
|
|
|
|
# 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"
|
|
}
|
|
}
|
|
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"
|
|
else
|
|
print_error "Failed to reload Caddy configuration"
|
|
print_info "Check Caddy logs: docker logs $CADDY_CONTAINER"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
echo ""
|
|
print_success "Caddy setup helper completed!"
|
|
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"
|
|
echo ""
|