Files
Proxmox-Coolify-Manager/docs/cloudflare-tunnel-coolify-agent_1.md
T
urieljareth f587a8baa2 Agrega scripts y runbooks: Cloudflare API, autostart Coolify, parche Chatwoot, deploy Solo Leveling + docs de casos
- scripts/Invoke-CloudflareApi.ps1: control de tunnel/DNS via API
- scripts/Install-CoolifyAutostart.ps1 + scripts/host/: autostart de LXC 102 + tunnel tras corte
- scripts/Apply-ChatwootEnterprisePatch.ps1: parche enterprise (autodetecta creds)
- Deploy-SoloLeveling.ps1: deploy build-on-server verificado
- docs/runbooks/cloudflare-tunnel.md y autostart-coolify.md
- docs/casos/chatwoot-enterprise-patch.md (credenciales redactadas)
- docs/AGENTS-coolify-apps.md + issues y reportes
- deploy_skill/references/coolify-4.1.2-notes.md: hallazgos verificados (seccion 7)
- package.json para verify-online.mjs del coolify-deploy skill
- .gitignore: excluye .claude/ y .opencode/ (config local de herramientas)
2026-07-18 11:31:31 -06:00

406 lines
14 KiB
Markdown

# Instrucciones para Agente IA — Cloudflare Tunnel + Coolify
**Entorno:** Proxmox VE → LXC CT 102 → Coolify (Docker) → Traefik + cloudflared
**Dominio:** urieljareth.org
**IP interna del servidor Coolify:** 192.168.0.117
---
## HERRAMIENTAS DISPONIBLES
El agente tiene acceso a:
1. **SSH a Proxmox** — para ejecutar comandos en el servidor
2. **API de Cloudflare** — para configurar DNS, Tunnel y Zero Trust sin usar la UI
Credenciales necesarias antes de comenzar:
- `CF_API_TOKEN` — token con permisos: `Zona → DNS → Editar` y `Zero Trust → Editar` para urieljareth.org
- `CF_ACCOUNT_ID` — ID de cuenta de Cloudflare (visible en el panel principal)
- `CF_ZONE_ID` — ID de zona DNS (visible en la sección DNS del dominio)
- `TUNNEL_TOKEN` — token del túnel cloudflared (se obtiene o regenera via API)
- Acceso SSH al host Proxmox o directamente al CT 102 (root@192.168.0.117)
---
## PASO 1 — Verificar conectividad básica desde el servidor
Conectarse por SSH al contenedor de Coolify y ejecutar:
```bash
# Verificar que TCP/443 saliente funciona
curl -v https://cloudflare.com 2>&1 | head -10
# Verificar que UDP/7844 NO está disponible (es común en redes domésticas)
nc -zv 198.41.192.37 7844
```
**Resultado esperado:**
- `curl` debe mostrar `Connected to cloudflare.com`
- `nc` debe mostrar `Connection timed out` (UDP bloqueado — esto es normal y se resuelve en el Paso 3)
---
## PASO 2 — Configurar DNS en Cloudflare via API
### 2.1 Verificar registros existentes
```bash
curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-H "Content-Type: application/json" | jq '.result[] | {name, type, content, proxied}'
```
### 2.2 Crear registro CNAME wildcard (obligatorio)
Reemplazar `<TUNNEL_ID>` con el ID del túnel (formato: `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`):
```bash
curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-H "Content-Type: application/json" \
--data '{
"type": "CNAME",
"name": "*",
"content": "<TUNNEL_ID>.cfargotunnel.com",
"proxied": true,
"ttl": 1
}'
```
### 2.3 Crear registro CNAME para coolify
```bash
curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-H "Content-Type: application/json" \
--data '{
"type": "CNAME",
"name": "coolify",
"content": "<TUNNEL_ID>.cfargotunnel.com",
"proxied": true,
"ttl": 1
}'
```
> ⚠️ Ambos registros deben tener `proxied: true` (ícono naranja en la UI). Sin el wildcard `*`, los subdominios de las apps desplegadas devuelven `DNS_PROBE_FINISHED_NXDOMAIN`.
---
## PASO 3 — Obtener o regenerar el token del túnel
### 3.1 Listar túneles existentes
```bash
curl -s -X GET "https://api.cloudflare.com/client/v4/accounts/$CF_ACCOUNT_ID/cfd_tunnel" \
-H "Authorization: Bearer $CF_API_TOKEN" | jq '.result[] | {id, name, status}'
```
### 3.2 Obtener el token del túnel existente
```bash
curl -s -X GET "https://api.cloudflare.com/client/v4/accounts/$CF_ACCOUNT_ID/cfd_tunnel/<TUNNEL_ID>/token" \
-H "Authorization: Bearer $CF_API_TOKEN" | jq -r '.result'
```
Guardar el resultado como `TUNNEL_TOKEN`.
### 3.3 (Alternativa) Crear un túnel nuevo
Solo si no existe un túnel previo:
```bash
curl -s -X POST "https://api.cloudflare.com/client/v4/accounts/$CF_ACCOUNT_ID/cfd_tunnel" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-H "Content-Type: application/json" \
--data '{
"name": "coolify-tunnel",
"tunnel_secret": "'$(openssl rand -base64 32)'"
}' | jq '{id: .result.id, name: .result.name}'
```
Luego obtener el token con el endpoint del paso 3.2.
---
## PASO 4 — Configurar las rutas del túnel via API
Este es el paso más crítico. Las rutas se llaman "ingress rules" y deben configurarse en orden exacto.
### Orden obligatorio de rutas
| # | Hostname | Path | Servicio | Protocolo | Notas |
|---|----------|------|----------|-----------|-------|
| 1 | coolify.urieljareth.org | /build/* | 192.168.0.117:8000 | http | Build logs |
| 2 | coolify.urieljareth.org | /project/* | 192.168.0.117:8000 | http | **CRÍTICO: UI de apps** |
| 3 | coolify.urieljareth.org | /app/* | 192.168.0.117:6001 | http | Websocket realtime |
| 4 | coolify.urieljareth.org | /terminal/ws* | 192.168.0.117:6002 | http | Terminal websocket |
| 5 | coolify.urieljareth.org | * | 192.168.0.117:8000 | http | Catch-all Coolify UI |
| 6 | *.urieljareth.org | * | 192.168.0.117:443 | https | Apps via Traefik |
### Aplicar configuración via API
```bash
curl -s -X PUT "https://api.cloudflare.com/client/v4/accounts/$CF_ACCOUNT_ID/cfd_tunnel/<TUNNEL_ID>/configurations" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-H "Content-Type: application/json" \
--data '{
"config": {
"ingress": [
{
"hostname": "coolify.urieljareth.org",
"path": "/build/*",
"service": "http://192.168.0.117:8000"
},
{
"hostname": "coolify.urieljareth.org",
"path": "/project/*",
"service": "http://192.168.0.117:8000"
},
{
"hostname": "coolify.urieljareth.org",
"path": "/app/*",
"service": "http://192.168.0.117:6001"
},
{
"hostname": "coolify.urieljareth.org",
"path": "/terminal/ws*",
"service": "http://192.168.0.117:6002"
},
{
"hostname": "coolify.urieljareth.org",
"service": "http://192.168.0.117:8000"
},
{
"hostname": "*.urieljareth.org",
"service": "https://192.168.0.117:443",
"originRequest": {
"noTLSVerify": true
}
},
{
"service": "http_status:404"
}
]
}
}'
```
> ⚠️ El último elemento `http_status:404` es obligatorio. La API rechaza la configuración si no hay un catch-all final sin hostname.
### Verificar que las rutas se aplicaron correctamente
```bash
curl -s -X GET "https://api.cloudflare.com/client/v4/accounts/$CF_ACCOUNT_ID/cfd_tunnel/<TUNNEL_ID>/configurations" \
-H "Authorization: Bearer $CF_API_TOKEN" | jq '.result.config.ingress[] | {hostname, path, service}'
```
---
## PASO 5 — Reglas críticas de las rutas (no negociables)
### Puertos 6001 y 6002 — siempre HTTP, nunca HTTPS
Los puertos `6001` (websocket realtime) y `6002` (terminal websocket) de Coolify NO hablan TLS internamente. Si se configura `https://` para estas rutas, cloudflared devuelve:
```
tls: first record does not look like a TLS handshake
```
Siempre usar `http://192.168.0.117:6001` y `http://192.168.0.117:6002`.
### Ruta /project/* — va ANTES de /app/*
Sin la regla `/project/*` en posición 2, cualquier URL con `/application/` en el path (como `/project/xxx/environment/xxx/application/xxx`) es capturada por `/app/*` y enviada al websocket del puerto 6001. La UI de Coolify carga en blanco.
### Ruta /terminal/ws* — sin barra al final
Escribir `/terminal/ws*` y NO `/terminal/ws/*`. La variante con barra no hace match con las conexiones del terminal.
### Ruta *.urieljareth.org — siempre al final con noTLSVerify
El wildcard debe ir después de todas las rutas específicas de `coolify.urieljareth.org`. Apunta a Traefik en puerto 443 con HTTPS y requiere `noTLSVerify: true` porque Traefik usa certificados autofirmados internamente.
---
## PASO 6 — Desplegar cloudflared en el servidor
Ejecutar via SSH en el contenedor de Coolify:
```bash
# Detener y eliminar el contenedor anterior si existe
docker stop cloudflared 2>/dev/null
docker rm cloudflared 2>/dev/null
# Crear el contenedor con protocolo HTTP2 forzado
docker run -d \
--name cloudflared \
--restart always \
-e TUNNEL_EDGE_PORT=443 \
cloudflare/cloudflared:latest \
tunnel --no-autoupdate --protocol http2 run --token $TUNNEL_TOKEN
```
### Por qué --protocol http2 es obligatorio
En redes domésticas, el puerto UDP/7844 que usa QUIC (el protocolo por defecto de cloudflared) está bloqueado por la mayoría de routers e ISPs. Sin `--protocol http2`, el túnel se conecta brevemente y cae en timeout cada ~5 minutos con el error:
```
failed to dial to edge with quic: timeout: no recent network activity
```
`-e TUNNEL_EDGE_PORT=443` fuerza la conexión TCP por el puerto 443, que siempre está abierto.
### Verificar que el túnel está activo
```bash
docker logs cloudflared --tail 20
```
Resultado correcto — deben aparecer 4 líneas como esta:
```
INF Registered tunnel connection connIndex=0 ... protocol=http2
INF Registered tunnel connection connIndex=1 ... protocol=http2
INF Registered tunnel connection connIndex=2 ... protocol=http2
INF Registered tunnel connection connIndex=3 ... protocol=http2
```
Si aparece `Provided Tunnel token is not valid`, el token se truncó. Repetir el Paso 3.2 para obtenerlo completo.
---
## PASO 7 — Configurar Traefik con DNS Challenge
Esto elimina la dependencia de validación HTTP para obtener certificados SSL. Es necesario cuando "Always Use HTTPS" está activo en Cloudflare (que redirige las validaciones HTTP de Let's Encrypt antes de que lleguen a Traefik).
### 7.1 Crear token de API para DNS Challenge
El token debe tener permisos: `Zona → DNS → Editar` para `urieljareth.org`.
```bash
# Verificar que el token tiene los permisos correctos
curl -s -X GET "https://api.cloudflare.com/client/v4/user/tokens/verify" \
-H "Authorization: Bearer $CF_DNS_API_TOKEN" | jq '{status: .result.status}'
```
### 7.2 Editar el docker-compose de Traefik via SSH
```bash
# Hacer backup primero
cp /data/coolify/proxy/docker-compose.yml /data/coolify/proxy/docker-compose.yml.bak
# Editar
nano /data/coolify/proxy/docker-compose.yml
```
Agregar dentro del servicio `traefik`, sección `environment`:
```yaml
environment:
- CF_DNS_API_TOKEN=<token-dns-api>
```
Reemplazar las líneas de `httpchallenge` en `command` por:
```yaml
- '--certificatesresolvers.letsencrypt.acme.dnschallenge=true'
- '--certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare'
- '--certificatesresolvers.letsencrypt.acme.dnschallenge.resolvers=1.1.1.1:53,8.8.8.8:53'
- '--certificatesresolvers.letsencrypt.acme.storage=/traefik/acme.json'
```
### 7.3 Limpiar certificados anteriores y reiniciar
```bash
echo '{}' > /data/coolify/proxy/acme.json
chmod 600 /data/coolify/proxy/acme.json
docker compose -f /data/coolify/proxy/docker-compose.yml up -d --force-recreate
```
> ⚠️ Si hay error 429 en los logs de coolify-proxy, Let's Encrypt aplicó rate limit por intentos fallidos previos. Esperar 1 hora antes de reintentar.
---
## PASO 8 — Verificación final
### Verificar estado de todos los servicios
```bash
# Contenedores corriendo
docker ps --format "table {{.Names}}\t{{.Status}}" | grep -E "coolify|cloudflared|traefik"
# Túnel con 4 conexiones activas
docker logs cloudflared --tail 5 | grep "Registered tunnel"
# Traefik sin errores de certificado
docker logs coolify-proxy --tail 50 2>&1 | grep -i "error\|certificate\|acme\|429"
```
### Verificar rutas desde afuera
```bash
# Coolify UI debe responder 200
curl -s -o /dev/null -w "%{http_code}" https://coolify.urieljareth.org
# Una app desplegada debe responder 200 o 301
curl -s -o /dev/null -w "%{http_code}" https://miapp.urieljareth.org
```
---
## DIAGNÓSTICO DE ERRORES COMUNES
| Error | Causa más probable | Verificar |
|-------|--------------------|-----------|
| `530` — Unregistered from Argo Tunnel | cloudflared caído | `docker logs cloudflared --tail 20` |
| `502 Bad Gateway` | Traefik sin cert SSL o puerto 3000 | Logs de coolify-proxy |
| Página en blanco al abrir app en Coolify | Falta regla `/project/*` | Verificar rutas del túnel |
| `tls: first record does not look like TLS` | https:// en puerto 6001 o 6002 | Corregir a http:// en ingress rules |
| `DNS_PROBE_FINISHED_NXDOMAIN` | Falta CNAME wildcard | Verificar registro `*` en DNS |
| Túnel cae cada 5 min | UDP/7844 bloqueado | Confirmar `--protocol http2` en el contenedor |
| `429` en logs de Traefik | Rate limit de Let's Encrypt | Esperar 1h, luego reiniciar Traefik |
| `Provided Tunnel token is not valid` | Token truncado | Obtener token completo via API (Paso 3.2) |
---
## FLUJO PARA DESPLEGAR UNA APP NUEVA
Por un bug en Coolify beta.472, la UI de apps individuales no carga tras la creación. Usar este flujo:
```bash
# 1. Obtener el ID y UUID de la app recién creada
docker exec coolify-db psql -U coolify -d coolify \
-c "SELECT id, uuid, name, fqdn FROM applications ORDER BY id DESC LIMIT 3;"
# 2. Actualizar dominio y puerto antes del deploy
docker exec coolify-db psql -U coolify -d coolify \
-c "UPDATE applications SET fqdn='https://miapp.urieljareth.org', ports_exposes='80' WHERE id=<ID>;"
# 3. Hacer deploy via API de Coolify
curl -X POST "http://localhost:8000/api/v1/deploy?uuid=<UUID>&force=false" \
-H "Authorization: Bearer <COOLIFY_API_TOKEN>"
# 4. Verificar que Traefik apunta al puerto correcto
grep "server.port" /data/coolify/applications/<UUID>/docker-compose.yaml
```
Si el puerto sigue siendo 3000 en lugar de 80:
```bash
sed -i 's/loadbalancer.server.port=3000/loadbalancer.server.port=80/g' \
/data/coolify/applications/<UUID>/docker-compose.yaml
docker compose -f /data/coolify/applications/<UUID>/docker-compose.yaml up -d --force-recreate
```
---
## ARCHIVOS DE REFERENCIA EN EL SERVIDOR
| Archivo | Ruta |
|---------|------|
| Docker-compose de Traefik | `/data/coolify/proxy/docker-compose.yml` |
| Backup de Traefik | `/data/coolify/proxy/docker-compose.yml.bak` |
| Certificados SSL | `/data/coolify/proxy/acme.json` |
| Docker-compose de cada app | `/data/coolify/applications/<UUID>/docker-compose.yaml` |