diff --git a/launcher.sh b/launcher.sh index 6720903..7207432 100755 --- a/launcher.sh +++ b/launcher.sh @@ -196,6 +196,10 @@ show_website_menu() { echo "" echo -e " ${BLUE}3)${NC} 📦 WordPress Tools → WP-Cron manager & diagnostics" echo "" + echo -e "${BOLD}Domain Analysis:${NC}" + echo "" + echo -e " ${BLUE}4)${NC} 🔶 Cloudflare Detector - Which domains use Cloudflare + location" + echo "" echo -e " ${RED}0)${NC} Back to Main Menu" echo "" echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}" @@ -211,6 +215,7 @@ handle_website_menu() { 1) run_module "website" "website-error-analyzer.sh" ;; 2) run_module "website" "500-error-tracker.sh" ;; 3) bash "$MODULES_DIR/website/wordpress-menu.sh" ;; + 4) run_module "website" "cloudflare-detector.sh" ;; 0) return ;; *) echo -e "${RED}Invalid option${NC}"; sleep 1 ;; esac diff --git a/modules/website/cloudflare-detector.sh b/modules/website/cloudflare-detector.sh new file mode 100755 index 0000000..f3b2a2a --- /dev/null +++ b/modules/website/cloudflare-detector.sh @@ -0,0 +1,528 @@ +#!/bin/bash +################################################################################ +# Cloudflare Detector - Identify domains using Cloudflare +################################################################################ +# Scans all domains on the server and detects Cloudflare usage +# Checks: Nameservers, IP ranges, and HTTP headers +################################################################################ + +# Source common functions +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/../../lib/common-functions.sh" 2>/dev/null || { + echo "[ERROR] Cannot load common-functions.sh" + exit 1 +} + +# Detect control panel and system +if type -t detect_system &>/dev/null; then + detect_system +fi + +################################################################################ +# Configuration +################################################################################ + +# Cloudflare IP ranges (IPv4) - updated as of 2024 +# Source: https://www.cloudflare.com/ips-v4 +CLOUDFLARE_IPV4_RANGES=( + "173.245.48.0/20" + "103.21.244.0/22" + "103.22.200.0/22" + "103.31.4.0/22" + "141.101.64.0/18" + "108.162.192.0/18" + "190.93.240.0/20" + "188.114.96.0/20" + "197.234.240.0/22" + "198.41.128.0/17" + "162.158.0.0/15" + "104.16.0.0/13" + "104.24.0.0/14" + "172.64.0.0/13" + "131.0.72.0/22" +) + +################################################################################ +# Helper Functions +################################################################################ + +get_domains() { + local domains=() + + # Get domains from cPanel userdata + if [ -d /var/cpanel/userdata ]; then + while IFS= read -r domain_file; do + local domain=$(basename "$domain_file") + + # Skip cache files, main file, and config files + if [[ "$domain_file" =~ \.cache$ ]] || \ + [[ "$domain_file" =~ /cache$ ]] || \ + [[ "$domain_file" =~ /main$ ]] || \ + [[ "$domain" =~ _SSL$ ]] || \ + [[ "$domain" =~ \.yaml$ ]] || \ + [[ "$domain" =~ \.json$ ]] || \ + [[ "$domain" =~ \.conf$ ]] || \ + [[ "$domain" =~ \.backup$ ]]; then + continue + fi + + # Skip system/template domains + if [[ "$domain" =~ cloudvpstemplate\. ]] || \ + [[ "$domain" =~ cprapid\. ]] || \ + [[ "$domain" =~ ^[0-9\-]+\. ]] || \ + [[ "$domain_file" =~ /nobody/ ]]; then + continue + fi + + # Only add if it looks like a domain + if [[ "$domain" != "." && "$domain" != ".." && "$domain" =~ \. ]]; then + domains+=("$domain") + fi + done < <(find /var/cpanel/userdata -type f 2>/dev/null) + fi + + printf '%s\n' "${domains[@]}" | sort -u +} + +is_cloudflare_ip() { + local ip="$1" + + # Convert IP to integer for range checking + local ip_int=$(echo "$ip" | awk -F. '{print ($1 * 16777216) + ($2 * 65536) + ($3 * 256) + $4}') + + for range in "${CLOUDFLARE_IPV4_RANGES[@]}"; do + local network=$(echo "$range" | cut -d'/' -f1) + local cidr=$(echo "$range" | cut -d'/' -f2) + + # Calculate network range + local net_int=$(echo "$network" | awk -F. '{print ($1 * 16777216) + ($2 * 65536) + ($3 * 256) + $4}') + local mask=$((0xFFFFFFFF << (32 - cidr))) + local start=$((net_int & mask)) + local end=$((start + (1 << (32 - cidr)) - 1)) + + # Check if IP is in this range + if [ "$ip_int" -ge "$start" ] && [ "$ip_int" -le "$end" ]; then + return 0 + fi + done + + return 1 +} + +check_nameservers() { + local domain="$1" + + # Get nameservers + local nameservers=$(dig +short NS "$domain" 2>/dev/null | tr '[:upper:]' '[:lower:]') + + if [ -z "$nameservers" ]; then + echo "UNKNOWN" + return 2 + fi + + # Check if any nameserver contains cloudflare + if echo "$nameservers" | grep -q "cloudflare"; then + echo "CLOUDFLARE" + return 0 + fi + + echo "OTHER" + return 1 +} + +check_ip_address() { + local domain="$1" + + # Get A record + local ip=$(dig +short A "$domain" 2>/dev/null | head -1) + + if [ -z "$ip" ]; then + echo "UNKNOWN" + return 2 + fi + + # Check if IP is in Cloudflare range + if is_cloudflare_ip "$ip"; then + echo "CLOUDFLARE" + return 0 + fi + + echo "DIRECT" + return 1 +} + +check_http_headers() { + local domain="$1" + + # Try HTTP request and look for Cloudflare headers + local headers=$(curl -sI -m 3 "http://$domain" 2>/dev/null) + + if [ -z "$headers" ]; then + echo "UNKNOWN" + return 2 + fi + + # Check for CF-RAY header (definitive Cloudflare indicator) + if echo "$headers" | grep -qi "CF-RAY:"; then + echo "CLOUDFLARE" + return 0 + fi + + # Check for Server: cloudflare + if echo "$headers" | grep -qi "Server:.*cloudflare"; then + echo "CLOUDFLARE" + return 0 + fi + + echo "DIRECT" + return 1 +} + +get_cloudflare_location() { + local domain="$1" + + # Get CF-RAY header which contains datacenter code + local cf_ray=$(curl -sI -m 3 "http://$domain" 2>/dev/null | grep -i "CF-RAY:" | head -1 | awk '{print $2}' | tr -d '\r') + + if [ -z "$cf_ray" ]; then + echo "N/A" + return 1 + fi + + # Extract datacenter code (last 3 characters, e.g., "ORD" from "9c1b340e5cdacc3a-ORD") + local colo=$(echo "$cf_ray" | awk -F'-' '{print $NF}') + + if [ -n "$colo" ]; then + echo "$colo" + return 0 + fi + + echo "N/A" + return 1 +} + +detect_cloudflare() { + local domain="$1" + + local ns_result=$(check_nameservers "$domain") + local ip_result=$(check_ip_address "$domain") + local http_result=$(check_http_headers "$domain") + + # Cloudflare is confirmed if ANY check returns CLOUDFLARE + if [ "$ns_result" = "CLOUDFLARE" ] || \ + [ "$ip_result" = "CLOUDFLARE" ] || \ + [ "$http_result" = "CLOUDFLARE" ]; then + echo "CLOUDFLARE" + return 0 + fi + + # If all checks say DIRECT, it's definitely not Cloudflare + if [ "$ns_result" = "OTHER" ] && \ + [ "$ip_result" = "DIRECT" ] && \ + [ "$http_result" = "DIRECT" ]; then + echo "DIRECT" + return 1 + fi + + # Otherwise, uncertain + echo "UNKNOWN" + return 2 +} + +################################################################################ +# Main Functions +################################################################################ + +scan_all_domains() { + print_banner "Cloudflare Detection Scan" + + echo "Scanning all domains on this server..." + echo "Checking: Nameservers, IP addresses, HTTP headers" + echo "" + + # Get all domains + local domains=$(get_domains) + + if [ -z "$domains" ]; then + print_error "No domains found on this server" + echo "" + echo "This tool requires cPanel with configured domains" + press_enter + return 1 + fi + + local domain_count=$(echo "$domains" | wc -l) + + echo "Found $domain_count domains to check..." + echo "" + sleep 1 + + # Arrays to store results + local -a cloudflare_domains=() + local -a cloudflare_locations=() + local -a direct_domains=() + local -a unknown_domains=() + + # Progress tracking + local current=0 + + while IFS= read -r domain; do + current=$((current + 1)) + + # Show progress + printf "\r[%d/%d] Checking: %-50s" "$current" "$domain_count" "$domain" + + # Detect Cloudflare + local result=$(detect_cloudflare "$domain") + + case "$result" in + "CLOUDFLARE") + cloudflare_domains+=("$domain") + # Get Cloudflare datacenter location + local location=$(get_cloudflare_location "$domain") + cloudflare_locations+=("$location") + ;; + "DIRECT") + direct_domains+=("$domain") + ;; + *) + unknown_domains+=("$domain") + ;; + esac + done <<< "$domains" + + echo "" + echo "" + + # Display results + echo "═══════════════════════════════════════════════════════════════" + echo " SCAN RESULTS" + echo "═══════════════════════════════════════════════════════════════" + echo "" + + # Cloudflare domains + if [ ${#cloudflare_domains[@]} -gt 0 ]; then + print_success "🔶 Domains using Cloudflare: ${#cloudflare_domains[@]}" + echo "" + for i in "${!cloudflare_domains[@]}"; do + local domain="${cloudflare_domains[$i]}" + local location="${cloudflare_locations[$i]}" + if [ "$location" != "N/A" ]; then + printf " ✓ %-50s [%s]\n" "$domain" "$location" + else + echo " ✓ $domain" + fi + done + echo "" + else + echo "🔶 Domains using Cloudflare: 0" + echo "" + fi + + # Direct domains + if [ ${#direct_domains[@]} -gt 0 ]; then + print_info "🌐 Domains NOT using Cloudflare: ${#direct_domains[@]}" + echo "" + for domain in "${direct_domains[@]}"; do + echo " • $domain" + done + echo "" + else + echo "🌐 Domains NOT using Cloudflare: 0" + echo "" + fi + + # Unknown domains + if [ ${#unknown_domains[@]} -gt 0 ]; then + print_warning "❓ Uncertain (DNS/connectivity issues): ${#unknown_domains[@]}" + echo "" + for domain in "${unknown_domains[@]}"; do + echo " ? $domain" + done + echo "" + fi + + echo "═══════════════════════════════════════════════════════════════" + echo "" + echo "Summary:" + echo " Total domains: $domain_count" + echo " Cloudflare: ${#cloudflare_domains[@]}" + echo " Direct: ${#direct_domains[@]}" + echo " Unknown: ${#unknown_domains[@]}" + echo "" + + press_enter +} + +check_single_domain() { + print_banner "Check Single Domain" + + echo "" + read -p "Enter domain name to check: " domain + + if [ -z "$domain" ]; then + print_error "No domain entered" + press_enter + return 1 + fi + + echo "" + echo "Checking: $domain" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" + + # Check nameservers + print_info "1. Nameserver Check:" + local ns_result=$(check_nameservers "$domain") + local nameservers=$(dig +short NS "$domain" 2>/dev/null) + + echo " Nameservers:" + if [ -n "$nameservers" ]; then + echo "$nameservers" | sed 's/^/ /' + else + echo " (none found)" + fi + echo " Status: $ns_result" + echo "" + + # Check IP address + print_info "2. IP Address Check:" + local ip=$(dig +short A "$domain" 2>/dev/null | head -1) + + if [ -n "$ip" ]; then + echo " IP: $ip" + local ip_result=$(check_ip_address "$domain") + echo " Status: $ip_result" + else + echo " (no A record found)" + fi + echo "" + + # Check HTTP headers + print_info "3. HTTP Header Check:" + echo " Testing: http://$domain" + local http_result=$(check_http_headers "$domain") + echo " Status: $http_result" + + # Get location if using Cloudflare + if [ "$http_result" = "CLOUDFLARE" ]; then + local location=$(get_cloudflare_location "$domain") + echo " Datacenter: $location" + fi + echo "" + + # Final result + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" + local final_result=$(detect_cloudflare "$domain") + + case "$final_result" in + "CLOUDFLARE") + local location=$(get_cloudflare_location "$domain") + if [ "$location" != "N/A" ]; then + print_success "✓ $domain is using Cloudflare (Datacenter: $location)" + else + print_success "✓ $domain is using Cloudflare" + fi + ;; + "DIRECT") + print_info "• $domain is NOT using Cloudflare" + ;; + *) + print_warning "? Could not determine Cloudflare status for $domain" + ;; + esac + + echo "" + press_enter +} + +show_info() { + print_banner "About Cloudflare Detection" + + echo "" + echo "How Detection Works:" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" + echo "This tool checks three indicators to detect Cloudflare:" + echo "" + echo " 1. Nameservers:" + echo " Checks if domain uses Cloudflare nameservers" + echo " (e.g., ns1.cloudflare.com)" + echo "" + echo " 2. IP Address:" + echo " Checks if A record points to Cloudflare IP range" + echo " (e.g., 104.16.x.x, 172.64.x.x, etc.)" + echo "" + echo " 3. HTTP Headers:" + echo " Checks for CF-RAY header in HTTP response" + echo " (definitive proof of Cloudflare proxy)" + echo "" + echo " 4. Datacenter Location:" + echo " Extracts IATA airport code from CF-RAY header" + echo " (e.g., ORD=Chicago, LAX=Los Angeles, LHR=London)" + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" + echo "Why This Matters:" + echo "" + echo " • Cloudflare domains hide origin server IP" + echo " • Useful for cache debugging (Cloudflare vs Varnish)" + echo " • Important for firewall/security configuration" + echo " • Helps identify which domains get CDN benefits" + echo " • Datacenter location useful for debugging regional outages" + echo "" + echo "Common Datacenter Codes:" + echo " ORD - Chicago | LAX - Los Angeles | IAD - Virginia" + echo " DFW - Dallas | ATL - Atlanta | SEA - Seattle" + echo " LHR - London | FRA - Frankfurt | AMS - Amsterdam" + echo " SYD - Sydney | SIN - Singapore | NRT - Tokyo" + echo "" + press_enter +} + +################################################################################ +# Main Menu +################################################################################ + +show_menu() { + clear + print_banner "Cloudflare Domain Detector" + + echo "" + echo -e "${BOLD}Scan Options:${NC}" + echo "" + echo " 1) Scan All Domains - Check all domains on this server" + echo " 2) Check Single Domain - Detailed check for one domain" + echo " 3) About / How It Works - Detection methodology" + echo "" + echo " 0) Back to Website Menu" + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo -n "Select option: " +} + +main() { + while true; do + show_menu + read -r choice + + case $choice in + 1) scan_all_domains ;; + 2) check_single_domain ;; + 3) show_info ;; + 0) + clear + exit 0 + ;; + *) + echo "" + print_error "Invalid option" + sleep 1 + ;; + esac + done +} + +# Run main menu +main