269 lines
9.6 KiB
Bash
Executable File
269 lines
9.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Complete Let's Encrypt setup using Cloudflare Tunnel
|
|
# Falls back to public IP, then DNS-01 if needed
|
|
|
|
set -e
|
|
|
|
VMID=2500
|
|
DOMAIN="rpc-core.d-bis.org"
|
|
NAME="rpc-core"
|
|
IP="192.168.11.250"
|
|
PUBLIC_IP="45.49.67.248"
|
|
PROXMOX_HOST="192.168.11.10"
|
|
TUNNEL_VMID=102
|
|
|
|
# Colors
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m'
|
|
|
|
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
|
log_success() { echo -e "${GREEN}[✓]${NC} $1"; }
|
|
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
|
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
|
|
|
log_info "Let's Encrypt Setup - Cloudflare Tunnel Method"
|
|
log_info "Domain: $DOMAIN"
|
|
echo ""
|
|
|
|
# Load .env
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
if [ -f "$SCRIPT_DIR/../.env" ]; then
|
|
source "$SCRIPT_DIR/../.env" 2>/dev/null
|
|
fi
|
|
|
|
# Step 1: Configure Cloudflare Tunnel
|
|
log_info "Step 1: Configuring Cloudflare Tunnel route..."
|
|
|
|
# Get tunnel ID from token
|
|
if [ -n "$CLOUDFLARE_TUNNEL_TOKEN" ]; then
|
|
TUNNEL_ID=$(echo "$CLOUDFLARE_TUNNEL_TOKEN" | base64 -d 2>/dev/null | python3 -c "import sys, json; data=json.load(sys.stdin); print(data.get('a', ''))" 2>/dev/null || echo "")
|
|
if [ -z "$TUNNEL_ID" ]; then
|
|
# Fallback: use account ID as tunnel ID (they're often the same)
|
|
TUNNEL_ID="${CLOUDFLARE_ACCOUNT_ID:-52ad57a71671c5fc009edf0744658196}"
|
|
fi
|
|
log_info "Tunnel ID: $TUNNEL_ID"
|
|
else
|
|
TUNNEL_ID="${CLOUDFLARE_ACCOUNT_ID:-52ad57a71671c5fc009edf0744658196}"
|
|
log_warn "Using account ID as tunnel ID: $TUNNEL_ID"
|
|
fi
|
|
|
|
# Check if tunnel is running
|
|
if sshpass -p 'L@kers2010' ssh -o StrictHostKeyChecking=no root@${PROXMOX_HOST} \
|
|
"pct exec $TUNNEL_VMID -- systemctl is-active cloudflared >/dev/null 2>&1"; then
|
|
log_success "Cloudflare Tunnel is running"
|
|
else
|
|
log_warn "Cloudflare Tunnel may not be running on VMID $TUNNEL_VMID"
|
|
fi
|
|
|
|
# Configure tunnel route via API
|
|
if [ -n "$CLOUDFLARE_API_KEY" ] && [ -n "$CLOUDFLARE_EMAIL" ] && [ -n "$CLOUDFLARE_ACCOUNT_ID" ] && [ -n "$TUNNEL_ID" ]; then
|
|
log_info "Configuring tunnel route via API..."
|
|
|
|
# Get current tunnel config
|
|
TUNNEL_CONFIG=$(curl -s -X GET "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/cfd_tunnel/$TUNNEL_ID/configurations" \
|
|
-H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
|
|
-H "X-Auth-Key: $CLOUDFLARE_API_KEY" \
|
|
-H "Content-Type: application/json")
|
|
|
|
# Check if route already exists in config
|
|
if echo "$TUNNEL_CONFIG" | grep -q "$DOMAIN"; then
|
|
log_info "Tunnel route already exists for $DOMAIN"
|
|
else
|
|
log_info "Adding tunnel route: $DOMAIN → http://$IP:443"
|
|
log_warn "Tunnel route configuration via API is complex - using manual method"
|
|
log_info "Please configure in Cloudflare Dashboard:"
|
|
log_info " Zero Trust → Networks → Tunnels → Your Tunnel → Configure"
|
|
log_info " Add Public Hostname: rpc-core.d-bis.org → http://$IP:443"
|
|
log_info ""
|
|
log_info "Continuing with DNS setup (tunnel route can be added manually)..."
|
|
fi
|
|
else
|
|
log_warn "Missing credentials for tunnel API configuration"
|
|
fi
|
|
|
|
# Step 2: Update DNS to use Tunnel (CNAME)
|
|
log_info ""
|
|
log_info "Step 2: Updating DNS to use Cloudflare Tunnel..."
|
|
|
|
# Delete existing A record if exists
|
|
if [ -f "$SCRIPT_DIR/create-dns-record-rpc-core.sh" ]; then
|
|
log_info "Checking existing DNS record..."
|
|
EXISTING_RECORD=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_ZONE_ID/dns_records?name=$DOMAIN" \
|
|
-H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
|
|
-H "X-Auth-Key: $CLOUDFLARE_API_KEY" \
|
|
-H "Content-Type: application/json")
|
|
|
|
if echo "$EXISTING_RECORD" | grep -q '"id"'; then
|
|
RECORD_ID=$(echo "$EXISTING_RECORD" | grep -o '"id":"[^"]*' | head -1 | cut -d'"' -f4)
|
|
log_info "Deleting existing A record..."
|
|
curl -s -X DELETE "https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_ZONE_ID/dns_records/$RECORD_ID" \
|
|
-H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
|
|
-H "X-Auth-Key: $CLOUDFLARE_API_KEY" \
|
|
-H "Content-Type: application/json" >/dev/null
|
|
fi
|
|
fi
|
|
|
|
# Create CNAME to tunnel
|
|
if [ -z "$TUNNEL_ID" ]; then
|
|
log_error "Tunnel ID not found. Cannot create CNAME."
|
|
exit 1
|
|
fi
|
|
TUNNEL_TARGET="${TUNNEL_ID}.cfargotunnel.com"
|
|
log_info "Creating CNAME: $NAME → $TUNNEL_TARGET"
|
|
|
|
CNAME_RESPONSE=$(curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_ZONE_ID/dns_records" \
|
|
-H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
|
|
-H "X-Auth-Key: $CLOUDFLARE_API_KEY" \
|
|
-H "Content-Type: application/json" \
|
|
--data "{
|
|
\"type\": \"CNAME\",
|
|
\"name\": \"$NAME\",
|
|
\"content\": \"$TUNNEL_TARGET\",
|
|
\"ttl\": 1,
|
|
\"proxied\": true
|
|
}")
|
|
|
|
if echo "$CNAME_RESPONSE" | grep -q '"success":true'; then
|
|
log_success "CNAME record created (proxied)"
|
|
else
|
|
log_error "Failed to create CNAME record"
|
|
log_info "Response: $CNAME_RESPONSE"
|
|
exit 1
|
|
fi
|
|
|
|
# Step 3: Wait for tunnel route to be active
|
|
log_info ""
|
|
log_info "Step 3: Waiting for tunnel route to be active (10 seconds)..."
|
|
sleep 10
|
|
|
|
# Step 4: Wait for DNS propagation
|
|
log_info ""
|
|
log_info "Step 4: Waiting for DNS propagation (30 seconds)..."
|
|
sleep 30
|
|
|
|
# Step 5: Try Let's Encrypt HTTP-01 through tunnel
|
|
log_info ""
|
|
log_info "Step 5: Attempting Let's Encrypt HTTP-01 challenge through tunnel..."
|
|
CERTBOT_OUTPUT=$(sshpass -p 'L@kers2010' ssh -o StrictHostKeyChecking=no root@${PROXMOX_HOST} \
|
|
"pct exec $VMID -- certbot --nginx \
|
|
--non-interactive \
|
|
--agree-tos \
|
|
--email admin@d-bis.org \
|
|
-d $DOMAIN \
|
|
--redirect 2>&1" || echo "FAILED")
|
|
|
|
if echo "$CERTBOT_OUTPUT" | grep -q "Successfully received certificate\|Congratulations"; then
|
|
log_success "Certificate obtained via HTTP-01 through tunnel!"
|
|
exit 0
|
|
else
|
|
log_warn "HTTP-01 through tunnel failed"
|
|
log_info "Trying fallback: Public IP method..."
|
|
fi
|
|
|
|
# Fallback: Try with public IP
|
|
log_info ""
|
|
log_info "Fallback: Trying with public IP $PUBLIC_IP..."
|
|
|
|
# Update DNS to point to public IP
|
|
log_info "Updating DNS to point to public IP..."
|
|
PUBLIC_IP_RESPONSE=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_ZONE_ID/dns_records/$RECORD_ID" \
|
|
-H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
|
|
-H "X-Auth-Key: $CLOUDFLARE_API_KEY" \
|
|
-H "Content-Type: application/json" \
|
|
--data "{
|
|
\"type\": \"A\",
|
|
\"name\": \"$NAME\",
|
|
\"content\": \"$PUBLIC_IP\",
|
|
\"ttl\": 1,
|
|
\"proxied\": false
|
|
}" 2>/dev/null || curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_ZONE_ID/dns_records" \
|
|
-H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
|
|
-H "X-Auth-Key: $CLOUDFLARE_API_KEY" \
|
|
-H "Content-Type: application/json" \
|
|
--data "{
|
|
\"type\": \"A\",
|
|
\"name\": \"$NAME\",
|
|
\"content\": \"$PUBLIC_IP\",
|
|
\"ttl\": 1,
|
|
\"proxied\": false
|
|
}")
|
|
|
|
if echo "$PUBLIC_IP_RESPONSE" | grep -q '"success":true'; then
|
|
log_success "DNS updated to public IP"
|
|
sleep 30
|
|
|
|
CERTBOT_OUTPUT=$(sshpass -p 'L@kers2010' ssh -o StrictHostKeyChecking=no root@${PROXMOX_HOST} \
|
|
"pct exec $VMID -- certbot --nginx \
|
|
--non-interactive \
|
|
--agree-tos \
|
|
--email admin@d-bis.org \
|
|
-d $DOMAIN \
|
|
--redirect 2>&1" || echo "FAILED")
|
|
|
|
if echo "$CERTBOT_OUTPUT" | grep -q "Successfully received certificate\|Congratulations"; then
|
|
log_success "Certificate obtained via HTTP-01 with public IP!"
|
|
exit 0
|
|
else
|
|
log_warn "HTTP-01 with public IP failed"
|
|
fi
|
|
else
|
|
log_warn "Failed to update DNS to public IP"
|
|
fi
|
|
|
|
# Final fallback: DNS-01 challenge
|
|
log_info ""
|
|
log_info "Final fallback: Using DNS-01 challenge..."
|
|
|
|
# Install DNS plugin
|
|
sshpass -p 'L@kers2010' ssh -o StrictHostKeyChecking=no root@${PROXMOX_HOST} \
|
|
"pct exec $VMID -- apt-get install -y -qq python3-certbot-dns-cloudflare" || {
|
|
log_error "Failed to install certbot-dns-cloudflare"
|
|
exit 1
|
|
}
|
|
|
|
# Create credentials file
|
|
sshpass -p 'L@kers2010' ssh -o StrictHostKeyChecking=no root@${PROXMOX_HOST} \
|
|
"pct exec $VMID -- bash -c '
|
|
mkdir -p /etc/cloudflare
|
|
cat > /etc/cloudflare/credentials.ini <<EOF
|
|
dns_cloudflare_api_token = ${CLOUDFLARE_API_TOKEN:-}
|
|
dns_cloudflare_email = $CLOUDFLARE_EMAIL
|
|
dns_cloudflare_api_key = $CLOUDFLARE_API_KEY
|
|
EOF
|
|
chmod 600 /etc/cloudflare/credentials.ini
|
|
'"
|
|
|
|
# Try DNS-01
|
|
log_info "Obtaining certificate via DNS-01 challenge..."
|
|
CERTBOT_OUTPUT=$(sshpass -p 'L@kers2010' ssh -o StrictHostKeyChecking=no root@${PROXMOX_HOST} \
|
|
"pct exec $VMID -- certbot certonly --dns-cloudflare \
|
|
--dns-cloudflare-credentials /etc/cloudflare/credentials.ini \
|
|
--non-interactive \
|
|
--agree-tos \
|
|
--email admin@d-bis.org \
|
|
-d $DOMAIN 2>&1" || echo "FAILED")
|
|
|
|
if echo "$CERTBOT_OUTPUT" | grep -q "Successfully received certificate\|Congratulations"; then
|
|
log_success "Certificate obtained via DNS-01 challenge!"
|
|
|
|
# Update Nginx manually
|
|
log_info "Updating Nginx configuration..."
|
|
sshpass -p 'L@kers2010' ssh -o StrictHostKeyChecking=no root@${PROXMOX_HOST} \
|
|
"pct exec $VMID -- bash -c '
|
|
sed -i \"s|ssl_certificate /etc/nginx/ssl/rpc.crt;|ssl_certificate /etc/letsencrypt/live/$DOMAIN/fullchain.pem;|\" /etc/nginx/sites-available/rpc-core
|
|
sed -i \"s|ssl_certificate_key /etc/nginx/ssl/rpc.key;|ssl_certificate_key /etc/letsencrypt/live/$DOMAIN/privkey.pem;|\" /etc/nginx/sites-available/rpc-core
|
|
nginx -t && systemctl reload nginx
|
|
'"
|
|
|
|
log_success "Nginx updated with Let's Encrypt certificate!"
|
|
exit 0
|
|
else
|
|
log_error "All methods failed"
|
|
log_info "Output: $CERTBOT_OUTPUT"
|
|
exit 1
|
|
fi
|
|
|