#!/usr/bin/env bash # Export Cloudflare DNS records for verification # Generates JSON export and verification report comparing to baseline docs set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" EVIDENCE_DIR="$PROJECT_ROOT/docs/04-configuration/verification-evidence" # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' NC='\033[0m' log_info() { echo -e "${BLUE}[INFO]${NC} $1"; } log_success() { echo -e "${GREEN}[✓]${NC} $1"; } log_warn() { echo -e "${YELLOW}[⚠]${NC} $1"; } log_error() { echo -e "${RED}[✗]${NC} $1"; } cd "$PROJECT_ROOT" # Source .env if [ -f .env ]; then set +euo pipefail source .env 2>/dev/null || true set -euo pipefail fi CLOUDFLARE_API_TOKEN="${CLOUDFLARE_API_TOKEN:-}" CLOUDFLARE_EMAIL="${CLOUDFLARE_EMAIL:-}" CLOUDFLARE_API_KEY="${CLOUDFLARE_API_KEY:-}" PUBLIC_IP="${PUBLIC_IP:-76.53.10.36}" # Expected domains from baseline docs declare -A DOMAIN_ZONES=( ["explorer.d-bis.org"]="d-bis.org" ["rpc-http-pub.d-bis.org"]="d-bis.org" ["rpc-ws-pub.d-bis.org"]="d-bis.org" ["rpc-http-prv.d-bis.org"]="d-bis.org" ["rpc-ws-prv.d-bis.org"]="d-bis.org" ["dbis-admin.d-bis.org"]="d-bis.org" ["dbis-api.d-bis.org"]="d-bis.org" ["dbis-api-2.d-bis.org"]="d-bis.org" ["secure.d-bis.org"]="d-bis.org" ["mim4u.org"]="mim4u.org" ["www.mim4u.org"]="mim4u.org" ["secure.mim4u.org"]="mim4u.org" ["training.mim4u.org"]="mim4u.org" ["sankofa.nexus"]="sankofa.nexus" ["www.sankofa.nexus"]="sankofa.nexus" ["phoenix.sankofa.nexus"]="sankofa.nexus" ["www.phoenix.sankofa.nexus"]="sankofa.nexus" ["the-order.sankofa.nexus"]="sankofa.nexus" ["www.the-order.sankofa.nexus"]="sankofa.nexus" ["studio.sankofa.nexus"]="sankofa.nexus" ["rpc.public-0138.defi-oracle.io"]="defi-oracle.io" ) TIMESTAMP=$(date +%Y%m%d_%H%M%S) OUTPUT_DIR="$EVIDENCE_DIR/dns-verification-$TIMESTAMP" mkdir -p "$OUTPUT_DIR" echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "🔍 Cloudflare DNS Records Verification & Export" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" # Check authentication if [ -n "$CLOUDFLARE_API_TOKEN" ]; then AUTH_HEADERS=(-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN") log_success "Using Cloudflare API Token" elif [ -n "$CLOUDFLARE_EMAIL" ] && [ -n "$CLOUDFLARE_API_KEY" ]; then AUTH_HEADERS=(-H "X-Auth-Email: $CLOUDFLARE_EMAIL" -H "X-Auth-Key: $CLOUDFLARE_API_KEY") log_success "Using Cloudflare Email/Key authentication" else log_error "No Cloudflare credentials found in .env" exit 1 fi # Get zone IDs declare -A ZONE_IDS log_info "Fetching zone IDs..." for zone_name in d-bis.org mim4u.org sankofa.nexus defi-oracle.io; do zone_var="CLOUDFLARE_ZONE_ID_$(echo "$zone_name" | tr '.-' '_' | tr '[:lower:]' '[:upper:]')" zone_id="${!zone_var:-}" if [ -z "$zone_id" ]; then # Get from API zone_response=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones?name=$zone_name" \ "${AUTH_HEADERS[@]}" \ -H "Content-Type: application/json" 2>/dev/null || echo "{}") zone_id=$(echo "$zone_response" | jq -r '.result[0].id // empty' 2>/dev/null || echo "") fi if [ -n "$zone_id" ] && [ "$zone_id" != "null" ]; then ZONE_IDS[$zone_name]="$zone_id" log_success "Zone $zone_name: $zone_id" else log_warn "Zone $zone_name: Not found" fi done echo "" # Export all DNS records for each zone ALL_RECORDS=() VERIFICATION_RESULTS=() log_info "Exporting DNS records..." for zone_name in "${!ZONE_IDS[@]}"; do zone_id="${ZONE_IDS[$zone_name]}" log_info "Exporting records for zone: $zone_name" # Get A and CNAME records (CNAME explains "Not found" for rpc-http-pub, rpc-http-prv, rpc.public-0138) records_response_a=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records?type=A" \ "${AUTH_HEADERS[@]}" \ -H "Content-Type: application/json" 2>/dev/null || echo "{}") records_response_cname=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records?type=CNAME" \ "${AUTH_HEADERS[@]}" \ -H "Content-Type: application/json" 2>/dev/null || echo "{}") records=$(jq -s '.[0].result // [] + (.[1].result // [])' <(echo "$records_response_a") <(echo "$records_response_cname") 2>/dev/null || echo "[]") echo "$records" > "$OUTPUT_DIR/${zone_name//./_}_records.json" record_count=$(echo "$records" | jq '. | length' 2>/dev/null || echo "0") log_success " Exported $record_count A+CNAME records" # Add to all records array while IFS= read -r record; do if [ -n "$record" ] && [ "$record" != "null" ]; then ALL_RECORDS+=("$record") fi done < <(echo "$records" | jq -c '.[]' 2>/dev/null || true) done # Write complete export echo "${ALL_RECORDS[@]}" | jq -s '.' > "$OUTPUT_DIR/all_dns_records.json" # Verify each expected domain log_info "" log_info "Verifying expected domains against baseline docs..." verified_count=0 documented_count=0 unknown_count=0 needs_fix_count=0 for domain in "${!DOMAIN_ZONES[@]}"; do zone_name="${DOMAIN_ZONES[$domain]}" zone_id="${ZONE_IDS[$zone_name]:-}" if [ -z "$zone_id" ]; then status="unknown" unknown_count=$((unknown_count + 1)) VERIFICATION_RESULTS+=("{\"domain\":\"$domain\",\"zone\":\"$zone_name\",\"status\":\"$status\",\"reason\":\"Zone ID not found\"}") continue fi # Find record in export record=$(echo "${ALL_RECORDS[@]}" | jq -s ".[] | select(.name == \"$domain\")" 2>/dev/null || echo "null") if [ "$record" = "null" ] || [ -z "$record" ]; then status="unknown" unknown_count=$((unknown_count + 1)) VERIFICATION_RESULTS+=("{\"domain\":\"$domain\",\"zone\":\"$zone_name\",\"status\":\"$status\",\"reason\":\"DNS record not found\"}") log_warn "$domain: Not found" continue fi record_type=$(echo "$record" | jq -r '.type' 2>/dev/null || echo "") record_value=$(echo "$record" | jq -r '.content' 2>/dev/null || echo "") proxied=$(echo "$record" | jq -r '.proxied // false' 2>/dev/null || echo "false") ttl=$(echo "$record" | jq -r '.ttl' 2>/dev/null || echo "") record_id=$(echo "$record" | jq -r '.id' 2>/dev/null || echo "") # Verify against expected values if [ "$record_type" = "A" ] && [ "$record_value" = "$PUBLIC_IP" ] && [ "$proxied" = "false" ]; then status="verified" verified_count=$((verified_count + 1)) log_success "$domain → $record_value (DNS Only, TTL: $ttl)" elif [ "$record_type" = "A" ] && [ "$record_value" = "$PUBLIC_IP" ] && [ "$proxied" = "true" ]; then status="needs-fix" needs_fix_count=$((needs_fix_count + 1)) log_warn "$domain → $record_value (Proxied - should be DNS Only)" elif [ "$record_type" = "A" ] && [ "$record_value" != "$PUBLIC_IP" ]; then status="needs-fix" needs_fix_count=$((needs_fix_count + 1)) log_error "$domain → $record_value (should be $PUBLIC_IP)" else status="documented" documented_count=$((documented_count + 1)) log_info "$domain → $record_value (type: $record_type, proxied: $proxied)" fi VERIFICATION_RESULTS+=("{\"domain\":\"$domain\",\"zone\":\"$zone_name\",\"record_type\":\"$record_type\",\"record_value\":\"$record_value\",\"proxied\":$proxied,\"ttl\":$ttl,\"status\":\"$status\",\"record_id\":\"$record_id\"}") done # Write verification results echo "${VERIFICATION_RESULTS[@]}" | jq -s '.' > "$OUTPUT_DIR/verification_results.json" # Generate markdown report REPORT_FILE="$OUTPUT_DIR/verification_report.md" cat > "$REPORT_FILE" <> "$REPORT_FILE" echo "|--------|------|------|--------|---------|-----|--------|" >> "$REPORT_FILE" for result in "${VERIFICATION_RESULTS[@]}"; do domain=$(echo "$result" | jq -r '.domain' 2>/dev/null || echo "") zone=$(echo "$result" | jq -r '.zone' 2>/dev/null || echo "") record_type=$(echo "$result" | jq -r '.record_type // ""' 2>/dev/null || echo "") record_value=$(echo "$result" | jq -r '.record_value // ""' 2>/dev/null || echo "") proxied=$(echo "$result" | jq -r '.proxied // false' 2>/dev/null || echo "false") ttl=$(echo "$result" | jq -r '.ttl // ""' 2>/dev/null || echo "") status=$(echo "$result" | jq -r '.status' 2>/dev/null || echo "unknown") if [ "$proxied" = "true" ]; then proxied_display="Yes (⚠️)" else proxied_display="No" fi echo "| $domain | $zone | $record_type | $record_value | $proxied_display | $ttl | $status |" >> "$REPORT_FILE" done cat >> "$REPORT_FILE" <