Fix Magic Mirror npm setup and add ActualBudget, Keycloak, Caddy/fail2ban support

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.
This commit is contained in:
Claude
2026-01-11 21:43:52 +00:00
parent db0b9993a5
commit 5dbb27ee8b
7 changed files with 1227 additions and 10 deletions
+371
View File
@@ -0,0 +1,371 @@
# Caddy with Fail2ban Setup Guide
This guide helps you integrate new services with an existing Caddy reverse proxy and set up fail2ban protection.
## Quick Start
For servers with Caddy already installed:
```bash
# Run the automated helper script
./caddy-setup-helper.sh
```
This script will:
- ✅ Detect your Caddy installation
- ✅ Locate and backup your Caddyfile
- ✅ Check for fail2ban configuration
- ✅ Provide examples for adding new services
## Manual Setup
### 1. Backup Your Caddyfile
**IMPORTANT:** Always backup before making changes!
```bash
# Find your Caddyfile location
CADDYFILE=~/docker/caddy/Caddyfile # Adjust path as needed
# Create backup directory
mkdir -p $(dirname "$CADDYFILE")/backups
# Backup with timestamp
cp "$CADDYFILE" "$(dirname "$CADDYFILE")/backups/Caddyfile.backup.$(date +%Y%m%d_%H%M%S)"
```
### 2. Add New Services to Caddy
Add these blocks to your Caddyfile:
#### ActualBudget (Personal Finance)
```caddy
budget.yourdomain.com {
log {
output file /var/log/caddy/actualbudget-access.log
format json
level INFO
}
reverse_proxy localhost:5006
# 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"
}
}
```
#### Keycloak (Identity & Access Management)
```caddy
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"
}
}
```
### 3. Reload Caddy Configuration
After editing the Caddyfile:
```bash
# Format the Caddyfile (optional but recommended)
docker exec -w /etc/caddy caddy caddy fmt --overwrite
# Reload Caddy configuration
docker exec -w /etc/caddy caddy caddy reload
```
If you get errors, check Caddy logs:
```bash
docker logs caddy
```
### 4. Restore from Backup (if needed)
If something goes wrong:
```bash
# Find your backup
ls -lah ~/docker/caddy/backups/
# Restore the backup
cp ~/docker/caddy/backups/Caddyfile.backup.YYYYMMDD_HHMMSS ~/docker/caddy/Caddyfile
# Reload Caddy
docker exec -w /etc/caddy caddy caddy reload
docker exec -w /etc/caddy caddy caddy fmt --overwrite
```
## Fail2ban Configuration
### Prerequisites
1. **Enable JSON logging in Caddy** (shown in examples above)
2. **Install fail2ban** on the host:
```bash
sudo apt update
sudo apt install fail2ban -y
```
### Installation Steps
#### Step 1: Install Fail2ban Filter
```bash
# Copy the filter configuration
sudo cp fail2ban-caddy-filter.conf /etc/fail2ban/filter.d/caddy-auth.conf
```
Or create it manually:
```bash
sudo tee /etc/fail2ban/filter.d/caddy-auth.conf > /dev/null <<'EOF'
[Definition]
failregex = ^.*"remote_ip":"<HOST>".*"status":(?:401|403|429).*$
^.*"remote_addr":"<HOST>.*"status":(?:401|403|429).*$
ignoreregex = ^.*"remote_ip":"(?:127\.0\.0\.1|::1)".*$
datepattern = "ts":%%s
EOF
```
#### Step 2: Install Fail2ban Jail
```bash
# Copy the jail configuration
sudo cp fail2ban-caddy-jail.conf /etc/fail2ban/jail.d/caddy.conf
```
Or create it manually:
```bash
sudo tee /etc/fail2ban/jail.d/caddy.conf > /dev/null <<'EOF'
[caddy-auth]
enabled = true
port = http,https
filter = caddy-auth
logpath = /var/log/caddy/access.log
/var/log/caddy/*-access.log
maxretry = 5
findtime = 600
bantime = 3600
action = iptables-multiport[name=CaddyAuth, port="http,https", protocol=tcp]
backend = auto
EOF
```
#### Step 3: Create Log Directory
```bash
# Create log directory if using Docker Caddy
sudo mkdir -p /var/log/caddy
sudo chmod 755 /var/log/caddy
# If Caddy runs as specific user:
# sudo chown caddy:caddy /var/log/caddy
```
#### Step 4: Update Caddy Docker Compose
Add log volume to your Caddy docker-compose.yml:
```yaml
services:
caddy:
image: caddy:latest
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- ./data:/data
- ./config:/config
- /var/log/caddy:/var/log/caddy # Add this line
```
Then restart Caddy:
```bash
cd ~/docker/caddy
docker compose down
docker compose up -d
```
#### Step 5: Restart Fail2ban
```bash
sudo systemctl restart fail2ban
sudo systemctl status fail2ban
```
### Testing Fail2ban
```bash
# Check if jail is running
sudo fail2ban-client status caddy-auth
# Test the filter against your logs
sudo fail2ban-regex /var/log/caddy/access.log /etc/fail2ban/filter.d/caddy-auth.conf
# View banned IPs
sudo fail2ban-client get caddy-auth banip
# Manually ban/unban an IP (for testing)
sudo fail2ban-client set caddy-auth banip 1.2.3.4
sudo fail2ban-client set caddy-auth unbanip 1.2.3.4
```
### Troubleshooting
#### Fail2ban not detecting attacks
1. **Check log format:**
```bash
tail -f /var/log/caddy/access.log
```
Ensure it's JSON format with `remote_ip` or `remote_addr` field.
2. **Test filter manually:**
```bash
sudo fail2ban-regex /var/log/caddy/access.log /etc/fail2ban/filter.d/caddy-auth.conf --print-all-matched
```
3. **Check fail2ban logs:**
```bash
sudo tail -f /var/log/fail2ban.log
```
#### Caddy configuration errors
1. **Validate Caddyfile:**
```bash
docker exec caddy caddy validate --config /etc/caddy/Caddyfile
```
2. **Check Caddy logs:**
```bash
docker logs caddy --tail 50
```
## Advanced Configuration
### Aggressive Fail2ban Settings
For tighter security:
```ini
[caddy-auth]
maxretry = 3 # Ban after 3 attempts (instead of 5)
findtime = 300 # Within 5 minutes (instead of 10)
bantime = 86400 # Ban for 24 hours (instead of 1)
```
### Ban Time Increment
Ban repeat offenders for longer:
```ini
[caddy-auth]
bantime.increment = true
bantime.factor = 24
bantime.maxtime = 604800 # Maximum 1 week ban
```
### Email Notifications
Get notified when IPs are banned:
```ini
[caddy-auth]
action = iptables-multiport[name=CaddyAuth, port="http,https", protocol=tcp]
sendmail-whois[name=CaddyAuth, dest=admin@yourdomain.com]
```
### Per-Service Jails
Create separate jails for different services:
```ini
[caddy-actualbudget]
enabled = true
port = http,https
filter = caddy-auth
logpath = /var/log/caddy/actualbudget-access.log
maxretry = 3
bantime = 7200
[caddy-keycloak]
enabled = true
port = http,https
filter = caddy-auth
logpath = /var/log/caddy/keycloak-access.log
maxretry = 5
bantime = 3600
```
## Best Practices
1. **Always backup before changes**
2. **Test configuration before reloading** (`caddy validate`)
3. **Monitor fail2ban logs** initially to tune settings
4. **Use strong passwords** for admin interfaces
5. **Keep services updated** (`docker compose pull && docker compose up -d`)
6. **Regular backups** of configuration and data
7. **Use HTTPS** via Caddy for all services
8. **Implement rate limiting** in Caddy for API endpoints
## Quick Reference
### Common Commands
```bash
# Caddy
docker exec -w /etc/caddy caddy caddy reload
docker exec -w /etc/caddy caddy caddy fmt --overwrite
docker exec caddy caddy validate --config /etc/caddy/Caddyfile
docker logs caddy --tail 50
# Fail2ban
sudo systemctl restart fail2ban
sudo fail2ban-client status caddy-auth
sudo fail2ban-client set caddy-auth unbanip 1.2.3.4
sudo tail -f /var/log/fail2ban.log
# Backup
cp ~/docker/caddy/Caddyfile ~/docker/caddy/Caddyfile.backup
```
### Service Ports
- **ActualBudget**: 5006
- **Keycloak**: 8180
- **Caddy**: 80 (HTTP), 443 (HTTPS)
## Support
For issues:
- Caddy documentation: https://caddyserver.com/docs/
- Fail2ban manual: https://www.fail2ban.org/wiki/index.php/MANUAL_0_8
- ActualBudget docs: https://actualbudget.org/docs/
- Keycloak docs: https://www.keycloak.org/documentation
+347
View File
@@ -0,0 +1,347 @@
#!/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 ""
+60
View File
@@ -0,0 +1,60 @@
# ActualBudget - Open-source personal finance management
# https://actualbudget.org/
#
# DEPLOYMENT INSTRUCTIONS:
# 1. Create directory: mkdir -p ~/docker/actualbudget
# 2. Copy this file: cp docker-compose-actualbudget.yml ~/docker/actualbudget/docker-compose.yml
# 3. Create data directory: mkdir -p ~/docker/actualbudget/data
# 4. Start the service: cd ~/docker/actualbudget && docker compose up -d
# 5. Access at: http://localhost:5006
#
# REVERSE PROXY SETUP (with Caddy):
# Add to your Caddyfile:
# budget.yourdomain.com {
# reverse_proxy localhost:5006
# }
#
# BANK ACCOUNT SYNC:
# ActualBudget supports SimpleFIN (https://simplefin.org/) for bank synchronization.
# You'll need a SimpleFIN account to sync with your bank accounts.
# Setup instructions: https://actualbudget.org/docs/advanced/bank-sync
name: actualbudget
services:
actualbudget:
image: actualbudget/actual-server:latest
container_name: actualbudget
restart: unless-stopped
ports:
- "5006:5006"
volumes:
- ./data:/data
environment:
# Set your timezone
- TZ=UTC
# Uncomment and set these for HTTPS/production deployment:
# - ACTUAL_UPLOAD_FILE_SYNC_SIZE_LIMIT_MB=20
# - ACTUAL_UPLOAD_SYNC_ENCRYPTED_FILE_SYNC_SIZE_LIMIT_MB=50
# - ACTUAL_UPLOAD_FILE_SIZE_LIMIT_MB=20
labels:
# Fail2ban support - logs HTTP requests
- "io.podman.annotations.label/fail2ban.enable=true"
- "io.podman.annotations.label/fail2ban.filter=caddy-auth"
# NOTES:
# - Default port: 5006
# - Data stored in: ./data
# - First run: Create an account at http://localhost:5006
# - Bank sync requires SimpleFIN: https://simplefin.org/
# - Official docs: https://actualbudget.org/docs/
#
# BACKUP YOUR DATA:
# Regular backups are important! ActualBudget stores data in SQLite.
# docker compose down
# cp -r data data-backup-$(date +%Y%m%d)
# docker compose up -d
#
# UPDATES:
# docker compose pull
# docker compose up -d
+143
View File
@@ -0,0 +1,143 @@
# Keycloak - Open-source Identity and Access Management
# https://www.keycloak.org/
#
# DEPLOYMENT INSTRUCTIONS:
# 1. Create directory: mkdir -p ~/docker/keycloak
# 2. Copy this file: cp docker-compose-keycloak.yml ~/docker/keycloak/docker-compose.yml
# 3. IMPORTANT: Update KEYCLOAK_ADMIN_PASSWORD below!
# 4. Start the service: cd ~/docker/keycloak && docker compose up -d
# 5. Access at: http://localhost:8180/admin (admin console)
#
# REVERSE PROXY SETUP (with Caddy):
# Add to your Caddyfile:
# auth.yourdomain.com {
# reverse_proxy localhost:8180
# }
#
# PRODUCTION DEPLOYMENT:
# For production, you should:
# 1. Use a PostgreSQL database (see postgres service below)
# 2. Enable HTTPS via reverse proxy
# 3. Set KC_HOSTNAME to your domain
# 4. Use strong admin password
# 5. Configure proper realm and clients
name: keycloak
services:
# PostgreSQL database for Keycloak (recommended for production)
postgres:
image: postgres:16-alpine
container_name: keycloak-db
restart: unless-stopped
environment:
POSTGRES_DB: keycloak
POSTGRES_USER: keycloak
POSTGRES_PASSWORD: keycloak_db_password_CHANGE_THIS
volumes:
- ./postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U keycloak"]
interval: 10s
timeout: 5s
retries: 5
keycloak:
image: quay.io/keycloak/keycloak:latest
container_name: keycloak
restart: unless-stopped
command:
- start-dev # Use 'start' for production mode
environment:
# Admin credentials - CHANGE THESE!
- KEYCLOAK_ADMIN=admin
- KEYCLOAK_ADMIN_PASSWORD=CHANGE_THIS_SECURE_PASSWORD
# Database configuration (PostgreSQL)
- KC_DB=postgres
- KC_DB_URL=jdbc:postgresql://postgres:5432/keycloak
- KC_DB_USERNAME=keycloak
- KC_DB_PASSWORD=keycloak_db_password_CHANGE_THIS
# Hostname configuration
# For production, set to your domain:
# - KC_HOSTNAME=auth.yourdomain.com
# - KC_HOSTNAME_STRICT=true
- KC_HOSTNAME_STRICT=false
# Proxy configuration (required when behind Caddy/nginx)
- KC_PROXY=edge
- KC_HTTP_ENABLED=true
# Logging
- KC_LOG_LEVEL=INFO
# Health check
- KC_HEALTH_ENABLED=true
- KC_METRICS_ENABLED=true
ports:
- "8180:8080" # HTTP port (use reverse proxy for HTTPS)
# - "8787:8787" # Debug port (uncomment if needed)
volumes:
# Optional: Custom themes
# - ./themes:/opt/keycloak/themes
# Optional: Custom providers/extensions
# - ./providers:/opt/keycloak/providers
- ./data:/opt/keycloak/data
depends_on:
postgres:
condition: service_healthy
labels:
# Fail2ban support
- "io.podman.annotations.label/fail2ban.enable=true"
- "io.podman.annotations.label/fail2ban.filter=caddy-auth"
# NOTES:
# - Admin console: http://localhost:8180/admin
# - Default credentials: admin / CHANGE_THIS_SECURE_PASSWORD
# - Database: PostgreSQL (persistent data in ./postgres-data)
# - For H2 database (dev only), remove postgres service and database env vars
#
# FIRST-TIME SETUP:
# 1. Login to admin console
# 2. Create a realm (e.g., "myrealm")
# 3. Create clients for your applications
# 4. Configure authentication flows
# 5. Add users or configure identity providers (LDAP, SAML, OAuth)
#
# COMMON USE CASES:
# - Single Sign-On (SSO) for multiple applications
# - OAuth2/OIDC provider for custom apps
# - SAML 2.0 identity provider
# - User federation with LDAP/Active Directory
# - Multi-factor authentication (MFA/2FA)
# - Social login (Google, GitHub, etc.)
#
# PRODUCTION CHECKLIST:
# [ ] Change admin password
# [ ] Change database password
# [ ] Set KC_HOSTNAME to your domain
# [ ] Use 'start' instead of 'start-dev' command
# [ ] Configure HTTPS via reverse proxy (Caddy)
# [ ] Enable hostname strict mode
# [ ] Configure backup strategy for PostgreSQL
# [ ] Set up monitoring (metrics on port 9000)
#
# BACKUP:
# docker compose down
# tar -czf keycloak-backup-$(date +%Y%m%d).tar.gz postgres-data data
# docker compose up -d
#
# RESTORE:
# docker compose down
# tar -xzf keycloak-backup-YYYYMMDD.tar.gz
# docker compose up -d
#
# UPDATES:
# docker compose pull
# docker compose up -d
#
# DOCUMENTATION:
# - Official docs: https://www.keycloak.org/documentation
# - Getting started: https://www.keycloak.org/getting-started/getting-started-docker
# - Server admin: https://www.keycloak.org/docs/latest/server_admin/
+49
View File
@@ -0,0 +1,49 @@
# Fail2ban filter for Caddy web server
#
# INSTALLATION:
# 1. Copy this file to: /etc/fail2ban/filter.d/caddy-auth.conf
# sudo cp fail2ban-caddy-filter.conf /etc/fail2ban/filter.d/caddy-auth.conf
#
# 2. Create jail configuration at: /etc/fail2ban/jail.d/caddy.conf
# (See fail2ban-caddy-jail.conf in this directory)
#
# 3. Ensure Caddy is logging in JSON format to /var/log/caddy/access.log
# (See caddy-setup-helper.sh for configuration examples)
#
# 4. Restart fail2ban:
# sudo systemctl restart fail2ban
#
# 5. Check status:
# sudo fail2ban-client status caddy-auth
[INCLUDES]
before = common.conf
[Definition]
# Match failed authentication attempts and forbidden access
# Caddy JSON log format: {"remote_ip":"1.2.3.4","status":401,...}
failregex = ^.*"remote_ip":"<HOST>".*"status":(?:401|403|429).*$
^.*"remote_addr":"<HOST>.*"status":(?:401|403|429).*$
^.*"client_ip":"<HOST>".*"status":(?:401|403|429).*$
# Ignore localhost and common false positives
ignoreregex = ^.*"remote_ip":"(?:127\.0\.0\.1|::1)".*$
^.*"remote_addr":"(?:127\.0\.0\.1|::1)".*$
# Optional: Date/time pattern for log analysis
# Most Caddy JSON logs include "ts" field with Unix timestamp
datepattern = "ts":%%s
[Init]
journalmatch = _SYSTEMD_UNIT=caddy.service
# NOTES:
# - This filter looks for HTTP status codes:
# 401 = Unauthorized (failed authentication)
# 403 = Forbidden (access denied)
# 429 = Too Many Requests (rate limiting)
#
# - Adjust the status codes based on your needs
# - For more aggressive blocking, add: 404|500
# - Test the filter: fail2ban-regex /var/log/caddy/access.log /etc/fail2ban/filter.d/caddy-auth.conf
+82
View File
@@ -0,0 +1,82 @@
# Fail2ban jail configuration for Caddy web server
#
# INSTALLATION:
# 1. Copy this file to: /etc/fail2ban/jail.d/caddy.conf
# sudo cp fail2ban-caddy-jail.conf /etc/fail2ban/jail.d/caddy.conf
#
# 2. Ensure the filter is installed:
# sudo cp fail2ban-caddy-filter.conf /etc/fail2ban/filter.d/caddy-auth.conf
#
# 3. Create log directory if it doesn't exist:
# sudo mkdir -p /var/log/caddy
# sudo chown caddy:caddy /var/log/caddy # Or appropriate user
#
# 4. Restart fail2ban:
# sudo systemctl restart fail2ban
#
# 5. Check status:
# sudo fail2ban-client status caddy-auth
[caddy-auth]
# Enable this jail
enabled = true
# Ports to protect (HTTP and HTTPS)
port = http,https
# Filter to use (must match filename in /etc/fail2ban/filter.d/)
filter = caddy-auth
# Log file to monitor
# Adjust this path if your Caddy logs are elsewhere
logpath = /var/log/caddy/access.log
/var/log/caddy/*-access.log
# For Docker Caddy, you might need to use Docker logs:
# logpath = /var/lib/docker/containers/*-caddy*/*.log
# Maximum retry before ban
# 5 attempts within findtime period will trigger a ban
maxretry = 5
# Time window (seconds) to count failures
# 600 = 10 minutes
findtime = 600
# Ban duration (seconds)
# 3600 = 1 hour
# 86400 = 24 hours
bantime = 3600
# Action to take when banning
# iptables-multiport: Block on multiple ports
action = iptables-multiport[name=CaddyAuth, port="http,https", protocol=tcp]
# Optional: Send email notification
# sendmail-whois[name=CaddyAuth, dest=admin@yourdomain.com]
# Backend to use for monitoring log file
# auto = automatically detect (systemd journal or file polling)
backend = auto
# OPTIONAL SETTINGS:
# Increase ban time on repeat offenders
# First ban: 1 hour, second: 24 hours, third: 1 week
# bantime.increment = true
# bantime.factor = 24
# bantime.maxtime = 604800 # 1 week max
# Find all jails using this ban
# This enables ban synchronization across jails
# banaction_allports = iptables-allports
# NOTES:
# - Adjust maxretry, findtime, and bantime based on your security needs
# - More aggressive: maxretry=3, findtime=300, bantime=86400
# - More lenient: maxretry=10, findtime=1200, bantime=1800
#
# TESTING:
# - Check if jail is running: sudo fail2ban-client status caddy-auth
# - View banned IPs: sudo fail2ban-client get caddy-auth banip
# - Unban an IP: sudo fail2ban-client set caddy-auth unbanip 1.2.3.4
# - Test filter: fail2ban-regex /var/log/caddy/access.log /etc/fail2ban/filter.d/caddy-auth.conf
+175 -10
View File
@@ -2763,17 +2763,11 @@ MM_COMPOSE
cd ..
# Run npm install for each downloaded module
# Note: This runs AFTER container is started, inside the container
echo ""
echo " Installing npm dependencies for modules..."
for mod_dir in modules/MMM-*/; do
if [ -d "$mod_dir" ] && [ -f "$mod_dir/package.json" ]; then
mod_name=$(basename "$mod_dir")
echo " Installing dependencies for $mod_name..."
(cd "$mod_dir" && npm install --production 2>/dev/null) && \
echo "$mod_name dependencies installed" || \
echo "$mod_name - npm install failed (may work anyway)"
fi
done
echo " Note: Module dependencies will be installed when container starts"
echo " If you need to manually install module dependencies, run:"
echo " docker exec magicmirror-$MM_PORT sh -c 'cd /opt/magic_mirror/modules/<module-name> && npm install --production'"
echo ""
fi
else
@@ -2877,6 +2871,21 @@ MM_CONFIG
MM_PORT=$((8080 + i))
MM_DIR="$DOCKER_DIR/magicmirror-$MM_PORT"
(cd "$MM_DIR" && docker compose up -d 2>/dev/null) && echo " ✓ Magic Mirror #$i started (port $MM_PORT)" || echo " ⚠ Failed to start Magic Mirror #$i"
# Install npm dependencies for third-party modules inside container
if [ -d "$MM_DIR/modules" ]; then
echo " Installing module dependencies inside container..."
sleep 3 # Wait for container to fully start
for mod_dir in "$MM_DIR/modules"/MMM-*/; do
if [ -d "$mod_dir" ] && [ -f "$mod_dir/package.json" ]; then
mod_name=$(basename "$mod_dir")
echo " Installing $mod_name dependencies..."
docker exec magicmirror-$MM_PORT sh -c "cd /opt/magic_mirror/modules/$mod_name && npm install --production" 2>/dev/null && \
echo "$mod_name dependencies installed" || \
echo "$mod_name - npm install failed (container may need restart)"
fi
done
fi
done
fi
@@ -2886,6 +2895,162 @@ MM_CONFIG
fi
fi
# ---- ACTUALBUDGET ----
echo ""
echo "┌─────────────────────────────────────────────────────────────────┐"
echo "│ ACTUALBUDGET - Open-source Personal Finance Management │"
echo "│ Budget tracking with bank account synchronization via SimpleFIN│"
echo "│ Port: 5006 │"
echo "└─────────────────────────────────────────────────────────────────┘"
prompt_yn "Install ActualBudget? (y/n):" "n" INSTALL_ACTUALBUDGET
if [ "$INSTALL_ACTUALBUDGET" = "y" ] || [ "$INSTALL_ACTUALBUDGET" = "Y" ]; then
AB_DIR="$DOCKER_DIR/actualbudget"
if [ "$DRY_RUN" = true ]; then
echo "[DRY-RUN] Would create $AB_DIR"
else
echo "Installing ActualBudget..."
mkdir -p "$AB_DIR/data"
cd "$AB_DIR"
cat > docker-compose.yml << 'AB_COMPOSE'
name: actualbudget
services:
actualbudget:
image: actualbudget/actual-server:latest
container_name: actualbudget
restart: unless-stopped
ports:
- "5006:5006"
volumes:
- ./data:/data
environment:
- TZ=UTC
labels:
- "io.podman.annotations.label/fail2ban.enable=true"
- "io.podman.annotations.label/fail2ban.filter=caddy-auth"
AB_COMPOSE
echo " ✓ ActualBudget configured at $AB_DIR"
prompt_yn "Start ActualBudget now? (y/n):" "y" START_AB
if [ "$START_AB" = "y" ] || [ "$START_AB" = "Y" ]; then
docker compose up -d 2>/dev/null && echo " ✓ ActualBudget started" || echo " ⚠ Failed to start ActualBudget"
fi
echo ""
echo " Access at: http://localhost:5006"
echo " Bank sync: https://simplefin.org/ (SimpleFIN account required)"
echo " Data dir: $AB_DIR/data"
echo ""
fi
fi
# ---- KEYCLOAK ----
echo ""
echo "┌─────────────────────────────────────────────────────────────────┐"
echo "│ KEYCLOAK - Identity and Access Management (IAM) │"
echo "│ SSO, OAuth2, SAML, User Management, MFA │"
echo "│ Port: 8180 (HTTP) - Use reverse proxy for HTTPS │"
echo "└─────────────────────────────────────────────────────────────────┘"
prompt_yn "Install Keycloak? (y/n):" "n" INSTALL_KEYCLOAK
if [ "$INSTALL_KEYCLOAK" = "y" ] || [ "$INSTALL_KEYCLOAK" = "Y" ]; then
KC_DIR="$DOCKER_DIR/keycloak"
if [ "$DRY_RUN" = true ]; then
echo "[DRY-RUN] Would create $KC_DIR"
else
echo "Installing Keycloak..."
echo ""
echo "⚠ SECURITY WARNING:"
echo " You MUST change the default admin password!"
echo ""
read -p "Enter Keycloak admin password [admin123]: " KC_ADMIN_PASS
KC_ADMIN_PASS=${KC_ADMIN_PASS:-admin123}
read -p "Enter database password [keycloak_db_pass]: " KC_DB_PASS
KC_DB_PASS=${KC_DB_PASS:-keycloak_db_pass}
mkdir -p "$KC_DIR/data" "$KC_DIR/postgres-data"
cd "$KC_DIR"
cat > docker-compose.yml << KC_COMPOSE
name: keycloak
services:
postgres:
image: postgres:16-alpine
container_name: keycloak-db
restart: unless-stopped
environment:
POSTGRES_DB: keycloak
POSTGRES_USER: keycloak
POSTGRES_PASSWORD: $KC_DB_PASS
volumes:
- ./postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U keycloak"]
interval: 10s
timeout: 5s
retries: 5
keycloak:
image: quay.io/keycloak/keycloak:latest
container_name: keycloak
restart: unless-stopped
command:
- start-dev
environment:
- KEYCLOAK_ADMIN=admin
- KEYCLOAK_ADMIN_PASSWORD=$KC_ADMIN_PASS
- KC_DB=postgres
- KC_DB_URL=jdbc:postgresql://postgres:5432/keycloak
- KC_DB_USERNAME=keycloak
- KC_DB_PASSWORD=$KC_DB_PASS
- KC_HOSTNAME_STRICT=false
- KC_PROXY=edge
- KC_HTTP_ENABLED=true
- KC_LOG_LEVEL=INFO
- KC_HEALTH_ENABLED=true
- KC_METRICS_ENABLED=true
ports:
- "8180:8080"
volumes:
- ./data:/opt/keycloak/data
depends_on:
postgres:
condition: service_healthy
labels:
- "io.podman.annotations.label/fail2ban.enable=true"
- "io.podman.annotations.label/fail2ban.filter=caddy-auth"
KC_COMPOSE
echo " ✓ Keycloak configured at $KC_DIR"
prompt_yn "Start Keycloak now? (y/n):" "y" START_KC
if [ "$START_KC" = "y" ] || [ "$START_KC" = "Y" ]; then
echo " Starting Keycloak (this may take a minute)..."
docker compose up -d 2>/dev/null && echo " ✓ Keycloak started" || echo " ⚠ Failed to start Keycloak"
fi
echo ""
echo " Admin console: http://localhost:8180/admin"
echo " Username: admin"
echo " Password: $KC_ADMIN_PASS"
echo " Database: PostgreSQL (./postgres-data)"
echo ""
echo " ⚠ For production:"
echo " - Use HTTPS via reverse proxy (Caddy)"
echo " - Change command to 'start' instead of 'start-dev'"
echo " - Set KC_HOSTNAME to your domain"
echo ""
fi
fi
# ---- LYRION MUSIC SERVER ----
echo ""
echo "┌─────────────────────────────────────────────────────────────────┐"