# 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](#activar--desactivar-el-tunnel). --- ## Configuración inicial (una sola vez) ### 1. Crear el tunnel en Cloudflare 1. Ve a [dash.cloudflare.com](https://dash.cloudflare.com) → **Zero Trust** → **Networks** → **Conectores** 2. **Create connector** → nombre: `homeserver` 3. Selecciona **Docker** → copia el **token** (cadena larga tras `--token`) ### 2. Crear un API Token En [dash.cloudflare.com](https://dash.cloudflare.com) → **My Profile** → **API Tokens** → **Create 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` ```bash nano .env ``` Variables obligatorias para **ambos modos**: ```env CLOUDFLARE_API_TOKEN= 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**: ```env CLOUDFLARE_TUNNEL_TOKEN= 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: ```bash 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: ```bash 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 ```bash # 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 ```bash # 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 ```bash # 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 `) puede desbanear y allowlistear en CrowdSec, fail2ban y firewalld simultáneamente.