En un nuevo servidor LAMP (Linux, Apache, PHP, MySQL) con Debian 7 tuve la necesidad de desarrollar mi propio cortafuegos (firewall), utilizando la herramienta iptables y el paquete "iptables-persistent".



iptables (wiki.debian.org/iptables) es una herramienta de línea de comandos en espacio usuario que se utiliza para configurar reglas de filtrado de paquetes IP, a partir del kernel Linux 2.4x. Ha sido desarrollada casi completamente por el equipo de netfilter. Provee filtrado, NAT (network address translation) y manipulación de paquetes IPv4 e IPv6. Apunta a administradores de sistemas y en general se utiliza para implementar firewalls y proveer NAT.

Como las reglas de filtrado de paquetes se aplican a nivel kernel Linux (y son gestionadas por el mismo), son configuraciones volátiles. Es decir, en caso de un reinicio se pierden. Por lo tanto es necesario implementar algún mecanismo para guardarlas y restaurarlas durante el inicio del sistema. Muchos administradores optan por crear un script Bash con todas las reglas (comandos iptables), que se ejecute automáticamente al iniciar el sistema (por ejemplo desde rc.local).

Aunque, en Debian existe el paquete iptables-persistent, el cual provee un script de inicio que restaura automáticamente las reglas del firewall desde un archivo de configuración, durante el proceso de boot (boot-time).

La creación del archivo de configuración del paquete iptables-persistent es transparente si se siguen los siguientes pasos. Primero es necesario desarrollar un script Bash con todas las reglas de filtrado, utilizando iptables. Luego de testearlo y verificar que funcione correctamente, aplicarlo y dejarlo funcionando. A continuación, instalar el paquete iptables-persistent:

# apt-get install iptables-persistent

Luego de instalar el script de inicio en el directorio /etc/init.d/, durante la etapa de configuración del paquete se ejecutará automáticamente el comando iptables-save, para guardar las reglas actuales en los archivos de configuración rules.v4 y rules.v6 dentro del directorio /etc/iptables/ (por ello es importante haber dejado funcionando el firewall automáticamente).

Si luego es necesario realizar modificaciones en el firewall, se puede hacer de forma segura ejecutando comandos iptables. Si la nueva configuración es correcta, es posible guardar las reglas ejecutando service iptables-persistent save. De lo contrario, se puede restaurar la configuración ejecutando service iptables-persistent reload o service iptables-persistent restart.

NOTA: en caso de desarrollar un firewall sobre un sistema remoto, es posible configurar un cron job que ejecute service iptables-persistent restart periódicamente (por ejemplo cada 5 minutos) para no quedar "fuera", o sin acceso al sistema. De esta forma, si se agrega una nueva regla, y la misma nos deja fuera del sistema, sólo hay que esperar un período de tiempo hasta que el firewall se reinicie automáticamente. En cambio, si la regla funciona, es posible guardarla ejecutando service iptables-persistent save. Al finalizar con las modificaciones, es posible eliminar el cron job.

Para guardar y restaurar las reglas del firewall, iptables-persistent utiliza las herramientas iptables-save y iptables-restore, provistas por iptables (de forma similar a la implementación de Red Hat y derivados).

Como se trata de una configuración de seguridad, las reglas del firewall dependen del uso o los servicios que provee cada sistema. En este artículo presento un firewall simple para servidores Web, que está enfocado en proteger principalmente el tráfico entrante. Dejo a ustedes la tarea de proteger el tráfico saliente, que no es menos importante que el trafico entrante. ¿Por qué? La respuesta es simple. Porque si un atacante logra acceso al sistema (por ejemplo logra instalar una shell PHP en un sitio vulnerable) y no se protege el tráfico de salida, el atacante podrá salir hacia cualquier puerto. ¿Qué implicancias tiene ésto? Puede profundizar su ataque, usar nuestro servidor para atacar otros servidores o enviar correo, conectarse a otros sistemas remotos para enviar información, etc.

Firewall

A continuación, el script Bash que implementa el firewall, con protecciones para los ataques más comunes (sólo tráfico entrante):

#!/bin/bash

# Dirección IP del servidor
IP="192.168.145.32"

# Vaciar todas las cadenas y contadores
iptables -F
iptables -X
iptables -Z

# Activar diferentes protecciones a nivel kernel
#
# Ignorar broadcasts icmp
echo -n '1' > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts
# Deshabilitar Source Routing
echo -n '0' > /proc/sys/net/ipv4/conf/all/accept_source_route
# Deshabilitar ICMP redirects
echo -n '0' > /proc/sys/net/ipv4/conf/all/accept_redirects
# Proteger contra "bad error messages"
echo -n '1' > /proc/sys/net/ipv4/icmp_ignore_bogus_error_responses
# Deshabilitar ip forwarding
echo -n '0' > /proc/sys/net/ipv4/ip_forward
# Loguear sospechosos, "source routed" y redirects
echo -n '1' >/proc/sys/net/ipv4/conf/all/log_martians

# Por defecto, bloquear todo en las cadenas INPUT y FORWARD
iptables -P INPUT DROP
iptables -P FORWARD DROP

# Permitir todo el tráfico saliente
iptables -P OUTPUT ACCEPT

# Bloquear conexiones TCP nuevas que no comiencen con SYN
iptables -A INPUT -i eth0 -p tcp ! --syn -m state --state NEW -m limit --limit 5/m --limit-burst 7 -j LOG --log-level 4 --log-prefix "TCP RST,ACK,FIN"
iptables -A INPUT -i eth0 -p tcp ! --syn -m state --state NEW -j DROP

# Bloquear fragmentos entrantes
iptables -A INPUT -i eth0 -f -m limit --limit 5/m --limit-burst 7 -j LOG --log-level 4 --log-prefix "Fragment Packets"
iptables -A INPUT -i eth0 -f -j DROP

# Bloquear paquetes malformados
iptables -A INPUT -i eth0 -p tcp --tcp-flags ALL FIN,URG,PSH -j DROP
iptables -A INPUT -i eth0 -p tcp --tcp-flags ALL ALL -j DROP
iptables -A INPUT -i eth0 -p tcp --tcp-flags SYN,RST SYN,RST -j DROP

# Bloquear NULL packets
iptables -A INPUT -i eth0 -p tcp --tcp-flags ALL NONE -m limit --limit 5/m --limit-burst 7 -j LOG --log-level 4 --log-prefix "NULL Packets"
iptables -A INPUT -i eth0 -p tcp --tcp-flags ALL NONE -j DROP

# Bloquear paquetes "Christmas tree" malformados
iptables -A INPUT -i eth0 -p tcp --tcp-flags SYN,FIN SYN,FIN -m limit --limit 5/m --limit-burst 7 -j LOG --log-level 4 --log-prefix "XMAS Packets"
iptables -A INPUT -i eth0 -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP

# Bloquear ataques Fin Scan
iptables -A INPUT -i eth0 -p tcp --tcp-flags FIN,ACK FIN -m limit --limit 5/m --limit-burst 7 -j LOG --log-level 4 --log-prefix "Fin Packets Scan"
iptables -A INPUT -i eth0 -p tcp --tcp-flags FIN,ACK FIN -j DROP

# Validar flags TCP
iptables -A INPUT -i eth0 -p tcp --tcp-flags ALL SYN,RST,ACK,FIN,URG -j DROP

# Bloquear paquetes inválidos
iptables -A INPUT -m conntrack --ctstate INVALID -j DROP

# Permitir todo en la interfaz loopback
iptables -A INPUT -i lo -j ACCEPT

# Permitir paquetes de conexiones establecidas
iptables -A INPUT -p all -m state --state RELATED,ESTABLISHED -j ACCEPT

# Permitir ping
iptables -A INPUT -p icmp --icmp-type 8 -m conntrack --ctstate NEW -j ACCEPT

# Permitir conexiones hacia el puerto 22 (SSH)
iptables -A INPUT -p tcp -d $IP --sport 1024:65535 --dport 22 -m state --state NEW -j ACCEPT

# Permitir conexiones hacia el puerto 80 y 443 (HTTP y HTTPS)
iptables -A INPUT -p tcp -d $IP --sport 1024:65535 --dport 80 -m state --state NEW -j ACCEPT
iptables -A INPUT -p tcp -d $IP --sport 1024:65535 --dport 443 -m state --state NEW -j ACCEPT

# Loguear y rechazar el resto
iptables -A INPUT -j LOG
iptables -A FORWARD -j LOG
iptables -A INPUT -j DROP

Para completar este firewall es necesario filtrar el tráfico saliente. Cambiar la política de la cadena OUTPUT a "DROP", para bloquear todo el tráfico saliente (excepto el de conexiones establecidas):

iptables -P OUTPUT DROP
iptables -A OUTPUT -p all -m state --state RELATED,ESTABLISHED -j ACCEPT

Luego, permitir la salida sólo hacia puertos estrictamente necesarios. Por ejemplo, puede ser necesario salir hacia los puertos 80 y 443 (HTTP y HTTPS) para descargar actualizaciones, y hacia el puerto 25 (SMTP), si alguno de los sitios Web envía notificaciones por correo electrónico (algo muy común en estos días). Aunque se desaconseja abrir el puerto 25, en la medida de lo posible, ya que podría ser utilizado para enviar spam. Por supuesto, denegar el acceso a sistemas remotos (22, SSH).

iptables -A OUTPUT -s $IP -p tcp --sport 1024:65535 --dport 80 -m state --state NEW -j ACCEPT
iptables -A OUTPUT -s $IP -p tcp --sport 1024:65535 --dport 443 -m state --state NEW -j ACCEPT

NOTA: enviar spam no es algo malo sólo porque le hacemos mal a otras personas. También es malo porque nuestro servidor, léase hostname y dirección IP, puede caer en una lista negra (blacklist) de correo electrónico (por ejemplo spamhaus) y por ende verse imposibilitado para enviar futuro correo válido.

Protecciones a nivel kernel

Como medidas adicionales de seguridad, recomiendo deshabilitar IPv6 desde la configuración del sistema (/etc/sysctl.conf) y activar otras protecciones a nivel kernel.

# Activar diferentes protecciones a nivel kernel
#
# Ignorar broadcasts icmp
net.ipv4.icmp_echo_ignore_broadcasts = 1
# Deshabilitar Source Routing
net.ipv4.conf.all.accept_source_route = 0
# Deshabilitar ICMP redirects
net.ipv4.conf.all.accept_redirects = 0
# Proteger contra "bad error messages"
net.ipv4.icmp_ignore_bogus_error_responses = 1
# Deshabilitar ip forwarding
net.ipv4.ip_forward = 0
# Loguear sospechosos, "source routed" y redirects
net.ipv4.conf.all.log_martians = 1

Referencias

DebianFirewall - Using iptables for IPv4 traffic

ArchWiki - Simple stateful firewall


Tal vez pueda interesarte


Compartí este artículo