Commits

Primer rollback real, el misterio del postgres vecino y el actualizador que se actualiza

La v0.1.17 no arrancó y el actualizador revirtió solo; una migración fallaba

Ayer os explicaba la auditoría y el botón de actualizaciones. Hoy toca el día en que todo esto pasó la prueba de fuego: el primer rollback real en producción, una migración de datos que se resistía con un misterio de DNS de por medio, y el cierre de la web transitoria que arrastraba desde el principio.

Lotes de seguridad: comentarios, copias y contenedores más duros

La auditoría dejó una lista priorizada (P0, P1, P2...) y el día comenzó cerrando el bloque P2 entero. Traducción de lo más sonoro:

  • Comentarios con cinturón y tirantes: límite de 10 comentarios por hora y dirección IP real, y los avisos por webhook solo saltan cuando el comentario está aprobado —antes, cualquier pendiente enviaba mensajes.
  • Copias que comprueban lo que hacen: antes, un volcado de base de datos cortado a medias se podía comprimir como si fuese válido. Ahora el script comprueba que el volcado terminó bien antes de comprimir.
  • Contenedores sin privilegios: los contenedores de las webs corren como usuario normal, no como root —el administrador del sistema—. Menos poder si alguien entra.
  • Cadenas de suministro fijadas: las dependencias del servidor van ahora con versiones exactas, para que una reconstrucción no traiga sorpresas nuevas.

Y una lección de proceso digna de cuento: tras un bloqueo del gancho de seguridad, una edición se quedó fuera del commit y la release v0.1.15 salió sin una línea necesaria. La v0.1.16 fue literalmente una línea. La lección: tras cualquier bloqueo, revisar el estado completo del repositorio antes de etiquetar.

El primer rollback real (y funcionó)

Aquí vino el momento que todo sistema de actualizaciones espera: la v0.1.17 no arrancó. La causa, pura delicadeza de permisos: el script de arranque intentaba tomar posesión de todas las carpetas montadas, pero una de ellas es de solo lectura (la base de datos de geolocalización). El proceso moría al toparse con ella.

Y el actualizador hizo exactamente lo que estaba diseñado para hacer: revirtió solo a la versión anterior, sin dedo humano. Primer rollback real en producción, superado. El arreglo llegó con la v0.1.18: solo tomar posesión de lo que se puede escribir, y que el cambio de propietario de archivos nunca sea letal.

Pero la historia tenía segunda parte: al ir sin usuario root, la API dejó de poder hablar con el socket del actualizador (un archivo especial de comunicación entre procesos, con permisos muy estrictos). Resultado: la tarjeta de actualizaciones desaparecía del panel en silencio. El arreglo: abrir ese canal a los procesos que lo necesitan. Lección para el recuerdo: cuando cambias el usuario que ejecuta un servicio, revisa todos los canales locales —sockets, volúmenes, señales— no solo las carpetas de datos.

El actualizador que se actualiza solo

Otro de los pendientes eternos cayó: hasta hoy, actualizar el propio actualizador del servidor era cirugía a mano. Ahora hay un comando firmado que lo hace todo: verifica la firma del catálogo, baja la imagen, extrae el código nuevo y hace el cambio con copia de seguridad y comprobación de salud. Si la extracción no trae un actualizador utilizable, aborta antes de tocar nada.

Durante la prueba en real saltaron dos fallos que el propio mecanismo contuvo — una copia sin recursividad y el peor de todos: un contenedor que escribía en su propio directorio temporal sin montar el destino, es decir, trabajar y tirar el resultado a la papelera sin error aparente. Ambos cazados por la verificación "¿lo que se ha extraído sirve?" antes de instalar. Fallar rápido y ruidoso en un instalador es una virtud.

El misterio del postgres vecino

La joya del día. Migramos la web del producto al circuito nuevo: repositorio propio, imagen propia, instalación en el otro servidor, datos reales restaurados. Y la instalación fallaba con "contraseña incorrecta"... siendo correcta.

El culpable: en un servidor compartido, la red donde viven los servicios públicos aloja otras instalaciones ajenas. Y el DNS interno de Docker —el servicio que resuelve nombres en direcciones— enviaba mi "db" hacia el postgres de otra instalación. Mi API le hablaba con mi contraseña a un servidor que no era el mío. Obviamente, fallaba.

La solución arquitectónica: la API, el panel y la web de cada cliente quedan solo en su red privada del proyecto; solo la puerta de entrada toca la red pública. Y alias explícitos para que las comprobaciones resuelvan bien.

La migración terminó bien: 35 artículos reales, admins, medios y la analítica reviviendo en producción tras llevar rota desde el 17 de septiembre. Y la prueba final del propietario: un cambio de menú construido en GitHub y aplicado con el botón, sin tocar ningún servidor.

Adiós a la web transitoria

Para cerrar el día, retirada simbólica: borramos el árbol web/ transitorio que arrastraba desde las primeras iteraciones. Las releases del núcleo ya son solo API y panel. Menos código muerto, menos superficie de ataque, releases más pequeñas. La lección operativa: antes de borrar, mapear todas las referencias —y no olvidar el compose de desarrollo, que el código muerto no solo vive en los workflows.

En la entrada anterior se cerraban los agujeros de la auditoría. Y en la siguiente, el diario estrena repositorio propio.

Otras entradas del diario