Cómo proteger los servicios en Linux con Latch y NFQUEUE

martes, 30 de septiembre de 2014

Uno de los escenarios más habituales de Latch es el de proteger el login en una aplicación web, aunque también podamos usarlo para proteger el acceso por login a nuestro Windows, Linux, Mac o cualquier otro servicio que utilice PAM. Gracias a Latch, vamos a poder proteger nuestra identidad en el caso de que alguien pueda robar nuestro usuario y contraseña. Pero también podemos aplicar el mismo escenario no sólo a proteger nuestra cuenta en un determinado servicio, sino que además, podemos proteger ese servicio de ser accedido por cualquier persona; de esta manera, podríamos activar ese servicio cuando se necesitara y ya sólo no nos protegeríamos de robos de identidad, sino que también nos podríamos proteger de cualquier vulnerabilidad que le pudiera afectar (como ShellShock, gotofail, fallos en la implementación de criptografía, etc.).

Estos servicios que queremos proteger, generalmente suelen ser los servicios que se utilizan de forma privada, como la administración de un servidor usando OpenSSH, un servidor FTP, OpenVPN, etc. es decir, servicios que utilizamos esporádicamente pero no de forma habitual. De esta manera, tendríamos una doble protección con Latch, puesto que con Latch se podría activar ese servicio cuando se deseara, y también posteriormente activar el acceso con una cuenta de usuario concreta.

Algunas de las prácticas utilizadas para proteger estos servicios son bastante tediosas, como por ejemplo, filtrar el acceso por dirección IP (que limita el uso) o el uso de portknocking (que añade complejidad), entre otros. Esta solución propuesta puede sustituir al portknocking con menor complejidad.

NFQUEUE

En sistemas operativos Linux, existe una forma muy sencilla de realizarlo, mediante el uso de NFQUEUE.

NFQUEUE es una acción de "iptables" que permite delegar la decisión sobre qué hacer con un paquete de red a un programa que se ejecute en el espacio de usuario. Si bien, típicamente las acciones habituales son ACCEPT, DROP o REJECT, en este caso al hacer NFQUEUE, lo que estamos haciendo es enviar el paquete a una cola implementada como una lista enlazada de paquetes y sus metadatos, indexados por un entero. Posteriormente, desde el espacio de usuario, podemos acceder al contenido de esta cola de paquetes, y después de tratarlos, emitir un veredicto de qué queremos hacer con ellos (por ejemplo ACCEPT, DROP o REJECT).

La única desventaja de este método es que cuando la cola se llena, cualquier otro paquete que estaba dirigido a esa cola se descarta, con lo que hay que tener cuidado con que el tratamiento de los paquetes sea lo suficientemente rápido para y jugar con el tamaño máximo de esa cola.

El protocolo de comunicación entre el kernel y el espacio de usuario es nfnetlink. Los mensajes no utilizan ningún tipo de memoria compartida, sino que el funcionamiento está basado en un socket para enviar el paquete y sus metadatos.

Para poder utilizarlo, la configuración es muy sencilla:

  • Primero tenemos que establecer una regla en "iptables" para enviar ciertos paquetes a NFQUEUE (podemos incluso tener varias colas)
  • Posteriormente, tenemos que tener nuestro programa en espacio de usuario que lea las colas que hemos definido, trate los paquetes, y por último emita su veredicto.

El primer paso es muy sencillo, puesto que tan sólo tenemos que utilizar el comando "iptables"; por ejemplo, si quiero proteger el acceso a mi servidor de OpenSSH, utilizaría el siguiente comando:

iptables -A INPUT -p tcp --dport 22 -j NFQUEUE --queue-num 0

Con esta regla de "iptables" podemos tratar cualquier paquete cuyo puerto destino sea el 22 (generalmente el de OpenSSH), pero trataríamos todos los paquetes incluyendo el uso normal de OpenSSH. Como nuestra intención es poder controlar el inicio de la conexión solamente (y para así evitar sobrecargar la comprobación con Latch), podemos añadir esta otra regla "iptables" para que en el caso de que la conexión esté establecida, se dejen pasar los paquetes sin tratarlos, y tan sólo tratemos los inicios de conexión:

iptables -I INPUT -m state -p tcp --dport 22 --state ESTABLISHED -j ACCEPT

Existen además otros argumentos interesantes cuando estamos utilizando NFQUEUE:

  • --queue-balance: nos permite balancear los paquetes entre varias colas
  • --queue-bypass: nos permite aceptar el paquete si no hay ningún programa en espacio de usuario escuchando
  • --fail-open: en vez de descartar todos los paquetes cuando la cola está llena, los acepta.
  • --batching-verdict: permite emitir un veredicto a un grupo de paquetes

A continuación, es necesario tener ejecutándose el programa de espacio de usuario encargado de poder tratar los intentos de conexión, y para ello podemos usar la librería libnetfilter_queue en C, o cualquiera de sus bindings en Perl y Python.

Por ejemplo, en Python es tan sencillo como:




O en C, basándonos en el ejemplo que incluye libnetfilter_queue (no lo incluimos en el post por su extensión, pero se puede ver aquí).

Estas pruebas de concepto pueden ser claramente mejorables, sobre todo añadiendo operaciones asíncronas o hilos de ejecución para que sea más eficientes, así como filtros más concretos.

De esta manera podemos controlar en todo momento cuándo deseamos tener un cierto servicio accesible para su uso, o sea, una capa adicional de seguridad para evitar futuros incidentes de seguridad.


David Barroso
david.barroso@11paths.com

No hay comentarios:

Publicar un comentario en la entrada