sábado, 31 de diciembre de 2011

Unix: Stateful firewalls vs. Non-Stateful firewalls

Hace unos días tuve una interesante discusión en Twitter sobre las ventajas de utilizar como firewall Packet Filter contra Netfilter (IPTables). Cabe destacar que tengo mas conocimientos de PF que de IPTables, por lo cual conozco más las ventajas prácticas del primero que del último.

En teoría de firewalls, encontramos los denominados stateful firewalls, son firewalls que actúan en layer 3 del modelo OSI/ISO (network) donde su punto fuerte es el poder de inspeccionar estados, y saber si los mismos corresponden a una conexión ya existente o no. Esto es posible lograrlo en Linux mediante connectrion tracking utilizando los módulos conntrack, y  así mantener un record de los estados existentes en el sistema operativo.

Tanto Packet Filter como IP Filter utilizan una estructura denominada tabla de estados (o status table), para mantener dicha información. Si un paquete IP ingresa al firewall, se va a buscar mediante una previa inspección del header si el mismo forma parte de una conexión ya existente, y si esto es cierto no se van a inspeccionar reglas de filtrado, sino que el paquete va a pasar directamente. Si un paquete llega por primera vez al firewall, el mismo si matchea con alguna regla, entonces va a crear una entrada en la tabla de estados del firewall, y ya, mientras la conexión siga viva no va a pasar por dicha inspección de reglas.

Por su parte IPTables, trabaja distinto. Cada vez que un paquete llega (sea nuevo o no), va a pasar por las reglas de filtrado, si matchea con alguna regla se aplicará esta, en caso contrario se aplica la policy por default previamente definida. Pero como dije anteriormente, mediante la utilización de los módulos conntrack es posible logar esto. Como se muestra a continuación:
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
En esta regla de filtrado todos los paquetes que ingresen al firewall con estado ESTABLISHED y RELATED van a ser aceptados, los cuales forman parte de una conexión ya existente posiblemente inicializada por un equipo de la red interna y de esta manera, por ejemplo es capaz mantener conexiones mediante NAT.

Esto puede ser realizado con una sintaxis más clara utilizando PF, y con la posibilidad de usar macros ;-)
wan_if='10.0.0.1'
lan_net='192.168.1.0/24'
pass in on $wan_if from any to $lan_net flags S/SA keep state
O mediante IP Filter:
pass in on em0 from any to 192.168.1.0/24 flags S/SA keep state
A modo resumen de lo visto hasta ahora podemos decir que todo firewall basado en estados se compone de dos elementos, ellos son: El conjunto de reglas de filtrado y la tabla de estados.

Esta tabla de estados en PF es una estructura en formato de árbol binario balanceado denominado AVL. Como sabemos la utilización de un árbol binario tiene un coste computacional un poco superior al de utilizar arboles hash, la decisión de utilizar un árbol binario en PF se basa en que fue concebido como parte del proyecto OpenBSD donde la principal preocupación al momento del diseño es la seguridad. Los hashes pueden provocar colisiones, esto es, dada distintas entradas obtener una misma salida, conociendo que a mayor cantidad de entradas en la tabla de estados, existe mayor posibilidad de colisiones, se opto por elegir un árbol binario como método de almacenar dichos estados.
Algoritmos de hashing como SHA1/SHA2 han demostrado tener casi nulas colisiones, pero el costo computacional de procesar búsquedas mediante estos algoritmos es bastante mas alto que el de utilizar algoritmos mas sencillos como MD4/MD5 que son fácilmente colisionables. Por lo cual, en la teoría, un atacante podría generar paquetes maliciosos para provocar colisiones en la tabla de estados y de esta manera realizar un ataque del tipo spoofing contra la red interna o DMZ y ganar acceso a equipos de acceso restringido.

Sabemos que el estado es una parte importante de los paquetes TCP, por cada paquete que matchee van a ser verificados varios campos por parte del firewall, esto es ISN (Initial Sequence Number), el origen (SRC), el destino (DST), puerto de origen (SRC PORT), y puerto de destino (DST PORT). PF computa el ISN añadiendo un valor aleatorio y de esta manera evitar ataques donde se altera el ISN y de esta manera evitar los ya conocidos SYN Flood que pueden provocar DoS/DDoS. IPTables no es capaz de realizar esta acción, aunque el costo computacional de no realizarlo es mucho menor del utilizado por PF.

Por su parte UDP, es conocido que es un protocolo sin estado por naturaleza, PF, también tiene la capacidad de manejar estados en conexiones UDP, ¿como lo consigue?. Las conexiones solo conocen dos pseudo estados, START y STOP, PF añade a esto un tiempo de expiración (acorde al nivel optimización seteado en el firewall), por lo cual mientras no haya expirado la conexión el paquete no va a tener que pasar por las reglas de filtrado, sino que simplemente se va a realizar una búsqueda en la tabla de estados.
Es importante conocer que para poder manejar esto, el tiempo de expiración se va a ir incrementando gradualmente a medida que lleguen paquetes que formen parte de la misma conexión hasta alcanzar su tope máximo. De esta manera se evitan que múltiples estados UDP sean creados y queden muertos a la espera de nuevos paquetes.

Los paquetes ICMP se dividen en dos categorías, ICMP-error e ICMP-querys (por ejemplo echo request/echo reply o más conocido como ping). Ambos tipos tienen la posibilidad de crear estados y ser manejados por el firewall, por ejemplo, si llega un ICMP-error al firewall, que es parte de una conexión existente, este matchea en la tabla de estados y puede pasar para notificar a la aplicación de que un error ocurrió. Lo mismo se aplica con los ICMP-querys.

Las posibles formas de mantener estados de PF, son las siguientes:
  • keep state: Trabaja con TCP, UDP e ICMP, es el default en las reglas de filtrado.
  • modulate state: Trabaja solo con TCP, recalcula el ISN.
  • synproxy state: Crea un proxy SYN para las conexiones TCP entrantes a fin de evitar conexiones spoofeadas. Combina las ventajas de modulate state y keep state. 
  • non state: Funciona con TCP, UDP e ICMP. Ningún estado es creado.
El costo de evaluación de reglas de firewall puede ser calculado mediante O(n), donde n es el número total de reglas por las que debe pasar el paquete. Mientras que el costo de evaluación de la existencia de estados es O(log m), donde m es el número de estados. Mediante esto podemos concluir, que la evaluación de reglas tiene un coste parecido a la evaluación de estados.

Es interesante la posibilidad que tiene PF de crear TCP SYN Proxies (mediante synproxy state), de manera, si un cliente quiere contactar a un host remoto, le pasa un SYN al firewall, el firewall crea una entrada en la tabla de estados, y es el propio firewall el que inicializa el trheeway-handshake con el host remoto, pasandole la conexión una vez establecida al cliente. Con esto se busca evitar TCP Syn Floods que pueden terminar en DoS/DDoS.

Por su parte PF, también optimiza el conjunto de evaluación de reglas, mediante un algoritmo denominado skip-steps, el cual utiliza un puntero en cada parámetro de la regla apuntando a la siguiente regla a ser evaluada, que especifica un valor diferente para el parámetro; Si la regla no matchea con algún parámetro el puntero es utilizado para saltar a la siguiente regla que puede llegar a matchear. Esta optimización es totalmente transparente, debido a que no se modifica el ruleset, sino que se genera una estructura que simplemente mejora la performance del firewall, obviamente acorde a como esté escrito el ruleset cargado.

La performance de un firewall esta dada por varios factores, tanto de hardware como de software. Por parte del hardware, es determinante el tipo de NIC utilizada, el bus al que se conecta esta NIC (ISA/PCI/PCIe), la memoria. Pero paradojimanete, múltiples CPU's o cores no ayudan a PF, ya que únicamente trabaja con un solo procesador.

Por su lado, a nivel software, es factor determinante la complejidad con que fue configurado el ruleset, mientras mas complejo sea, más lento es. Por su parte, la utilización de la tabla de estados por parte de PF puede ser optimizado para actuar de diferentes formas; Esto puede ser establecido mediante el parámetro set, en el archivo /etc/pf.conf, como se muestra a continuación:
set ruleset-optimization aggresive
Las distintas optimizaciones posibles del ruleset pueden ser:

  • normal: Utilizable para la mayoría de las redes.
  • high-latency: Para redes de alta latencia, como conexiones satélitales. 
  • aggresive: Expiración agresiva de conexiones de la tabla de estados, puede ayudar a reducir el uso de memoria en firewalls de alta carga, eliminando las conexiones inactivas.
  • conservative: Configuración extremadamente conservativa de recursos, puede aumentar la carga de la CPU debido al coste computacional utilizado para eliminar entradas de la tabla de estado.

El monitoreo de la tabla de estados, puede hacerse en tiempo real, de manera totalmente transparente, con herramientas disponibles tanto en packages como en ports, tales como pftop, o el propio pfctl ( pfctl -ss / pfctl -si), etc.

Los siguientes gráficos fueron extraidos del papper de Daniel Hartmeier "Design and Performance of the OpenBSD Stateful Packet Filter (pf)" y demuestran de manera clara y concisa lo expresado aquí. Las pruebas se basaron utilizando dos equipos 486, con dos NIC cada uno conectadas entre sí, un firewall utilizando PF, IPF e IPTables (de a uno por vez), y otro generando paquetes enviandolos a dicho Firewall. Como sistemas operativos se utilizaron OpenBSD 3.0 (Para PF tanto como para IPF), y Red Hat 7.2 con IPTables.

El primer test, se realiza el envío de paquetes de 256 bytes para un set de 100 reglas sin utilizar estados. La degradación de PF parece peor que la de IPTables y la de IPF.

En el segundo test se realiza la misma prueba con paquetes de 256 bytes, un set de 100 reglas y la utilización de estados. IPTables gana incrementalmente a mayor número de paquetes este test.
En el tercer test se intenta obtener el throughput máximo incrementando el número de reglas. El test es ganado por iptabales, debido a que tanto PF como IPF tienen un doble esfuerzo debido al filtrado de reglas inicial, y la búsqueda en la tabla de estados (y la creación de un estado si este aún no existe).

El cuarto test, es una comparativa de filtrado y utilización de estados entre PF e IPF, se puede notar que el throughput de PF decae mucho después que el de IPF a medida que se incrementa el número de paquetes.
En el quinto test, se comparan las latencias entre PF e IPF, puede observarse que IPF tiene una latencia un poco más alta que la de PF a medida que se incrementa el número de paquetes.
Luego de esto, se puede concluir que la evaluación de reglas es bastante mas cara que la evaluación de estados. Durante los tests iniciales se nota que PF es bastante más lento evaluando reglas que IPF e IPTables, pero hace un triple trabajo, que es gradualmente menos costoso que el que realiza IPTables. La baja performance en estas pruebas es debida a la forma que PF utiliza para evaluar las reglas, lo que realiza tres veces por cada paquete que pasa por la interface de red. Dos veces para buscar reglas de scrubbing, que determinan la normalización IP y TCP, (cosa que netfilter no realiza de la mejor manera) y una vez para las reglas pass o block. Por lo cual, podemos determinar que evaluar reglas de filtrado, es mas caro computacionalmente, que la búsqueda de estados.

Mi punto a favor de los firewalls basados en estados.


Material de referencia:
Les recomiendo el siguiente material de lectura para ampliar lo expresado en este post.

5 comentarios:

  1. Muy buen informe!, para mi ambos fueron pensados para propositos/ideas diferentes, ya conocemos de estas cosas.
    Solo decirte que si te jode mucho la sintax de iptables probate Vyatta, es +/- como estar frente a un Cisco. EOF

    ResponderEliminar
  2. Facundo, no entiendo tu punto a favor de los firewalls basados en estados.
    En los tres primeros gráficos gana iptables; incluso en el segundo (que ponés que gana packet filter) iptables es el que siempre tiene la menor latencia.
    Me gustaría ver gráficos donde las ventajas de los S-B-Fw se traduzcan en una mejor performance.

    ResponderEliminar
  3. @Sergiodf, el costo computacional de hacer una búsqueda en la tabla de estados mediante árboles binarios es menor que el de pasar una regla de firewall. Si a esto le añadimos en el primer SYN que se crea la entrada en la tabla de estados, se pasa atravéz de la regla, y luego solo la coincidencia dentro de la tabla, el costo baja notablemente. Al fin del post explico cuales son los tres checkeos que PF realiza dos veces hace el scrubbing para normalizar el paquete, cosa que IPTables no realiza, y por último hace el matcheo en la tabla de estados. Por lo cual si vemos la perdida de performance de PF es menor en realación a la de IPTables, que simplemente busca el matcheo en la regla.

    Respecto al primer gráfico, un stateless based firewall, sin estados tiene baja performance, y en el test se pone unicamente a fitlrar sin mantener a ninguno con estados. Por eso le gana IPTables, ya que el punto fuerte del firewall es justamente el mantener estados.

    En el segundo gráfico ya corregí lo que me decías fue un error en el tipeo, gracias por notificármelo.

    El punto fuerte mas allá de la performance, es la posibilidad de poder controlar los estados de los paquetes para así trabajar de manera mas eficiente con ellos y que nos garantizen una mayor seguridad ante el spoofing o DoS/DDoS, esas son ventajas prácticas que nos proveen los firewalls basados en estados. Además destacar que la búsqueda en el AVL binario no es más rápida que la búsqueda a través de un árbol hash balanceado, pero donde la seguridad importa, es preferible perder una pequeña cantidad de performance a favor de la seguridad.

    No siempre lo más rápido es lo mejor, y si tenemos en cuenta la cantidad de trabajo que realiza PF en relación a IPTables, se puede ver como más rápido PF.

    Te recomiendo ampliar mi post con el papper en que me base para armar este:

    http://www.benzedrine.cx/pf-paper.html

    Saludos.

    ResponderEliminar
  4. Muy buen articulo.

    "-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT" Esta regla y sus derivados siempre me asusto, no suena muy seguro. Quiza no esto acostumbrado a iptables.

    ResponderEliminar
  5. Hola Facundo, recién eh aprendido a manejar iptables desde la raíz, ya que siempre lo hacia con algún complemente gráfico tipo UFW.

    No te voy a decir quien es mejor de los dos, pero por ahora, me encanta lo que estoy experimentando con iptables.

    Anónimo "13 de enero de 2012 00:41", esa regla que colocaste la utilizo cuando la conexión con el host es segura o ya esta establecida anteriormente con una regla NEW.

    Saludos

    ResponderEliminar