Rasguñando Netfilter (parte 2)

En la primera parte, expuse algunos de los conceptos básicos de netfilter, ahora mostraré como aplicar estos conceptos para configurar un cortafuegos.

Para configurar netfilter, se utilizan fundamentalmente los comandos iptables (para ipv4) e ip6tables (para ipv6), como también otros comandos relacionados, que veremos más adelante. Por el momento, nos centraremos en ipv4.

Políticas por defecto

Ahora bien; la manera de preparar reglas para configurar un cortafuegos, depende de las políticas por defecto. Estas pueden ser permisivas (en cuyo caso las reglas que se declaren funcionarán como una lista negra), o restrictivas (en cuyo caso las reglas funcionarán como una lista blanca). En otras palabras, las políticas por defecto dicen a netfilter cómo comportarse en caso de que un paquete de red no coincida con ninguna regla (los paquetes de red no son más que bloques de información que varían en tamaño y contenido en dependencia del protocolo de comunicación y los parámetros que utilicen).

Para ilustrarlo de una manera más simple, imaginemos un guardia de seguridad cuidando la entrada de un local, que en caso de haber políticas permisivas, dejase pasar a todos excepto aquellos en la lista negra, y en caso de haber políticas restrictivas, no dejase pasar a nadie más que los privilegiados miembros de la lista blanca.

Una distribución de GNU/Linux recién instalada, usualmente viene con políticas permisivas por defecto. Para comprobarlo, podemos ejecutar el siguiente comando como superusuario:

iptables -S

Deberíamos obtener algo como esto:

-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT

Esto equivale a declarar las siguientes reglas para la tabla filter:

  • se aceptarán los paquetes de red provenientes de otros equipos
  • se aceptará que este equipo reenvíe paquetes de red entre otros dos equipos
  • se aceptará que este equipo envíe paquetes de red a otros equipos

De modo que en el caso anterior, netfilter permitirá cualquier cosa, dejando nuestro equipo vulnerable ante posibles ataques.

Supongamos que instalamos en nuestro equipo un servicio web para programar una aplicación en php, y por el momento deseamos impedir que de otros equipos accedan a esta; si nuestro equipo detecta la interfaz de red como eth0, podríamos lograr esto con la siguiente regla:

iptables -A INPUT -i eth0 -p tcp -m tcp --dport 80 -j DROP

Esta sencilla regla equivale a pasar la siguiente orden a netfilter:

Agrega en la cadena INPUT de la tabla filter (la predeterminada) una regla que descarte cualquier paquete entrante por la interfaz eth0 usando el protocolo tcp y destinado al puerto 80.

Con esto, estaremos cerrando un puerto para un protocolo. Pero existen decenas de protocolos y miles de puertos; de modo que como vemos, con una política permisiva por defecto, se hace difícil prever todas las posibles vulnerabilidades.

Si nuestro equipo realmente no funciona como enrutador, podemos cambiar a DROP la política por defecto para la cadena FORWARD:

iptables -P FORWARD DROP

Con esto, al menos impediremos que utilicen nuestro equipo como trampolín para llegar a otros equipos. Ahora bien, si hacemos lo mismo para la cadena INPUT, y no creamos reglas adicionales para permitir el tráfico entrante, estaremos incomunicando a nuestro equipo. En netfilter, que se permita la salida de peticiones no implica necesariamente que se aceptarán las respuestas a dichas peticiones.

Cortafuegos restrictivo minimalista

De modo que como hemos visto, para configurar un cortafuegos con política restrictiva para la entrada, es necesario garantizar una comunicación fluida. Aquí es donde resulta útil no solo la separación de interfaces sino también el rastreo de conexiones y el control de estado. Con el siguiente ejemplo de solo 5 líneas obtendríamos un cortafuegos minimalista y funcional para una estación de trabajo sin servicios públicos.

iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT ! -i lo -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

Las primeras tres líneas establecen las políticas por defecto: para las cadenas INPUT y FORWARD descartar todos los paquetes, y permitirlos para la cadena OUTPUT. A continuación, permitir para la cadena INPUT cualquier paquete que provenga de la interfaz local (en caso contrario el equipo no podrá utilizar servicios locales). La última línea ordena a netfilter que acepte aquellos paquetes que entren por una interfaz que no sea la local, siempre y cuando formen parte o estén relacionados con una conexión que hemos iniciado.

Mantener la cadena OUTPUT con políticas permisivas por defecto busca sencillamente simplificar la declaración de reglas, lo cual suele ser suficiente para una estación de trabajo o un servidor en un entorno razonablemente protegido, pero para servidores bajo constante intento de ataque o estaciones de trabajo donde se prueban aplicaciones cuya seguridad se desconoce, conviene establecer también políticas de denegación por defecto, y crear reglas de salida que cubran todas nuestras necesidades. Como esto depende mucho del uso que demos a nuestro eqiupo, de momento no nos extenderemos al respecto.

Permitir el diagnóstico

Con la configuración minimalista que mostramos arriba, sera difícil comprobar si nuestro equipo está activo desde otro equipo de nuestra red, porque nuestro equipo por defecto rechazará todos los paquetes de red entrantes nuevos. Es decir, comandos como ping o traceroute fallarán. De modo que vamos a permitirlos agregando las siguientes líneas.

iptables -A INPUT -p icmp -m icmp --icmp-type echo-request -j ACCEPT
iptables -A INPUT -p udp -m udp --dport 33434:33534 -m conntrack --ctstate NEW -j REJECT

En el bloque anterior, la primera línea permite a los paquetes entrantes el acceso mediante el protocolo ICMP, pero sólo si son echo-request (en otra palabras, sólo se admite el comando ping). La segunda línea permite consultas mediante el comando traceroute; nótese que el destino que se da al paquete es REJECT y no ACCEPT, porque el equipo que hace la consulta no necesita acceso a ningún servicio, sólo necesita saber cuánto tarda nuestro equipo en responder.

De modo que ahora las reglas de nuestro cortafuegos lucirían así:

iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT ! -i lo -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -p icmp -m icmp --icmp-type echo-request -j ACCEPT
iptables -A INPUT -p udp -m udp --dport 33434:33534 -m conntrack --ctstate NEW -j REJECT

Persistencia

Todo parece funcionar, hasta que reiniciamos … y perdemos las reglas. ¿Por qué?

Sencillamente, porque han sido reglas declaradas puntualmente, interpretadas y ejecutadas al vuelo, no son persistentes. Hay varias maneras de volverlas persistentes de manera que se apliquen en cuanto nuestro equipo encienda o reinicie. La manera más sencilla, probablemente sea instalando el paquete iptables-persistent. En Debian y derivadas, esto es tan simple como:

apt-get install iptables-persistent

Una vez hecho esto, tecleamos nuevamente las reglas y esta vez, ejecutamos a continuación el siguiente comando:

iptables-save > /etc/iptables/rules.v4

Si reiniciamos ahora, notaremos que las reglas se mantienen. El contenido del archivo /etc/iptables/rules.v4 lucirá como esto:

# Generated by iptables-save v1.4.21 on Sun Dec 10 14:42:35 2017
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -i lo -j ACCEPT
-A INPUT ! -i lo -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT
-A INPUT -p udp -m udp --dport 33434:33534 -m conntrack --ctstate NEW -j REJECT --reject-with icmp-port-unreachable
COMMIT
# Completed on Sun Dec 10 14:42:35 2017

Como vemos, a veces netfilter interpreta las reglas con una sintaxis ligeramente diferente (generalmente más detallada), pero esencialmente igual.

Otra variante para garantizar la persistencia de las reglas puede ser colocarla dentro de un script e invocarlo desde una tarea de crontab con el horario @reboot, o desde el directorio de scripts que se ejecutan al iniciar las interfaces (/etc/network/if-up.d/), pero en estos casos deben tomarse precauciones adicionales, como colocar estas líneas antes del resto:

iptables -F
iptables -X

Lo que hacen estas líneas sobre la tabla filter es vaciar las cadenas por defecto y eliminar cualquier cadena personalizada, sobre las que hablaremos más adelante. De esta manera se evita duplicar reglas en caso de que el script se cargue mientras las reglas se encuentran activas en memoria.

Permitir el acceso a servicios

¿Que sucede entonces si después de un plazo, nuestra aplicación web está suficientemente madura y deseamos que otros colegas la prueben? Pues tendremos que permitirles el acceso al servidor web:

iptables -A INPUT -i eth0 -p tcp -m tcp --dport 80 -m conntrack --ctstate NEW -j ACCEPT

En este caso, hemos especificado que se permita el acceso a paquetes nuevos (o sea, peticiones) provenientes de cualquier parte, siempre que usen el protocolo tcp y estén dirigidos al puerto 80, que es donde escucha nuestro servidor web.

Pero, ¿y si no deseamos que el servicio sea visible para todos, sino sólo para una dirección específica? Pues modificamos la línea anterior para incluir la dirección de origen, por ejemplo:

iptables -A INPUT -i eth0 -s 192.168.0.102 -p tcp -m tcp --dport 80 -m conntrack --ctstate NEW -j ACCEPT

Si quisieramos permitir el acceso a dos o tres equipos, bastaría con duplicar la línea y cambiar la dirección. Pero, ¿y si quisieramos permitir todo el bloque de equipos de nuestra misma subred? Pues ajustamos la dirección de origen, por ejemplo:

iptables -A INPUT -i eth0 -s 192.168.0.0/24 -p tcp -m tcp --dport 80 -m conntrack --ctstate NEW -j ACCEPT

¿Y si quisiéramos permitir acceso a equipos de nuestra misma subred, pero solo aquellos entre las direcciones 100 y 200, por ejemplo? En este caso, podemos utilizar un rango para las direcciones de origen:

iptables -A INPUT -i eth0 -m iprange --src-range 192.168.0.100-192.168.0.200 -p tcp -m tcp --dport 80 -m conntrack --ctstate NEW -j ACCEPT

(Continuará)

En la próxima parte de este tema, abundaremos en el tema de los servicios y veremos maneras de limitar el acceso que se permite a estos para mitigar posibles intentos de ataque.

¿De cuánta utilidad te ha parecido este contenido?

¡Haz clic en una estrella para puntuar!

Promedio de puntuación 0 / 5. Recuento de votos: 0

Hasta ahora, ¡no hay votos!. Sé el primero en puntuar este contenido.

Sobre Hugo Florentino 11 artículos
Administrador de redes y sistemas. Usuario regular de GNU/Linux desde Octubre de 2008. Miembro fundador del Grupo de Usuarios de Tecnologías Libres (GUTL).

Sé el primero en comentar

Dejar una contestacion

Tu dirección de correo electrónico no será publicada.


*