# Proxmox ACME Setup - Complete Command Reference This document provides all commands needed for the Proxmox ACME certificate setup process. ## Prerequisites ### Verify Network Connectivity ```bash # Test Cloudflare API connectivity from Proxmox nodes curl -I https://api.cloudflare.com # Test DNS resolution for nodes dig ml110-01.sankofa.nexus dig r630-01.sankofa.nexus # Test HTTPS connectivity to Proxmox nodes curl -k -I https://ml110-01.sankofa.nexus:8006 curl -k -I https://r630-01.sankofa.nexus:8006 ``` ## Step 1: Create Cloudflare API Token **Note**: This step must be done via Cloudflare Web UI (no CLI commands). 1. Go to: https://dash.cloudflare.com/profile/api-tokens 2. Click "Create Token" 3. Use "Edit zone DNS" template 4. Select zone: `sankofa.nexus` 5. Copy token and save securely ### Verify Token (Optional - Test from Local Machine) ```bash # Replace YOUR_TOKEN with actual token TOKEN="YOUR_CLOUDFLARE_API_TOKEN" ZONE_ID="YOUR_ZONE_ID" # Get from Cloudflare dashboard # Test token can read DNS records curl -X GET "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" | jq '.result[] | select(.name | contains("sankofa.nexus"))' # Test token can create DNS records (creates test record - delete after) curl -X POST "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{"type":"TXT","name":"_acme-test.sankofa.nexus","content":"test-validation","ttl":120}' | jq # Delete test record RECORD_ID="RECORD_ID_FROM_ABOVE" curl -X DELETE "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records/${RECORD_ID}" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" ``` ## Step 2: Configure ACME via Proxmox CLI (Alternative to Web UI) ### SSH to ml110-01 (Admin Node) ```bash # SSH to admin node ssh root@ml110-01.sankofa.nexus # or ssh root@192.168.11.10 ``` ### Register ACME Account via CLI ```bash # Register Let's Encrypt account pvesh create /cluster/acme/account \ --name letsencrypt-prod \ --directory https://acme-v02.api.letsencrypt.org/directory \ --contact email@example.com \ --account # Verify account registration pvesh get /cluster/acme/account/letsencrypt-prod ``` ### Add Cloudflare DNS Plugin via CLI ```bash # Replace YOUR_TOKEN with actual Cloudflare API token CLOUDFLARE_TOKEN="YOUR_CLOUDFLARE_API_TOKEN" # Add Cloudflare DNS plugin pvesh create /cluster/acme/plugins \ --id cloudflare-dns \ --type cloudflare \ --data "api={${CLOUDFLARE_TOKEN}} domain=sankofa.nexus" # Verify plugin configuration pvesh get /cluster/acme/plugins/cloudflare-dns ``` ### Order Certificates via CLI ```bash # Order wildcard certificate pvesh create /cluster/acme/certificate \ --name wildcard-sankofa-nexus \ --acme letsencrypt-prod \ --plugin cloudflare-dns \ --domain "*.sankofa.nexus" # Order single-host certificate for ml110-01 pvesh create /cluster/acme/certificate \ --name ml110-01-sankofa-nexus \ --acme letsencrypt-prod \ --plugin cloudflare-dns \ --domain "ml110-01.sankofa.nexus" # Order single-host certificate for r630-01 pvesh create /cluster/acme/certificate \ --name r630-01-sankofa-nexus \ --acme letsencrypt-prod \ --plugin cloudflare-dns \ --domain "r630-01.sankofa.nexus" # Check certificate order status pvesh get /cluster/acme/certificate/wildcard-sankofa-nexus pvesh get /cluster/acme/certificate/ml110-01-sankofa-nexus pvesh get /cluster/acme/certificate/r630-01-sankofa-nexus # List all ACME certificates pvesh get /cluster/acme/certificate ``` ### Apply Certificate to Nodes via CLI ```bash # Apply wildcard certificate to ml110-01 node pvesh create /nodes/ml110-01/certificates/custom \ --filename wildcard-sankofa-nexus.pem \ --certificates wildcard-sankofa-nexus # Apply wildcard certificate to r630-01 node pvesh create /nodes/r630-01/certificates/custom \ --filename wildcard-sankofa-nexus.pem \ --certificates wildcard-sankofa-nexus # Verify certificate assignment pvesh get /nodes/ml110-01/certificates/custom pvesh get /nodes/r630-01/certificates/custom ``` **Note**: Certificate application to nodes is typically done automatically when certificates are ordered. The above commands may need adjustment based on Proxmox version. ### Alternative: Apply via Web UI Certificate Manager If CLI certificate application doesn't work: 1. Go to: `https://ml110-01.sankofa.nexus:8006` 2. Navigate: **Datacenter** → **Certificates** 3. Select certificate: `wildcard-sankofa-nexus` 4. Click **"Edit"** 5. Check boxes for: `ml110-01` and `r630-01` 6. Click **"OK"** ## Step 3: Verify Certificate Installation ### Check Certificate Status on Nodes ```bash # On ml110-01 ssh root@ml110-01.sankofa.nexus # Check ACME certificates pvesh get /cluster/acme/certificate # Check node certificates pvesh get /nodes/ml110-01/certificates/custom # View certificate file cat /etc/pve/nodes/ml110-01/pveproxy-ssl.pem | openssl x509 -noout -text # Check certificate expiration cat /etc/pve/nodes/ml110-01/pveproxy-ssl.pem | openssl x509 -noout -dates # Verify issuer (should show Let's Encrypt) cat /etc/pve/nodes/ml110-01/pveproxy-ssl.pem | openssl x509 -noout -issuer ``` ### Test SSL Connection from Local Machine ```bash # Test ml110-01 certificate echo | openssl s_client -connect ml110-01.sankofa.nexus:8006 -servername ml110-01.sankofa.nexus 2>/dev/null | \ openssl x509 -noout -dates -subject -issuer # Test r630-01 certificate echo | openssl s_client -connect r630-01.sankofa.nexus:8006 -servername r630-01.sankofa.nexus 2>/dev/null | \ openssl x509 -noout -dates -subject -issuer # Check certificate chain echo | openssl s_client -connect ml110-01.sankofa.nexus:8006 -servername ml110-01.sankofa.nexus -showcerts 2>/dev/null | \ openssl x509 -noout -text | grep -A 5 "Certificate chain" ``` ### Run Verification Script ```bash # From project root cd /home/intlc/projects/Sankofa # Run verification script ./scripts/verify-proxmox-certs.sh # Or with explicit path bash scripts/verify-proxmox-certs.sh ``` ### Check Proxmox Logs for ACME ```bash # On ml110-01 ssh root@ml110-01.sankofa.nexus # View ACME-related logs journalctl -u pvedaemon -f | grep -i acme # Check recent ACME activity journalctl -u pvedaemon --since "1 hour ago" | grep -i acme # View ACME certificate order logs journalctl -u pvedaemon | grep -i "wildcard-sankofa-nexus" ``` ## Step 4: Deploy Updated Tunnel Configurations ### Copy Updated Configs to Proxmox Nodes ```bash # From project root cd /home/intlc/projects/Sankofa # Copy config to ml110-01 (Site 1) scp cloudflare/tunnel-configs/proxmox-site-1.yaml root@ml110-01.sankofa.nexus:/etc/cloudflared/tunnel-configs/proxmox-site-1.yaml # Copy config to r630-01 (Site 2) scp cloudflare/tunnel-configs/proxmox-site-2.yaml root@r630-01.sankofa.nexus:/etc/cloudflared/tunnel-configs/proxmox-site-2.yaml # If Site 3 exists, copy config # scp cloudflare/tunnel-configs/proxmox-site-3.yaml root@r630-01.sankofa.nexus:/etc/cloudflared/tunnel-configs/proxmox-site-3.yaml ``` ### Verify Config Files on Nodes ```bash # On ml110-01 ssh root@ml110-01.sankofa.nexus # Verify config file exists and is correct cat /etc/cloudflared/tunnel-configs/proxmox-site-1.yaml # Verify skipVerify is removed (should NOT appear in file) grep -i "skipverify" /etc/cloudflared/tunnel-configs/proxmox-site-1.yaml # Should return no results # Check YAML syntax cloudflared tunnel ingress validate --config /etc/cloudflared/tunnel-configs/proxmox-site-1.yaml ``` ### Restart Cloudflare Tunnel Services ```bash # On ml110-01 ssh root@ml110-01.sankofa.nexus # Check tunnel service status systemctl status cloudflared-tunnel # Restart tunnel service systemctl restart cloudflared-tunnel # Verify service started successfully systemctl status cloudflared-tunnel # View tunnel logs journalctl -u cloudflared-tunnel -f # Check for errors (especially certificate verification) journalctl -u cloudflared-tunnel --since "5 minutes ago" | grep -i error ``` ```bash # On r630-01 ssh root@r630-01.sankofa.nexus # Same commands as above systemctl status cloudflared-tunnel systemctl restart cloudflared-tunnel systemctl status cloudflared-tunnel journalctl -u cloudflared-tunnel -f ``` ### Verify Tunnel Connectivity ```bash # Test tunnel connectivity via Cloudflare # From local machine or any device with internet # Test ml110-01 tunnel curl -I https://ml110-01.sankofa.nexus # Test ml110-01 API tunnel curl -I https://ml110-01-api.sankofa.nexus # Test r630-01 tunnel curl -I https://r630-01.sankofa.nexus # Test r630-01 API tunnel curl -I https://r630-01-api.sankofa.nexus # Verify certificate is valid (should show Let's Encrypt) echo | openssl s_client -connect ml110-01.sankofa.nexus:443 -servername ml110-01.sankofa.nexus 2>/dev/null | \ openssl x509 -noout -issuer ``` ## Step 5: Monitor Certificate Renewal ### Check Certificate Expiration Dates ```bash # Run verification script to check expiration ./scripts/verify-proxmox-certs.sh # Or check manually for node in ml110-01.sankofa.nexus r630-01.sankofa.nexus; do echo "Checking ${node}..." echo | openssl s_client -connect ${node}:8006 -servername ${node} 2>/dev/null | \ openssl x509 -noout -dates done ``` ### Monitor ACME Renewal Process ```bash # On ml110-01 ssh root@ml110-01.sankofa.nexus # Watch ACME logs in real-time journalctl -u pvedaemon -f | grep -i acme # Check renewal schedule pvesh get /cluster/acme/certificate/wildcard-sankofa-nexus | jq '.data.renewal' # Manually trigger renewal (if needed) pvesh post /cluster/acme/certificate/wildcard-sankofa-nexus/renew ``` ### Set Up Certificate Expiration Monitoring ```bash # Create monitoring script cat > /usr/local/bin/check-proxmox-certs.sh <<'EOF' #!/bin/bash # Check certificate expiration and alert if expiring soon NODES=("ml110-01.sankofa.nexus:8006" "r630-01.sankofa.nexus:8006") WARN_DAYS=30 for node_port in "${NODES[@]}"; do node=$(echo $node_port | cut -d: -f1) port=$(echo $node_port | cut -d: -f2) expiry=$(echo | openssl s_client -connect ${node}:${port} -servername ${node} 2>/dev/null | \ openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2) if [ -n "$expiry" ]; then expiry_epoch=$(date -d "$expiry" +%s 2>/dev/null || date -j -f "%b %d %H:%M:%S %Y %Z" "$expiry" +%s 2>/dev/null) now_epoch=$(date +%s) days=$(( (expiry_epoch - now_epoch) / 86400 )) if [ $days -lt $WARN_DAYS ]; then echo "WARNING: Certificate for ${node} expires in ${days} days (${expiry})" else echo "OK: Certificate for ${node} expires in ${days} days" fi fi done EOF chmod +x /usr/local/bin/check-proxmox-certs.sh # Add to cron for daily checks (crontab -l 2>/dev/null; echo "0 9 * * * /usr/local/bin/check-proxmox-certs.sh | mail -s 'Proxmox Certificate Status' admin@example.com") | crontab - ``` ## Step 6: Troubleshooting Commands ### If Certificate Order Fails ```bash # Check ACME logs journalctl -u pvedaemon | grep -i acme | tail -50 # Verify DNS plugin configuration pvesh get /cluster/acme/plugins/cloudflare-dns # Test Cloudflare API connectivity curl -I https://api.cloudflare.com # Check DNS TXT record creation dig _acme-challenge.sankofa.nexus TXT +short # Verify token still works curl -X GET "https://api.cloudflare.com/client/v4/user/tokens/verify" \ -H "Authorization: Bearer YOUR_TOKEN" ``` ### If Certificate Not Applied to Node ```bash # Check certificate exists pvesh get /cluster/acme/certificate/wildcard-sankofa-nexus # Check node certificate directory ls -la /etc/pve/nodes/ml110-01/ # Restart pveproxy to reload certificates systemctl restart pveproxy # Check pveproxy logs journalctl -u pveproxy -f ``` ### If Tunnel Fails After Removing skipVerify ```bash # Check tunnel logs for certificate errors journalctl -u cloudflared-tunnel --since "10 minutes ago" | grep -i "certificate\|tls\|ssl" # Temporarily re-enable skipVerify for debugging (NOT recommended) # Edit config file and add back: # tls: # skipVerify: true # Then restart tunnel # Verify certificate is accessible from tunnel service curl -k https://localhost:8006 ``` ### Reset ACME Configuration (If Needed) ```bash # Delete certificate pvesh delete /cluster/acme/certificate/wildcard-sankofa-nexus # Delete DNS plugin pvesh delete /cluster/acme/plugins/cloudflare-dns # Delete ACME account pvesh delete /cluster/acme/account/letsencrypt-prod # Start fresh and reconfigure ``` ## Quick Reference: All-in-One Setup Commands ### Complete Setup on ml110-01 (via SSH) ```bash # SSH to node ssh root@ml110-01.sankofa.nexus # Set variables CLOUDFLARE_TOKEN="YOUR_TOKEN_HERE" EMAIL="admin@example.com" # Register ACME account pvesh create /cluster/acme/account \ --name letsencrypt-prod \ --directory https://acme-v02.api.letsencrypt.org/directory \ --contact ${EMAIL} # Add DNS plugin pvesh create /cluster/acme/plugins \ --id cloudflare-dns \ --type cloudflare \ --data "api={${CLOUDFLARE_TOKEN}} domain=sankofa.nexus" # Order wildcard certificate pvesh create /cluster/acme/certificate \ --name wildcard-sankofa-nexus \ --acme letsencrypt-prod \ --plugin cloudflare-dns \ --domain "*.sankofa.nexus" # Order single-host certificates pvesh create /cluster/acme/certificate \ --name ml110-01-sankofa-nexus \ --acme letsencrypt-prod \ --plugin cloudflare-dns \ --domain "ml110-01.sankofa.nexus" pvesh create /cluster/acme/certificate \ --name r630-01-sankofa-nexus \ --acme letsencrypt-prod \ --plugin cloudflare-dns \ --domain "r630-01.sankofa.nexus" # Verify certificates pvesh get /cluster/acme/certificate # Check certificate details pvesh get /cluster/acme/certificate/wildcard-sankofa-nexus | jq ``` ### Deploy Tunnel Configs (from local machine) ```bash # From project root cd /home/intlc/projects/Sankofa # Copy configs scp cloudflare/tunnel-configs/proxmox-site-1.yaml root@ml110-01.sankofa.nexus:/etc/cloudflared/tunnel-configs/ scp cloudflare/tunnel-configs/proxmox-site-2.yaml root@r630-01.sankofa.nexus:/etc/cloudflared/tunnel-configs/ # Restart services ssh root@ml110-01.sankofa.nexus "systemctl restart cloudflared-tunnel && systemctl status cloudflared-tunnel" ssh root@r630-01.sankofa.nexus "systemctl restart cloudflared-tunnel && systemctl status cloudflared-tunnel" ``` ### Full Verification (from local machine) ```bash # Run verification script ./scripts/verify-proxmox-certs.sh # Manual verification for node in ml110-01.sankofa.nexus r630-01.sankofa.nexus; do echo "=== ${node} ===" echo | openssl s_client -connect ${node}:8006 -servername ${node} 2>/dev/null | \ openssl x509 -noout -dates -subject -issuer | head -5 echo done ``` ## Related Documentation - [ACME Setup Guide](./acme-setup.md) - Complete step-by-step guide - [TLS Configuration](./TLS_CONFIGURATION.md) - General TLS configuration - [Cloudflare Tunnel Configs](../cloudflare/tunnel-configs/) - Tunnel configuration files