La primera sección que observarás en el ejemplo rc.firewall.txt es la de la configuración. Siempre debería ser modificada, pues contiene la información que resulta imprescindible para tu configuración actual. Por ejemplo, tu dirección IP normalmente será distinta, por lo cual se especifica aquí. La variable $INET_IP siempre debería ser una dirección IP válida, si es que tienes una (si no es así, probablemente deberías ver rc.DHCP.firewall.txt, pero de todas formas sigue leyendo este script, ya que ofrece una introducción de bastantes cosas interesantes). Del mismo modo, la variable $INET_IFACE debe señalar al adaptador utilizado para tu conexión a Internet, como puede ser eth0, eth1, ppp0, tr0, etc, por nombrar unos pocos de los adaptadores posibles.
Este script no contiene ninguna configuración especial para DHCP o PPPoE, por lo que estas secciones permanecen vacías. Lo mismo ocurre con el resto de secciones vacías, que sin embargo se mantienen para que puedas ver las diferencias entre los diferentes scripts de una forma más efectiva. Si por algún motivo necesitaras estas configuraciones, siempre puedes crear una mezcla a partir de los diferentes scripts o también crear el tuyo propio desde cero.
La sección Configuración de la red local (LAN) incluye la mayoría de las opciones necesarias de configuración de tu LAN. Por ejemplo, necesitas especificar el nombre y la dirección IP de la interfaz física conectada a tu LAN, así como el rango IP que utiliza la red local.
Asímismo, podrás observar que hay una sección de configuración del host local. Sin embargo es casi seguro que no tendrás que cambiar ningún valor en esta sección, puesto que normalmente utilizarás "127.0.0.1" como dirección IP y la interfaz casi con toda seguridad se llamará lo.
Por fin, justo bajo la configuración del host local, verás una breve sección perteneciente a iptables. En principio sólo contiene la variable $IPTABLES, que le indica al script la situación correcta de la aplicación iptables en tu instalación. El lugar concreto puede variar algo, aunque al compilar a mano el paquete iptables la ruta por defecto es /usr/local/sbin/iptables. Sin embargo, muchas distribuciones sitúan la aplicación en otro directorio, como puede ser /usr/sbin/iptables, por ejemplo.
Para empezar, vemos que se comprueba si los archivos de dependencias de módulos están actualizados ejecutando el comando /sbin/depmod -a. A continuación se cargan los módulos que se requerirán en el script. Evita siempre cargar módulos que no vayas a necesitar y si es posible trata de evitar tener módulos sólo "por si acaso" a no ser que los vayas a usar. La razón de ésto es por seguridad, ya que de otra manera supondrá un esfuerzo extra escribir reglas adicionales. Así, si por ejemplo quieres tener soporte para los objetivos LOG, REJECT y MASQUERADE y no los tienes compilados estáticamente en el núcleo, deberás cargar los módulos como sigue:
/sbin/insmod ipt_LOG /sbin/insmod ipt_REJECT /sbin/insmod ipt_MASQUERADE
En éste y los demás scripts de ejemplo forzamos la carga de módulos, lo cual puede originar fallos durante el proceso de carga. Estos fallos pueden deberse a muchos factores y generarán mensajes de error. Si alguno de los módulos más básicos no se cargan, el error más probable es que el módulo o funcionalidad esté compilado estáticamente en el núcleo. Para más información al respecto, léete la sección Problemas en la carga de módulos del apéndice Problemas y preguntas frecuentes. |
Seguimos con los módulos no requeridos y encontramos la opción de cargar el módulo ipt_owner, que entre otras cosas se puede utilizar para permitir únicamente a determinados usuarios la posibilidad de establecer determinadas conexiones. Este módulo no se utiliza en el presente ejemplo, pero básicamente podrías permitir sólo al usuario root establecer conexiones FTP y HTTP con "redhat.com" y DROP (denegárselo/desechar) a todos los demás. También puedes denegar el acceso desde tu máquina hacia Internet a todos los usuarios excepto a tu propio usuario (tú mismo) y a root, lo cual puede no gustar a los demás, pero estarás un poco más seguro ante los ataques tipo "bouncing" (de forma que parezca que la conexión la realiza tu host) y ante los ataques en que el hacker sólo utilizará tu host como máquina intermedia. Para mayor información sobre la comparación ipt_owner léete la sección Comparación Propietario (Owner) del capítulo Cómo se escribe una regla.
En este momento también podemos cargar módulos extra para la comparación de estados. Todos los módulos que mejoran el comportamiento del código de comparación de estados y de seguimiento de las conexiones se llaman ip_conntrack_* e ip_nat_*. Los asistentes para el seguimiento de las conexiones son módulos especiales que le indican al núcleo cómo hacer un seguimiento adecuado de conexiones de un tipo específico. Sin estos asistentes, el núcleo no sabrá qué buscar al intentar hacer un seguimiento de esas conexiones específicas. Por otra parte, los asistentes NAT son a su vez extensiones de los anteriores asistentes y le indican al núcleo qué buscar en paquetes específicos, además de cómo interpretarlos para que las conexiones funcionen. Por ejemplo, FTP es un protocolo complejo por definición, que envía la información de conexión dentro del contenido del paquete. Así pues, si una de tus máquinas "NATeadas" conecta con un servidor FTP de Internet, enviará su propia dirección IP de la red local dentro del paquete y le indicará al servidor FTP que realice la conexión con esa dirección. Puesto que las direcciones locales no son válidas fuera de tu propia red local, el servidor FTP no sabrá qué hacer con éllas y la conexión se perderá. Los asistentes FTP NAT efectúan las conversiones necesarias en estas conexiones, de manera que el servidor FTP sabrá con quién debe establecer la conexión. El mismo caso ocurre con las transferencias (envíos) DCC de archivos y los chats. La creación de este tipo de conexiones precisa la dirección IP y los puertos con los que conectar a través del protocolo IRC, lo cual exige ciertas "traducciones". Sin estos asistentes determinadas tareas con FTP e IRC se podrán llevar a cabo, qué duda cabe, pero habrá otras que no. Por ejemplo, serás capaz de recibir ficheros mediante DCC, pero no podrás enviarlos. Éllo es debido a la manera de establecer conexiones de DCC. Al iniciar una conexión, le indicas al receptor que quieres enviarle un fichero y dónde debe conectarse. Sin los asistentes para la conexión DCC, este aviso inicial parecerá indicarle al receptor que debe conectarse a algún host de su propia red local (la del receptor). Como resultado la conexión se perderá. Sin embargo, cuando seas tú el que descargue ficheros no habrá problemas puesto que lo más probable es que el que te diga dónde debes conectar, te envíe la dirección correcta.
Si estás experimentando problemas con los mIRC DCCs y tu cortafuegos, mientras que todo funciona perfectamente con otros clientes IRC, léete la sección Problemas con mIRC DCC del apéndice Problemas y preguntas frecuentes. |
En el momento de escribir estas líneas, sólo existe la opción de cargar módulos que añadan soporte para los protocolos FTP e IRC. Si necesitas una explicación más profunda sobre estos módulos (conntrack y nat), lee el apéndice Problemas y preguntas frecuentes. También se pueden encontrar asistentes para seguimiento de conexiones (conntrack) de tipo H.323 (entre otros) en el patch-o-matic, así como otros asistentes para NAT. Para poder utilizarlos tienes que utilizar el patch-o-matic y compilar tu propio núcleo. Para ver una explicación detallada de cómo hacer ésto, léete el capítulo Preparativos.
Ten en cuenta que necesitas cargar los módulos ip_nat_irc e ip_nat_ftp si quieres que funcione correctamente la traducción de direcciones (Network Addres Translation) tanto en el protocolo FTP como en el IRC. Además, tendrás que cargar los módulos ip_conntrack_irc e ip_conntrack_ftp antes de cargar los módulos NAT anteriores. Se emplean de la misma forma que los módulos conntrack y permitirán al ordenador efectuar traducciones NAT en estos dos protocolos. |
Es ahora cuando arrancamos el reenvío IP (IP forwarding) al enviar el valor "1" a /proc/sys/net/ipv4/ip_forward con el comando siguiente:
echo "1" > /proc/sys/net/ipv4/ip_forward
Puede valer la pena pensar dónde y cuándo arrancamos el reenvío IP. En este script, como en el resto de este tutorial, se arranca antes de crear cualquier tipo de filtros IP (o sea, los conjuntos de reglas de iptables). Esto implica que durante un breve lapso de tiempo el cortafuegos aceptará el reenvío de cualquier tipo de tráfico desde cualquier lugar, estando este lapso comprendido entre un milisegundo y algunos minutos, dependiendo del script que estemos ejecutando y de la velocidad de la máquina. Así pues, la "gente interesada" (los "chicos malos") tendrá su oportunidad de atravesar el cortafuegos. Como podrás comprender en realidad esta opción debería ejecutarse después de crear todas las reglas del cortafuegos, si bien he decidido hacerlo antes para mantener una estructura consistente en todos los scripts. |
Si necesitaras soporte para IPs dinámicas, como cuando usas SLIP, PPP o DHCP, puedes activar la siguiente opción (ip_dynaddr) ejecutando lo siguiente:
echo "1" > /proc/sys/net/ipv4/ip_dynaddr
Si existe cualquier otra opción que necesites arrancar, debes hacerlo de la forma indicada en los dos ejemplos anteriores. Existen otros documentos que explican cómo hacerlo de otra manera, aunque ésto queda fuera de los objetivos del tutorial. Puedes leer un buen (y corto) documento sobre el sistema proc disponible con el núcleo y que también tienes en el apéndice Otras fuentes y enlaces. Este apéndice es un buen punto de partida cuando busques información de áreas específicas que no hayas encontrado aquí.
El script rc.firewall.txt, así como el resto de scripts de este tutorial, contiene una pequeña sección de configuraciones no requeridas de proc, las cuales pueden ser un buen punto de partida cuando algo no funciona tal como deseas. Sin embargo, no cambies nada antes de saber qué significa. |
En esta sección se describirán brevemente las alternativas que he escogido en el tutorial en lo que concierne a las cadenas específicas de usuario y algunas opciones más específicas del script rc.firewall.txt. Puedo estar equivocado en la forma de enfocar algunos temas, pero espero explicarlos con sus posibles problemas allí donde ocurran y en el momento en que ocurran. Además también se recuerda brevemente el capítulo Atravesando tablas y cadenas. Esperemos que la ayuda de un ejemplo real sea suficiente para que recuerdes cómo se atraviesan las tablas y cadenas.
He dispuesto las diferentes cadenas de usuario de forma que ahorren el máximo de CPU, manteniendo al mismo tiempo la máxima seguridad y legibilidad posibles. En lugar de dejar a los paquetes TCP atravesar las reglas ICMP, UDP y TCP, simplemente escojo los paquetes TCP y les hago atravesar una cadena de usuario. De esta forma no sobrecargamos en exceso el sistema. La siguiente imagen intentará explicar los fundamentos sobre el camino que sigue un paquete entrante al atravesar Netfilter. Con todo éllo espero aclarar los objetivos de este script. Todavía no vamos a discutir detalles específicos, pues ésto se hará más adelante, en este mismo capítulo. En realidad es una simplificación de la imagen que puedes encontrar en el capítulo Atravesando tablas y cadenas, dónde se discutió el proceso completo en profundidad.
Basándonos en este gráfico, vamos a dejar claro qué nos hemos planteado. La totalidad de este ejemplo asume que el escenario al que nos referimos tiene una red local conectada a un cortafuegos y el cortafuegos conectado a Internet. También se asume que tenemos una dirección IP estática para conectar con Internet (todo lo contrario a cuando utilizamos DHCP, PPP, SLIP u otros). En este caso, queremos permitir al cortafuegos que actúe como servidor de ciertos servicios de Internet y además confiamos plenamente en nuestra red local, por lo que no bloquearemos ningún tráfico desde ésta. Por fin, este script tiene como principal prioridad permitir únicamente el tráfico que explícitamente deseamos permitir. Para conseguirlo, estableceremos las políticas por defecto de las cadenas como DROP (desechar). En la práctica ésto eliminará todas las conexiones y paquetes que no hayamos permitido explícitamente dentro de nuestra red o nuestro cortafuegos.
En otras palabras, deseamos que la red local pueda conectar con Internet. Puesto que la red local es de plena confianza, deseamos permitir todo tipo de tráfico desde la red hacia Internet. Sin embargo, Internet es una red a la que no concedemos confianza alguna, por lo que deseamos bloquear cualquier acceso desde élla hacia nuestra red local. Así pues, en base a estas premisas generales, veamos qué debemos hacer y qué es lo que no queremos ni debemos hacer.
Para empezar, ya hemos dicho que deseamos que la red local sea capaz de conectarse a Internet. Para conseguirlo necesitaremos traducir mediante NAT todos los paquetes, ya que ningún ordenador de la red local tiene una dirección IP real [refiriéndonos a la conexión directa a Internet]. Ésto lo haremos dentro de la cadena PREROUTING, que se crea al final de este script. Esto significa que también tendremos que efectuar algún filtrado en la cadena FORWARD, ya que de lo contrario cualquiera desde el exterior tendrá acceso total a nuestra red. Confiamos al máximo en la red local y por éllo permitimos específicamente todo tráfico desde ésta hacia Internet. Ya que no queremos permitir a nadie del exterior acceder a la red local, bloquearemos todo tráfico desde el exterior excepto las conexiones establecidas y las relacionadas, lo cual significa que permitiremos todo el tráfico de retorno desde Internet hacia la red local.
En cuanto al cortafuegos, quizá estemos algo cortos de capital, o quizá deseemos ofrecer algunos servicios a los navegantes de Internet. Así pues, hemos decidido permitir el acceso a HTTP, FTP, SSH e IDENTD a través del cortafuegos. Todos estos protocolos están disponibles en el cortafuegos, por lo que deben ser admitidos en la cadena INPUT, así como también debe estar permitido todo el tráfico de retorno (hacia Internet) a través de la cadena OUTPUT. Sin embargo, seguimos confiando completamente en la red local y tanto el periférico de bucle local como su dirección IP también son de confianza. Debido a ésto queremos añadir reglas especiales para permitir todo el tráfico desde la red local así como la interfaz de bucle local. Además, no queremos permitir el tráfico de paquetes específicos o cabeceras de paquetes específicas en coyunturas específicas, así como tampoco deseamos que determinados rangos de direcciones IP puedan alcanzar el cortafuegos desde Internet (por ejemplo, el rango de direcciones 10.0.0.0/8 está reservado para las redes locales, de aquí que normalmente no queramos dejar pasar paquetes provenientes de este rango, ya que en un 90% de los casos serán intentos de acceso falseados mediante spoofing; sin embargo, antes de implementar ésto, debes tener en cuenta que determinados Proveedores de Servicios de Internet utilizan este rango en sus propias redes; si te interesa una explicación más detallada sobre este problema, lee el capítulo Problemas y preguntas frecuentes).
Puesto que tenemos un servidor FTP ejecutándose en el servidor y deseamos que los paquetes atraviesen el menor número de reglas posibles, añadimos una regla al principio de la cadena INPUT que permita el paso a todo el tráfico establecido y relacionado. Por la misma razón deseamos dividir las reglas en subcadenas, de manera que los paquetes sólo tengan que atravesar el menor número de reglas posible. Con éllo conseguiremos que el conjunto de reglas consuman el menor tiempo posible con cada paquete, eliminando redundancias en la red.
En este script decidimos dividir los diferentes paquetes según las familias de protocolos, por ejemplo TCP, UDP o ICMP. Todos los paquetes TCP atraviesan una cadena específica llamada "tcp_packets", que contendrá reglas para todos los puertos y protocolos TCP que queramos permitir. Además, deseamos efectuar algunos chequeos extra a los paquetes TCP, por lo que nos gustaría crear otra subcadena para todos aquellos paquetes que sean aceptados por utilizar números de puerto válidos en el cortafuegos. Decidimos llamar a esta subcadena "allowed", y debe contener unos pocos chequeos para acabar de aceptar los paquetes. En cuanto a los paquetes ICMP, atravesarán la cadena "icmp_packets". Cuando se decidió el contenido de esta cadena, no se vió necesario realizar ningún chequeo extra antes de aceptar los paquetes, siempre que coincidieran con los tipos y códigos permitidos; así pues se aceptan directamente. Por último tenemos que manejar los paquetes UDP y los enviaremos a la cadena "udp_packets". Todos los paquetes que coincidan con los tipos permitidos serán aceptados de inmediato sin más chequeos.
Dado que estamos trabajando con una red relativamente pequeña, esta máquina también se utiliza como estación de trabajo secundaria y para imponer una carga extra debido a éllo, queremos permitir que determinados protocolos específicos puedan contactar con el cortafuegos, como speak freely e ICQ.
Por último, tenemos la cadena OUTPUT. Ya que confiamos bastante en el cortafuegos, permitimos casi todo el tráfico de salida. No efectuamos ningún bloqueo específico de usuario, así como tampoco efectuamos ningún bloqueo de protocolos específicos. Sin embargo, no queremos que la gente utilice esta máquina para camuflar (spoof) paquetes que dejen el cortafuegos, por lo que sólo permitiremos el tráfico desde las direcciones asignadas al cortafuegos. Lo más probable es que implementemos ésto añadiendo reglas que acepten (ACCEPT) todos lo paquetes que dejen el cortafuegos siempre que procedan de una de las direcciones IP que le han sido asignadas, siendo desechados como política por defecto todos los que no cumplan esta premisa.
Casi al comenzar el proceso de creación de nuestro conjunto de reglas, establecemos las políticas por defecto en las diferentes cadenas con un comando bastante simple, como puedes ver a continuación:
iptables [-P {cadena} {política}]
La política por defecto se utiliza siempre que los paquetes no coincidan con ninguna regla de ninguna cadena. Por ejemplo, supongamos que llega un paquete que no encaja en ninguna regla de todas las que tenemos en nuestro conjunto de reglas. Si ésto ocurre debemos decidir qué debe pasarle al paquete en cuestión y es aquí donde entra en escena la política por defecto.
Se cuidadoso con la política por defecto que estableces en las cadenas de otras tablas, pues no están pensadas para filtrar como la tabla FILTER y pueden desembocar en comportamientos realmente extraños. |
Ahora ya tienes una buena idea de qué queremos conseguir con el cortafuegos, así que empecemos con la creación del conjunto de reglas. Es el momento de cuidarnos de especificar todas las reglas y cadenas que deseamos crear y utilizar, así como todos los conjuntos de reglas en las cadenas.
Después de esta introducción, empezaremos por crear las diferentes cadenas especiales que necesitamos, mediante el comando -N. Las nuevas cadenas se crean vacías, sin ninguna regla en éllas, y tal como ya se ha dicho son: icmp_packets, tcp_packets, udp_packets y la cadena allowed, que es utilizada por la cadena tcp_packets. Los paquetes entrantes por $INET_IFACE, de tipo ICMP, serán redirigidos a la cadena icmp_packets. Los paquetes de tipo TCP, serán redirigidos a la cadena tcp_packets y los paquetes de tipo UDP a la cadena udp_packets. Todo ésto se explica en profundidad más adelante, en la sección La cadena INPUT. Crear una cadena es bastante sencillo, pues sólo consiste en una corta declaración similar a:
iptables [-N cadena]
En las secciones siguientes desarrollaremos ampliamente cada una de las cadenas que acabamos de crear. Veremos cómo son y qué reglas contendrán, así como qué conseguiremos con éllas.
La cadena bad_tcp_packets se ha concebido para contener reglas que inspeccionen los paquetes entrantes e identifiquen cabeceras malformadas, entre otros problemas. Así pues, decidimos incluir únicamente dos filtros: el primero bloquea todos los paquetes TCP entrantes que sean considerados como nuevos (NEW), pero no tengan establecido el bit SYN; el segundo bloquea los paquetes SYN/ACK que se consideren nuevos (NEW). Esta cadena se puede utilizar para comprobar posibles inconsistencias como las anteriores, o también como los chequeos de puertos XMAS, etc. También podemos añadir reglas que busquen el estado INVALID.
Si deseas entender a fondo el "NEW not SYN", deberías leerte la sección Paquetes cuyo estado es NEW pero cuyo bit SYN no se ha establecido del apéndice Problemas y preguntas frecuentes, acerca del estado "NEW and non-SYN" de los paquetes que atraviesan otras reglas. Bajo determinadas circunstancias a estos paquetes se les puede permitir atravesar el cortafuegos, aunque en el 99% de los casos no querremos que pasen, por lo que registramos su llegada en los registros (logs) y los desechamos (DROP).
El motivo para rechazar(REJECT) los paquetes SYN/ACK que se consideran nuevos es muy simple. Se describe en profundidad en la sección Paquetes SYN/ACK y NEW del apéndice Problemas y preguntas frecuentes. Básicamente, hacemos ésto como cortesía hacia otros hosts, ya que les prevendrá de ataques de predicción de secuencia numérica (sequence number prediction attack).
Si un paquete que llega a la interfaz $INET_IFACE es del tipo TCP, viajará por la cadena tcp_packets, y si la conexión es a través de un puerto en el que permitimos el tráfico, desearemos hacer algunos chequeos finales para ver si realmente le permitiremos el paso al paquete o no. Todo estos chequeos finales se realizan en la cadena allowed (permitido).
Empezamos comprobando si estamos ante un paquete SYN. Si es así, lo más probable es que sea el primer paquete de una nueva conexión, por lo que, obviamente, le permitimos el paso. Entonces comprobamos si el paquete procede de una conexión establecida (ESTABLISHED) o relacionada (RELATED). Si lo es, de nuevo permitimos el paso. Una conexión ESTABLISHED es aquélla que ha "visto" tráfico en ambas direcciones y puesto que nos hemos encontrado con un paquete SYN, la conexión debe estar en el estado ESTABLISHED, de acuerdo con la máquina de estados. La última regla de esta cadena desechará (DROP) todo lo demás. Ésto viene a englobar todo aquéllo que no ha "visto" tráfico en ambas direcciones, es decir, o no hemos respondido al paquete SYN, o están intentando empezar una conexión mediante un paquete "no-SYN". No existe ninguna utilidad práctica en comenzar una conexión sin un paquete SYN, a excepción de la gente que está haciendo escaneo de puertos. Hasta dónde yo sé, no hay actualmente ninguna implementación de TCP/IP que soporte la apertura de una conexión TCP con algo diferente a un paquete SYN, por lo que casi con total certeza se tratará de un escaneo de puertos y es recomendable desechar (DROP) estos intentos de conexión.
La cadena tcp_packets especifica qué puertos se podrán alcanzar desde Internet a través del cortafuegos. Sin embargo, hay más chequeos para estos paquetes, ya que los enviamos a la cadena "allowed", descrita anteriormente.
La opción -A tcp_packets le indica a iptables en qué cadena debe añadir la nueva regla, la cuál se incluirá al final de la lista de reglas de esa cadena. La opción -p TCP especifica el primer requisito a cumplir por el paquete, es decir, debe ser un paquete TCP; de la misma forma, -s 0/0 busca (compara) las direcciones de origen, empezando por la 0.0.0.0 con máscara de red 0.0.0.0, es decir todas las direcciones de origen. Aunque éste es el comportamiento por defecto, lo indico para que todo quede lo más claro posible (en este caso todos los puertos de origen cumplirán el requisito exigido por la regla). La opción --dport 21 indica el puerto de destino 21, o sea que si el paquete está destinado al puerto 21, cumplirá el último criterio de selección de la regla. Si todos los criterios coinciden, el paquete será enviado a la cadena allowed. Si no coincide con alguna de las opciones, será devuelto a la cadena original (la que generó el salto a la cadena tcp_packets).
Tal como queda el ejemplo, permito el puerto TCP 21, o sea, el puerto de control FTP, empleado para controlar las conexiones FTP. Ésto unido a que más alante se permiten todas las conexiones relacionadas (RELATED), permitirá las conexiones ftp PASIVAS y ACTIVAS, pues en principio el módulo ip_conntrack_ftp ya se habrá cargado. Si no queremos permitir ningún tipo de conexión FTP, podemos descargar (o no cargar) el módulo ip_conntrack_ftp y borrar (o no añadir) la línea $IPTABLES -A tcp_packets -p TCP -s 0/0 --dport 21 -j allowed del fichero rc.firewall.txt.
El puerto 22 es el SSH y emplear este puerto cuando dejas a cualquiera acceder a tu máquina mediante shell, es definitivamente mucho mejor que permitir el acceso mediante telnet por el puerto 23. Ten en cuenta que estás trabajando con un cortafuegos y siempre es una mala idea permitir a alguien más que tú mismo cualquier tipo de acceso a la máquina del cortafuegos. Los usuarios con acceso al cortafuegos siempre deben limitarse al mínimo imprescindible y ninguno más.
El puerto 80 es el de HTTP, o sea, tu servidor web. Borra esta línea si no quieres ejecutar un servidor web directamente en tu cortafuegos.
Y, por último, se permite el puerto 113, que es el IDENTD y puede ser necesario para algunos protocolos como IRC, para trabajar correctamente. Ten en cuenta que puede ser interesante utilizar las librerías oidentd si quieres efectuar traducciones NAT de varios hosts en tu red local. oidentd admite peticiones de retransmisión de IDENTD a las máquinas correctas de tu red local.
Si necesitas añadir más puertos abiertos en el script, parece bastante obvio cómo deberías hacerlo: simplemente copia y pega una de las líneas de la cadena tcp_packets y cambia el puerto que necesitas abrir.
Si se nos presenta un paquete UDP en la cadena INPUT, lo enviamos a la cadena udp_packets, dónde comenzaremos las comprobaciones empezando por el protocolo con -p UDP y después comprobaremos que proviene de cualquier dirección comenzando por la 0.0.0.0 y con máscara de red 0.0.0.0, o sea, que de nuevo cualquier dirección de origen es válida. Sin embargo sólo aceptamos puertos UDP específicos que queramos mantener abiertos para hosts de Internet. Puedes observar que no necesitamos abrir agujeros dependiendo del puerto de origen del host que envía el paquete, ya que de éllo se debería ocupar la máquina de estados. Sólo necesitamos abrir puertos en nuestro host si vamos a ejecutar un servidor en algún puerto UDP, como el servidor DNS, etc. Los paquetes que lleguen al cortafuegos y sean parte de una conexión ya establecida (por nuestra red local), serán aceptados automáticamente por las reglas --state ESTABLISHED,RELATED del principio de la cadena INPUT.
Tal como está el ejemplo, NO aceptamos (ACCEPT) paquetes UDP entrantes por el puerto 53, pues es el puerto usual cuando se efectúan búsquedas DNS. Observa que la regla está ahí, pero por defecto está comentada (#) y por éllo no se aceptan paquetes destinados al puerto 53. Si deseas que tu cortafuegos actúe como servidor DNS, elimina el símbolo de comentario (para permitir el tráfico por ése puerto).
Personalmente también permito el acceso por el puerto 123, que es el empleado por NTP (o Network Time Protocol). Este protocolo se emplea para ajustar la hora del reloj de tu ordenador de acuerdo a la hora de ciertos "servidores de horas" (time servers) que poseen relojes muy precisos. La mayoría de la gente probablemente no utiliza este protocolo y por éllo por defecto no está permitido el acceso a este puerto. Al igual que antes, sólo tienes que eliminar el símbolo de comentario para activar la regla.
En el ejemplo autorizamos el uso del puerto 2074, empleado por ciertas aplicaciones multimedia en tiempo real, como speak freely, con la que puedes hablar con otra gente en tiempo real (lógicamente a través de Internet), mediante unos altavoces y un micrófono o bien unos auriculares con micro. Si no deseas utilizar este servicio, simplemente comenta la línea (#).
El puerto 4000 es el del protocolo ICQ. Debería ser un protocolo conocidísimo, usado por la aplicación de Mirabilis denominada ICQ. Hay al menos 2 ó 3 clones diferentes de ICQ para Linux y es uno de los programas de chat más ampliamente difundido. Dudo que se necesiten más explicaciones.
Una vez en este punto, tienes disponibles dos reglas más para cuando experimentes un número excesivo de entradas en el registro debidas a distintas circunstancias. La primera regla bloqueará los paquetes de difusión (broadcast packets) hacia el rango de puertos entre el 135 y el 139. Este tipo de paquetes los emplean NetBIOS o SMB para gran parte de los usuarios de las plataformas de Microsoft. Con éllo bloquearemos todas las entradas de registro que pudiéramos tener a causa de Redes Microsoft desde el exterior de nuestra red local. La segunda regla también se ha creado para evitar los problemas de un excesivo número de entradas en el registro, pero en este caso se encarga de las peticiones DHCP que provienen del exterior. Ésto es especialmente cierto si tu red exterior se basa en una red de tipo "Ethernet no conmutado", donde los clientes reciben sus direcciones IP a través de DHCP. En estas circunstancias y debido únicamente a éllo, puedes verte barrido por una gran cantidad de registros.
Ten en cuenta que las dos últimas reglas son especialmente opcionales, puesto que habrá quien tenga interés en conservar este tipo de registros. Si estás teniendo problemas por un excesivo número de entradas legítimas en el registro, prueba a eliminar las originadas por estas clases de paquetes. También hay algunas reglas más del mismo tipo justo antes de las reglas de registro en la cadena INPUT. |
Aquí es donde decidimos qué tipos ICMP permitiremos. Si un paquete ICMP llega a través de la interfaz eth0 hasta la cadena INPUT, lo enviamos a la cadena icmp_packets tal como ya se ha explicado en los casos anteriores. Es entonces cuando comprobamos a qué tipo ICMP pertenece y si le permitimos el paso o no. Por el momento, sólo permito el paso a las peticiones de eco ICMP (Echo requests), al "tiempo de vida del paquete nulo durante el tránsito" (TTL equals 0 during transit) y al "tiempo de vida nulo durante el reensamblado" (TTL equals 0 during reassembly). La razón para no permitir el paso a ningún otro tipo ICMP como opción por defecto es que prácticamente todos éllos deben ser gestionados por las reglas para el estado "paquete relacinado" (RELATED).
Si un paquete ICMP es enviado como respuesta a un paquete existente o a un flujo de paquetes, se considera como relacionado (RELATED) con el flujo original. Para más información sobre los estados, léete el capítulo La máquina de estados. |
A continuación explicaré las razones por las que permito estos tipo de paquetes ICMP: las peticiones de eco se utilizan para pedir una respuesta de eco de la máquina destino, lo cual sirve principalmente para hacer "pings" a otros hosts y ver si están disponibles en alguna red. Sin esta regla los demás hosts no serán capaces de hacer ping sobre nuestra máquina y no podrán saber si estamos disponibles en alguna conexión de red. Hay que destacar sin embargo que hay quien elimina esta regla, simplemente porque no desean ser "vistos" (ser detectables) en Internet. Borrando esta regla consigues que cualquier ping que llegue a tu cortafuegos sea inútil, ya que el cortafuegos no le contestará.
En cuanto al "tiempo excedido" (TTL equals 0 during transit o bien TTL equals 0 during reassembly), nos convendrá que los paquetes ICMP de este tipo tengan permiso para atravesar el cortafuegos siempre que deseemos conocer la ruta hasta un host (o sea, efectuar un trace-route), o también en los casos en los que un paquete que hayamos enviado a alcanzado el estado "Time To Live=0" y deseemos recibir notificación del evento.
Por ejemplo, cuando efectúas un trace-route a alguien, empiezas enviando un paquete con TTL = 1, lo cual implica que en el primer salto entre hosts (en el primer "hop") el TTL se reduce en una unidad y se iguala a 0, de forma que se envía de vuelta un paquete ICMP de tipo Time Exceeded desde el primer punto de la ruta de acceso que lleva al host que tratamos de localizar. Entonces se envía otro paquete, pero esta vez con TTL = 2 y en el segundo "hop" la pasarela (gateway) devolverá un ICMP Time Exceeded, con lo que ya se conocen los dos primeros puntos de la ruta. Continuando de manera análoga se llegará hasta el host que deseamos alcanzar, con lo que conoceremos la ruta completa y podremos saber qué host intermedio tiene problemas.
Para ver un listado completo de los tipos ICMP, léete el apéndice Tipos ICMP. Para más información acerca de los tipos ICMP y su utilización, te recomiendo leer los siguietes documentos e informes:
Protocolo de Mensajes de Control de Internet por Ralph Walden.
RFC 792 - Protocolo de Mensajes de Control de Internet por J. Postel.
Debo advertir que desde tu punto de vista puedo estar equivocado al bloquear algunos de los tipos ICMP, pero en mi caso todo funciona perfectamente después de bloquear todos los tipos ICMP a los que no permito el paso. |
Tal como la he definido en el script, la cadena INPUT utiliza principalmente otras cadenas para hacer su trabajo. De esta forma no sobrecargamos demasiado a iptables y funcionará mucho mejor en máquinas lentas, que de otra manera desecharían paquetes en situaciones de mucha carga para el sistema. Ésto lo conseguimos al buscar detalles concretos que deberían coincidir en los paquetes del mismo tipo, enviándolos entonces a cadenas especificadas por el usuario. Actuando así podremos subdividir nuestro conjunto de reglas de forma que cada paquete deba atravesar el menor número de reglas y con éllo el cortafuegos sufrirá una menor carga debida al filtrado de paquetes.
Para empezar realizamos ciertas comprobaciones para encontrar paquetes malformados o incorrectos: lo conseguimos enviando todos los paquetes TCP a la cadena bad_tcp_packets, la cual incluye unas pocas reglas para comprobar malformaciones o anomalías que no deseamos aceptar. Para una explicación completa, lee la sección La cadena "bad_tcp_packets" de este capítulo.
Es ahora cuando empezamos a buscar tráfico de las redes que normalmente son de confianza, como el procedente del adaptador de red local (lan_iface), o el tráfico entrante y saliente de nuestra interfaz de bucle local (loopback interface, lo_iface), incluyendo también todas nuestras direcciones IP actualmente asignadas (y ésto significa todas, incluyendo nuestra dirección IP de Internet). Basándonos en lo anterior hemos decidido añadir la regla que permite la actividad de la red local (LAN) al principio, ya que en nuestro caso la red local genera más tráfico que la conexión de Internet. Éllo permite una menor sobrecarga ya que se debe comparar cada paquete en cada regla, por lo que es una buena idea averiguar qué tipo de tráfico atraviesa con más frecuencia el cortafuegos. Cuando sepamos la carga relativa de cada tipo, podremos reordenar las reglas para que sean más eficientes, produciendo así una menor sobrecarga al cortafuegos y una menor congestión de tu red.
Antes de empezar con las "reglas auténticas" que deciden qué permitimos o no desde la interfaz de Internet, tenemos una regla relacionada configurada para reducir la carga del sistema. Es una regla de estado que permite el paso a todos los paquetes que sean parte de un flujo ESTABLISHED (establecido) o bien RELATED (relacionado) con la dirección IP de Internet. Existe una regla equivalente en la cadena "allowed", lo cual es redundante ya que antes de llegar a esa cadena, en este punto del script los paquetes ya son filtrados. Sin embargo la regla --state ESTABLISHED,RELATED de la cadena "allowed" se ha mantenido por varias razones, entre las que se encuentra el deseo de algunos de poder cortar y pegar esta función.
Tras éllo y todavía en la cadena INPUT, filtramos todos los paquetes TCP que lleguen a la interfaz $INET_IFACE, enviándolos a la cadena tcp_packets. Después se efectúa la misma operación con los paquetes UDP, enviándolos a la cadena udp_packets. Por último los paquetes ICMP son enviados a la cadena icmp_packets.
Normalmente un cortafuegos recibirá un mayor número de paquetes TCP, en menor cantidad los paquetes UDP y por último los ICMP. Sin embargo ten en cuenta que ésto es lo normal pero no tiene por qué ser tu caso. Igual que hicimos con las reglas específicas del tipo de tráfico de la red, es conveniente averiguar qué paquetes causan un mayor tráfico para reordenar convenientemente estas tres reglas. Es preciso tener en cuenta que una máquina equivalente a un Pentium III, con una tarjeta Ethernet de 100Mbit trabajando a plena potencia, puede claudicar ante un simple conjunto de 100 reglas mal escrito. Es importante tenerlo en cuenta a la hora de escribir el conjunto de reglas para tu red local.
Ahora nos encontramos con una regla extra, que por defecto es opcional (#) y puede utilizarse para evitar un excesivo número de registros en el caso de que tengas una red de Microsoft en el exterior del cortafuegos Linux: los clientes con sistema operativo Microsoft tienen la mala costumbre de enviar toneladas de paquetes de multidifusión (multicast) al rango 224.0.0.0/8, por lo que tenemos la oportunidad de bloquear esos paquetes de forma que no inunden los registros. También hay dos reglas más que hacen algo parecido en la cadena udp_packets, descrita en La cadena "udp_packets".
Antes de llegar a la política por defecto de la cadena INPUT, registramos el tráfico que no se ha filtrado aún, de manera que seamos capaces de encontrar posibles problemas y/o fallos: puede que sean paquetes que simplemente no queremos dejar pasar, puede ser alguien intentando hacernos algo malo, o puede ser un problema de nuestro cortafuegos que no deja pasar algún tráfico que debería estar permitido. En cualquier caso queremos saberlo, de forma que podamos solucionar el problema. De todas formas no registramos más de 3 paquetes por minuto, ya que no queremos inundar los registros con basura y con éllo llenar por completo la partición del registro; además, incluímos un prefijo a todas las entradas para saber de dónde provenían.
Todo lo que no haya sido filtrado será desechado (DROP) por la política por defecto de la cadena INPUT, que ha sido establecida casi al principio de este script, en la sección Estableciendo las políticas por defecto.
Esta cadena tiene muy pocas reglas en el escenario que establecimos al principio. Tenemos una regla que envía todos los paquetes a la cadena bad_tcp_packets, que ya ha sido descrita anteriormente y se ha definido de manera que pueda ser empleada por varias cadenas, independientemente del tipo de paquete que la atraviese.
Después de este primer chequeo para buscar paquetes TCP incorrectos, tenemos las reglas principales de la cadena FORWARD. La primera regla permite el tráfico sin restricciones desde nuestra interfaz de red local ($LAN_IFACE) hacia cualquier otra interfaz. En otras palabras, permite todo el tráfico desde nuestra LAN hacia Internet. La segunda regla permite el tráfico establecido y relacionado (ESTABLISHED, RELATED) de vuelta a través del cortafuegos. Es decir, permite que los paquetes pertenecientes a conexiones iniciadas por nuestra red interna fluyan libremente hacia nuestra red. Estas reglas son necesarias para que nuestra red local sea capaz de acceder a Internet, ya que la política por defecto de la cadena FORWARD se ha establecido previamente como DROP (desechar). Ésta es una forma de actuar bastante inteligente, ya que permitirá la conexión a Internet a los hosts de nuestra red local, pero al mismo tiempo bloqueará a los hosts de Internet que estén intentando conectar con nuestros hosts internos.
Por último, igual que hicimos en la cadena INPUT, tenemos una regla que registrará los paquetes que de alguna manera no tengan permiso para pasar a través de la cadena FORWARD. Con éllo probablemente veremos algun caso de paquete incorrectamente formado, entre otros problemas. Una causa posible pueden ser los intentos de ataque de hackers, aunque otras veces simplemente serán paquetes malformados. Esta regla es prácticamente la misma que la de la cadena INPUT, salvo por el prefijo de registro, que en este caso es "IPT FORWARD packet died: ". Los prefijos se emplean usualmente para separar entradas de registro y pueden emplearse para distinguir registros y saber qué cadenas los produjeron, así como algunas opciones de las cabeceras.
Puesto que esta máquina sólo la utilizo yo y es al mismo tiempo cortafuegos y estación de trabajo, permito prácticamente todo lo que proviene de las direcciones de origen siguientes: $LOCALHOST_IP, $LAN_IP o $STATIC_IP. Cualquier otra dirección debería ser un intento de spoofing (camuflaje de direcciones), aunque dudo que nadie que conozca pudiera hacer ésto en mi máquina. Por último registramos todo aquello que resulte desechado: si se desecha, definitivamente querremos saber por qué, de manera que podamos atajar el problema. Puede que sea un asqueroso error, o que sea un paquete extraño que ha sido "camuflado" (spoofed). Tras registrarlos, desechamos (DROP) los paquetes como política por defecto.
La cadena PREROUTING es básicamente lo que indica su nombre: realiza la traducción de direcciones de red en los paquetes, antes de que lleguen a la decisión de asignación de ruta que les enviará a las cadenas INPUT o FORWARD de la tabla filter. La única razón por la que nos referimos a esta cadena en el script es que nos sentimos obligados a repetir que NO DEBES efectuar ningún tipo de filtrado en élla. La cadena PREROUTING sólo es atravesada por el primer paquete de un flujo, lo cual significa que todos los demás paquetes del flujo serán totalmente ignorados por esta cadena. Tal como está definido este script, no utilizamos en absoluto la cadena PREROUTING, si bien es el lugar donde deberíamos trabajar si quisiéramos hacer alguna traducción DNAT en paquetes específicos: por ejemplo si quisieras albergar tu servidor web dentro de tu red local. Para más información acerca de la cadena PREROUTING, léete el capítulo Atravesando tablas y cadenas.
La cadena PREROUTING no debe usarse para ningún tipo de filtrado puesto que, entre otras causas, esta cadena sólo es atravesada por el primer paquete de un flujo. La cadena PREROUTING debe usarse únicamente para efectuar traducciones de direcciones de red, a no ser que sepas realmente lo que estás haciendo. |
Así pues, nuestro último cometido es tener en funcionamiento la traducción de direcciones (Network Address Translation), ¿no? Por lo menos lo es para mí. Para empezar añadimos una regla a la tabla nat, en la cadena POSTROUTING, que "traducirá" todos los paquetes que salgan de nuestra interfaz conectada a Internet. En mi caso se trata de la interfaz eth0. Sin embargo, existen variables específicas añadidas a todos los scripts de ejemplo que pueden usarse para configurar automáticamente estos valores. La opción -t le indica a iptables en qué tabla insertar la regla, que en nuestro caso es la tabla nat. El comando -A indica que queremos agregar (Append) una nueva regla a una cadena existente llamada POSTROUTING, mientras que -o $INET_IFACE indica que buscará todos los paquetes salientes a través de la interfaz INET_IFACE (o eth0, como valor por defecto en este script). Por último definimos el objetivo para que efectúe un SNAT a los paquetes. Es decir, todos los paquetes que concuerden con esta regla verán traducida su dirección de origen, para que parezca que provienen de tu interfaz de Internet. Ten en cuenta que debes definir la dirección IP que se les asignará a los paquetes salientes en el objetivo SNAT mediante la opción --to-source.
En este script hemos decidido utilizar el objetivo SNAT en lugar de MASQUERADE (enmascarar) debido a dos razones: la primera es que este script se supone que trabaja en un cortafuegos con dirección IP estática; la segunda razón es consecuencia de la primera, ya que resulta más rápido y eficiente utilizar el objetivo SNAT, siempre que sea posible. Por supuesto, también se ha usado para mostrar cómo podría funcionar y cómo debería utilizarse en un caso real. Si no tienes direccion IP estática, definitivamente deberías pensar en cambiar al objetivo MASQUERADE, que ofrece una forma simple y sencilla de traducir las direcciones y que además captará automáticamente la dirección IP a utilizar. Ésto consume un poco más de potencia del sistema, pero valdrá la pena si utilizas DHCP, por ejemplo. Si quieres saber más acerca de cómo funciona el objetivo MASQUERADE, léete el script rc.DHCP.firewall.txt.