El propósito de un cortafuego (firewall) es controlar el flujo de datos que salen o entran por una interfase de red. Se construye mediante un filtro de paquetes, que es un código que lee el encabezado de un paquete de red que recibe una interfase (e.g. lo, eth0 o ppp0) y luego decide si aceptarlo, rechazarlo o redirigirlo antes de alcanzar un proceso local (daemon). En Linux, el filtro de paquetes es parte del núcleo. A partir de la versión del núcleo 2.4 se reescribió completamente y pasó a llamarse iptables, reemplazando al antiguo ipchains.
La palabra "cortafuego" puede dar una sensación falsa de seguridad. No existe red completamente segura y si bien un cortafuego puede ser considerado una primera línea de defensa, la vigilancia ante actividades extrañas en el computador son esenciales para mantener el sistema seguro. Si no has leído aún sobre la seguridad del sistema Debian sugerimos que lo hagas antes de continuar.
Nuestra propuesta de cortafuego intenta ser lo más simple y transparente posible. Está ideada para proteger a una estación de trabajo o servidor Debian de intrusos externos, sin restringir de modo alguno al usuario.
iptables es la herramienta que inserta o remueve "reglas" (rules) en la tabla del filtro de paquetes del núcleo y es instalada por omisión en el sistema base. El núcleo parte con una tabla que contiene tres listas básicas de reglas llamadas "cadenas" (chain). Estas son INPUT, OUTPUT y FORWARD, respectivamente (entrada, salida y redirección). Cuando un paquete entra a una interfase de red, el núcleo examina primero el destino del paquete y decide que ruta tomar (INPUT o FORWARD). Luego el paquete es examinado en la cadena, en donde la decisión de desechar (DROP) o aceptar (ACCEPT) el paquete es tomada. Si la decisión es aceptar, el paquete continua hacia el destino, siendo recibido por algún proceso local (un daemon). En cambio, si la decisión es desechar, el paquete es descartado completamente, muriendo en el lugar, antes de alcanzar un proceso local.
_____ Entrante / \ Saliente -->[Decisión]--->|FORWARD|-------> [de ruta ] \_____/ ^ | | v ____ ___ / \ / \ |OUTPUT| |INPUT| \____/ \___/ ^ | | ----> Proceso local ----
Una cadena es entonces una lista de reglas de control. Cada regla dice, "si el encabezado del paquete es de esta manera, aplico tal acción sobre él". La regla puede estar compuesta de una o múltiples condiciones. Si cualquiera de las condiciones de la regla no se cumple, la próxima regla es consultada. Si el paquete llega al final de la cadena sin que alguna regla le aplique, el núcleo consulta la "norma" (policy) definida para la cadena. La norma puede ser DROP o ACCEPT. En un sistema en donde la seguridad es prioridad, la norma de las cadenas INPUT y FORWARD debería ser siempre DROP.
Un programa o proceso también puede generar un paquete para ser enviado, pasando por la cadena OUTPUT. Si la decisión es ACCEPT, el paquete continua, saliendo por la interfase de red. Un sistema en donde el control sobre sus usuarios es mínimo, la norma de la cadena OUTPUT debería ser siempre ACCEPT.
Aparte de las tres cadenas básicas, que son imborrables, iptables permite crear nuevas cadenas e insertar reglas en ellas. Estas no tienen norma y al llegar al final retornan al punto que fueron llamadas. Las operaciones que se pueden realizar sobre una cadena son:
Las operaciones para manipular una regla dentro de una cadena son:
Para comenzar, da el comando 'iptables -L' para producir una lista de las cadenas y reglas. Ya que la tabla del filtro de paquetes no ha sido manipulada, el comando retornará a la pantalla las tres cadenas básicas, vacías de reglas,
# iptables -L Chain INPUT (policy ACCEPT) target prot opt source destination Chain FORWARD (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination
La norma por omisión de las tres cadenas es ACCEPT.
A modo de ejemplo, vamos a manipular la tabla para bloquear paquetes del protocolo ICMP (Internet Control Message Protocol) provenientes de la dirección IP 127.0.0.1 (loopback). Este protocolo es usado por ejemplo por ping para recibir un eco de un servidor en la red. Para comenzar da el comando,
# ping -c1 127.0.0.1 PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data. 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.040 ms --- 127.0.0.1 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 0.040/0.040/0.040/0.000 ms
Este comando envía un paquete ICMP tipo 8 (echo request) a la interfase loopback y el servidor (localhost) responde con otro paquete ICMP tipo 0 (echo reply). El argumento -c1 obliga a ping a enviar uno de estos paquetes y luego terminar.
Vamos a añadir (-A) una regla a la cadena INPUT para que pruebe todos aquellos paquetes cuyo protocolo (-p) es ICMP, provenientes de la fuente (source, -s) 127.0.0.1 y luego "salte" (jump, -j) al blanco (target) DROP,
# iptables -A INPUT -p ICMP -s 127.0.0.1 -j DROP
La lista en la tabla contiene la regla recién introducida,
# iptables -L INPUT Chain INPUT (policy ACCEPT) target prot opt source destination DROP icmp -- localhost anywhere
Si un paquete recibido proviene de la fuente 127.0.0.1 y cuyo protocolo es ICMP, entonces la regla es cumplida y salta al blanco DROP, es decir, el paquete es descartado. El blanco también puede ser ACCEPT o el nombre de otra cadena, en cuyo caso el paquete sufre un nuevo escrutinio con las reglas de esa cadena. Todas las partes de una regla tienen que cumplirse para que la regla salte al blanco. Si no es así, se consulta la siguiente regla hasta que haya un concordancia completa o llegue al final de la cadena y se le aplique la norma.
El comando 'ping -c1 127.0.0.1' no producirá eco alguno del servidor, pues el paquete enviado morirá en la interfase loopback antes que se produzca una respuesta. Para terminar ping cancela con <ctrl>-<c>.
La primera manipulación de la tabla inserta automáticamente los módulos ip_iptables y iptable_filter en el núcleo. Corre lspci para ver el estado de los módulos. Nota también que cualquier regla que insertes en la tabla será borrada cada vez que reinicies el computador. Más adelante mostraremos como hacer para insertar reglas en el arranque.
Para borrar la regla recién insertada existen varias posibilidades. El comando,
# iptables -D INPUT 1
borra la primera regla de la cadena INPUT. Una réplica del comando, reemplazando '-A' por '-D',
# iptables -D INPUT -p ICMP -s 127.0.0.1 -j DROP
produce el mismo efecto. Esta última forma es conveniente de usar cuando la cadena contiene reglas complejas. Finalmente, el comando,
# iptables -F INPUT
vacía la cadena INPUT de todas sus reglas.
El protocolo (-p) del paquete a que se aplica la regla puede ser "TCP", "UDP", "ICMP" o "ALL" (todos). Mayúsculas o minúsculas no tienen importancia. El prefijo "!" invierte el argumento. Por ejemplo, "-p ! TCP" especifica todos los protocolos a excepción de los de tipo TCP. "!" es equivalente a un no lógico.
La dirección de la fuente (-s) o destino (-d) puede especificarse de cuatro maneras. Puede ser un nombre, como "localhost" o "debian.ciencias.uchile.cl", o un número IP. También puede ser un rango de números IP, como "200.89.74.0/24" o equivalentemente "200.89.74.0/255.255.255.0". Ambos casos especifican el rango de números IP de 200.89.74.0 a 200.89.74.255. Los dígitos después de "/" denotan la parte del número IP que es significativo. "/24" dice que los 24 primeros bits del número son significativos, o si quieres, los tres primeros grupos de 8 bits. "/32" o "/255.255.255.255" es equivalente a una correspondencia completa del número IP que va delante. "/0" o "/0.0.0.0" especifica cualquier número IP y es equivalente a omitir "-s" o "-d" completamente. La cuarta forma de especificar la fuente o destino es similar a la máscara de red y es por ende conveniente de usar cuando la máscara es poco convencional. El prefijo "!" invierte el argumento. Por ejemplo, "-s ! localhost" especifica toda fuente a excepción de localhost.
Existen muchos blancos. Aquí vamos a usar solo los blancos DROP y ACCEPT. Otro blanco común es RETURN usado para terminar la cadena actual y retornar a la cadena inicial.
También se puede especificar la interfase de entrada (-i), la que recibe el paquete, o la interfase de salida (-o). Por ejemplo, "-i eth0" o "-i ppp0". En ambos casos se puede invertir el argumento con un "!".
El puerto de destino del paquete o el puerto de la fuente lo especifican las opciones "--destination-port" (o "--dport") y "--source-port" (o "--sport"). Por ejemplo, "--dport 80", prueba aquellos paquetes destinados al puerto http. Para saber qué número de puerto corresponde a cada protocolo consulta el archivo /etc/services.
Existen condiciones genéricas, implícitas y explícitas. Las genéricas son aquellas condiciones que no dependen del protocolo que se está probando, por ejemplo, el protocolo (-p), la fuente (-s), el destino (-d), la interfase de entrada (-i) o de salida (-o). Las condiciones implícitas son aquellas que están asociadas a un protocolo, por ejemplo, el puerto de la fuente o destino, "-p TCP --sport 80" y "-p UDP --dport 80", respectivamente, o el tipo de paquete ICMP, "-p ICMP --icmp-type 8". Las condiciones explícitas, a diferencia de las implícitas, no son cargadas automáticamente por iptables y tienen que cargarse con la opción "-m" antes de la condición. Por ejemplo, la condición "--state" requiere declararse previamente, "-m state --state NEW,RELATED,ESTABLISHED".
La primera propuesta de cortafuego es apta para una estación de trabajo Debian, conectado a la red por la interfase eth0, pero que no presta ningún servicio de servidor.
Crea el archivo /etc/network/if-up.d/firewall con un editor,
# nano /etc/network/if-up.d/firewall
Inserta las siguientes líneas:
#!/bin/sh IPTABLES=/sbin/iptables if [ ! -x $IPTABLES ]; then exit 0 fi if [ "$IFACE" = eth0 ]; then # 1) Definir la norma de cada cadena básica $IPTABLES -P INPUT DROP $IPTABLES -P FORWARD DROP $IPTABLES -P OUTPUT ACCEPT # 2) Aceptar paquetes en estado establecido y relacionado $IPTABLES -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT # 3) Aceptar paquetes en estado nuevo, excepto en eth0 $IPTABLES -A INPUT -m state --state NEW -i ! eth0 -j ACCEPT # 4) Optativo: Registrar paquetes que van a ser desechados #$IPTABLES -A INPUT -j LOG --log-level debug --log-prefix "Drop INPUT: " fi
Graba el archivo.
En 1) se modifica la norma de las cadenas INPUT y FORWARD a DROP. La cadena OUTPUT se deja con la norma ACCEPT. A estas alturas, la tabla va a descartar cualquier paquete entrante o redirigido en cualquier interfase de red y va aceptar cualquier paquete saliente.
En 2) se añade una regla a la cadena INPUT que acepta todo paquete de una conexión ya establecida o relacionada. Si una interfase envía o recibe un paquete de cualquier dirección interna o externa requiriendo una conexión, todo paquete que el receptor o emisor envíe de vuelta y llegue a INPUT será en estado establecido o relacionado, siendo aceptada por esta regla.
En 3) se añade una regla a la cadena INPUT que acepta todo paquete en INPUT que requiere una nueva conexión pero que no provenga de la interfase externa "-i ! eth0". En particular, cualquier nueva conexión requerida por la interfase loopback será aceptada. Modifica eth0 por el nombre del dispositivo de red externa, por ejemplo, ppp0 para una conexión ADLS con PPPoE. Corre ifconfig para determinar el nombre de la interfase que va en esta regla. Esta regla es aceptable solo si tienes una, y solo una, interfase conectada a la red externa. Cualquier otra interfase conectada al exterior quedará desprotegida con esta regla, creando un agujero por el cual un intruso podría meterse.
La regla en 4) (que está comentada) sirve para hacer diagnósticos. Al descomentarla, todo paquete que no haya cumplido ninguna de las reglas previas es registrado con syslog. El blanco LOG siempre retorna a dónde fue llamado. El nivel de registro puede ser "debug", "info", "notice", "warning", "err", "crit", "alert" o "emerg". Si el nivel es "debug", todo paquete descartado por la norma será registrado en /var/log/debug con el prefijo "Drop INPUT:".
Un paquete que cruza una cadena lo hace en forma secuencial, probándose cada regla una por una. Si la regla no se aplica al paquete en su totalidad, la próxima regla es probada. Si el paquete cumple cualquiera de estas reglas, salta inmediatamente al blanco y sale de la cadena, a no ser que el blanco sea LOG. Si un paquete logra llegar al final de la cadena, es descartado por la norma de INPUT.
Todo script en el directorio /etc/network/if-up.d/ se corre al momento de levantar la interfase. Siendo un script, requiere permisos de ejecución,
# chmod 755 /etc/network/if-up.d/firewall
Reinicia el computador.
El comando,
# iptables -L -v
hará un listado de las cadenas y reglas de la tabla del filtro con los contadores de paquetes y bytes.
Ahora vamos a presentar un cortafuego de uso más amplio, apto para un servidor Debian que presta algunos servicios de red, como http, ssh, ntp y otros.
Crea el archivo /etc/network/if-pre-up.d/firewall con un editor,
# nano /etc/network/if-pre-up.d/firewall
Inserta las siguientes líneas:
#!/bin/sh IPTABLES=/sbin/iptables if [ ! -x $IPTABLES ]; then exit 0 fi # número IP externo INET_IP="200.89.74.17" # números IP loopback LO_IP="127.0.0.0/8" if [ "$IFACE" = lo ]; then $IPTABLES -F $IPTABLES -P INPUT DROP $IPTABLES -P FORWARD DROP $IPTABLES -P OUTPUT ACCEPT ############################################### ############### INPUT chain ################### ############################################### # 1) new not in syn $IPTABLES -A INPUT -p TCP -m state --state NEW ! --syn -j DROP # 2) loopback $IPTABLES -A INPUT -i $IFACE -s $LO_IP -j ACCEPT $IPTABLES -A INPUT -i $IFACE -s $INET_IP -j ACCEPT # 3) Aceptar paquetes en estado establecido y relacionado $IPTABLES -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT # 4) inserta reglas en este punto (ver más adelante) # 5) Optativo: Registrar paquetes que van a ser desechados #$IPTABLES -A INPUT -j LOG --log-level debug --log-prefix "Drop INPUT: " # 6) desechar todo el resto $IPTABLES -A INPUT -j DROP ############################################### ############## FORWARD chain ################## ############################################### # desechar todo #$IPTABLES -A FORWARD -j LOG --log-level debug --log-prefix "Drop FORWARD: " $IPTABLES -A FORWARD -j DROP ############################################### ############## OUTPUT chain ################### ############################################### # new not in syn $IPTABLES -A OUTPUT -p TCP -m state --state NEW ! --syn -j DROP # establecido, relacionado $IPTABLES -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT # 7) aceptar todo el resto $IPTABLES -A OUTPUT -m state --state NEW -j ACCEPT $IPTABLES -A OUTPUT -j ACCEPT fi
Modifica la variable INET_IP con el número IP del servidor.
Todas estas reglas se insertan en la tabla antes de levantar la interfase loopback.
La regla en 1) requiere explicación. Todo paquete TCP tiene una serie de indicadores llamados banderines (flags). En iptables los banderines TCP se prueban con "--tcp-flags", por ejemplo, "iptables -p TCP --tcp-flags SYN,ACK,FIN SYN". En este caso, los banderines SYN, ACK y FIN son probados exigiéndose que SYN esté puesto pero no ACK ni FIN. En un paquete TCP que requiere una nueva conexión, SYN debería estar puesto, pero no ACK y FIN. El acrónimo "--syn" es equivalente a esto. La regla dice entonces que descarte todo paquete TCP en estado nuevo que no tenga el banderín SYN puesto y ACK y FIN no puestos.
Las reglas en 2) aceptan paquetes que llegan a la interfase loopback provenientes de números IP loopback y del servidor.
La regla en 3) acepta todo paquete de una conexión ya establecida o relacionada. Si una interfase envía o recibe un paquete de cualquier dirección interna o externa requiriendo una conexión, todo paquete que el receptor o emisor envíe de vuelta y llegue a INPUT será en estado establecido o relacionado, siendo aceptada por esta regla.
En el punto 4) vamos a insertar reglas más adelante.
La regla en 5) (que está comentada) sirve para hacer diagnósticos. Al descomentarla, todo paquete que no haya cumplido ninguna de las reglas previas es registrado con syslog. El blanco LOG siempre retorna a donde fue llamado. El nivel de registro puede ser "debug", "info", "notice", "warning", "err", "crit", "alert" o "emerg". Si el nivel es "debug" todo paquete descartado por la norma será registrado en /var/log/debug con el prefijo "Drop INPUT:".
La regla en 6) desecha todo el resto de paquetes en la cadena INPUT.
Las reglas en 7) aceptan, en la cadena OUTPUT, todo paquete saliente, en estado nuevo y todo los restantes. La última regla es en realidad innecesaria, pero sirve para hacer un conteo del número y bytes de paquetes salientes.
Todo script en el directorio /etc/network/if-pre-up.d/ se corre antes de levantar la interfase. Siendo un script, requiere permisos de ejecución,
# chmod 755 /etc/network/if-pre-up.d/firewall
Ahora crea el archivo /etc/network/if-up.d/firewall con un editor,
# nano /etc/network/if-up.d/firewall
Inserta las siguientes líneas:
#!/bin/sh IPTABLES=/sbin/iptables if [ ! -x $IPTABLES ]; then exit 0 fi if [ "$IFACE" = eth0 ]; then # 8) Definir la cadena "bloquear-spoof" $IPTABLES -N bloquear-spoof $IPTABLES -A bloquear-spoof -i $IFACE -s 10.0.0.0/8 -j DROP $IPTABLES -A bloquear-spoof -i $IFACE -s 172.16.0.0/12 -j DROP $IPTABLES -A bloquear-spoof -i $IFACE -s 192.168.0.0/16 -j DROP # 9) Cadena "pqtes-icmp-permitidos" $IPTABLES -N pqtes-icmp-permitidos $IPTABLES -A pqtes-icmp-permitidos -p ICMP --icmp-type 8 -j ACCEPT $IPTABLES -A pqtes-icmp-permitidos -p ICMP --icmp-type 11 -j ACCEPT # 10) Cadena "pqtes-udp-permitidos" $IPTABLES -N pqtes-udp-permitidos $IPTABLES -A pqtes-udp-permitidos -p UDP -m state --state NEW --dport 53 -j ACCEPT $IPTABLES -A pqtes-udp-permitidos -p UDP -m state --state NEW --dport 123 -j ACCEPT # 11) Cadena "pqtes-tcp-permitidos" $IPTABLES -N pqtes-tcp-permitidos $IPTABLES -A pqtes-tcp-permitidos -p TCP -m state --state NEW --dport 21 -j ACCEPT $IPTABLES -A pqtes-tcp-permitidos -p TCP -m state --state NEW --dport 25 -j ACCEPT $IPTABLES -A pqtes-tcp-permitidos -p TCP -m state --state NEW --dport 80 -j ACCEPT $IPTABLES -A pqtes-tcp-permitidos -p TCP -m state --state NEW --dport 443 -j ACCEPT $IPTABLES -A pqtes-tcp-permitidos -p TCP -m state --state NEW \ -m multiport --dport 110,995 -j ACCEPT $IPTABLES -A pqtes-tcp-permitidos -p TCP -m state --state NEW \ -m multiport --dport 143,993 -j ACCEPT $IPTABLES -A pqtes-tcp-permitidos -p TCP -m state --state NEW \ -s 200.89.74.0/24 --dport 22 -j ACCEPT # 12) Insertar reglas en cadena INPUT $IPTABLES -I INPUT 5 -i $IFACE -j bloquear-spoof $IPTABLES -I INPUT 6 -i $IFACE -p ICMP -j pqtes-icmp-permitidos $IPTABLES -I INPUT 7 -i $IFACE -p UDP -j pqtes-udp-permitidos $IPTABLES -I INPUT 8 -i $IFACE -p TCP -j pqtes-tcp-permitidos fi
Graba el archivo.
En 8) se define la cadena "bloquear-spoof" para descartar paquetes TCP en la interfase externa cuya fuente son números IP de redes privadas. Estos números son reservados para redes internas y no deberían circular en forma externa.
En 9) se define la cadena "pqtes-icmp-permitidos" con reglas relacionadas al protocolo ICMP. La primera regla acepta paquetes ICMP de tipo 8 (echo request) para permitir que la red externa reciba una respuesta con ping. La segunda regla acepta paquetes ICMP de tipo 11 (time exceeded).
En 10) se define la cadena "pqtes-udp-permitidos" con reglas relacionadas al protocolo UDP. La primera regla prueba paquetes de la interfase externa, en estado nuevo, cuyo destino es el puerto domain (53), usado por BIND. La segunda regla prueba paquetes cuyo destino es el puerto ntp (123) para permitir a ntpd servir correctamente requerimientos externos.
En 11) se define la cadena "pqtes-tcp-permitidos" con reglas relacionadas al protocolo TCP. En las cuatro primeras reglas, los puertos 21 (ftp), 25 (smtp), 80 (http) y 443 (https) son abiertos a requerimientos de todo número IP por la interfase de la red externa. Las dos siguientes reglas permite requerimientos nuevos a los puertos 110 (pop2), 995 (pop3s), 143 (imap2) y 993 (imaps). Para poder especificar múltiples puertos es necesario cargar la condición "multiport" explícitamente. Si no prestas servicios por cualquiera de estos puertos, borra el número correspondiente, o agrega propios. Para que la regla del puerto 21 (ftp) funcione correctamente, es necesario insertar en módulo ip_conntrack_ftp en el núcleo. Abrir el puerto 25 (smtp) es necesario para recibir correo electrónico con un MTA como exim4 o sendmail. La regla siguiente permite a los números IP 200.89.74.0 a 200.89.74.255 hacer requerimientos SSH (puerto 22).
En 12) se insertan reglas de la cadena INPUT, a partir del puesto número 5, en el punto 4), antes de desechar todo en 6), La primera de ellas hace saltar todo paquete que llega a la interfase externa a la cadena "bloquear-spoof" para descartar todo paquete obviamente malicioso. Las próximas tres reglas hacen saltar todo paquete que llega a la interfase externa, de los protocolos ICMP, UDP y TCP, a las cadenas "pqtes-icmp-permitidos", "pqtes-udp-permitidos" y "pqtes-tcp-permitidos", respectivamente.
Dale a /etc/network/if-up.d/firewall permisos de ejecución,
# chmod 755 /etc/network/if-up.d/firewall
Detiene la red, limpia las interfases y luego inicia la red otra vez,
# /etc/init.d/networking stop # /etc/init.d/ifupdown-clean restart # /etc/init.d/networking start
Alternativamente, reinicia el computador.
El comando,
# iptables -L -v
hará un listado de las cadenas y reglas de la tabla del filtro con los contadores de paquetes y bytes.
Recuerda que esta es una proposición. Las reglas en las cadenas "pqtes-icmp-permitidos", "pqtes-udp-permitidos" y "pqtes-tcp-permitidos" son las que definen los servicios permitidos y las que deberías modificar para tu caso en particular. El resto de cadenas y reglas son de uso general y no requieren modificación alguna.
Hemos basado estas instrucciones en la documentación escrita por el creador de iptables, 'Rusty' Russell, incluida en el paquete Debian iptables y que puedes encontrar en,
/usr/share/doc/iptables/html/packet-filtering-HOWTO.html
This document was generated using AFT v5.096