Files
hermes-brain/methodology/CLOUDFLARE-SETUP.md
2026-05-23 06:18:37 +00:00

8.0 KiB

type, title, created, tags
type title created tags
methodology Cloudflare Infrastructure Setup 2026-05-11
methodology
cloudflare
infrastructure
networking

Cloudflare Infrastructure Setup

Architecture Overview

Browser ──► Cloudflare (orange cloud) ──► Tunnel ──► cloudflared ──► Traefik:8081
                                                                     │
                                                          (tunnel entrypoint)
                                                                     │
                                                          ┌──────────┴──────────┐
                                                     secureweb              tunnel
                                                  (direct internet)     (via tunnel)
                                                       :443                  :8081
                                                    LetsEncrypt          plain HTTP

All external traffic goes through Cloudflare Tunnel. Traefik serves as the internal reverse proxy, routing by Host header. Traefik's LetsEncrypt certs exist for LAN/direct access but aren't used externally (Cloudflare edge terminates TLS for visitors).

Active Stack

Component Location Notes
Tunnel name home Created at Cloudflare Zero Trust → Networks → Tunnels
Tunnel ID c29295c5-946a-4ddf-bdfe-7eafcd74faa3 Visible in dashboard
Token .env TUNNEL_TOKEN=... Used by cloudflared to authenticate
cloudflared production-1 Docker container Runs with --restart unless-stopped
Traefik production-1 Docker container Ports 80, 443, 8080, 8082
Traefik entrypoints web (:80→redirect), secureweb (:443, TLS), tunnel (:8081), metrics (:8082)
Traefik config /docker/appdata/traefik/traefik.yaml
Compose file /docker/compose/docker-compose.yaml cloudflared service defined but runs standalone due to interpolation issues
Traefik labels Per-service in docker-compose.yaml Pattern: entrypoints=tunnel, Host(subdomain.gharbeia.net)

1. Adding a Second Domain

Example: adding example.com alongside gharbeia.net.

Step 1: Cloudflare DNS

  1. Go to Cloudflare Dashboard → Add site → enter example.com
  2. Cloudflare scans existing DNS records — verify and continue
  3. Change nameservers at your registrar to Cloudflare's
  4. Wait for DNS to propagate

Step 2: Cloudflare Tunnel — add hostname

  1. Zero TrustNetworksConnectorsCloudflare Tunnels
  2. Select tunnel homeEdit
  3. Click Add a public hostname
  4. Set:
    • Subdomain: *
    • Domain: example.com
    • Service Type: HTTP
    • URL: traefik:8081
  5. Save

Cloudflare DNS will automatically create a wildcard CNAME for *.example.com pointing to c29295c5-946a-4ddf-bdfe-7eafcd74faa3.cfargotunnel.com.

Step 3: Traefik — update cert resolver

Cloudflare wildcard certs only cover *.gharbeia.net. For *.example.com, either:

Option A: Add to Traefik's ACME config

In /docker/appdata/traefik/traefik.yaml, in certificatesResolvers.letsencrypt.acme:

dnsChallenge:
  provider: cloudflare
  resolvers:
    - 1.1.1.1:53
    - 1.0.0.1:53
  propagation:
    delayBeforeChecks: 120s  # increase for wildcards

Then in entryPoints.secureweb.http.tls.domains:

domains:
  - main: gharbeia.net
    sans:
      - "*.gharbeia.net"
  - main: example.com
    sans:
      - "*.example.com"

Important: Before ACME DNS challenge can succeed for wildcard domains proxied via Cloudflare, you MUST create a DNS-only placeholder record for _acme-challenge to break the wildcard:

Go to Cloudflare DNS → Add record:

  • Type: A
  • Name: _acme-challenge
  • Content: 127.0.0.1
  • Proxy: DNS-only (gray cloud)

Without this, the proxied wildcard CNAME intercepts _acme-challenge.* queries and LetsEncrypt can't verify the TXT challenge records.

Option B: Skip HTTPS cert for now

If the second domain doesn't need Traefik's own cert (i.e., Cloudflare edge certs are sufficient), no Traefik changes needed. The tunnel handles everything.

Step 4: Add Traefik labels

For each service on the new domain, add Traefik labels in docker-compose.yaml:

labels:
  - traefik.enable=true
  - traefik.http.routers.servicename.rule=Host(`subdomain.example.com`)
  - traefik.http.routers.servicename.entrypoints=tunnel
  - traefik.http.services.servicename.loadbalancer.server.port=3000

Then restart the container to pick up labels (compose or docker run depending on env state).


2. Security Panic — Changing All Tokens and Settings

Step 1: Cloudflare API Token

  1. Go to Cloudflare Dashboard → My ProfileAPI Tokens
  2. Delete the old token
  3. Create TokenEdit zone DNS template:
    • Permissions: Zone → DNS → Edit
    • Zone Resources: Include → Specific zone → gharbeia.net
    • TTL: No expiration (or set a reasonable one)
  4. Copy the new token

Step 2: Update .env on production-1

ssh production-1
# Edit the token
sed -i "s|^CLOUDFLARE_DNS_API_TOKEN=.*|CLOUDFLARE_DNS_API_TOKEN=<new-token>|" /docker/compose/.env

Step 3: Restart Traefik

Traefik runs standalone (not under compose). Restart it with the new token:

docker rm -f traefik
docker run -d --name traefik --restart unless-stopped --network networking \
  -p 80:80 -p 443:443 -p 8080:8080 -p 8082:8082 \
  -e CF_DNS_API_TOKEN="<new-token>" \
  -e TZ="America/New_York" \
  -v /var/run/docker.sock:/var/run/docker.sock:ro \
  -v /docker/appdata/logs/traefik:/var/log \
  -v /docker/appdata/traefik:/etc/traefik \
  -v /docker/appdata/traefik/letsencrypt:/letsencrypt \
  traefik:latest

Traefik will automatically renew its LetsEncrypt cert with the new token.

Step 4: Cloudflare Tunnel Token

If you also rotate the tunnel token:

  1. Go to Zero TrustNetworksTunnelshome
  2. Click the three dots → Recreate token
  3. Copy the new token

On production-1:

docker rm -f cloudflared
docker run -d --name cloudflared --restart unless-stopped --network networking \
  -v /docker/appdata/cloudflared:/home/nonroot/.cloudflared \
  cloudflare/cloudflared:latest tunnel --no-autoupdate run --token "<new-token>"

Also update the .env file:

TUNNEL_TOKEN=<new-token>

Step 5: Verify everything works

# Check cloudflared is connected
docker logs cloudflared --tail 5 | grep "Registered tunnel connection"

# Check Traefik is running and has routes
docker logs traefik --tail 10 | grep "Register"

# Test end-to-end
curl -sI https://git.gharbeia.net/ | head -5
# Should return HTTP/2 200

Step 6: Rotate other secrets (if needed)

Other credentials in .env that may need rotation:

  • AUTHENTIK_SECRET_KEY — generate new: openssl rand -base64 60 | tr -d '\n'
  • POSTGRESQL_PASSWORD — generate new
  • EMAIL_PASSWORD — Fastmail app password
  • GITEA_TOKEN — Gitea settings → Applications
  • OPENROUTER_API_KEY — OpenRouter dashboard

Troubleshooting

Cert renewal fails with "Invalid format for Authorization header"

The Cloudflare API token in CF_DNS_API_TOKEN is wrong or expired. Generate a new one and restart Traefik.

Cert renewal stuck on "Waiting for DNS record propagation"

Likely the proxied wildcard CNAME is intercepting _acme-challenge subdomain. Add a DNS-only A record for _acme-challenge127.0.0.1 in Cloudflare DNS to break the wildcard.

Tunnel shows 502 Bad Gateway

  • Check cloudflared logs: docker logs cloudflared --tail 10
  • If connection refused → Traefik isn't listening on the expected port
  • If tls: unrecognized name → SNI mismatch (revert to HTTP mode in tunnel config)

Adding a new service

  1. Add Traefik labels to the service in docker-compose.yaml
  2. If the container was started without labels, recreate it with docker run including -l flags (compose may fail due to .env interpolation issues)
  3. The tunnel wildcard already catches *.gharbeia.net — no tunnel changes needed