cutover: paso 0 para wp-nuevo, LLAR fuera y el modo SSL ya confirmado

Aviso de Mixbot (2-ago): los bots de la carta publican por wp-nuevo.feadulta.com,
no por www, porque el WAF de Cloudflare los bloquea por www (regla "bloquear bots
1"). Ese hostname no tiene registro propio, lo cubre el wildcard, y Traefik solo
enruta nuevo.feadulta.com: al mover el A del apex los bots dejarian de publicar.
La carta 738 se compone el martes.

Se anade el paso 0 al runbook. Hay que hacerlo en el PANEL: por API el PATCH de
fqdn da 422 y actualizar SERVICE_FQDN_WORDPRESS cambia la variable pero no
regenera los routers (comprobado hoy). Plan B documentado: los bots pueden pasar
a nuevo.feadulta.com, que es grey cloud y por tanto no pasa por Cloudflare ni le
aplica ninguna regla del WAF.

Decision de Rafa: el bloqueo de login lo lleva Wordfence y sale
limit-login-attempts-reloaded. Wordfence hace lo mismo y ademas firewall de
aplicacion, escaner de malware y 2FA; los dos juntos son dos contadores
compitiendo. Retirado del staging (7 plugins activos, sitio en pie) y anadido al
post-import-seguridad.sh (paso 3c) porque el dump lo revive en cada import, junto
con sus 12 opciones huerfanas. Nuevo paso 3d que verifica que Wordfence sigue
activo y lo reinstala si faltara.

Inma confirma que el modo SSL de la zona es Full, no Flexible: cae el riesgo de
bucle de redireccion. Marcado como resuelto en los verdes previos.

Anadida la seccion del correo: el cutover no lo toca (MX a otra IP), pero la
caducidad del 07/08 si, y ediciones@/contenido@ son de donde sale la carta.

Anotado tambien que Cloudflare devuelve 403 a la IP de Rafa para cualquier
hostname de feadulta: un 403 desde su maquina no prueba nada, la verificacion de
los bots la tienen que hacer ellos.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-02 21:31:37 -04:00
parent da3dfae0a3
commit 603eb1ecb4
2 changed files with 104 additions and 13 deletions
+68 -13
View File
@@ -8,13 +8,49 @@ Todos los tiempos de abajo están **medidos en el ensayo del 31-jul**, no estima
## Antes de empezar (verde obligatorio)
- [ ] **Freeze editorial** avisado a Inma. El cutover no puede caer el día de publicación de la carta.
*(La carta 738 se compone el martes: el lunes está libre.)*
- [ ] **CDMON sigue vivo** (§1). Es el rollback: si no está, no se ejecuta.
- [ ] **Modo SSL de la zona en Cloudflare = Full (strict)**. Si está en *Flexible*, Cloudflare habla
HTTP con el origen mientras Traefik fuerza HTTPS → **bucle de redirección y sitio caído**.
Nuestro token no puede leer este ajuste: lo confirma Inma.
- [x] ~~**Modo SSL de la zona**~~**CONFIRMADO POR INMA (2-ago): está en `Full`, no `Flexible`.**
El riesgo de bucle de redirección queda descartado. (*Full (strict)* además valida el
certificado del origen; es mejor y se puede subir después, no bloquea el cutover.)
- [ ] 🔴 **`wp-nuevo.feadulta.com` declarado en Coolify — ver paso 0. SIN ESTO SE ROMPEN LOS BOTS.**
- [ ] **Inventario del wildcard.** La zona tiene `*.feadulta.com` → apex, proxied. Al mover el A del
apex, **todo subdominio no declarado se va a Hetzner** y Traefik devolverá su 404. Comprobado
pendiente: `info.feadulta.com`.
apex, **todo subdominio no declarado se va a Hetzner** y Traefik devolverá su 404.
`info.feadulta.com`: Rafa lo da por residual, se acepta que dé 404.
---
## 0. 🔴 `wp-nuevo.feadulta.com` — hacer ANTES del cutover
**Los bots de la carta (`teologasbot`, `escritoresbot`, `recordatoriobot`) publican por
`wp-nuevo.feadulta.com`, no por `www`**, porque el WAF de Cloudflare los bloquea por `www` (regla
"bloquear bots 1"). Aviso de Mixbot, 2-ago.
Ese hostname **no tiene registro propio**: lo cubre el wildcard. Al mover el A del apex viaja a
Hetzner, y **Traefik solo tiene router para `nuevo.feadulta.com`** → devolvería su 404 y los bots
dejarían de publicar. La carta 738 se compone el **martes**.
⚠️ **Hay que hacerlo en el PANEL de Coolify.** Por API no vale: `PATCH /services/{uuid}` con `fqdn`
da `422 "This field is not allowed"`, y actualizar `SERVICE_FQDN_WORDPRESS` por API cambia la
variable pero **no regenera los routers de Traefik** (comprobado el 2-ago).
Servicio `feadulta-wp` → campo de dominios:
```
antes del cutover: https://nuevo.feadulta.com,https://wp-nuevo.feadulta.com
después del cutover: https://www.feadulta.com,https://feadulta.com,https://wp-nuevo.feadulta.com
```
**Plan B si el martes wp-nuevo falla:** los bots cambian una línea de su `.env` y publican por
**`nuevo.feadulta.com`** — es grey cloud, así que **no pasa por Cloudflare y ninguna regla del WAF
le aplica**, ya tiene certificado y ya está enrutado. Mixbot puede hacerlo al momento.
Verificación tras el cutover (no vale hacerla desde la máquina de Rafa: **Cloudflare devuelve 403 a
esa IP para cualquier hostname de feadulta**, así que un 403 desde ahí no significa nada):
```bash
curl -sI https://wp-nuevo.feadulta.com/wp-json/ | head -3 # desde el Hetzner o desde los bots
```
## Tiempos medidos (ensayo del 31-jul)
@@ -113,20 +149,30 @@ payload de SEO spam que dejó el atacante (div oculto con enlaces a un casino, c
de una letra). Está inerte —quien lo leía era `wp-highlits`, en cuarentena— pero **viaja dentro del
dump y cada import lo reintroduce**.
## 6b. Plugins de seguridad — son 8, no 7
## 6b. Plugins de seguridad — Wordfence entra, LLAR sale
A los 7 plugins de producción se añade **Wordfence** (8.2.2, instalado limpio desde wordpress.org el
31-jul). Motivo: CDMON tenía **ModSecurity** delante a nivel de hosting —fue lo que detectó las
subidas de `wp-cl.php` y `health-check.php` en el #183—, y **en Hetzner con Traefik no hay WAF
ninguno**. Sin Wordfence, el servidor nuevo estrenaría con una capa defensiva menos que la que tenía
el sitio comprometido.
**Siguen siendo 7 plugins activos**, pero no los mismos: entra **Wordfence** (8.2.2, limpio desde
wordpress.org) y sale **`limit-login-attempts-reloaded`** (decisión de Rafa, 2-ago).
Motivo de que entre Wordfence: CDMON tenía **ModSecurity** delante a nivel de hosting —fue lo que
detectó las subidas de `wp-cl.php` y `health-check.php` en el #183— y **en Hetzner con Traefik no hay
WAF ninguno**. Sin él, el servidor nuevo estrenaría con una capa defensiva menos que la que tenía el
sitio comprometido.
Motivo de que salga LLAR: Wordfence hace lo mismo (límite de intentos, bloqueos) **y además**
firewall de aplicación, escáner de malware y 2FA. Los dos a la vez son dos contadores compitiendo y
bloqueos difíciles de diagnosticar.
**El dump trae LLAR en `active_plugins`, así que cada import lo revive** → lo retira
`post-import-seguridad.sh` (paso 3c), junto con sus 12 opciones huérfanas. El mismo script comprueba
que Wordfence sigue activo y lo reinstala si faltara.
⚠️ Wordfence queda con **protección básica** (a nivel de plugin). El *extended protection* exige
configurar `auto_prepend_file` desde su asistente; se hace **después** del cutover y con calma, no
en la ventana.
⚠️ Wordfence y `limit-login-attempts-reloaded` solapan en el bloqueo de login. No es un conflicto,
pero conviene decidir cuál manda para no acabar con dos contadores distintos.
⚠️ `fea-cloudflare-realip.php` es obligatorio con Wordfence por el mismo motivo que lo era con LLAR:
sin él ve todas las visitas como si vinieran de las IPs de Cloudflare. Está en el manifiesto.
## 7. Quitar el WP_HOME/WP_SITEURL del staging
@@ -176,6 +222,15 @@ intacto y sirviendo — comprobado con `curl --resolve` contra `134.0.10.170`.
**Esto solo funciona mientras CDMON siga vivo.** Es la razón por la que el §1 no es opcional.
## 🔴 El correo — no es del cutover, pero es lo que más puede doler
El cutover **no toca el correo**: el MX apunta a `mail.feadulta.com``134.0.13.123`, una IP
distinta de la del web. Mover el A del apex no lo afecta.
**Pero la caducidad del 07/08 sí.** Los 17 buzones viven en CDMON, y `ediciones@` y `contenido@` son
de donde los bots sacan el material de la carta: si cae el correo, se para la carta. Hay que
resolverlo con CDMON **esta semana**, vaya como vaya el cutover.
## Después (no el mismo día)
- **Backups en Hetzner: hoy NO HAY NINGUNO.** La tabla `scheduled_database_backups` de Coolify está
+36
View File
@@ -92,6 +92,42 @@ else
REMOTE
fi
echo ""
echo "=== 3c. Retirar limit-login-attempts-reloaded ==="
# Decision de Rafa (2-ago): el bloqueo de login lo lleva Wordfence, que hace lo mismo
# y ademas firewall de aplicacion, escaner de malware y 2FA. Tener los dos = dos
# contadores compitiendo. El dump trae LLAR en active_plugins, asi que cada import
# lo revive: por eso se retira aqui y no una sola vez a mano.
if [ $DRY -eq 1 ]; then
echo " [dry-run] desactivaria y borraria limit-login-attempts-reloaded"
echo " [dry-run] limpiaria las opciones limit_login_*"
else
wp plugin deactivate limit-login-attempts-reloaded >/dev/null 2>&1 || true
wp plugin delete limit-login-attempts-reloaded >/dev/null 2>&1 || true
echo " limit-login-attempts-reloaded: retirado"
ssh -o BatchMode=yes "$SRV" "bash -s" <<REMOTE
RP=\$(docker inspect mysql-$U --format '{{range .Config.Env}}{{println .}}{{end}}' | sed -n 's/^MYSQL_ROOT_PASSWORD=//p')
docker exec mysql-$U mysql --default-character-set=utf8mb4 -uroot -p"\$RP" -N -e \
"DELETE FROM wp_options WHERE option_name LIKE 'limit_login%';" wordpress 2>/dev/null
n=\$(docker exec mysql-$U mysql -uroot -p"\$RP" -N -e "SELECT COUNT(*) FROM wp_options WHERE option_name LIKE 'limit_login%';" wordpress 2>/dev/null)
echo " opciones limit_login_* restantes: \$n"
REMOTE
fi
echo ""
echo "=== 3d. Wordfence presente? ==="
if [ $DRY -eq 1 ]; then
echo " [dry-run] comprobaria Wordfence"
else
if wp plugin is-installed wordfence 2>/dev/null; then
wp plugin activate wordfence >/dev/null 2>&1 || true
echo " wordfence: instalado y activo"
else
echo " wordfence: NO instalado -> instalando desde wordpress.org"
wp plugin install wordfence --activate >/dev/null 2>&1 && echo " wordfence: instalado" || echo " wordfence: FALLO la instalacion, revisar a mano"
fi
fi
echo ""
echo "=== 4. Verificacion ==="
echo " administradores que quedan:"