#!/bin/bash # Deploy Traefik Reverse Proxy # This script deploys Traefik with SSL, security, and monitoring set -euo pipefail echo "🌐 Deploying Traefik reverse proxy..." # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color # Function to print colored output print_status() { echo -e "${GREEN}[INFO]${NC} $1" } print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1" } print_error() { echo -e "${RED}[ERROR]${NC} $1" } # Configuration MANAGER_HOST="omv800" TRAEFIK_CONFIG_DIR="/opt/migration/configs/traefik" DOMAIN="yourdomain.com" EMAIL="admin@yourdomain.com" # 1. Create Traefik configuration directory print_status "Step 1: Creating Traefik configuration directory..." mkdir -p "$TRAEFIK_CONFIG_DIR" mkdir -p "$TRAEFIK_CONFIG_DIR/dynamic" mkdir -p "$TRAEFIK_CONFIG_DIR/certificates" # 2. Create Traefik static configuration print_status "Step 2: Creating Traefik static configuration..." cat > "$TRAEFIK_CONFIG_DIR/traefik.yml" << EOF # Traefik Static Configuration global: checkNewVersion: false sendAnonymousUsage: false api: dashboard: true insecure: false entryPoints: web: address: ":80" http: redirections: entrypoint: to: websecure scheme: https permanent: true websecure: address: ":443" http: tls: certResolver: letsencrypt domains: - main: "*.${DOMAIN}" sans: - "*.${DOMAIN}" providers: docker: swarmMode: true exposedByDefault: false network: traefik-public watch: true file: directory: /etc/traefik/dynamic watch: true certificatesResolvers: letsencrypt: acme: email: ${EMAIL} storage: /certificates/acme.json httpChallenge: entryPoint: web log: level: INFO format: json accessLog: filePath: /var/log/traefik/access.log format: json fields: defaultMode: keep headers: defaultMode: keep metrics: prometheus: addEntryPointsLabels: true addServicesLabels: true buckets: - 0.1 - 0.3 - 1.2 - 5.0 ping: entryPoint: web providers: docker: swarmMode: true exposedByDefault: false network: traefik-public watch: true file: directory: /etc/traefik/dynamic watch: true EOF # 3. Create dynamic configuration print_status "Step 3: Creating dynamic configuration..." # Copy middleware configuration cp "$(dirname "$0")/../configs/traefik/dynamic/middleware.yml" "$TRAEFIK_CONFIG_DIR/dynamic/" # Create service-specific configurations cat > "$TRAEFIK_CONFIG_DIR/dynamic/services.yml" << EOF # Service-specific configurations http: routers: # Immich Photo Management immich-api: rule: "Host(\`immich.${DOMAIN}\`) && PathPrefix(\`/api\`)" service: immich-api entryPoints: - websecure tls: certResolver: letsencrypt middlewares: - security-headers@file - rate-limit@file - cors@file immich-web: rule: "Host(\`immich.${DOMAIN}\`)" service: immich-web entryPoints: - websecure tls: certResolver: letsencrypt middlewares: - security-headers@file - rate-limit@file - compression@file # Jellyfin Media Server jellyfin: rule: "Host(\`jellyfin.${DOMAIN}\`)" service: jellyfin entryPoints: - websecure tls: certResolver: letsencrypt middlewares: - security-headers@file - rate-limit@file - compression@file # Home Assistant homeassistant: rule: "Host(\`home.${DOMAIN}\`)" service: homeassistant entryPoints: - websecure tls: certResolver: letsencrypt middlewares: - security-headers@file - rate-limit@file - websocket@file # AppFlowy Collaboration appflowy: rule: "Host(\`appflowy.${DOMAIN}\`)" service: appflowy entryPoints: - websecure tls: certResolver: letsencrypt middlewares: - security-headers@file - rate-limit@file - cors@file # Paperless Document Management paperless: rule: "Host(\`paperless.${DOMAIN}\`)" service: paperless entryPoints: - websecure tls: certResolver: letsencrypt middlewares: - security-headers@file - rate-limit@file - auth@file # Portainer Container Management portainer: rule: "Host(\`portainer.${DOMAIN}\`)" service: portainer entryPoints: - websecure tls: certResolver: letsencrypt middlewares: - security-headers@file - auth@file - ip-whitelist@file # Grafana Monitoring grafana: rule: "Host(\`grafana.${DOMAIN}\`)" service: grafana entryPoints: - websecure tls: certResolver: letsencrypt middlewares: - security-headers@file - auth@file - ip-whitelist@file # Prometheus Metrics prometheus: rule: "Host(\`prometheus.${DOMAIN}\`)" service: prometheus entryPoints: - websecure tls: certResolver: letsencrypt middlewares: - security-headers@file - auth@file - ip-whitelist@file # Uptime Kuma Monitoring uptime-kuma: rule: "Host(\`uptime.${DOMAIN}\`)" service: uptime-kuma entryPoints: - websecure tls: certResolver: letsencrypt middlewares: - security-headers@file - auth@file - ip-whitelist@file services: # Service definitions will be auto-discovered by Docker provider # These are fallback definitions for external services # Error service for maintenance pages error-service: loadBalancer: servers: - url: "http://error-page:8080" # Auth service for forward authentication auth-service: loadBalancer: servers: - url: "http://auth-service:8080" EOF # 4. Create users file for basic auth print_status "Step 4: Creating users file for basic auth..." cat > "$TRAEFIK_CONFIG_DIR/users" << EOF # Basic Auth Users # Format: username:hashed_password # Generate with: htpasswd -nb username password admin:\$2y\$10\$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi migration:\$2y\$10\$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi EOF # 5. Set proper permissions print_status "Step 5: Setting proper permissions..." chmod 600 "$TRAEFIK_CONFIG_DIR/users" chmod 644 "$TRAEFIK_CONFIG_DIR/traefik.yml" chmod 644 "$TRAEFIK_CONFIG_DIR/dynamic/"*.yml # 6. Deploy Traefik stack print_status "Step 6: Deploying Traefik stack..." cd "$TRAEFIK_CONFIG_DIR" # Create docker-compose file for deployment cat > "docker-compose.yml" << EOF version: '3.8' services: traefik: image: traefik:v3.0 command: # API and dashboard - --api.dashboard=true - --api.insecure=false # Docker provider - --providers.docker.swarmMode=true - --providers.docker.exposedbydefault=false - --providers.docker.network=traefik-public # Entry points - --entrypoints.web.address=:80 - --entrypoints.websecure.address=:443 - --entrypoints.web.http.redirections.entrypoint.to=websecure - --entrypoints.web.http.redirections.entrypoint.scheme=https # SSL/TLS configuration - --certificatesresolvers.letsencrypt.acme.email=${EMAIL} - --certificatesresolvers.letsencrypt.acme.storage=/certificates/acme.json - --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web # Security headers - --entrypoints.websecure.http.middlewares=security-headers@file - --entrypoints.websecure.http.middlewares=rate-limit@file # Logging - --log.level=INFO - --accesslog=true - --accesslog.filepath=/var/log/traefik/access.log - --accesslog.format=json # Metrics - --metrics.prometheus=true - --metrics.prometheus.addEntryPointsLabels=true - --metrics.prometheus.addServicesLabels=true # Health checks - --ping=true - --ping.entryPoint=web # File provider for static configuration - --providers.file.directory=/etc/traefik/dynamic - --providers.file.watch=true ports: - "80:80" - "443:443" - "8080:8080" # Dashboard (internal only) volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - traefik-certificates:/certificates - traefik-logs:/var/log/traefik - ./dynamic:/etc/traefik/dynamic:ro - ./traefik.yml:/etc/traefik/traefik.yml:ro - ./users:/etc/traefik/users:ro networks: - traefik-public deploy: placement: constraints: - node.role == manager preferences: - spread: node.labels.zone replicas: 2 resources: limits: memory: 512M cpus: '0.5' reservations: memory: 256M cpus: '0.25' labels: # Traefik dashboard - "traefik.enable=true" - "traefik.http.routers.traefik-dashboard.rule=Host(\`traefik.${DOMAIN}\`)" - "traefik.http.routers.traefik-dashboard.entrypoints=websecure" - "traefik.http.routers.traefik-dashboard.tls.certresolver=letsencrypt" - "traefik.http.routers.traefik-dashboard.service=api@internal" - "traefik.http.routers.traefik-dashboard.middlewares=auth@file" # Health check - "traefik.http.routers.traefik-health.rule=PathPrefix(\`/ping\`)" - "traefik.http.routers.traefik-health.entrypoints=web" - "traefik.http.routers.traefik-health.service=ping@internal" # Metrics - "traefik.http.routers.traefik-metrics.rule=Host(\`traefik.${DOMAIN}\`) && PathPrefix(\`/metrics\`)" - "traefik.http.routers.traefik-metrics.entrypoints=websecure" - "traefik.http.routers.traefik-metrics.tls.certresolver=letsencrypt" - "traefik.http.routers.traefik-metrics.service=prometheus@internal" - "traefik.http.routers.traefik-metrics.middlewares=auth@file" restart_policy: condition: on-failure delay: 5s max_attempts: 3 window: 120s update_config: parallelism: 1 delay: 10s order: start-first rollback_config: parallelism: 1 delay: 5s order: stop-first volumes: traefik-certificates: driver: local traefik-logs: driver: local networks: traefik-public: external: true EOF # 7. Deploy the stack print_status "Step 7: Deploying Traefik stack..." ssh "$MANAGER_HOST" "cd $TRAEFIK_CONFIG_DIR && docker stack deploy -c docker-compose.yml traefik" # 8. Wait for deployment print_status "Step 8: Waiting for deployment to complete..." sleep 30 # 9. Verify deployment print_status "Step 9: Verifying deployment..." ssh "$MANAGER_HOST" "docker service ls | grep traefik" ssh "$MANAGER_HOST" "docker service ps traefik_traefik" # 10. Test Traefik health print_status "Step 10: Testing Traefik health..." sleep 10 # Test HTTP to HTTPS redirect if curl -s -I "http://$MANAGER_HOST" | grep -q "301\|302"; then print_status "✅ HTTP to HTTPS redirect working" else print_warning "⚠️ HTTP to HTTPS redirect may not be working" fi # Test Traefik dashboard (internal) if curl -s "http://$MANAGER_HOST:8080/api/rawdata" | grep -q "traefik"; then print_status "✅ Traefik dashboard accessible" else print_warning "⚠️ Traefik dashboard may not be accessible" fi # 11. Create health check script print_status "Step 11: Creating health check script..." cat > "/opt/migration/scripts/check_traefik_health.sh" << 'EOF' #!/bin/bash # Check Traefik Health set -euo pipefail MANAGER_HOST="omv800" DOMAIN="yourdomain.com" echo "🏥 Checking Traefik health..." # Check service status echo "📋 Service status:" ssh "$MANAGER_HOST" "docker service ls | grep traefik" # Check service tasks echo "🔧 Service tasks:" ssh "$MANAGER_HOST" "docker service ps traefik_traefik" # Check logs echo "📝 Recent logs:" ssh "$MANAGER_HOST" "docker service logs --tail 20 traefik_traefik" # Test HTTP redirect echo "🔄 Testing HTTP redirect:" if curl -s -I "http://$MANAGER_HOST" | grep -q "301\|302"; then echo "✅ HTTP to HTTPS redirect working" else echo "❌ HTTP to HTTPS redirect not working" fi # Test dashboard echo "📊 Testing dashboard:" if curl -s "http://$MANAGER_HOST:8080/api/rawdata" | grep -q "traefik"; then echo "✅ Traefik dashboard accessible" else echo "❌ Traefik dashboard not accessible" fi # Test SSL certificate echo "🔒 Testing SSL certificate:" if curl -s -I "https://$MANAGER_HOST" | grep -q "HTTP/2\|HTTP/1.1 200"; then echo "✅ SSL certificate working" else echo "❌ SSL certificate not working" fi echo "✅ Traefik health check completed" EOF chmod +x "/opt/migration/scripts/check_traefik_health.sh" # 12. Create configuration summary print_status "Step 12: Creating configuration summary..." cat > "/opt/migration/traefik_summary.txt" << EOF Traefik Deployment Summary Generated: $(date) Configuration: Domain: ${DOMAIN} Email: ${EMAIL} Manager Host: ${MANAGER_HOST} Services Configured: - Immich Photo Management: https://immich.${DOMAIN} - Jellyfin Media Server: https://jellyfin.${DOMAIN} - Home Assistant: https://home.${DOMAIN} - AppFlowy Collaboration: https://appflowy.${DOMAIN} - Paperless Documents: https://paperless.${DOMAIN} - Portainer Management: https://portainer.${DOMAIN} - Grafana Monitoring: https://grafana.${DOMAIN} - Prometheus Metrics: https://prometheus.${DOMAIN} - Uptime Kuma: https://uptime.${DOMAIN} - Traefik Dashboard: https://traefik.${DOMAIN} Security Features: - SSL/TLS with Let's Encrypt - Security headers - Rate limiting - Basic authentication - IP whitelisting - CORS support Monitoring: - Prometheus metrics - Access logging - Health checks - Dashboard Configuration Files: - Static config: ${TRAEFIK_CONFIG_DIR}/traefik.yml - Dynamic config: ${TRAEFIK_CONFIG_DIR}/dynamic/ - Users file: ${TRAEFIK_CONFIG_DIR}/users - Health check: /opt/migration/scripts/check_traefik_health.sh Next Steps: 1. Update DNS records to point to ${MANAGER_HOST} 2. Test SSL certificate generation 3. Deploy monitoring stack 4. Begin service migration EOF print_status "✅ Traefik deployment completed successfully!" print_status "📋 Configuration summary saved to: /opt/migration/traefik_summary.txt" print_status "🔧 Health check script: /opt/migration/scripts/check_traefik_health.sh" echo "" print_status "Next steps:" echo " 1. Update DNS: Point *.${DOMAIN} to ${MANAGER_HOST}" echo " 2. Test SSL: ./scripts/check_traefik_health.sh" echo " 3. Deploy monitoring: ./scripts/setup_monitoring.sh" echo " 4. Begin migration: ./scripts/start_migration.sh"