diff --git a/.env.example b/.env.example index 0c02647..eb7103e 100644 --- a/.env.example +++ b/.env.example @@ -266,6 +266,10 @@ IMGPROXY_ENABLE_WEBP_DETECTION=true # Add your OpenAI API key to enable SQL Editor Assistant OPENAI_API_KEY= +# ============================================ +# Cloudflare Tunnel Configuration (Optional) +# ============================================ +CLOUDFLARE_TUNNEL_TOKEN= ############ # Functions - Configuration for Functions diff --git a/README.md b/README.md index 03cd5c0..da68995 100644 --- a/README.md +++ b/README.md @@ -153,6 +153,117 @@ After successful installation, your services are up and running! Here's how to g 4. **Check Monitoring (Optional):** - Visit Grafana (`grafana.yourdomain.com`) to see dashboards monitoring your system's performance (data sourced from Prometheus). + + +## 🔒 Secure Access with Cloudflare Tunnel (Optional) + +Cloudflare Tunnel provides zero-trust access to your services without exposing any ports on your server. All traffic is routed through Cloudflare's secure network, providing DDoS protection and hiding your server's IP address. + +### Benefits +- **No exposed ports** - Ports 80/443 can be completely closed +- **DDoS protection** - Built-in Cloudflare protection +- **IP hiding** - Your server's real IP is never exposed +- **Zero-trust security** - Optional Cloudflare Access integration +- **No public IP required** - Works on private networks + +### Setup Instructions + +#### 1. Create a Cloudflare Tunnel + +1. Go to [Cloudflare Zero Trust Dashboard](https://one.dash.cloudflare.com/) +2. Navigate to **Access** → **Tunnels** +3. Click **Create a tunnel** +4. Choose **Cloudflared** connector +5. Name your tunnel (e.g., "n8n-installer") +6. Copy the tunnel token (you'll need this during installation) + +#### 2. Configure DNS and Routing + +1. In the tunnel configuration, add a public hostname: + - **Subdomain**: `*` (wildcard for all services) + - **Domain**: Select your domain + - **Service**: `http://caddy:80` + - **Additional settings**: Leave defaults + +2. In Cloudflare DNS: + - Add CNAME: `*.yourdomain.com` → `.cfargotunnel.com` + - Or add individual CNAMEs for each service if wildcard not available + +#### 3. Install with Tunnel Support + +1. Run the n8n-installer as normal +2. When prompted for **Cloudflare Tunnel Token**, paste your token +3. The tunnel service will be automatically enabled +4. Complete the rest of the installation + +#### 4. Secure Your VPS (Recommended) + +After confirming services work through the tunnel: + +```bash +# Close web ports (UFW example) +sudo ufw delete allow 80/tcp +sudo ufw delete allow 443/tcp +sudo ufw delete allow 7687/tcp +sudo ufw reload + +# Verify only SSH remains open +sudo ufw status +``` + +### Verifying Tunnel Connection + +Check if the tunnel is running: +```bash +docker logs cloudflared +``` + +You should see: +``` +INF Connection established connIndex=0 +INF Registered tunnel connection +``` + +### Troubleshooting + +**Services not accessible:** +- Verify tunnel status: `docker ps | grep cloudflared` +- Check tunnel logs: `docker logs cloudflared` +- Ensure DNS records point to tunnel +- Verify Caddy is running: `docker ps | grep caddy` + +**Tunnel not starting:** +- Verify token is correct in `.env` +- Check if token has correct format (long base64 string) +- Ensure tunnel is active in Cloudflare dashboard + +**Mixed mode (tunnel + direct access):** +- You can run both tunnel and direct access simultaneously +- Useful for testing before closing firewall ports +- Simply keep ports 80/443 open until ready to switch + +### Disabling Tunnel + +To disable Cloudflare Tunnel and return to direct access: + +1. Remove from compose profiles: + ```bash + # Edit .env and remove "cloudflare-tunnel" from COMPOSE_PROFILES + nano .env + ``` + +2. Restart services: + ```bash + docker compose down cloudflared + docker compose up -d + ``` + +3. Re-open firewall ports if closed: + ```bash + sudo ufw allow 80/tcp + sudo ufw allow 443/tcp + sudo ufw reload + ``` ### Using Pre-installed Libraries in n8n's Custom JavaScript diff --git a/docker-compose.yml b/docker-compose.yml index 6fd3e86..2f04ad9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -227,6 +227,26 @@ services: options: max-size: "1m" max-file: "1" + + cloudflared: + image: cloudflare/cloudflared:latest + container_name: cloudflared + profiles: ["cloudflare-tunnel"] + restart: unless-stopped + command: tunnel run + environment: + - TUNNEL_TOKEN=${CLOUDFLARE_TUNNEL_TOKEN} + networks: + default: + aliases: + - cloudflared + depends_on: + - caddy + logging: + driver: "json-file" + options: + max-size: "1m" + max-file: "1" langfuse-worker: image: langfuse/langfuse-worker:3 diff --git a/scripts/03_generate_secrets.sh b/scripts/03_generate_secrets.sh index 949e339..ecfd584 100755 --- a/scripts/03_generate_secrets.sh +++ b/scripts/03_generate_secrets.sh @@ -274,6 +274,46 @@ fi # Ensure N8N_WORKER_COUNT is definitely set (should be by logic above) N8N_WORKER_COUNT="${N8N_WORKER_COUNT:-1}" +# Cloudflare Tunnel Token (optional) +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "Cloudflare Tunnel Configuration (Optional)" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +echo "Cloudflare Tunnel provides secure zero-trust access to your services" +echo "without exposing ports 80/443 on your server." +echo "" +echo "To set up:" +echo "1. Create a tunnel at https://one.dash.cloudflare.com/" +echo "2. Configure hostname: *.${DOMAIN} → http://caddy:80" +echo "3. Copy the tunnel token" +echo "" + +if [[ -v existing_env_vars[CLOUDFLARE_TUNNEL_TOKEN] ]]; then + CLOUDFLARE_TUNNEL_TOKEN="${existing_env_vars[CLOUDFLARE_TUNNEL_TOKEN]}" + if [[ -n "$CLOUDFLARE_TUNNEL_TOKEN" ]]; then + log_info "Found existing Cloudflare Tunnel Token in .env" + else + log_info "Found empty Cloudflare Tunnel Token in .env. You can provide one now or leave empty." + echo "" + read -p "Cloudflare Tunnel Token: " CLOUDFLARE_TUNNEL_TOKEN + fi +else + echo "" + read -p "Cloudflare Tunnel Token: " CLOUDFLARE_TUNNEL_TOKEN +fi + +if [ -n "$CLOUDFLARE_TUNNEL_TOKEN" ]; then + log_success "Cloudflare Tunnel Token configured" + echo "" + echo "🔒 After confirming the tunnel works, enhance security by:" + echo " Closing ports 80, 443, and 7687 in your VPS firewall" + echo " Example: sudo ufw delete allow 80/tcp" + echo "" +else + log_info "Cloudflare Tunnel skipped - you can enable it later in the service selection wizard" +fi + log_info "Generating secrets and creating .env file..." # --- Helper Functions --- @@ -375,6 +415,10 @@ if [[ -n "$OPENAI_API_KEY" ]]; then generated_values["OPENAI_API_KEY"]="$OPENAI_API_KEY" fi +if [[ -n "$CLOUDFLARE_TUNNEL_TOKEN" ]]; then + generated_values["CLOUDFLARE_TUNNEL_TOKEN"]="$CLOUDFLARE_TUNNEL_TOKEN" +fi + # Create a temporary file for processing TMP_ENV_FILE=$(mktemp) # Ensure temp file is cleaned up on exit @@ -389,6 +433,7 @@ found_vars["RUN_N8N_IMPORT"]=0 found_vars["PROMETHEUS_USERNAME"]=0 found_vars["SEARXNG_USERNAME"]=0 found_vars["OPENAI_API_KEY"]=0 +found_vars["CLOUDFLARE_TUNNEL_TOKEN"]=0 found_vars["LANGFUSE_INIT_USER_EMAIL"]=0 found_vars["N8N_WORKER_COUNT"]=0 found_vars["WEAVIATE_USERNAME"]=0 @@ -522,7 +567,7 @@ if [[ -z "${generated_values[SERVICE_ROLE_KEY]}" ]]; then fi # Add any custom variables that weren't found in the template -for var in "FLOWISE_USERNAME" "DASHBOARD_USERNAME" "LETSENCRYPT_EMAIL" "RUN_N8N_IMPORT" "OPENAI_API_KEY" "PROMETHEUS_USERNAME" "SEARXNG_USERNAME" "LANGFUSE_INIT_USER_EMAIL" "N8N_WORKER_COUNT" "WEAVIATE_USERNAME" "NEO4J_AUTH_USERNAME" "COMFYUI_USERNAME"; do +for var in "FLOWISE_USERNAME" "DASHBOARD_USERNAME" "LETSENCRYPT_EMAIL" "RUN_N8N_IMPORT" "OPENAI_API_KEY" "PROMETHEUS_USERNAME" "SEARXNG_USERNAME" "LANGFUSE_INIT_USER_EMAIL" "N8N_WORKER_COUNT" "WEAVIATE_USERNAME" "NEO4J_AUTH_USERNAME" "COMFYUI_USERNAME" "CLOUDFLARE_TUNNEL_TOKEN"; do if [[ ${found_vars["$var"]} -eq 0 && -v generated_values["$var"] ]]; then # Before appending, check if it's already in TMP_ENV_FILE to avoid duplicates if ! grep -q -E "^${var}=" "$TMP_ENV_FILE"; then diff --git a/scripts/04_wizard.sh b/scripts/04_wizard.sh index 126b931..4a7c486 100755 --- a/scripts/04_wizard.sh +++ b/scripts/04_wizard.sh @@ -55,6 +55,7 @@ base_services_data=( "flowise" "Flowise (AI Agent Builder)" "monitoring" "Monitoring Suite (Prometheus, Grafana, cAdvisor, Node-Exporter)" "portainer" "Portainer (Docker management UI)" + "cloudflare-tunnel Cloudflare_Tunnel_(Zero-Trust_Secure_Access) off" "langfuse" "Langfuse Suite (AI Observability - includes Clickhouse, Minio)" "qdrant" "Qdrant (Vector Database)" "supabase" "Supabase (Backend as a Service)" diff --git a/scripts/06_final_report.sh b/scripts/06_final_report.sh index 46d33d8..a8e69f3 100755 --- a/scripts/06_final_report.sh +++ b/scripts/06_final_report.sh @@ -253,6 +253,35 @@ echo # --- Update Script Info (Placeholder) --- log_info "To update the services, run the 'update.sh' script: bash ./scripts/update.sh" +# ============================================ +# Cloudflare Tunnel Security Notice +# ============================================ +if is_profile_active "cloudflare-tunnel" && [ -n "$CLOUDFLARE_TUNNEL_TOKEN" ]; then + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "🔒 CLOUDFLARE TUNNEL SECURITY" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" + echo "✅ Cloudflare Tunnel is configured and running!" + echo "" + echo "Your services are accessible through Cloudflare's secure network." + echo "All traffic is encrypted and routed through the tunnel." + echo "" + echo "🛡️ RECOMMENDED SECURITY ENHANCEMENT:" + echo " For maximum security, close the following ports in your VPS firewall:" + echo " • Port 80 (HTTP)" + echo " • Port 443 (HTTPS)" + echo " • Port 7687 (Neo4j Bolt)" + echo "" + echo " Example commands:" + echo " └─ UFW: sudo ufw delete allow 80/tcp && sudo ufw delete allow 443/tcp" + echo " └─ IPtables: sudo iptables -D INPUT -p tcp --dport 80 -j ACCEPT" + echo "" + echo " ⚠️ Only close ports AFTER confirming tunnel connectivity!" + echo " Test first: Visit https://${N8N_HOSTNAME} through Cloudflare" + echo "" +fi + echo echo "======================================================================" echo