- Organized 252 files across project - Root directory: 187 → 2 files (98.9% reduction) - Moved configuration guides to docs/04-configuration/ - Moved troubleshooting guides to docs/09-troubleshooting/ - Moved quick start guides to docs/01-getting-started/ - Moved reports to reports/ directory - Archived temporary files - Generated comprehensive reports and documentation - Created maintenance scripts and guides All files organized according to established standards.
400 lines
14 KiB
Bash
Executable File
400 lines
14 KiB
Bash
Executable File
#!/bin/bash
|
|
# Complete Cloudflare Configuration for Explorer - Automated
|
|
# Uses .env credentials to configure DNS, SSL, and tunnel routes
|
|
|
|
set -euo pipefail
|
|
|
|
# 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"; }
|
|
log_section() { echo -e "${CYAN}════════════════════════════════════════${NC}"; }
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
ENV_FILE="${ENV_FILE:-$SCRIPT_DIR/../.env}"
|
|
|
|
# Configuration
|
|
DOMAIN="${DOMAIN:-d-bis.org}"
|
|
EXPLORER_DOMAIN="explorer.d-bis.org"
|
|
EXPLORER_IP="${EXPLORER_IP:-192.168.11.140}"
|
|
EXPLORER_PORT="${EXPLORER_PORT:-80}"
|
|
VMID=5000
|
|
PROXMOX_HOST="${PROXMOX_HOST:-192.168.11.10}"
|
|
TUNNEL_TOKEN="eyJhIjoiNTJhZDU3YTcxNjcxYzVmYzAwOWVkZjA3NDQ2NTgxOTYiLCJ0IjoiYjAyZmUxZmUtY2I3ZC00ODRlLTkwOWItN2NjNDEyOThlYmU4IiwicyI6Ik5HTmtOV0kwWXpNdFpUVmxaUzAwTVRFMkxXRXdNMk10WlRJNU1ETTFaRFF4TURBMiJ9"
|
|
|
|
echo ""
|
|
log_section
|
|
log_info " COMPLETE CLOUDFLARE EXPLORER CONFIGURATION"
|
|
log_info " Using .env Credentials for Full Automation"
|
|
log_section
|
|
echo ""
|
|
|
|
# Step 1: Load .env file
|
|
log_info "Step 1: Loading credentials from .env file..."
|
|
if [ ! -f "$ENV_FILE" ]; then
|
|
log_error ".env file not found: $ENV_FILE"
|
|
log_info "Looking for .env files..."
|
|
find "$SCRIPT_DIR/.." -maxdepth 2 -name ".env" -type f 2>/dev/null | head -3
|
|
log_info ""
|
|
log_info "Please create .env file with:"
|
|
echo " CLOUDFLARE_API_TOKEN=your-token"
|
|
echo " CLOUDFLARE_ZONE_ID=your-zone-id (optional)"
|
|
echo " CLOUDFLARE_ACCOUNT_ID=your-account-id (optional)"
|
|
echo " DOMAIN=d-bis.org"
|
|
exit 1
|
|
fi
|
|
|
|
# Source .env file
|
|
set -a
|
|
source "$ENV_FILE"
|
|
set +a
|
|
|
|
log_success ".env file loaded"
|
|
|
|
# Check for required credentials
|
|
CLOUDFLARE_API_TOKEN="${CLOUDFLARE_API_TOKEN:-}"
|
|
CLOUDFLARE_API_KEY="${CLOUDFLARE_API_KEY:-}"
|
|
CLOUDFLARE_EMAIL="${CLOUDFLARE_EMAIL:-}"
|
|
CLOUDFLARE_ZONE_ID="${CLOUDFLARE_ZONE_ID:-}"
|
|
CLOUDFLARE_ACCOUNT_ID="${CLOUDFLARE_ACCOUNT_ID:-}"
|
|
|
|
# Determine auth method
|
|
AUTH_METHOD=""
|
|
AUTH_HEADERS=()
|
|
|
|
# Check for API_TOKEN first (preferred), then API_KEY
|
|
if [ -n "$CLOUDFLARE_API_TOKEN" ]; then
|
|
AUTH_METHOD="token"
|
|
AUTH_HEADERS=(-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN")
|
|
log_success "Using API Token authentication"
|
|
elif [ -n "$CLOUDFLARE_API_KEY" ] && [ -n "$CLOUDFLARE_EMAIL" ]; then
|
|
AUTH_METHOD="key"
|
|
# Remove quotes from API_KEY if present
|
|
CLOUDFLARE_API_KEY=$(echo "$CLOUDFLARE_API_KEY" | tr -d '"')
|
|
CLOUDFLARE_EMAIL=$(echo "$CLOUDFLARE_EMAIL" | tr -d '"')
|
|
AUTH_HEADERS=(-H "X-Auth-Email: $CLOUDFLARE_EMAIL" -H "X-Auth-Key: $CLOUDFLARE_API_KEY")
|
|
log_success "Using API Key authentication"
|
|
else
|
|
log_error "No Cloudflare API credentials found in .env"
|
|
log_info "Required: CLOUDFLARE_API_TOKEN or (CLOUDFLARE_API_KEY + CLOUDFLARE_EMAIL)"
|
|
exit 1
|
|
fi
|
|
|
|
# Function to find container node
|
|
find_container_node() {
|
|
ssh -o StrictHostKeyChecking=no root@"$PROXMOX_HOST" \
|
|
"for node in ml110 pve pve2; do if pvesh get /nodes/\$node/lxc/$VMID/status/current --output-format json >/dev/null 2>&1; then echo \$node; break; fi; done" 2>/dev/null || echo "pve2"
|
|
}
|
|
|
|
# Function to execute command in container
|
|
exec_container() {
|
|
local cmd="$1"
|
|
# Try direct pct exec via main host first
|
|
ssh -o StrictHostKeyChecking=no root@"$PROXMOX_HOST" "pct exec $VMID -- bash -c '$cmd'" 2>&1
|
|
}
|
|
|
|
# Step 2: Install Cloudflare Tunnel Service
|
|
log_section
|
|
log_info "Step 2: Installing Cloudflare Tunnel Service"
|
|
log_section
|
|
|
|
log_info "Checking cloudflared installation..."
|
|
if ! exec_container "command -v cloudflared >/dev/null 2>&1"; then
|
|
log_info "Installing cloudflared..."
|
|
exec_container "cd /tmp && wget -q https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb && dpkg -i cloudflared-linux-amd64.deb || apt install -f -y"
|
|
log_success "cloudflared installed"
|
|
else
|
|
log_success "cloudflared already installed"
|
|
fi
|
|
|
|
log_info "Installing tunnel service with token..."
|
|
INSTALL_OUTPUT=$(exec_container "cloudflared service install $TUNNEL_TOKEN 2>&1" || echo "INSTALL_FAILED")
|
|
if echo "$INSTALL_OUTPUT" | grep -q -E "successfully|installed|Service installed"; then
|
|
log_success "Tunnel service installed"
|
|
else
|
|
log_warn "Installation output: $INSTALL_OUTPUT"
|
|
# Continue - service might already be installed
|
|
fi
|
|
|
|
log_info "Starting cloudflared service..."
|
|
exec_container "systemctl start cloudflared" || true
|
|
exec_container "systemctl enable cloudflared" || true
|
|
sleep 3
|
|
|
|
CLOUDFLARED_STATUS=$(exec_container "systemctl is-active cloudflared 2>/dev/null || echo 'inactive'")
|
|
if [ "$CLOUDFLARED_STATUS" = "active" ]; then
|
|
log_success "Cloudflared service is running"
|
|
else
|
|
log_warn "Cloudflared service is $CLOUDFLARED_STATUS"
|
|
fi
|
|
|
|
# Get tunnel ID
|
|
log_info "Getting tunnel ID..."
|
|
TUNNEL_LIST=$(exec_container "cloudflared tunnel list 2>&1" || echo "")
|
|
TUNNEL_ID=$(echo "$TUNNEL_LIST" | grep -v "NAME" | head -1 | awk '{print $1}' || echo "")
|
|
|
|
if [ -n "$TUNNEL_ID" ]; then
|
|
log_success "Tunnel ID: $TUNNEL_ID"
|
|
else
|
|
log_warn "Could not get tunnel ID from tunnel list"
|
|
# Try to extract from token (base64 decode)
|
|
TUNNEL_ID=$(echo "$TUNNEL_TOKEN" | base64 -d 2>/dev/null | jq -r '.TunnelID // empty' 2>/dev/null || echo "")
|
|
if [ -n "$TUNNEL_ID" ]; then
|
|
log_success "Tunnel ID from token: $TUNNEL_ID"
|
|
else
|
|
log_error "Cannot determine tunnel ID"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# Step 3: Get Zone ID
|
|
log_section
|
|
log_info "Step 3: Getting Cloudflare Zone ID"
|
|
log_section
|
|
|
|
if [ -z "$CLOUDFLARE_ZONE_ID" ]; then
|
|
log_info "Fetching zone ID for $DOMAIN..."
|
|
ZONE_RESPONSE=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones?name=$DOMAIN" \
|
|
"${AUTH_HEADERS[@]}" \
|
|
-H "Content-Type: application/json")
|
|
|
|
ZONE_ID=$(echo "$ZONE_RESPONSE" | jq -r '.result[0].id // empty' 2>/dev/null || echo "")
|
|
|
|
if [ -z "$ZONE_ID" ] || [ "$ZONE_ID" = "null" ]; then
|
|
ERROR=$(echo "$ZONE_RESPONSE" | jq -r '.errors[0].message // "Unknown error"' 2>/dev/null || echo "API call failed")
|
|
log_error "Failed to get zone ID: $ERROR"
|
|
exit 1
|
|
fi
|
|
|
|
log_success "Zone ID: $ZONE_ID"
|
|
else
|
|
ZONE_ID="$CLOUDFLARE_ZONE_ID"
|
|
log_success "Using provided Zone ID: $ZONE_ID"
|
|
fi
|
|
|
|
# Step 4: Get Account ID (for tunnel configuration)
|
|
log_section
|
|
log_info "Step 4: Getting Cloudflare Account ID"
|
|
log_section
|
|
|
|
if [ -z "$CLOUDFLARE_ACCOUNT_ID" ]; then
|
|
log_info "Fetching account ID..."
|
|
ACCOUNT_RESPONSE=$(curl -s -X GET "https://api.cloudflare.com/client/v4/accounts" \
|
|
"${AUTH_HEADERS[@]}" \
|
|
-H "Content-Type: application/json")
|
|
|
|
CLOUDFLARE_ACCOUNT_ID=$(echo "$ACCOUNT_RESPONSE" | jq -r '.result[0].id // empty' 2>/dev/null || echo "")
|
|
|
|
if [ -z "$CLOUDFLARE_ACCOUNT_ID" ] || [ "$CLOUDFLARE_ACCOUNT_ID" = "null" ]; then
|
|
log_warn "Could not get account ID automatically"
|
|
else
|
|
log_success "Account ID: $CLOUDFLARE_ACCOUNT_ID"
|
|
fi
|
|
else
|
|
log_success "Using provided Account ID: $CLOUDFLARE_ACCOUNT_ID"
|
|
fi
|
|
|
|
# Step 5: Configure DNS Record
|
|
log_section
|
|
log_info "Step 5: Configuring DNS Record"
|
|
log_section
|
|
|
|
TARGET="${TUNNEL_ID}.cfargotunnel.com"
|
|
log_info "DNS Target: $TARGET"
|
|
|
|
# Check if record exists
|
|
EXISTING_RECORD=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?name=$EXPLORER_DOMAIN" \
|
|
"${AUTH_HEADERS[@]}" \
|
|
-H "Content-Type: application/json")
|
|
|
|
RECORD_ID=$(echo "$EXISTING_RECORD" | jq -r '.result[0].id // empty' 2>/dev/null || echo "")
|
|
EXISTING_TYPE=$(echo "$EXISTING_RECORD" | jq -r '.result[0].type // empty' 2>/dev/null || echo "")
|
|
|
|
DNS_DATA=$(jq -n \
|
|
--arg name "explorer" \
|
|
--arg target "$TARGET" \
|
|
'{
|
|
type: "CNAME",
|
|
name: $name,
|
|
content: $target,
|
|
proxied: true,
|
|
ttl: 1
|
|
}')
|
|
|
|
if [ -n "$RECORD_ID" ] && [ "$RECORD_ID" != "null" ]; then
|
|
log_info "Updating existing DNS record (ID: $RECORD_ID, Type: $EXISTING_TYPE)..."
|
|
|
|
if [ "$EXISTING_TYPE" != "CNAME" ]; then
|
|
log_warn "Existing record is type $EXISTING_TYPE, deleting and creating CNAME..."
|
|
curl -s -X DELETE "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$RECORD_ID" \
|
|
"${AUTH_HEADERS[@]}" \
|
|
-H "Content-Type: application/json" >/dev/null 2>&1
|
|
|
|
DNS_RESPONSE=$(curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" \
|
|
"${AUTH_HEADERS[@]}" \
|
|
-H "Content-Type: application/json" \
|
|
--data "$DNS_DATA")
|
|
else
|
|
DNS_RESPONSE=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$RECORD_ID" \
|
|
"${AUTH_HEADERS[@]}" \
|
|
-H "Content-Type: application/json" \
|
|
--data "$DNS_DATA")
|
|
fi
|
|
else
|
|
log_info "Creating new DNS record..."
|
|
DNS_RESPONSE=$(curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" \
|
|
"${AUTH_HEADERS[@]}" \
|
|
-H "Content-Type: application/json" \
|
|
--data "$DNS_DATA")
|
|
fi
|
|
|
|
if echo "$DNS_RESPONSE" | jq -e '.success' >/dev/null 2>&1; then
|
|
log_success "DNS record configured successfully"
|
|
DNS_NAME=$(echo "$DNS_RESPONSE" | jq -r '.result.name' 2>/dev/null || echo "$EXPLORER_DOMAIN")
|
|
DNS_TARGET=$(echo "$DNS_RESPONSE" | jq -r '.result.content' 2>/dev/null || echo "$TARGET")
|
|
DNS_PROXIED=$(echo "$DNS_RESPONSE" | jq -r '.result.proxied' 2>/dev/null || echo "true")
|
|
log_info " Name: $DNS_NAME"
|
|
log_info " Target: $DNS_TARGET"
|
|
log_info " Proxied: $DNS_PROXIED"
|
|
else
|
|
ERROR=$(echo "$DNS_RESPONSE" | jq -r '.errors[0].message // "Unknown error"' 2>/dev/null || echo "API call failed")
|
|
log_error "Failed to configure DNS: $ERROR"
|
|
echo "$DNS_RESPONSE" | jq '.' 2>/dev/null || echo "$DNS_RESPONSE"
|
|
exit 1
|
|
fi
|
|
|
|
# Step 6: Configure Tunnel Route
|
|
log_section
|
|
log_info "Step 6: Configuring Tunnel Route"
|
|
log_section
|
|
|
|
if [ -z "$CLOUDFLARE_ACCOUNT_ID" ] || [ "$CLOUDFLARE_ACCOUNT_ID" = "null" ]; then
|
|
log_warn "Account ID not available - tunnel route must be configured manually"
|
|
log_info "Configure in Cloudflare Zero Trust Dashboard:"
|
|
echo " 1. Go to: https://one.dash.cloudflare.com/"
|
|
echo " 2. Zero Trust → Networks → Tunnels"
|
|
echo " 3. Select tunnel: $TUNNEL_ID"
|
|
echo " 4. Configure → Public Hostnames → Add hostname"
|
|
echo " 5. Subdomain: explorer, Domain: $DOMAIN"
|
|
echo " 6. Service: http://$EXPLORER_IP:$EXPLORER_PORT"
|
|
else
|
|
log_info "Configuring tunnel route via API..."
|
|
|
|
# Get current tunnel configuration
|
|
TUNNEL_CONFIG=$(curl -s -X GET "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/cfd_tunnel/$TUNNEL_ID/configurations" \
|
|
"${AUTH_HEADERS[@]}" \
|
|
-H "Content-Type: application/json")
|
|
|
|
# Build new ingress configuration
|
|
NEW_CONFIG=$(jq -n \
|
|
--arg hostname "$EXPLORER_DOMAIN" \
|
|
--arg service "http://$EXPLORER_IP:$EXPLORER_PORT" \
|
|
'{
|
|
config: {
|
|
ingress: [
|
|
{
|
|
hostname: $hostname,
|
|
service: $service
|
|
},
|
|
{
|
|
service: "http_status:404"
|
|
}
|
|
]
|
|
}
|
|
}')
|
|
|
|
# Update tunnel configuration
|
|
TUNNEL_UPDATE=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/cfd_tunnel/$TUNNEL_ID/configurations" \
|
|
"${AUTH_HEADERS[@]}" \
|
|
-H "Content-Type: application/json" \
|
|
--data "$NEW_CONFIG")
|
|
|
|
if echo "$TUNNEL_UPDATE" | jq -e '.success' >/dev/null 2>&1; then
|
|
log_success "Tunnel route configured successfully"
|
|
else
|
|
ERROR=$(echo "$TUNNEL_UPDATE" | jq -r '.errors[0].message // "Unknown error"' 2>/dev/null || echo "API call failed")
|
|
log_warn "Tunnel route API configuration failed: $ERROR"
|
|
log_info "Please configure manually in Cloudflare Zero Trust Dashboard"
|
|
fi
|
|
fi
|
|
|
|
# Step 7: SSL/TLS Configuration (automatic with Cloudflare proxy)
|
|
log_section
|
|
log_info "Step 7: SSL/TLS Configuration"
|
|
log_section
|
|
|
|
log_info "SSL/TLS is automatically handled by Cloudflare when DNS is proxied"
|
|
log_success "SSL will be enabled automatically (Universal SSL)"
|
|
|
|
# Step 8: Verify Configuration
|
|
log_section
|
|
log_info "Step 8: Verifying Configuration"
|
|
log_section
|
|
|
|
log_info "Waiting for DNS propagation (10 seconds)..."
|
|
sleep 10
|
|
|
|
# Test DNS resolution
|
|
DNS_RESULT=$(dig +short "$EXPLORER_DOMAIN" 2>/dev/null | head -1 || echo "")
|
|
if [ -n "$DNS_RESULT" ]; then
|
|
log_success "DNS resolves to: $DNS_RESULT"
|
|
if echo "$DNS_RESULT" | grep -qE "^(104\.|172\.64\.|172\.65\.|172\.66\.|172\.67\.)"; then
|
|
log_success "DNS points to Cloudflare (proxied correctly)"
|
|
fi
|
|
else
|
|
log_warn "DNS not resolving yet (may need more time)"
|
|
fi
|
|
|
|
# Test public URL
|
|
log_info "Testing public URL..."
|
|
PUBLIC_HTTP=$(curl -s -o /dev/null -w "%{http_code}" "https://$EXPLORER_DOMAIN/api/v2/stats" 2>&1)
|
|
if [ "$PUBLIC_HTTP" = "200" ]; then
|
|
log_success "Public URL: HTTP 200 - Working!"
|
|
PUBLIC_RESPONSE=$(curl -s "https://$EXPLORER_DOMAIN/api/v2/stats" 2>&1)
|
|
if echo "$PUBLIC_RESPONSE" | grep -q -E "total_blocks|chain_id"; then
|
|
log_success "Public API: Valid response"
|
|
echo "$PUBLIC_RESPONSE" | jq -r '.total_blocks, .total_transactions, .total_addresses' 2>/dev/null || echo "$PUBLIC_RESPONSE" | head -5
|
|
fi
|
|
elif [ "$PUBLIC_HTTP" = "404" ]; then
|
|
log_warn "Public URL: HTTP 404 - May need more time for DNS/tunnel propagation"
|
|
elif [ "$PUBLIC_HTTP" = "502" ]; then
|
|
log_warn "Public URL: HTTP 502 - Tunnel routing issue, check tunnel route configuration"
|
|
else
|
|
log_warn "Public URL: HTTP $PUBLIC_HTTP"
|
|
fi
|
|
|
|
# Final Summary
|
|
echo ""
|
|
log_section
|
|
log_info " CONFIGURATION SUMMARY"
|
|
log_section
|
|
echo ""
|
|
|
|
log_success "✓ Cloudflared service: Installed and running"
|
|
log_success "✓ Tunnel ID: $TUNNEL_ID"
|
|
log_success "✓ DNS Record: $EXPLORER_DOMAIN → $TARGET (🟠 Proxied)"
|
|
if [ -n "$CLOUDFLARE_ACCOUNT_ID" ]; then
|
|
log_success "✓ Tunnel Route: Configured via API"
|
|
else
|
|
log_warn "⚠ Tunnel Route: Manual configuration required"
|
|
fi
|
|
log_success "✓ SSL/TLS: Automatic (Cloudflare Universal SSL)"
|
|
|
|
echo ""
|
|
log_info "Configuration complete!"
|
|
log_info ""
|
|
log_info "Access your explorer at:"
|
|
echo " https://$EXPLORER_DOMAIN"
|
|
echo ""
|
|
log_info "If public URL is not working yet, wait 1-5 minutes for DNS propagation"
|
|
echo ""
|
|
|