Atlasingeniería

Incidentes en un servidor propioQuedar expuesto sin darse cuentaTema 1

El puerto que el firewall no protegía

El firewall decía que sólo pasaban tres puertos. Un contenedor levantado con el archivo equivocado publicó una base sin contraseña y en dos minutos entró un gusano de minería. Por qué Docker se salta las reglas del firewall del sistema y cómo quedó cerrado.

Un servidor con una decena de aplicaciones, operado por una sola persona. El firewall del sistema —el de siempre, el que se configura con tres comandos— decía exactamente lo que tenía que decir: abiertos 22, 80 y 443, todo lo demás cerrado.

Dos minutos después de un despliegue mal hecho, una base de datos en memoria sin contraseña estaba respondiendo desde internet, con claves nuevas apuntando a un proceso de minería. El firewall seguía diciendo que sólo pasaban tres puertos. No mentía: nunca estuvo mirando los contenedores.

Por qué el firewall no ve a los contenedores

En Linux, tanto el firewall de usuario como Docker terminan escribiendo reglas en la misma maquinaria de filtrado del kernel. La diferencia está en el orden.

El recorrido de un paquete que llega de internet

  1. Entra por la interfaz de red del servidor.
  2. Pasa por la cadena de reenvío hacia contenedores, donde Docker inserta sus propias reglas al publicar un puerto.
  3. Esas reglas se evalúan antes que las del firewall de usuario, que está pensado para el tráfico dirigido al host.
  4. El paquete llega al contenedor. El firewall nunca tuvo la oportunidad de rechazarlo.

Por eso ports: "6379:6379" en un archivo de composición abre ese puerto a internet aunque el firewall diga lo contrario. No es un bug: es la consecuencia de que Docker administre su propio reenvío. Y es exactamente la clase de detalle que uno descubre el día que importa.

Qué pasó exactamente

El disparador no fue un ataque sofisticado, fue un comando de más:

La secuencia

  1. Se recreó una aplicación combinando su archivo de composición de desarrollo con el de producción.
  2. Al combinar archivos, las listas de puertos se suman, no se reemplazan. Y como ambos archivos resolvían al mismo nombre de proyecto, los contenedores recreados fueron los de producción.
  3. Producción quedó corriendo con los puertos de desarrollo publicados: base de datos, almacenamiento de objetos y una base en memoria.
  4. Esa base en memoria, pensada para una red privada, no pedía contraseña.
  5. En unos dos minutos entró un gusano automatizado, dejó claves con un cron de minería y ejecutó un borrado total de la base.

Nada llegó a ejecutarse en el host: el intento de persistencia dependía de escribir en lugares a los que el contenedor no tenía acceso. Pero el borrado se llevó puesto el registro que usaba la cola de trabajos para saber en qué tick iba, y el planificador quedó muerto en silencio. Ese fue el daño real: no la intrusión, sino una funcionalidad que dejó de correr sin que nada avisara.

Antes de seguir, predecí

El firewall del sistema muestra sólo 22, 80 y 443 abiertos. ¿Qué garantiza eso?

Cómo quedó cerrado

El arreglo tiene dos mitades: una que bloquea el tráfico aunque alguien vuelva a publicar un puerto, y otra que avisa cuando eso pasa.

MedidaQué resuelveQué no resuelve
Reglas propias en la cadena que Docker consulta primeroSólo se aceptan 80, 443 y un puerto de mensajería desde la red públicaUn contenedor que salga hacia afuera
Atar los puertos de desarrollo a la dirección localQue un `up` sin argumentos no pueda exponer nadaEl error de combinar archivos
Nombre de proyecto distinto en los archivos de desarrolloQue desarrollo no pueda recrear los contenedores de producciónNada del firewall
Aviso automático ante un puerto publicado fuera de la listaEnterarse en minutos, no en semanasEl tiempo entre que se abre y llega el aviso
Contraseña en las bases, aunque estén en red privadaQue publicar por error no sea fatalLa exposición en sí
Ninguna de las cinco alcanza sola. La que más sirvió fue la primera; la que más tranquilidad da es la cuarta.

La auditoría, en cambio, es de una línea: recorrer los contenedores en ejecución preguntando qué puertos publica cada uno. En un servidor sano, la respuesta debería ser aburrida —el proxy y poco más—.

El incidente en el log

Buscá en el log

El firewall del sistema decía que sólo estaban abiertos 22, 80 y 443. ¿Qué línea muestra por qué la base quedó igual expuesta?

debug 2info 6warn 2error 212 de 12

La regla que lo hace imposible

Cierre

Autoevaluación

¿Lo entendiste?

¿Por qué un puerto publicado por Docker responde aunque el firewall lo bloquee?
Al combinar dos archivos de composición, ¿qué pasa con las listas de puertos?
Dos servicios del mismo servidor se hablan entre sí. ¿Necesitan puertos publicados?
¿Cuál fue el daño más costoso del incidente?