One self-hosted console to run your entire business — commerce, ERP, HRM, CRM & manufacturing

Cómo proteger y publicar sistemas de alto volumen: borde reforzado y despliegues sin caída

Las capas de seguridad desde el CDN y el WAF hasta la red privada, y una canalización de entrega con despliegues graduales, blue/green y migraciones expand and contract, con una lista de puesta en marcha para el día del pico.

Author

Anichur Rahaman

hace 1 día13 min read1 views
Cómo proteger y publicar sistemas de alto volumen: borde reforzado y despliegues sin caída

Diecisiete minutos antes de la queja del pago en la parte 4, a las 9:44 de ese mismo día del lanzamiento, el ingeniero de guardia del sitio de boletos enfrenta otro problema. La venta abre a las 10:00 y unos 1,000 compradores actuarán en el mismo minuto. Un desarrollador quiere subir una corrección de una línea para un error de ortografía en el correo del pedido. A las 9:52 el tablero muestra 4,000 intentos de inicio de sesión por minuto desde un puñado de direcciones: un script de revendedores calentando motores. (Es un escenario ilustrativo, no un incidente real.)

Cómo termine esa mañana depende de dos decisiones tomadas semanas antes: cuánto del sistema alcanza Internet, y si publicar una versión exige detener el sitio. Si las respuestas son «todo» y «sí», el ingeniero tiene que rechazar la corrección y esperar que el script se canse solo.

Este último artículo cubre las dos cosas: un camino reforzado desde Internet hasta los datos y una canalización de entrega que nunca detiene el sitio. Parte de mis notas de campo en plataformas preparadas para picos programados, como una venta relámpago, un lanzamiento de boletos o el inicio de un examen. Son las lecciones de una configuración, no una receta universal.

Esta es la parte 5 de 5, la última, de la serie «Ingeniería para alto volumen». Partes anteriores: parte 1, la capa web y el autoescalado; parte 2, colas y workers; parte 3, la capa de datos; parte 4, observabilidad.

La seguridad es una hilera de muros pequeños

Ningún producto aislado protege un sistema. Un firewall no arregla una contraseña filtrada, y una contraseña fuerte no sirve si la base de datos está abierta a Internet. El enfoque práctico son las capas: cada una da por hecho que la de adelante fallará alguna vez.

El OWASP Top 10 es una buena prueba de realidad. La edición vigente, OWASP Top 10:2025, pone en primer lugar Broken Access Control y en segundo Security Misconfiguration. Ambas tratan de cómo está armado el sistema, no de exploits exóticos. Coincide con lo que veo: la mayoría de los incidentes nacen de un puerto que quedó abierto, un valor por defecto que nadie cambió o un permiso más amplio de lo necesario.

Diagrama de capas de seguridad desde Internet público, pasando por CDN, WAF y balanceador de carga, hasta una red privada con nodos web, workers, base de datos y caché
Solo el borde es público. Todo lo que está detrás vive en una red privada y acepta tráfico únicamente de orígenes concretos.

El borde: CDN, WAF, bots y límites de tasa

Pon un CDN con un firewall de aplicaciones web (WAF) delante de todo lo que sea público. Absorbe inundaciones de tráfico, sirve páginas en caché sin tocar tus servidores y bloquea los patrones de ataque más comunes antes de que lleguen a tu código. En un pico también protege tu capacidad: cada solicitud que responde el borde es una que tus nodos web nunca ven.

Tres ajustes pesan más que el resto:

  • Gestión de bots. Una venta relámpago atrae revendedores y scripts. Desafía a los clientes sospechosos en las rutas de pago e inicio de sesión, no en todo el sitio, para no frenar a los clientes reales.
  • Límites de tasa en el borde. Limita por cliente el inicio de sesión, el restablecimiento de contraseña, la búsqueda y el pago. Saca el límite del tráfico real: en una prueba de carga, reproduce la mezcla de solicitudes de un pico anterior y anota la tasa legítima más alta por cliente.
  • Límites de tasa en la aplicación. El borde se puede esquivar si alguien encuentra la dirección del servidor de origen. Mantén un segundo límite en la app, por cuenta y por acción, para que un solo usuario no martille un endpoint costoso.

Cierra el origen para que solo acepte tráfico de la red del borde. Si tus servidores responden directo a cualquiera en Internet, el WAF es una sugerencia, no un control.

TLS, y dónde termina

Cifra todo en tránsito. Usa TLS 1.3 donde los clientes lo soporten y deja TLS 1.2 como piso. PCI DSS 4.0 exige criptografía robusta para los datos de tarjetahabientes en redes públicas y excluye SSL y las versiones antiguas de TLS, así que si aceptas tarjetas, apaga TLS 1.0 y 1.1.

La pregunta de diseño es dónde termina TLS. Muchas configuraciones lo terminan en el CDN, otra vez en un proxy inverso del host y luego pasan HTTP simple al contenedor. Ese último salto está bien dentro de una red privada del host, pero solo si estás seguro de que de verdad es privada. Si el tráfico cruza una red que no controlas, cifra también ese salto.

Otra trampa son los límites que no coinciden. En una plataforma que operé había dos capas de nginx en serie: un proxy del host que terminaba TLS y un proxy del contenedor delante de PHP-FPM. El límite de cuerpo por defecto del host era de 1 MB, así que las cargas de archivos fallaban en silencio hasta que todas las capas se pusieron de acuerdo: client_max_body_size en ambos proxies, y post_max_size y upload_max_filesize en PHP. Es un problema de confiabilidad, pero también tienta a subir un límite a ciegas. Define el máximo una sola vez, aplícalo en cada capa y anótalo.

Una red privada: solo el borde es público

La regla más valiosa es también la más aburrida: nada es público salvo el borde y el balanceador de carga. Los nodos web, los workers, la base de datos, la caché y el nodo de búsqueda viven en una red privada. La base de datos y la caché administradas solo aceptan conexiones desde esa red, de modo que una contraseña filtrada por sí sola no le da al atacante un camino hacia adentro.

Suma reglas de firewall que digan quién puede hablar con quién, por ejemplo:

ComponenteQuién puede conectarse¿Público?
CDN / WAFInternetSí
Balanceador de cargaSolo los rangos de la red del bordeSí, restringido
Nodos web y SSRSolo el balanceador de cargaNo
Workers y programadorNadie desde afueraNo
Base de datos y cachéNodos web y workers, por dirección privadaNo
SSH y acceso administrativoUn host de salto o VPN, solo con llavesNo

Revisa esta tabla cada vez que cambie la topología. La mayoría de las historias de «estuvimos expuestos un mes» empiezan con un cambio rápido que nadie anotó.

El incidente del alias compartido: aislamiento dentro del host

El aislamiento de red no es solo cuestión de Internet. En un host encontré varios proyectos vecinos compartiendo una misma red de Docker, y cada uno había llamado app a su servicio de PHP. El proxy estaba configurado para enviar las solicitudes a app en el puerto 9000, y ese nombre se resolvía a varios contenedores. La mitad de las solicitudes llegaba al PHP-FPM de otro proyecto.

No se cayó nada. Las páginas simplemente venían del código equivocado, con la configuración equivocada y a veces con la base de datos equivocada. Es un problema de confiabilidad y de exposición de datos a la vez.

La solución es simple. Dale a cada proyecto su propia red, usa nombres de servicio únicos y no expongas un servicio a una red cuando solo necesita alcanzar otra cosa. Al revisar un despliegue, hazte una pregunta directa: ¿puede este contenedor llegar a algo a lo que no tiene motivo para llegar?

Secretos, cuentas y parches

Tres hábitos evitan una buena parte del daño.

  • Los secretos se quedan fuera de las imágenes. Una imagen se copia a registros, laptops y cachés de compilación. Inyecta los secretos en tiempo de ejecución desde el entorno o un almacén de secretos, y rótalos cuando alguien se va.
  • Mínimo privilegio en cada cuenta. El usuario de base de datos de la aplicación no debería poder borrar tablas ni crear usuarios. Los workers, los reportes y las migraciones pueden usar cuentas distintas con permisos distintos. El personal recibe roles, no un acceso de administrador compartido, y el acceso administrativo lleva un segundo factor.
  • Parcha con calendario. OWASP añadió Software Supply Chain Failures a su lista de 2025 por algo: lo que usas como dependencia forma parte de tu superficie de ataque. Reconstruye las imágenes con regularidad, escanéalas en busca de vulnerabilidades conocidas y mantén corta la lista de dependencias que de verdad usas. Puedes contrastar la configuración de tus servidores y contenedores con los CIS Benchmarks de Docker y de las distribuciones de Linux más comunes.

Por último, da por hecho que algo saldrá mal tarde o temprano. Guarda respaldos que un atacante no pueda alcanzar con las mismas credenciales y practica la restauración. Lo cubro en respaldos preparados contra ransomware para sistemas de negocio; aquí solo añado que una restauración que nunca has probado es una esperanza, no un plan.

Publicar sin caída: compila una vez y mueve el mismo artefacto

Un despliegue que da miedo hace que la gente evite los parches y los cambios, así que la meta es un despliegue aburrido. La primera regla: compila una vez y envía exactamente el mismo artefacto. Construye la imagen en una máquina, súbela y haz que todos los nodos descarguen esa misma imagen. Antes de cambiar el tráfico, compara los ID de imagen en todos los nodos. Nunca actualices el código fuente ni compiles en un nodo que está sirviendo: dos nodos compilados con una hora de diferencia pueden volverse distintos sin que nadie lo note, y una compilación que falla a medias puede dejar roto un servidor en vivo.

La configuración viaja aparte de la imagen, así que el mismo artefacto pasa de preproducción a producción sin cambios. Por eso puedes decir que lo que probaste es lo que publicaste.

Migraciones con el sitio arriba: expand and contract

El «cero caída» suele romperse en los cambios de base de datos. Durante un despliegue gradual, el código viejo y el nuevo corren a la vez sobre un mismo esquema, así que la migración debe servir para los dos.

La respuesta estándar es el patrón expand and contract, también llamado parallel change:

  1. Expand. Agrega la columna o tabla nueva de una forma que el código viejo ignore, por ejemplo una columna que admita nulos.
  2. Migrate. Publica código que escriba en la forma vieja y en la nueva, y rellena las filas existentes en lotes pequeños en segundo plano.
  3. Switch. Pasa las lecturas a la forma nueva y vigila los errores.
  4. Contract. Cuando ningún código en ejecución use la forma vieja, elimínala en una versión posterior.

Un ejemplo con aritmética, con cifras ilustrativas. Quieres renombrar la columna phone de una tabla customers de 2.4 millones de filas a contact_phone. Expand: agrega contact_phone, que admite nulos. Migrate: el código nuevo escribe en ambas columnas en cada guardado, mientras un trabajo en segundo plano copia 5,000 filas por lote. Son 480 lotes; a unos dos segundos cada uno, tarda cerca de 16 minutos sin bloquear la tabla para ningún usuario. Antes de mover las lecturas, comprueba que el número de filas con phone lleno y contact_phone vacío sea cero. Solo entonces haz el contract.

El borrado es el único paso irreversible, así que espera. Ejecuta las migraciones mientras el sitio atiende tráfico y verifica el resultado: el conteo de filas antes y después, y una comprobación de que nada quedó a medio convertir. Toma un respaldo fresco de la base de datos antes de la ventana y conserva también una referencia de reversa para el código.

Backends graduales, frontends blue/green, workers y programador

Cada capa necesita una estrategia distinta. Para los backends PHP sin estado uso un despliegue gradual, un nodo a la vez:

Diagrama de una canalización de despliegue sin caída con compuertas: compilar una vez, verificar ID de imagen, migrar con el sitio arriba, backend gradual, frontend blue/green, drenar workers, programador al final, periodo de observación
Cada etapa tiene una compuerta. Si una falla, la canalización se detiene y la versión anterior sigue atendiendo.
  1. Drena un nodo en el balanceador de carga, para que termine las solicitudes en curso y no reciba nuevas.
  2. Despliega la imagen nueva en ese nodo y reinicia los workers de PHP para que OPcache tome el código nuevo.
  3. Corre una prueba de humo directo contra el nodo: iniciar sesión, abrir un producto, agregarlo al carrito.
  4. Reincorpora el nodo al balanceador y déjalo en observación unos minutos mientras vigilas errores y latencia.
  5. Repite con el siguiente nodo.
Diagrama de flujo de un despliegue gradual de un nodo con una decisión: si la prueba de humo pasa, el nodo se reincorpora y entra en observación; si no, queda fuera y se restaura la imagen anterior
La decisión que importa ocurre cuando el nodo todavía está fuera del balanceador: una prueba de humo fallida no cuesta nada.

Con dos nodos, el sitio siempre tiene un servidor sano. En solicitudes idempotentes, una regla de reintento en el proxy puede ocultar a los usuarios una falla pasajera, pero no la actives para solicitudes que cobran una tarjeta.

Para los frontends renderizados en el servidor prefiero blue/green. Levanta la versión nueva junto a la vieja, pruébala en privado y cambia con la modificación de un solo archivo del proxy y una recarga ordenada. El rollback es el mismo paso al revés, en cerca de un segundo. StoreConsole opera su propia producción de esta manera.

Los workers y el programador necesitan su propio cuidado. Drena las colas antes de cambiar los workers: deja de tomar trabajos nuevos, da un margen generoso para que terminen los que están en curso y luego inicia la versión nueva. Inicia el programador al final, en exactamente un nodo, para que un sistema desplegado a medias no ejecute una tarea recurrente dos veces o sobre el código equivocado.

Verificación, observación y la lista de puesta en marcha

Un despliegue termina cuando lo has demostrado, no cuando el comando regresa. Corre una verificación de punta a punta que cubra una compra o un envío reales, confirma que las colas avanzan y luego observa: sigue mirando la tasa de errores, la latencia p95 y la antigüedad de las colas durante un tiempo definido antes de darlo por terminado. Usa los tableros de la parte 4 de esta serie y, ante una alerta alarmante, contrástala con el proceso real antes de actuar.

Esta es la lista que uso antes de un evento de alto volumen:

  1. Haz una prueba de carga a la tasa objetivo con umbrales de aborto y guarda los resultados.
  2. Escala por adelantado la capacidad web y de frontend antes del evento, en lugar de esperar al escalado reactivo.
  3. Confirma que el origen solo acepta tráfico del borde y que las reglas de WAF, bots y límites de tasa están activas en inicio de sesión y pago.
  4. Confirma que la base de datos, la caché y el nodo de búsqueda no tienen dirección pública.
  5. Revisa la configuración de TLS y las fechas de vencimiento de los certificados, y que los límites de carga coincidan en cada capa.
  6. Toma un respaldo fresco de la base de datos, prueba que se restaura y anota las referencias de reversa.
  7. Congela los cambios salvo correcciones de emergencia y define quién puede aprobarlas.
  8. Confirma que las alertas llegan a una persona despierta, con una ruta de escalamiento por escrito.
  9. Haz un ensayo del rollback, incluido el cambio de frontend con una sola recarga.
  10. Verifica que el programador corre en exactamente un nodo y que las colas no tienen trabajos viejos.

Volvamos a las 9:44. Con todo esto en su lugar, el ingeniero le dice que sí a la corrección ortográfica. La imagen se construyó y probó una hora antes, los nodos se actualizan de uno en uno y los 4,000 intentos de inicio de sesión por minuto chocan con un desafío solo en la ruta de acceso, mientras los compradores navegan sin estorbos. La base de datos no tiene dirección pública, así que detrás del borde el script no encuentra nada. La corrección queda en vivo y observada a las 9:55, cinco minutos antes de que abra la venta.

La tabla siguiente muestra cómo falla cada capa y cómo revisarla rápido.

CapaFalla típicaRevisión rápida
BordeEl origen es accesible directamenteSolicita la dirección del origen desde fuera de la red del borde
RedBase de datos abierta a InternetEscaneo de puertos desde un host externo
HostAlias de servicio compartidoLista a qué contenedores se resuelve cada nombre
SecretosCredenciales dentro de una imagenBusca en las capas de la imagen y en el historial del repositorio
DespliegueNodos con compilaciones distintasCompara los ID de imagen en cada nodo
Base de datosMigración destructiva bajo cargaRevisa con expand and contract; ensaya en una copia

Conclusiones clave

  • Defiéndete por capas: CDN y WAF, un origen cerrado, una red privada, cuentas con mínimo privilegio y secretos fuera de las imágenes.
  • Haz públicos solo el borde y el balanceador de carga, y revisa las rutas permitidas cada vez que cambie la topología.
  • Aísla los proyectos de un host con redes propias y nombres de servicio únicos, para que un alias compartido nunca mande tráfico al código equivocado.
  • Compila una vez, envía el mismo artefacto y verifica los ID de imagen; nunca compiles en un nodo que está sirviendo.
  • Cambia la base de datos en pasos pequeños con expand and contract, para que el código viejo y el nuevo funcionen durante todo el despliegue.
  • Actualiza los backends nodo por nodo, cambia los frontends con blue/green, drena los workers, inicia el programador al final y cierra con un periodo de observación y un rollback ya ensayado.

Anichur Rahaman es arquitecto de software y creador de StoreConsole. Diseña sistemas de comercio y ERP para negocios en crecimiento, con enfoque en arquitectura orientada a eventos, integridad de datos y operación en servidores propios.

About the Author

Anichur Rahaman

Continue Reading