2026-02-12 15:46:57 -08:00
|
|
|
#!/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"
|
2026-03-27 00:31:14 -07:00
|
|
|
["www.the-order.sankofa.nexus"]="sankofa.nexus"
|
2026-03-02 11:37:34 -08:00
|
|
|
["studio.sankofa.nexus"]="sankofa.nexus"
|
2026-02-12 15:46:57 -08:00
|
|
|
["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" <<EOF
|
|
|
|
|
# Cloudflare DNS Records Verification Report
|
|
|
|
|
|
|
|
|
|
**Date**: $(date -Iseconds)
|
|
|
|
|
**Public IP**: $PUBLIC_IP
|
|
|
|
|
**Verifier**: $(whoami)
|
|
|
|
|
|
|
|
|
|
## Summary
|
|
|
|
|
|
|
|
|
|
| Status | Count |
|
|
|
|
|
|--------|-------|
|
|
|
|
|
| Verified | $verified_count |
|
|
|
|
|
| Documented | $documented_count |
|
|
|
|
|
| Unknown | $unknown_count |
|
|
|
|
|
| Needs Fix | $needs_fix_count |
|
|
|
|
|
| **Total** | **${#DOMAIN_ZONES[@]}** |
|
|
|
|
|
|
|
|
|
|
## Verification Results
|
|
|
|
|
|
|
|
|
|
EOF
|
|
|
|
|
|
|
|
|
|
echo "| Domain | Zone | Type | Target | Proxied | TTL | Status |" >> "$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" <<EOF
|
|
|
|
|
|
|
|
|
|
## Expected Configuration
|
|
|
|
|
|
|
|
|
|
- All records should be type **A**
|
|
|
|
|
- All records should point to **$PUBLIC_IP**
|
|
|
|
|
- All records should have **proxied: false** (DNS Only / gray cloud)
|
|
|
|
|
- TTL should be Auto or reasonable value
|
|
|
|
|
|
|
|
|
|
## Files Generated
|
|
|
|
|
|
|
|
|
|
- \`all_dns_records.json\` - Complete DNS records export
|
|
|
|
|
- \`verification_results.json\` - Verification results with status
|
|
|
|
|
- \`*.json\` - Per-zone exports
|
|
|
|
|
- \`verification_report.md\` - This report
|
|
|
|
|
|
|
|
|
|
## Next Steps
|
|
|
|
|
|
|
|
|
|
1. Review verification results
|
|
|
|
|
2. Fix any records with status "needs-fix"
|
|
|
|
|
3. Investigate any records with status "unknown"
|
|
|
|
|
4. Update source-of-truth JSON after verification
|
|
|
|
|
EOF
|
|
|
|
|
|
|
|
|
|
log_info ""
|
|
|
|
|
log_info "Verification complete!"
|
|
|
|
|
log_success "JSON export: $OUTPUT_DIR/all_dns_records.json"
|
|
|
|
|
log_success "Report: $REPORT_FILE"
|
|
|
|
|
log_info "Summary: $verified_count verified, $documented_count documented, $unknown_count unknown, $needs_fix_count need fix"
|