Commit Graph

10 Commits

Author SHA1 Message Date
rafa d98f99d0ab certificados: emitidos, y las dos trampas del camino
www.feadulta.com, feadulta.com y wp-nuevo.feadulta.com, de Let's Encrypt y
validos hasta el 1-nov. Ya se puede subir la zona a Full (strict).

Lo que funciono es el plan B de Inma, porque la Configuration Rule que propuse
primero no se puede montar en su plan de Cloudflare: custom rule del WAF con
accion Skip para la ruta del reto, mas apagar Always Use HTTPS a nivel de zona
unos minutos.

Dos cosas que costaron tiempo y quedan anotadas:

acme.json escribe "main": "dominio" CON espacio. Un grep sin espacio da vacio
siempre y parece que no hay certificados. Los de hoy llevaban casi una hora
emitidos mientras mi bucle de vigilancia informaba de "sin novedad", y por eso
lanzamos un reintento que no hacia falta. Va el snippet que parsea el JSON.

Y se retira un riesgo que yo mismo habia documentado: el modo Full NO impide el
reto. Se temia que Cloudflare mandara la validacion al 443, donde el handler de
ACME no existe. Es falso y se midio: con Always Use HTTPS apagado, una peticion
HTTP a la ruta del reto a traves de Cloudflare devuelve 404 con 0 bytes y
server: cloudflare, o sea que reenvia al puerto 80 y responde Traefik. Mejor
quitarlo que dejar una advertencia falsa en un documento que va a leer otra
gente.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 09:17:31 -04:00
rafa 20528f0c5e docs: el plan de Cloudflare no permite la regla que propuse
Aviso de Inma: en su plan las Configuration Rules no ofrecen "Always Use HTTPS"
ni "Security Level", asi que la regla del documento no se puede montar tal cual.
Se sustituye por lo que si se puede hacer: custom rule del WAF con accion Skip
para la ruta del reto, y apagar "Always Use HTTPS" a nivel de zona durante el
reintento.

Se anade ademas un riesgo que hay que tener presente ANTES de gastar el
reintento: con el modo SSL de la zona en Full, Cloudflare habla con el origen por
HTTPS. Si eso se aplica tambien a lo que le entra por HTTP, la peticion de Let's
Encrypt llegara al 443 de Traefik, donde el handler de ACME no existe, y volvera
a fallar. No se puede saber desde aqui sin apagar el ajuste, porque mientras este
encendido Cloudflare redirige en su borde y nunca contacta con el origen por el
80.

Va una tabla para leer el siguiente error sin discutirlo otra vez: si sale el
certificado, bien; si da 403, el Skip no coge esa ruta; si da 404, Cloudflare va
al 443 y toca pasar a DNS-01 en vez de seguir quemando validaciones.

Y se matiza lo de los visitantes: el 302 del origen solo los cubre si Cloudflare
reenvia por el 80. Si va al 443, quien entre por http se queda en http durante la
ventana. Inocuo para unos minutos, pero mejor decirlo.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 08:42:14 -04:00
rafa f6170b70b1 docs: por que no salen los certificados de www y el apex
Documento para que lo pueda leer Inma sin tener que reconstruir la conversacion.

Lo esencial: el lado del servidor esta bien. Traefik intercepta la ruta del reto
en el entrypoint http y no la redirige, y se ve en que ese 404 viene vacio y sin
cabecera server mientras cualquier otra ruta da 302. El problema esta delante, en
el borde de Cloudflare: el reto acaba llegando por HTTPS y lo para un 403.

Se recoge tambien por que dos personas midiendo lo mismo obtuvieron resultados
distintos, que es lo que mas tiempo nos ha costado: Cloudflare devuelve 403 a
nuestras IPs para cualquier hostname de feadulta, y con el User-Agent de Let's
Encrypt deja pasar el bloqueo y entonces aparece el 301. La forma del token no
cambia nada. Conclusion util: el unico testigo que vale es lo que reporta Let's
Encrypt, no nuestros curl.

Y un aviso que puede hacernos perder la tarde: Let's Encrypt permite 5
validaciones fallidas por hostname y hora, www toco el limite a las 11:47 UTC y
cada redeploy quema tres. Reintentar antes de que pase la hora falla por rate
limit aunque la regla este bien puesta, y nos haria creer que no funciona. Va el
procedimiento en orden: primero la regla, luego esperar, luego un solo reintento.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 08:00:45 -04:00
rafa 4f91d68996 legacy-redirect: dejar en paz /.well-known/
fea-legacy-redirect manda al archivo cualquier 404, y eso incluia /.well-known/,
que nunca fue contenido de Joomla: son rutas de protocolo (retos de Let's
Encrypt, security.txt, change-password). Lo encontro el Claude de Inma mirando
por que no se emitian los certificados.

No es lo que bloquea los certificados. El reto de LE va por el puerto 80 y ahi
lo atiende Traefik, que lo intercepta antes de llegar a WordPress y no lo
redirige a HTTPS: se ve en que el 404 del puerto 80 viene vacio y sin cabecera
server, mientras cualquier otra ruta da 302. La configuracion lo confirma,
httpchallenge.entrypoint=http. Los certificados no salen porque Traefik no ha
reintentado desde las 10:44, cuando el DNS aun apuntaba a CDMON.

Pero el redirect estaba mal igualmente, y es una trampa para mas adelante: si
algun dia el reto llegara por el 443 (DNS-01 no, pero un cambio de entrypoint o
un renovador distinto si), este 301 lo tumbaria y el certificado no se
renovaria sin que nadie entendiera por que.

Comprobado que lo demas sigue igual: las URLs viejas de Joomla y los 404
genuinos siguen yendo al archivo, y /es sigue llevando a la home.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 07:34:23 -04:00
rafa 4a57ecfc56 cutover: el resultado real y lo que quedo abierto
El runbook era un documento de antes; ahora encabeza con lo que de verdad paso.
Ventana real 3 min 10 s, los recuentos cuadrando al digito con produccion, E2E
11 de 11 y el trafico real sin un solo 4xx ni 5xx.

Y sobre todo lo que quedo abierto, que es lo que se olvida: los certificados de
Let's Encrypt de www y el apex sin emitir porque el WAF de Cloudflare devuelve
403 a la validacion, con el aviso de que por eso no se puede subir la zona a
Full (strict); y los backups del Hetzner, que ya no son deuda tecnica porque ahi
vive ahora produccion.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 07:12:04 -04:00
rafa d528fe45ae cutover: wp-nuevo verificado, y por que no hace falta esperar al cert
Traefik ya tiene los dos routers (302 a ambos, 404 a lo no declarado).

Detalle que quita presion manana: wp-nuevo va proxied, asi que Cloudflare
presenta su propio cert a los bots y hacia el origen, con el modo SSL en Full
(no strict), acepta cualquiera - incluido el autofirmado de Traefik. El hostname
sirve desde el primer segundo, sin esperar a Lets Encrypt.

El reverso, anotado: subir la zona a Full (strict) antes de que los certs esten
emitidos romperia exactamente eso.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 21:36:46 -04:00
rafa 603eb1ecb4 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>
2026-08-02 21:31:37 -04:00
rafa 5ed2eaf3fe cutover: Wordfence entra en el plan (son 8 plugins, no 7)
CDMON tiene ModSecurity a nivel de hosting: fue lo que detecto las subidas de
wp-cl.php y health-check.php durante el incidente #183. En Hetzner, con Traefik,
NO hay WAF ninguno. Migrar tal cual estrenaria servidor con una capa defensiva
menos que la que tenia el sitio comprometido.

Wordfence 8.2.2 instalado limpio desde wordpress.org en el staging y verificado:
portada 200, wp-login 200, sin fatales, y fea-cloudflare-realip.php presente (sin
el veria todas las visitas como si vinieran de Cloudflare).

Queda en proteccion basica: el extended protection exige auto_prepend_file desde
su asistente y eso se hace despues del cutover, no en la ventana.

Anotado tambien que Wordfence y limit-login-attempts-reloaded solapan en el
bloqueo de login; hay que decidir cual manda.

Y documentada en el paso 6 la retirada de wordfence_core_options, que pese al
nombre no es de Wordfence sino el payload de spam del atacante.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 19:44:57 -04:00
rafa 508a6b4178 cutover: runbook del lunes, script de seguridad post-import y sitio E2E de staging
Con los tiempos REALES medidos en el ensayo del 31-jul, no estimados:
dump 25s + subida 74s + saneo 2s + import 107s = camino critico ~3min30s.

- docs/cutover/RUNBOOK-cutover-wordpress.md: los pasos en orden, copiables, con
  el rollback y los tres verdes obligatorios previos (freeze, CDMON vivo, modo
  SSL de la zona).
- scripts/cutover/post-import-seguridad.sh: rota la contrasena de los 6
  administradores (las lee del .env del perfil, nunca van en el repo), retira el
  rol a pabloarias/josek/andrey y limpia los cron huerfanos de Yoast y WP
  Profile Builder. Es un script y no una tarea manual porque el dump revive esas
  cuentas en CADA import.
- tools/e2e/sites/nuevo.json: la suite toma el host de un JSON, asi que apuntarla
  al staging es anadir un fichero.

Hallazgo del ensayo: el origen es MariaDB 11.8.6 y el destino MySQL 8.4.10; el
dump trae 20 tablas con collations uca1400_* que MySQL rechaza. El saneo las
mapea antes de importar. Sin ese paso, el import aborta.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 19:15:58 -04:00
rafa 5c5ea60348 cutover: manifiesto explicito de los mu-plugins a desplegar
La regla del #180 comment-516 dice "recrear los fea-* desde el repo", pero
carta-semana-plugin.php y stop-redirects.php estan vivos en produccion y no
empiezan por fea-: un glob los dejaria fuera. El despliegue copia esta lista.

28 ficheros con su sha256. Excluidos a proposito los .bak-*, el .disabled,
el health-check.php.quarantined-incident-183 (backdoor del #183) y
fea-support-campaign.php, que esta en el repo pero nunca llego a desplegarse.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 17:01:18 -04:00