#!/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$ ]] || \ [[ "$domain" =~ \.transferred$ ]] || \ [[ "$domain" =~ \.db$ ]] || \ [[ "$domain" =~ \.swp$ ]] || \ [[ "$domain" =~ \.tmp$ ]] || \ [[ "$domain" =~ php-fpm ]] || \ [[ "$domain" =~ \.lock$ ]]; 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_location_name() { local code="$1" # Map IATA codes to city names case "$code" in # North America ORD) echo "Chicago" ;; LAX) echo "Los Angeles" ;; IAD) echo "Ashburn, VA" ;; DFW) echo "Dallas" ;; ATL) echo "Atlanta" ;; SEA) echo "Seattle" ;; SJC) echo "San Jose, CA" ;; MIA) echo "Miami" ;; YYZ) echo "Toronto" ;; BNA) echo "Nashville" ;; DEN) echo "Denver" ;; PHX) echo "Phoenix" ;; EWR) echo "Newark, NJ" ;; BOS) echo "Boston" ;; # Europe LHR) echo "London" ;; FRA) echo "Frankfurt" ;; AMS) echo "Amsterdam" ;; CDG) echo "Paris" ;; MAD) echo "Madrid" ;; MAN) echo "Manchester" ;; ARN) echo "Stockholm" ;; WAW) echo "Warsaw" ;; VIE) echo "Vienna" ;; # Asia Pacific SIN) echo "Singapore" ;; NRT) echo "Tokyo" ;; HKG) echo "Hong Kong" ;; SYD) echo "Sydney" ;; ICN) echo "Seoul" ;; BOM) echo "Mumbai" ;; DEL) echo "New Delhi" ;; # Middle East DXB) echo "Dubai" ;; TLV) echo "Tel Aviv" ;; # South America GRU) echo "São Paulo" ;; EZE) echo "Buenos Aires" ;; # If unknown, show the code *) echo "$code" ;; esac } 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 # Return city name instead of code local city=$(get_location_name "$colo") echo "$city" return 0 fi echo "N/A" return 1 } domain_resolves() { local domain="$1" # Check if domain has any A records local ip=$(dig +short A "$domain" 2>/dev/null | head -1) # Also check AAAA for IPv6-only domains if [ -z "$ip" ]; then ip=$(dig +short AAAA "$domain" 2>/dev/null | head -1) fi # Return 0 if domain resolves, 1 if it doesn't [ -n "$ip" ] } detect_cloudflare() { local domain="$1" # Skip domains that don't resolve at all if ! domain_resolves "$domain"; then echo "NXDOMAIN" return 3 fi 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=() local -a nxdomain_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") ;; "NXDOMAIN") nxdomain_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 # NXDOMAIN domains (don't resolve) if [ ${#nxdomain_domains[@]} -gt 0 ]; then print_warning "⚠ Domains that don't resolve (NXDOMAIN): ${#nxdomain_domains[@]}" echo "" for domain in "${nxdomain_domains[@]}"; do echo " ✗ $domain" done echo "" echo " 💡 Tip: These domains are configured in cPanel but don't have DNS records." echo " Consider removing them or checking your DNS configuration." 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[@]}" if [ ${#nxdomain_domains[@]} -gt 0 ]; then echo " NXDOMAIN: ${#nxdomain_domains[@]} (don't resolve)" fi 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 if domain resolves if ! domain_resolves "$domain"; then print_error "✗ Domain does not resolve (NXDOMAIN)" echo "" echo "This domain has no DNS A or AAAA records." echo "It may be misconfigured, deleted, or not yet propagated." echo "" press_enter return 1 fi # 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