diff --git a/docs/cutover/RUNBOOK-cutover-wordpress.md b/docs/cutover/RUNBOOK-cutover-wordpress.md index 1113e9c..8c42875 100644 --- a/docs/cutover/RUNBOOK-cutover-wordpress.md +++ b/docs/cutover/RUNBOOK-cutover-wordpress.md @@ -20,8 +20,9 @@ disabled"*. | | | |---|---| -| 🔴 **Certificados LE de `www` y el apex** | Sin emitir. El WAF de Cloudflare devuelve **403 a la validación** de Let's Encrypt en `/.well-known/acme-challenge/…`. **No bloquea con la zona en `Full`** (Cloudflare acepta el cert por defecto de Traefik), pero **impide subir a `Full (strict)`**. Se arregla con una excepción del WAF para esa ruta, o pasando a validación **DNS-01** con el token que ya tenemos. Traefik no reintenta solo: hará falta un redeploy. | -| 🔴 **Backups en Hetzner** | Siguen sin existir. Ahora ahí vive producción, así que ya no es deuda técnica: es el único sitio donde están los datos vivos. | +| ✅ **Certificados LE de `www` y el apex** | **Emitidos el 3-ago a las 12:03 UTC**, válidos hasta el 1-nov. Detalle y las dos trampas del camino en `certificados-letsencrypt-cloudflare.md`. **Ya se puede subir la zona a `Full (strict)`.** | +| ✅ **Application passwords** | Wordfence las desactiva por defecto y eso dejaba a los bots sin poder publicar (401 en la REST API). Reactivado y **añadido como paso 3e de `post-import-seguridad.sh`**, porque el 3d reinstala Wordfence y volvería a romperse. | +| ✅ **Backups en Hetzner** | Ya existen (verificado por Rafa en otra sesión). | | 🟡 **CDMON** | Es el rollback. Inma lo está degradando a un plan barato con el correo. Caduca el **07/08**. | | 🟡 **`nuevo.feadulta.com`** | Ya no hace falta; retirar el registro cuando se quiera. | | 🟡 **Scripts que apuntan a `134.0.10.170` y `/web/`** | Cierra #158. | diff --git a/docs/cutover/certificados-letsencrypt-cloudflare.md b/docs/cutover/certificados-letsencrypt-cloudflare.md index 0563006..a027940 100644 --- a/docs/cutover/certificados-letsencrypt-cloudflare.md +++ b/docs/cutover/certificados-letsencrypt-cloudflare.md @@ -1,17 +1,59 @@ -# Los certificados de Let's Encrypt de `www` y el apex no se emiten +# Los certificados de Let's Encrypt de `www` y el apex -**Estado: abierto.** Bloqueado a la espera de una regla en Cloudflare, que gestiona Inma. -Fecha: 3-ago-2026, después del cutover. Plan maestro: `rafa/feadulta`#180. +## ✅ RESUELTO — 3-ago-2026, 12:03 UTC -## Resumen para quien tenga prisa +``` +www.feadulta.com Let's Encrypt YR2 3-ago-2026 -> 1-nov-2026 +feadulta.com Let's Encrypt YR2 3-ago-2026 -> 1-nov-2026 +wp-nuevo.feadulta.com Let's Encrypt YR1 3-ago-2026 -> 1-nov-2026 +``` -Traefik no consigue el certificado de `www.feadulta.com` ni de `feadulta.com`. **No hay nada roto -para los visitantes**: Cloudflare presenta su propio certificado, y hacia el origen —con el modo SSL -de la zona en `Full`— acepta el certificado por defecto de Traefik. El único coste es que **la zona -no puede subir a `Full (strict)`** hasta que se resuelva. +**Lo que funcionó** (el "plan B" de Inma, porque la Configuration Rule que se propuso primero **no +se puede montar en este plan de Cloudflare**): -La causa es que el reto HTTP-01 de Let's Encrypt acaba llegando por HTTPS y Cloudflare lo bloquea -con un 403. Se arregla con una **Configuration Rule** en Cloudflare acotada a la ruta del reto. +1. **Custom rule del WAF con acción *Skip*** para `/.well-known/acme-challenge/`. +2. **Apagar "Always Use HTTPS" a nivel de zona** durante unos minutos, y volver a encenderlo después. + +Con eso el reto se completa por el puerto 80, donde lo atiende Traefik. **Ya se puede subir la zona +a `Full (strict)`.** + +### ⚠️ Dos avisos para la próxima, que costaron tiempo + +**1. `acme.json` escribe `"main": "dominio"` CON espacio.** Un `grep '"main":"'` da vacío siempre y +parece que no hay certificados. Los certificados de hoy llevaban **casi una hora emitidos** mientras +un bucle de vigilancia mal escrito informaba de "sin novedad". **Parsear el JSON, no grepear:** + +```bash +docker exec coolify-proxy cat /traefik/acme.json > /tmp/a.json +python3 -c " +import json +d=json.load(open('/tmp/a.json')) +for r,v in d.items(): + if isinstance(v,dict): + for c in (v.get('Certificates') or []): print(c.get('domain',{}).get('main')) +" +``` + +**2. Se descartó un riesgo que se había anotado aquí: el modo `Full` NO impide el reto.** Se temía +que Cloudflare, al hablar con el origen por HTTPS, mandara la validación al 443 —donde el handler de +ACME no existe— y que HTTP-01 fuera imposible con la zona en `Full`. **Es falso**, y se midió: +con "Always Use HTTPS" apagado, una petición HTTP a la ruta del reto a través de Cloudflare devuelve +**404 con 0 bytes y `server: cloudflare`**, es decir, Cloudflare **sí reenvía al puerto 80** y +responde el handler de Traefik. + +--- + +*Lo que sigue es el diagnóstico, que se conserva porque explica cómo se llegó hasta aquí y contiene +un par de trampas que conviene no repetir.* + +## Resumen del problema (histórico) + +Traefik no conseguía el certificado de `www.feadulta.com` ni de `feadulta.com`. **No había nada roto +para los visitantes**: Cloudflare presentaba su propio certificado, y hacia el origen —con el modo +SSL de la zona en `Full`— aceptaba el certificado por defecto de Traefik. El único coste era que la +zona no podía subir a `Full (strict)`. + +La causa: el reto HTTP-01 acababa llegando por HTTPS y Cloudflare lo bloqueaba con un 403. ## Lo que dice Let's Encrypt, que es el único testigo que vale @@ -79,33 +121,11 @@ SSL. **La regla no se puede montar así.** Es una excepción mínima y acotada: esa ruta no sirve contenido, solo tokens de un solo uso que el propio Traefik genera y valida. -### ⚠️ Puede que aun así no salga, y conviene saber por qué antes de intentarlo - -Con el modo SSL de la zona en **`Full`, Cloudflare habla con el origen por HTTPS**. Si eso se aplica -también a las peticiones que le entran por HTTP, entonces al apagar "Always Use HTTPS" la petición -de Let's Encrypt llegará igualmente al **443** de Traefik, donde **el handler de ACME no existe** —y -volverá a fallar, ahora con un 404 en vez de un 403. - -No se puede determinar desde aquí sin apagar el ajuste: mientras "Always Use HTTPS" esté encendido, -Cloudflare redirige en su borde y **nunca llega a contactar con el origen por el puerto 80**, así que -no hay forma de observar qué puerto usaría. - -**Cómo distinguir los dos casos en el siguiente intento**, mirando el error de Traefik: - -| lo que reporte Let's Encrypt | qué significa | qué hacer | -|---|---|---| -| sale el certificado | Cloudflare reenvía por el 80 y Traefik atiende | nada más | -| `403` | el *Skip* del WAF no está cogiendo esa ruta | revisar el ámbito de la regla | -| **`404`** | **Cloudflare va al 443 del origen**: HTTP-01 no puede funcionar con la zona en `Full` | **pasar a DNS-01, dejar de quemar validaciones** | - ### Sobre los visitantes durante la ventana -Apagar "Always Use HTTPS" unos minutos es inocuo, pero **no exactamente por el motivo que parece**. -Si Cloudflare reenviara por el 80, Traefik devuelve un 302 a HTTPS por su cuenta (medido) y el -visitante acaba igual. Si Cloudflare va al 443, ese 302 no llega a dispararse y **un visitante que -entre por `http://` se quedará en `http://`** — con el contenido correcto y cifrado hasta Cloudflare, -pero sin subir a HTTPS. Para una ventana de minutos no tiene consecuencias, pero que nadie se -extrañe si lo ve. +Apagar "Always Use HTTPS" unos minutos resultó inocuo: Cloudflare reenvía al puerto 80 del origen y +**Traefik devuelve su propio 302 a HTTPS**, así que el visitante acaba en HTTPS igual. Medido antes +y durante. ### Alternativa de fondo, para más adelante @@ -148,11 +168,8 @@ Cuesta **unos 7 segundos** de corte: se ha medido dos veces, un único 500 y vue ### Comprobar si ya salió -```bash -docker exec coolify-proxy cat /traefik/acme.json | grep -oE '"main":"[^"]*feadulta[^"]*"' -``` - -Hoy sólo aparecen `antiguo.feadulta.com`, `nuevo.feadulta.com` y `gitea.feadulta.com`. +⚠️ **No con `grep`** — ver el aviso del principio: el fichero escribe `"main": "dominio"` con +espacio y un patrón sin espacio da vacío siempre. Parsear el JSON. ## Cabo suelto ya cerrado por el camino @@ -161,7 +178,7 @@ cualquier 404. No era la causa —el reto va por el 80 y ahí ni llega a WordPre era una trampa para el futuro. Arreglado y desplegado: las rutas de protocolo quedan exentas y el resto del redirect sigue igual. Lo encontró el Claude de Inma. -## Cuando el certificado salga +## `Full (strict)` -Entonces —y sólo entonces— se puede subir la zona a **`Full (strict)`**, que valida además el -certificado del origen. Hacerlo antes rompe el sitio. +Ya se puede: los tres certificados están emitidos. Sube la seguridad porque valida además el +certificado del origen. **Comprobar el sitio justo después de cambiarlo.**