Files
proxmox/scripts/verify/export-cloudflare-dns-records.sh

280 lines
10 KiB
Bash
Raw Normal View History

#!/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" <<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"