Files
Portainer/cloudflared

Cloudflare Tunnel (cloudflared)

Conecta el servidor a Cloudflare sin abrir puertos. Todo el tráfico entra por la red de Cloudflare, llega al contenedor cloudflared, y este lo reenvía a Traefik.

Internet → Cloudflare Edge → cloudflared → Traefik → servicios

Ventajas sobre exposición directa:

  • Tu IP pública nunca es visible — CrowdSec solo ve IPs de Cloudflare (allowlisteadas)
  • Funciona desde redes con CGNAT, CG-NAT corporativo o sin IPv4 fija
  • Cloudflare absorbe DDoS y filtra bots antes de que lleguen al servidor

Estado actual: el tunnel está parado (docker stop cloudflared). Los DNS apuntan directo a 193.70.84.224 (registros A, sin proxy Cloudflare). Ver sección Activar / desactivar el tunnel.


Configuración inicial (una sola vez)

1. Crear el tunnel en Cloudflare

  1. Ve a dash.cloudflare.comZero TrustNetworksConectores
  2. Create connector → nombre: homeserver
  3. Selecciona Docker → copia el token (cadena larga tras --token)

2. Crear un API Token

En dash.cloudflare.comMy ProfileAPI TokensCreate Token

Permisos mínimos necesarios:

Scope Recurso Permiso
Account Cloudflare Tunnel Edit
Zone DNS (sherlockhomeless.net) Edit

El API Token también lo usa add-domain.sh en modo directo (solo necesita Zone > DNS > Edit).

3. Configurar el .env

nano .env

Variables obligatorias para ambos modos:

CLOUDFLARE_API_TOKEN=<token del paso 2>
CLOUDFLARE_ZONE_ID=f7c4e16f9a434a947ad0f266b2e1f894
CLOUDFLARE_DOMAIN=sherlockhomeless.net
CLOUDFLARE_SERVER_IP=193.70.84.224   # IP para registros A directos

Variables adicionales solo necesarias con tunnel activo:

CLOUDFLARE_TUNNEL_TOKEN=<token del paso 1>
CLOUDFLARE_ACCOUNT_ID=328f4b942827169b11f7d1f11c640522
CLOUDFLARE_TUNNEL_ID=d405a2a2-69a1-40f1-b939-04cea5d60a50

Añadir un nuevo subdominio

Modo directo (sin tunnel, estado actual)

Crea un registro A apuntando a la IP del servidor, sin proxy de Cloudflare:

cd cloudflared
./add-domain.sh immich
./add-domain.sh immich recipes calibre   # varios a la vez

El script:

  1. Detecta la IP del servidor desde CLOUDFLARE_SERVER_IP (o la autodetecta via api.ipify.org)
  2. Crea o actualiza el registro DNS A con proxy desactivado (nube gris)

Modo tunnel (cuando el tunnel esté activo)

Crea un registro CNAME al tunnel con proxy naranja y añade el hostname al ingress:

cd cloudflared
./add-domain.sh --tunnel immich
./add-domain.sh --tunnel immich recipes calibre

El script:

  1. Lee el ingress actual del tunnel desde la API de Cloudflare
  2. Añade el hostname apuntando a https://traefik:443 (noTLSVerify)
  3. Guarda el ingress actualizado
  4. Crea o actualiza el registro DNS CNAME con proxy naranja

Activar / desactivar el tunnel

Activar

# 1. Levantar el contenedor
cd cloudflared
docker compose --env-file .env up -d

# 2. Migrar los DNS al tunnel (convierte A records → CNAME proxied)
./add-domain.sh --tunnel auth traefik gitea n8n nextcloud paperless paperless-ai \
  notas memos vikunja karakeep kasm remote beszel dozzle crowdsec grafana llm \
  ocode adblock onlyoffice www cockpit coolify kopia notes portainer \
  uptimekuma webtop wg

# 3. Verificar conexión
docker logs cloudflared | grep "Registered tunnel"

Desactivar

# 1. Parar el contenedor (no se levantará solo al reiniciar)
docker stop cloudflared

# 2. Revertir DNS a registros A directos
./add-domain.sh auth traefik gitea n8n nextcloud paperless paperless-ai \
  notas memos vikunja karakeep kasm remote beszel dozzle crowdsec grafana llm \
  ocode adblock onlyoffice www cockpit coolify kopia notes portainer \
  uptimekuma webtop wg

Subdominios registrados

Subdominio Servicio
auth Authentik SSO
traefik Traefik Dashboard
gitea Gitea
n8n n8n
nextcloud Nextcloud
paperless Paperless-ngx
paperless-ai Paperless AI
notas Trilium
memos Memos
vikunja Vikunja
karakeep Karakeep
kasm Kasm Workspaces
remote Guacamole
beszel Beszel
dozzle Dozzle
crowdsec CrowdSec Grafana
grafana Grafana
llm LiteLLM
ocode OpenCode
adblock AdGuard
onlyoffice OnlyOffice
www Homepage
cockpit Cockpit
coolify Coolify
kopia Kopia
notes Notes
portainer Portainer
uptimekuma Uptime Kuma
webtop Webtop
wg WireGuard

Comandos útiles

# Ver estado del tunnel
docker logs cloudflared

# Ver todos los subdominios en DNS de Cloudflare
curl -s "https://api.cloudflare.com/client/v4/zones/${CLOUDFLARE_ZONE_ID}/dns_records?per_page=100" \
  -H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" | \
  python3 -c "import sys,json; [print(r['type'].ljust(6), r['name']) for r in json.load(sys.stdin)['result']]"

# Ver ingress del tunnel configurado en Cloudflare
curl -s "https://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/cfd_tunnel/${CLOUDFLARE_TUNNEL_ID}/configurations" \
  -H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" | \
  python3 -c "import sys,json; [print(r.get('hostname','*'), '->', r['service']) for r in json.load(sys.stdin)['result']['config']['ingress']]"

Seguridad

  • API Token: permisos mínimos (Tunnel Edit + DNS Edit). Rótalo periódicamente.
  • Tunnel Token: si se compromete, regénéralo en el dashboard y reinicia el contenedor con el nuevo token en .env.
  • CrowdSec con tunnel activo: las IPs de Cloudflare están en la allowlist cloudflare. El bouncer usa forwardedHeadersTrustedIPs con los rangos de CF para ver la IP real del cliente.
  • CrowdSec sin tunnel: el bouncer ve la IP pública directa del cliente. El bot de Telegram (/allow <ip>) puede desbanear y allowlistear en CrowdSec, fail2ban y firewalld simultáneamente.