Conceptos de firewaling y una configuración elemental de netfilter

Bueno, vamos a hablar un poco de firewalling en GNU/Linux.

No intentando ser una explicación definitiva, sino de poner índice en algunos elementos esenciales que se deben conocer sobre este tema.

No vamos a presentar todas las cualidades técnicas del firewalling en GNU/Linux, por lo pronto nos concentraremos en una configuración básica para un desktop o portátil que nos permita comprender un poco el tema mientras aseguramos un equipo de estas características.
El firewall es un sistema de control sobre los puertos de nuestro sistema, de tal modo que podemos establecer cuales puertos pueden ser accedidos desde fuera y cuales no. Obviamente esta no es su única función y, de necesitarse alguna otra característica, algún otro que quiera incorporarla a este artículo será bienvenido.
El firewall de GNU/Linux es Netfilter, trabaja en espacio de kernel, no es un servicio en espacio de usuario y se configura por medio de IPTABLES.

Ahhh, iptables !!! hubieras empezado por ahí !!!

No, un error común es confundir netfilter con iptables, de hecho, nadie se refiere al firewalling en GNU/Linux de otra forma que no sea iptables.

¿y que es iptables?

Para explicarlo de manera simple vamos a olvidarnos del modelo OSI, asi que, quien lea esto y sepa del tema, no se enoje por las barbaridades que diré a continuación puesto que es con la intención de un entendimiento básico para alguién que quiere asegurar un sistema desktop y no sabe por dónde empezar.

Digamos que el kernel le encarga a netfilter la tarea de recibir y transmitir paquetes y los revisa antes de transmitirlos.

Para revisarlos netfilter se vale de una serie de "condiciones" que cada paquete debe cumplir. De tal modo que todo paquete que no cumpla con las "condiciones" se tira a la basura.

Estas famosas "condiciones" son las que permiten a netfilter clasificar cada paquete y hacer con cada uno lo que sea establecido por estas condiciones.

Netfilter necesita que estas "condiciones" sean coherentes, que no sean contradictorias. No podemos decirle "descarta este paquete" y luego decirle "ah, no, mejor transmite este paquete". Por eso, como no le gusta que le compliquen el trabajo, además de la coherencia, atenderá a cada "condición" en el orden en que fueron declaradas.

¿y cómo declaramos las "condiciones"?

Nuestro sistema funciona por medio de "sevicios" que "escuchan" en un "puerto" del sistema, esperando a que un "cliente" realize una "petición" al "servicio". Cada "Petición" es "atendida" por el "servicio" que pidió el "cliente".

Esto es lo que se conoce como un "sistema cliente/servidor" y GNU/Linux respeta este modelo, entonces podríamos decir que netfilter trabaja capturando las "peticiones" de todos los "puertos" antes de que sean "atendidas" por los "servicios".

Netfilter maneja esta tarea a traves de iptables. como cada "petición" es esencialmente un "paquete" de datos, diremos que iptables controla paquetes de la siguiente manera:

1)Iptables maneja tablas, haciendo que los paquetes atraviesen una tabla. (table)
2)Cada tabla posee cadenas. (chains)
3)En cada cadena se establece una política general. (policies)
4)Luego se establecen reglas que saltan (jump) la condición de la política.

Entonces, la tabla actuará como filtro de los paquetes. (filter)

Sucede que los paquetes pretenden realizar una de tres acciones:

1)Un paquete que quiere salir del sistema.
por ejemplo, solicitar una dirección web en el navegador. Esta petición bajara por las capas OSI, pretenderá salir por un puerto y será "atendida" por netfilter; si cumple las reglas sale el paquete del sistema.

2)Un paquete que quiere entrar al sistema.
por ejemplo, el resultado de la solicitud anterior. LLega el paquete que quiere entrar por el puerto 80 y será "atendida" por netfilter; si cumple las reglas entra el paquete y sube por las capas OSI para presentar la página en el navegador.

3)Un paquete que quiere pasar por el sistema.
por ejemplo, si nuestra máquina actua como proxy. Recibirá paquetes de la LAN queriendo ir a la WAN pasando obligadamente por netfilter para lograrlo.

En esta tabla de filtro, tendremos una cadena para cada acción:

1)Un paquete que quiere salir (OUTPUT) de nuestro sistema.
2)Un paquete que quiere entrar (INPUT) a nuestro sistema.
3)Un paquete que quiere pasar (FORWARD) por nuestro sistema.

A nivel de política, tendremos dos opciones:

1) Rechazar todos los paquetes y empezar a construir exepciones a esta política.
2) Aceptar todos los paquetes y empezar a construir exepciones a esta política.

¿se entiende?

Todo paquete que no cumpla con las condiciones de una "exepción" será "juzgado" a traves de la política. Si ponemos como política "aceptar todo" y un paquete no cumple con ninguna regla que provoque que iptables lo descarte, será aceptado. Del mismo modo, si ponemos como política "rechazar todo" y un paquete no cumple ninguna regla que provoque que iptables lo acepte, será descartado.

Una vez que definimos la "politica" que aplicará iptables, deberemos empezar con las reglas.

Ya que todos ustedes son un público maravilloso, inteligente y toda gente linda que entendió perfectamente todo lo explicado hasta aquí, vamos a continuar con otro tema que no tiene que ver con esto... pero que tiene que ver con esto.

Cuando andamos por la calle a las tres de la mañana y se nos acerca un desconocido, no vamos a hablar con él porque no lo conocemos y nuestros padres nos inculcaron que no debemos hablar con extraños. Sin embargo, si se nos acerca alguien que conocemos, podemos "iniciar" la conversación con un saludo a esta persona porque es alguien que conocemos.
Del mismo modo se comunican las máquinas y la razón ilógica de lo comentado anteriormente.

Vamos hacia "el saludo de tres vías".

Cuando una máquina A quiere acceder a otra máquina B lo hace desde un puerto de A hacia un puerto de B, mandando un saludo.
Entonces B recibe un saludo de A, marcando en su puerto una conexión nueva (NEW) y respondiendo el saludo de A.
Luego A recibe la respuesta de B, marca la conexión del puerto como establecida (ESTABLISHED) y se prepara para comunicarse con B.
Luego B recibe la respuesta de A, marca la conexión del puerto como establecida (ESTABLISHED) y se prepara a recibir datos de A.
Los datos transmitidos por A hacia B y por B hacia A son relacionados (RELATED) a la conexión.

El kernel mantiene un control del estado de éstas conexiones y pueden ser usadas para filtrar paquetes por iptables.

Si, bueno, todo muy lindo, pero todavía no pusimos a andar el firewal... y ya estamos un poco impacientes !!!

Esta bien, ya que lo piden de tan buena manera vamos a configurar un firewall con iptables; pero antes una pregunta:

qué es mejor: ¿bloquear un puerto abierto o hacer creer que el puerto está cerrado?

Si el puerto es bloqueado se sabe que está abierto y es nectar tcp para las abejas cracker's que quieren hacerse un picnic con nuestra maquinita, en cambio si un puerto está cerrado no existe fundamento para un ataque. Si "disfrasamos" los puertos abiertos como cerrados no tendremos abejas zumbándonos en los oídos.

Con todos estos conceptos en mente, haremos una configuración de iptables para una máquina sin servicios, ok? sin samba, apache, cups, squid, vsftp, ni nada parecido. Un simple y potente iptables para un desktop o laptop común y silvestre.

Damas y caballeros, con ustedes... los intérpretes.

Configuración de un stateful firewall con política de all drop, control flooding y ocultamiento de puertos.

Nos encomendamos a San iGNUcius, horamos un "Mamita Querida", abrimos una consola, nos logueamos como root y diremos:

# iptables -L

y nos devolverá algo como

Chain INPUT (policy ACCEPT)
target   prot opt source      destination

Chain OUTPUT (policy ACCEPT)
target   prot opt source      destination

Chain FORWARD (policy ACCEPT)
target   prot opt source      destination

que nos está diciendo que:
1)tenemos la cadena (Chain) para paquetes entrantes (INPUT) en la que se está aplicando la política (policy) de "aceptar todo" (ACCEPT)
2)tenemos la cadena (Chain) para paquetes salientes (OUTPUT) en la que se está aplicando la política (policy) de "aceptar todo" (ACCEPT)
3)tenemos la cadena (Chain) para paquetes pasantes (FORWARD) en la que se está aplicando la política (policy) de "aceptar todo" (ACCEPT)

Entonces sabemos que iptables funciona, queremos modificar su comportamiento para que nos proteja en lugar de dejar entrar a todo el mundo.

Abrimos un editor plano de nuestra preferencia y escribiremos:

#!/bin/bash

iptables -t filter -F

si, vamos a hacer un pequeño script con la finalidad de tenerlo guardado en el home de root y poder hacer ajustes de ser necesario. En esta primera linea le estamos diciendo a iptables que borre todas las reglas que esté ejecutando sobre los paquetes. Esto es necesario para limpiar todo cada vez que se ejecute este script.

Seguimos:

iptables -t filter -P INPUT DROP

es decir que en la tabla filter (-t filter) establecemos como política (-P) que descarte todo (INPUT DROP).

Seguimos:

iptables -N FLOOD

aquí creamos una nueva cadena y la llamamos FLOOD para controlar ataques de flooding, para lo cual necesitaremos establecer un par de reglas:

iptables -A FLOOD -m limit --limit 4/s --limit-burst 6 -j RETURN
iptables -A FLOOD -j DROP

le decimos a iptables que agrege (-A) en la cadena FLOOD una regla usando el módulo limit (-m limit) y establecemos el parámetro --limit a 4 paquetes por segundo (4/s) y establecemos el parámetro --limit-burst en 6. Esto quiere decir que todo paquete que deba pasar por esta cadena será evaluado en base a la carga por segundo de paquetes transmitidos y si durante 6 segundos la carga no supera el límite de 4 paquetes por segundo continue su curso saliendo de la cadena (-j RETURN); en caso contrario no se ejecutará el salto (-j RETURN) y continuará con la siguiente regla que lo descartará (-j DROP).

iptables -t filter -A INPUT -i lo -j ACCEPT

Esto es muy importante. Como nuestra política es descartar todo debemos hacer una exepción sobre la interfáz de loopback para poder ingresar al sistema, de lo contrario no podremos loguearnos en él. En la tabla filter (-t filter), agregamos (-A) a la cadena INPUT que todo paquete que venga por la interfáz (-i) de loopback (lo), salte (-j) la política de DROP y sea aceptado (ACCEPT).

iptables -t filter -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

Se acuerdan del saludo de tres vías? aquí le decimos a iptables que en la tabla filter agregue a la cadena INPUT, usando el módulo state, todo paquete en que la propiedad de estado sea una conexión establecida por nuestra máquina (ESTABLISHED) o un paquete relacionado (RELATED) a una conexión establecida por nuestra máquina, que salte la política y lo acepte.

Esto significa que solo aceptaremos que ingresen al sistema paquetes que sean producto de peticiones de nuestro sistema.

iptables -t filter -A INPUT -j FLOOD

Recuerden que las reglas son leídas por iptables en orden secuencial. Aquí le decimos a iptables que todo paquete que pase por la tabla filter, en la cadena INPUT, salte la política y entre a la cadena FLOOD. Es decir, todo lo que pasó por todas las reglas que declaramos para la cadena INPUT hasta aquí (las que no generaron una exepción a la política) pasarán por la cadena FLOOD para el control de ataques por flooding.

En el control de flooding definimos límites basados en la cantidad de paquetes por segundo, con lo cual si son un ataque de flooding serán descartados y sino son un ataque, serán un escaneo de puertos. A estos paquetes que son "abejas zumbando" les haremos creer que los puertos están cerrados de la siguiente manera:

itpables -t filter -A INPUT -p tcp -j REJECT --reject-with tcp-reset
iptables -t filter -A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable

Con esto cubrimos todos los paquetes validos para la cadena INPUT, pero pueden existir paquetes invalidos:

iptables -t filter -A INPUT -m state --state INVALID -m limit --limit 2/m --limit-burst 1 -j LOG --log-prefix 'IPTABLES LOG:'

Se entiende? tabla filter, agregamos a INPUT, modulos state y limit, paquetes con estado INVALID, limite de 2 por minuto durante un minuto, registramos un log de los paquetes invalidos.

Espero les sea de utilidad y les sirva como aprendizaje.

Lo importante es que todo esto es una simple descripción de una configuración básica para protección, necesitan hacer el script, darle permisos de ejecución y hacer que se ejecute en el arranque del sistema (que no forma parte de esta explicación).

Antes que me tiren con piedras como "todo muy lindo, pero ahora no me conecto a la máquina con windows® o ahora no me anda tal o cual servicio", es mi deber volver a aclarar que usamos la política de "ALL DROP", para todo lo demás

$ man iptables

Nastardes.