Legendary Arena Lab

Cloudflare TLS

wiki

Cloudflare TLS

Migration in progress. api.legendary-arena.com and its PostgreSQL are moving off Render onto a self-hosted DigitalOcean Ubuntu droplet fronted by Cloudflare, per docs/PLAN.md . Until decommission, Render stays warm as the rollback target.

Summary

This page terminates TLS on the origin with a Cloudflare Origin Certificate and narrows the firewall so only Cloudflare can reach port 443. It rewrites the Nginx site the reverse-proxy step created, redirecting HTTP to HTTPS and preserving the WebSocket upgrade path for Socket.IO.

Mechanics

The owning artifact is infra/scripts/21-cloudflare-tls.ps1 — root-required, Set-StrictMode/stop. It runs in order:

  1. Install cert material. Writes the Origin Certificate and key from CF_ORIGIN_CERT / CF_ORIGIN_KEY (or expects them pre-installed) with strict permissions:

    /etc/ssl/cloudflare/origin.crt   # 644
    /etc/ssl/cloudflare/origin.key   # 600
    
  2. Rewrite Nginx with TLS. Overwrites /etc/nginx/sites-available/la-api.conf: port 80 becomes a 301 redirect to HTTPS; port 443 is ssl http2 with ssl_certificate / ssl_certificate_key, ssl_protocols TLSv1.2 TLSv1.3, the same WebSocket-upgrade proxy block to 127.0.0.1:$UpstreamPort, and location = /health:

    DOMAIN        = api.legendary-arena.com
    UPSTREAM_PORT = 3000
    ssl_protocols TLSv1.2 TLSv1.3
    
  3. Validate + reload. Runs nginx -t and reloads.

  4. Restrict UFW to Cloudflare. If ufw is active, fetches Cloudflare’s IPv4 and IPv6 ranges live and adds one allow rule per CIDR, then (default REMOVE_PUBLIC_443=true) deletes the broad public 443 rule:

    REMOVE_PUBLIC_443 = true
    ufw allow proto tcp from <cidr> to any port 443   # per Cloudflare CIDR
    # then: delete allow 443/tcp
    

A final log line reminds the operator to set the Cloudflare DNS record to proxied (orange-cloud) and SSL mode to Full (strict) in the dashboard. Per docs/PLAN.md , the target posture is Cloudflare-proxied api. with an Origin Certificate under SSL mode Full (strict) and UFW 443 restricted to Cloudflare ranges; Socket.IO WebSockets pass the proxy.

Interactions

  • Nginx Reverse Proxy — this step rewrites the la-api.conf it created, upgrading the port-80 site to TLS on 443.
  • UFW Firewall — this narrows the 443 rule to Cloudflare CIDRs, so ufw must already be active (the 06 step) for the restriction to apply.
  • Render-to-DigitalOcean Migration — the proxied edge posture is part of Phase 2 of the migration plan.

Edge Cases

  • Origin Certificate requires the proxy. An Origin Cert is only valid while the record is proxied — grey-clouding for debugging breaks TLS and Full (strict) validation fails at the edge.
  • Key permissions. origin.key must stay 600; a world-readable private key is a credential leak.
  • UFW step is conditional. The firewall narrowing only runs if ufw is already active — run the UFW step first or 443 stays wide open.
  • Cloudflare ranges drift. The CIDR list is fetched live and changes over time; re-run to refresh, or new Cloudflare edge IPs will be blocked. The DNS-proxy and Full (strict) toggles are dashboard actions the script cannot perform. WebSockets pass the Cloudflare proxy and are exempt from the 100s HTTP timeout.

Execute

# Supply cert material via env (documented default REMOVE_PUBLIC_443=true)
sudo CF_ORIGIN_CERT="$(cat origin.pem)" CF_ORIGIN_KEY="$(cat origin.key)" \
  pwsh -File infra/scripts/21-cloudflare-tls.ps1

# Alternative: pre-install /etc/ssl/cloudflare/origin.crt + origin.key, then:
sudo pwsh -File infra/scripts/21-cloudflare-tls.ps1

Verify

sudo nginx -t
# Expected: syntax is ok / test is successful

sudo ufw status | grep 443
# Expected: ALLOW rules from Cloudflare CIDRs on 443; no broad "443/tcp ALLOW Anywhere"

curl -I https://api.legendary-arena.com/health
# Expected: HTTP/2 200

References