Introducción
Cualquiera que haya tenido un servidor en Internet habrá experimentado centenares de intentos de acceso, la búsqueda de rutas extrañas probando a ver si alguna falla o abre algún hueco inesperado por el que colarse.
Hace años uno podía montar un servidor y que este existiera de forma más o menos anónima hasta que quisieras, pero hace mucho que ha dejado de ser así.
Ahora enciendes una máquina en Internet y en cuestión de minutos ya tienes, como si de una película de zombies se tratase, al enemigo intentando tirar la puerta abajo.
Vamos a explorar qué opciones tenemos y a presentar una forma de detectar algunos de estos ataques con un módulo de Terraform nuestro, open source.
¿Qué es un ataque de escaneo?
En el caso de la web, cuando hablemos de ataque de escaneo estamos hablando de un ataque en el que se mandan múltiples solicitudes a nuestra web para intentar detectar qué servicios tenemos disponibles, qué puertos tenemos abiertos o qué software estamos utilizando en nuestro servidor.
En los logs del servidor veremos muchos accesos que devuelven errores 404 (No encontrado), y que también le sirven al atacante para ver qué hacemos en esos casos (devolvemos el error directamente o pasa por algún otro sistema nuestro que devuevle información intermedia que le podría servir al atacante). En definitiva, intentan averiguar cómo de bien protegidos estamos.
Parte de estas búsquedas que devuelven 404 son intentos de acceso a direcciones habituales en otros sistemas. Por ejemplo, aunque no estés usando un Wordpress, habrá muchos intentos de acceso a /wp-admin/ que es la dirección por defecto de administración de un Wordpress (esto es uno de los motivos por los que, en caso de tener Wordpress, de las primeras cosas que podemos hacer para darle algo más de seguridad es cambiar la dirección para acceder al Admin).
¿Qué opciones tenemos para protegernos?
Un poco más arriba hemos dicho que veremos una forma de detectar estos ataques con un módulo nuestro de Terraform, pero antes de ir al Marketing (mal Marketing porque es completamente gratis, pero...), te cuento otras opciones y el porqué de nuestro módulo.
Web Application Firewall o (WAF)
Es un Software que ponemos entre Internet y nuestra web para cortar todo lo que no queramos que llegue a nuestra máquina (incluídos escaneos y bots). Los hay más o menos sofisticados, más o menos "listos" y dinámicos. Opciones típicas, Cloudflare WAF, Google Cloud Armor, AWS WAF.
Si no tienes la infraestructura en Google Cloud o Amazon AWS, me iría de cabeza a Cloudflare WAF. Tienes una capa gratuita, la de pago empieza en cantidades razonables y es relativamente sencillo de usar. Aparte, integra otra serie de herramientas, como CDN, etc. Por contra, el tráfico a tu web tiene que pasar sí o si por Cloudflare.
Bloqueo de IP's con Fail2ban
Fail2ban es un software opensource que podemos instalar en nuestro servidor y que monitorizará los logs para ver señales de ataques y escaneos por parte de bots u otro tipo de usuarios. Si usas un servidor con alguna distribución de Linux, es altamente probable que ya lo tengas instalado. Puedes ver más en su repositorio de Github:
https://github.com/fail2ban/fail2ban
Un problema con fail2ban es que tenemos que tenerlo instalado sí o sí en nuestra máquina, no sirve para despliegues en sistemas de gestión de contenedores, como Kubernetes o Google Cloud Run, o en entornos serverless como Google App Engine.
Alternativas para protegernos en Google Cloud
Ya hemos visto que tenemos distintas opciones de Software ya existente, como fail2ban y productos tanto de Google como Amazon o Cloudflare para proteger nuestras máquinas. ¿Por qué tendríamos que probar a hacerlo de una forma diferente, con nuestro módulo de Terraform que monta un sink de los logs y unas colas de eventos de Pub/Sub? Suena muy complicado.
El motivo principal es económico. La alternativa oficial, sencilla, de Google Cloud es Google Cloud Armor, que tiene dos modelos de precio, Standard, en el que pagas en función del uso y Enterprise, en el que pagas una suscripción en función del número de recursos a proteger.
El Standard no incluye la protección "adaptativa" que genera reglas de protección dinámicas en función de ataques que estés recibiendo, que es lo que nos interesa en este artículo. Para eso tienes que tener obligatoriamente una suscripción Enterprise.
Hasta hace poco el Enterprise empezaba en 3000$ al mes, aunque actualmente existe un "pay as you go", un poco más económico (200$) hasta 2 recursos y 200$ adicionales por recurso, al mes, de suscripción. Luego hay que añadir otros costes por tráfico.
-Vale, me has convencido, quiero probar otras formas de conseguir lo mismo.
Existen en Google lo que se llaman sinks de logs. Un sink sería literalmente como un sumidero, pero técnicamente es una forma de filtrar los logs que hay en Google Cloud y mandar eso que hemos filtrado a un lugar diferente. Como si en medio del torrente de información que pasa por nuestra tubería hiciéramos un agujerito y le dijéramos que todo lo que caiga por ese agujero, nos lo pase a otro cubo. Hay muchas aplicaciones de esto, por ejemplo si integras Datadog con Google Cloud, para analítica de todos tus sistemas, profiling, trazabilidad de las aplicaciones, la forma recomendada por Datadog para mandarles todos los logs que quieras de Google Cloud es a través de un sink.

He metido una foto de un fregadero para no tener todo texto y porque sink, como decía, literalmente significa eso.
Pero me estoy enrollando. Básicamente, lo que podemos hacer es con nuestro sink es filtrar los logs que den un error de tipo 404 (no encontrado) y mandarlos a una cola de eventos. Luego esta cola de eventos la procesamos con una función que medirá cuántos errores de estos estamos teniendo en una ventana de tiempo, y si son del mismo usuario.
Claro, si lo hacemos mandando logs a una cola de eventos y procesándolos, para la función que los reciba cada línea será independiente del resto, tenemos que conseguir que se acuerde, de algún modo, de lo que ha ido pasando. Esto, en nuestro caso, lo haremos guardando en un Redis cada vez que una IP determinada nos pida un recurso y haya un error 404. Para detectar si nos han atacado más de X veces en la última hora, por ejemplo, guardaremos esas llamadas con un caducidad de una hora. Si quisiéramos media hora, guardaríamos con caducidad de media hora, todo esto es configurable.
Cuando la función que recibe los eventos detecta un ataque, lanza un evento a otra cola de Google Pub/Sub.
Podríamos configurar todo esto a mano, pero puede ser muy complejo cambiarlo, si el día de mañana tenemos que modificar cualquier cosa, si tenemos que montar esto en varios entornos andar repitiendo exactamente lo mismo, puede ser un foco de errores brutal. Y ya hay un montón de cosas inventadas ya...
Nuestro módulo en Terraform
Terraform es una herramienta de infraestructura como código (IaC) que te permite definir, desplegar y gestionar recursos en la nube de forma automatizada. En lugar de crear todo lo que hemos visto hasta ahora (colas de Pub/Sub, el Redis, la función de Google Cloud Function que recibe los eventos, el sink para los logs) manualmente desde la consola de tu proveedor, con Terraform puedes escribir un archivo de configuración, ejecutar un par de comandos y tener toda tu infraestructura lista en segundos.
Funciona con múltiples plataformas como Google Cloud, AWS y Azure, y se puede usar para automatizar despliegues y mantener todo bajo control con versiones y cambios bien documentados.
Por eso hemos hecho un módulo en Terraform, para montar todo lo que hemos comentado en este artículo, y lo hemos dejado de acceso libre en Github.
En nuestro módulo de Terraform tendrás unas variables not_found_request_limit para definir cuántas peticiones dejamos y not_found_request_window la ventana temporal en segundos.
Es decir, si llegan más peticiones que lo definido en not_found_request_limit, de la misma IP, que dan un error 404, en esos not_found_request_window segundos, lanzaremos una alerta.
Esta alerta a su vez la mandaremos como un evento a un topic de Google Pub/Sub, para poder gestionarla a posteriori como queramos.
Las variables que tenemos son:
- project: El proyecto de GCP donde se desplegará.
- region: La región de GCP donde se desplegará. Por defecto, es europe-west1.
- resource_prefix: El prefijo que se usará para todos los recursos. Por defecto, es scan-attack-detector.
- not_found_request_window: La ventana de tiempo en la que se comprobarán las solicitudes no encontradas. Por defecto, es 60.
- not_found_request_limit: El límite de solicitudes no encontradas dentro de la ventana de tiempo para activar un ataque. Por defecto, es 10.
- sink_filter: El filtro que se usará para el sink. Por defecto, es protoPayload.status=404 OR httpRequest.status=404.
- temporary_artifact_bucket_name: El nombre del bucket que se usará para los artefactos temporales.
- redis_host: El host de la instancia de Redis.
- redis_port: El puerto de la instancia de Redis. Por defecto, es 6379.
- redis_database: La base de datos que se usará en la instancia de Redis. Por defecto, es 0.
- redis_vpc_connector_id: El ID del VPC Access Connector que se usará para la instancia de Redis. Por defecto, es null.
- redis_vpc_connector_egress_settings: La configuración de salida que se usará para el VPC Access Connector. Por defecto, es null.
Hasta aquí nuestra explicación de qué son los ataques de escaneo y cómo podemos recibir alertas en Google Cloud (es fácilmente aplicable en otros entornos) configurando con Terraform un sistema de filtrado de eventos a partir de un sink de los logs.
Tenéis el módulo y más documentación en: https://github.com/softspring/tf-google-cloud-scan-attack-detector