Configurar el firewall iptables para permitir únicamente navegación web

por | diciembre 17, 2014

A la hora de administrar el firewall iptables de GNU/Linux, considero que es mucho más productivo crear un script que aplique todas las reglas y políticas que deseemos en vez de trabajar en la línea de comandos añadiendo, quitando y modificando reglas.

En el caso que nos ocupa en el artículo, he realizado un script con el que se permite únicamente la navegación tanto por HTTP como por HTTPS. El principal problema con el que nos podemos encontrar a la hora de crear unas reglas de este tipo, es que los navegadores abrirán en nuestra máquina puertos registrados o dinámicos (>1024) para comunicarse con servidores remotos (sin ir más lejos, como www.google.com). Estos puertos además, serán aleatorios, por lo que crear una regla para navegación – a no ser que el firewall acepte crear reglas para aplicaciones en concreto – se puede volver tedioso.

Script de Iptables para navegación web

El siguiente script deberá ser ejecutado como root, o bien por un usuario con permisos administrativos:

 
#!/bin/bash
#
#	Script para aplicar reglas del firewall Iptables (solo navegacion web)
#
#################################################################################

#Limpiar reglas
iptables -F INPUT
iptables -F FORWARD
iptables -F OUTPUT

#Flush chains
iptables -X

#Pondemos a 0 el contador de paquetes y bytes
iptables -Z

#Flush de la tabla NAT
iptables -t nat -F

#POLÍTICAS POR DEFECTO (se aplicarán a la tabla filter) Para mayor seguridad bloquearemos todo en un principio
echo "Aplicando politicas..."
iptables -P INPUT DROP
iptables -P OUTPUT DROP
iptables -P FORWARD DROP

#Permitimos operatividad con localhost para servicios internos del sistema
iptables -A INPUT -s 127.0.0.1 -i lo -j ACCEPT
iptables -A OUTPUT -d 127.0.0.1 -o lo -j ACCEPT

#Permitimos navegación por HTTP(80), HTTPS(443) 
iptables -A OUTPUT -j ACCEPT -o eth0 -p tcp --sport 1024:65535 -m multiport --dports 80,443

#Permitimos consultas DNS
iptables -A OUTPUT -o eth0 -p udp --sport 1024:65535 --dport 53 -m state --state NEW -j ACCEPT

#Permitir conexiones entrantes que estén relacionadas o establecidas anteriormente, es decir, HTTP, HTTPS Y DNS
iptables -A INPUT -i eth0 -m state --state ESTABLISHED,RELATED -j ACCEPT

#Guardar reglas
iptables-save > /root/fwrules

#Resumen
echo "Configuracion final de iptables:"
iptables -L -v

En líneas generales, os comento lo que realiza:

  • Limpieza inicial de todas las reglas anteriormente existentes.
  • Aplicar una política de denegación (DROP) por defecto para las tres cadenas principales: entrada (INPUT), salida (OUTPUT) y reenvío (FORWARD). Posteriormente voy aplicando poco a poco permisos concretos sobre qué navegación quiero permitir. El método recomendado para aplicar políticas de seguridad (no sólo en firewalls) muchas veces es éste: denegar todo e ir dando permisos restrictivos poco a poco en función de las necesidades.
  • Acepto las comunicaciones locales de la máquina (127.0.0.1). Esto es básico. Para que el sistema funcione correctamente algunos procesos tienen que intercomunicar entre ellos a nivel local.
  • Acepto las salidas (OUTPUT) hacia puertos (destination ports,»dports») 80 (HTTP), 443 (HTTPS) y 53 (DNS). Estos 3 puertos constituyen el eje central del firewall para permitir la navegación web, ya que cuando conectamos con un sitio web tendrá que darse una resolución DNS (53), y conectar si no es seguro por HTTP (80) o si es seguro por HTTPS (443).
  • Se permite la entrada (INPUT) de las conexiones establecidas. ¿Qué quiere decir esto? Pues que no se permite ninguna conexión de entrada a nuestra máquina salvo las que se hayan iniciado (y autorizado) previamente. Y estas conexiones permitidas son las que antes te he comentado: HTTP, HTTPS y DNS.
  • Para afinar un poco más, las conexiones hacia el exterior no podrán utilizar puertos de origen (source ports, «sport») protegidos (< 1024). Para navegación web no necesitamos los puertos protegidos para nada. Otra situación sería por ejemplo tuviéramos un servidor web o de correo, pero como digo no es el caso y los puertos protegidos no deben recibir ni emitir ninguna comunicación desde/hacia el exterior en nuestro caso.
  • Por otro lado y finalizando, con iptables-save (que vemos en el script) guardamos la configuración en /root/fwrules. Más tarde, si nos encontramos en un sistema Debian, podremos restaurar las reglas del firewall con iptables-restore. Una manera eficiente de implementar esto es agregar la siguiente línea a /etc/rc.local (todo lo que indiquemos en este fichero se ejecutará al inicio del sistema):
    iptables-restore < /root/fwrules
    

    En Red Hat o derivadas bastará con:

    /etc/init.d/iptables save 
    

    Reiniciamos:

    reboot
    

    Tras el reinicio, comprobamos que las reglas estén aplicadas ya con:

    iptables -L
    

El script es tan personalizable como quieras. Puedes añadir tus propias reglas, como las que puse para Steam en otro artículo.

Como curiosidad, en Debian también tenéis disponible el paquete iptables-persistent que cumple precisamente esa función: hacer persistentes en el sistema las reglas de iptables.

Consideraciones sobre seguridad adicional

En algunos generadores de iptables o manuales, aparte de las reglas de firewall se aprovecha para modificar el comportamiento del kernel en sysctl.conf. No soy partidario de hacerlo mediante un script y mucho menos automatizarlo para cada inicio, por dos razones:

  • Dichas modificaciones no sólo implican cuestiones de seguridad sino funcionales del sistema (si desactivamos la respuesta a ping por ejemplo, el servidor DHCP podría no detectar nuestra máquina). Además, tras las revisiones del kernel de Linux, la parametrización se lleva a cabo teniendo en cuenta la establidad y seguridad general del sistema. Cambiar un parámetro que en principio nos daría más seguridad, podría darnos más problemas futuros. Sin ir más lejos:
  • net.ipv4.conf.all.log_martians = 0

    Suele estar desactivado. Diríamos, ¿por qué no registrar todos los paquetes «extraños»? Podríamos hacerlo, pero bastaría con que nos hicieran un flood de ese tipo de paquetes para colapsar nuestra máquina con registros más inflados que el precio de la vivienda.

  • Automatizar cambios en el kernel no es recomendable si lo que pretendemos es tener el mayor control posible de nuestro sistema. Es mucho más seguro ir al fichero /etc/sysctl.conf y configurar manualmente los parámetros, ya que afectan a cuestiones clave de funcionamiento. Con el tiempo, tendemos a olvidarnos de aquello que automatizamos, y si surgiera algún problema, seguro que daríamos más de una vuelta hasta dar con el dichoso script que cambiaba X parámetro de forma automática.

Puedes encontrar la versión más actualizada de este script en mi repositorio de GitHub.