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:
@@ -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á
|
||||
|
||||
@@ -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:"
|
||||
|
||||
Reference in New Issue
Block a user