585 lines
17 KiB
Bash
Executable File
585 lines
17 KiB
Bash
Executable File
#!/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_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
|
|
}
|
|
|
|
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
|