9.8. Netfilter e 'IP Tables' (Núcleos 2.4)

Mientras desarrollaba el cortafuegos 'IP Chains', Paul Russell decidió que realizar funciones de cortafuegos de IP debería ser algo menos difícil; pronto asumió como tarea simplificar los aspectos de procesamiento de datagramas en el código de cortafuegos del núcleo y produjo un esquema de filtrado que era mucho más claro y mucho más flexible. Denominó este nuevo esquema netfilter.

Nota: En el momento de la preparación de este libro, el diseño de netfilter no está todavía estable. Esperamos que perdone cualquier error en la descripción de netfilter o de cualquiera de las herramientas de configuración asociadas que sea debido a cambios ocurridos tras la preparación de este material. Consideramos el trabajo realizado sobre netfilter suficientemente importante como para justificar la inclusión de este material, pese a que partes de él sean especulativas por sí mismas. Si tiene alguna duda, los documentos HOWTO correspondientes contendrán la información más precisa y actualizada sobre los detalles asociados con la configuración de netfilter.

Pero, ¿qué era lo que no estaba bien con las cadenas de IP de ipchains ? Habían aumentado de forma importante la eficiencia y la gestión de las reglas del cortafuegos. Pero la forma que tenían de procesar los datagramas eran todavía complejas, en especial en conjunción con características relacionadas con las funciones de cortafuegos como el enmascaramiento de IP (discutido en el Capítulo 11) y con otras formas de traducciones de direcciones. Parte de esta complejidad era debida a que el enmascaramiento de IP y la traducción de direcciones de red [1] fueron funciones desarrolladas independientemente del código de cortafuegos e integradas más tarde, en vez de haber sido diseñadas como partes mismas del código del cortafuegos desde el principio. Si un desarrollador deseara añadir todavía más características a la secuencia de procesamiento de datagramas, entonces se encontraría con dificultades para encontrar el lugar donde insertar el código y se habría visto obligado a realizar cambios en el núcleo.

Además, había otros problemas. En concreto, la cadena “input” describía la entrada a la capa de red de IP tomada en conjunto. La cadena input afectaba tanto a los datagramas que estaban destinados a este 'host' así como los datagramas que iban a ser encaminados. Esto resulta contrario a la intuición porque se confundía la función de la cadena 'input' con la de la cadena 'forward', que se aplicaba sólo a los datagramas que iban a ser reenviados, pero que siempre seguía a la cadena 'input'. Se se quería tratar de forma diferente los datagramas para el propio 'host' de los que iban a ser reenviados, era necesario construir reglas complejas que excluían a unos o a otros. El mismo problema aparecía con la cadena “output” de salida.

Esta complejidad influía de forma inevitable en el trabajo del administrador de sistemas porque se veía reflejada en la forma en que se debían diseñar los conjuntos de reglas. Además, cualquier extensión al proceso de filtrado exigía la modificación directa del núcleo, porque todas las políticas de filtrados estaban implementadas allí y no había forma de proporcionar una interfaz transparente. netfilter aborda tanto la complejidad como la rigidez de las soluciones antiguas implementando un esquema genérico en el núcleo que simplifica la forma en que se procesan los datagramas y proporciona la posibilidad de extender las políticas de filtrado sin tener que modificar el núcleo.

Veamos dos de los cambios claves realizados. La Figura 9-8 ilustra cómo se procesan los datagramas en la implementación de 'IP Chains', mientras que Figura 9-9 ilustra cómo se procesan en la implementación de netfilter. La diferencias claves consisten en la eliminación de la función de enmascaramiento del código central y de un cambio en la localización de las cadenas de entrada y de salida. En acompañamiento a estos cambios, se creó una herramienta de configuración nueva y extensible que se denominó iptables.

En 'IP Chains' la cadena de entrada se aplica a todos los datagramas recibidos por el 'host', independientemente de si están destinados al 'host' local o de si serán encaminados a otro 'host'. En netfilter, la cadena 'input' de entrada se aplica sólamente a los datagramas destinados al 'host' local, y la cadena 'forward' de reenvío se aplica sólo a los datagramas destinados a otro 'host'. De forma similar, en 'IP chains', la cadena 'output' de salida se aplica a todos los datagramas que abadonen el 'host' local, independientemente de si el datagrama se genera en el 'host' local o ha sido encaminado desde otro 'host'. En netfilter, la cadena 'output' de salida se aplica sólamente a los datagramas generados en este 'host' y no se aplica a los datagramas que están siendo encaminados provenientes de otro 'host'. Este cambio por sí solo ofrece una enorme simplificación de muchas configuraciones de cortafuegos.

Figura 9-8. Procesamiento de datagramas en 'IP Chains'

En la Figura 9-8, los componentes etiquetados como “demasq” y “masq” son componentes separados del núcleo que son responsables del procesamiento de los datagramas enmascarados entrantes y salientes. Estos componentes han sido reimplementados como módulos de netfilter.

Considérese el caso de una configuración para la que la política por defecto para cada una de las cadenas 'input', 'forward' y 'output' es deny. En 'IP Chains', se necesitarían seis reglas para permitir cualquier sesión a través del 'host' cortafuegos; dos para cada una de las cadenas 'input, 'forward' y 'output' (una cubriría el camino en un sentido y la otra en el sentido contrario). Puede imaginarse cómo esto puede llegar a resultar extremadamente complejo y difícil de gestionar cuando se mezclan sesiones que pueden ser encaminadas y sesiones que podrían conectarse al 'host' local sin que deban ser encaminadas. 'IP chains' le permite crear cadenas que le simplificarían esta tarea un poco, pero su diseño no resulta evidente y requiere de un cierto nivel de experiencia.

En la implementación de netfilter con iptables, esta complejidad desaparece completamente. Para que se pueda encaminar por un 'host' cortafuegos un servicio que se desea prohibir que termine en el propio 'host', sólo se necesitan dos reglas: una para un sentido y otra para el contrario ambas en la cadena 'forward'. Esto es la forma obvia de diseñar reglas de cortafuegos, y servirá para simplificar enormemente el diseño de las configuraciones del cortafuegos.

Figura 9-9. Cadena de procesamientos de datagramas en 'netfilter'

El documento PACKET-FILTERING-HOWTO ofrece una lista detallada de los cambios que se han realizado, por lo que aquí nos vamos a centrar en los aspectos más prácticos.

9.8.1. Compatibilidad hacia atrás con ipfwadm e ipchains

La flexibilidad notoria de netfilter en GNU/Linux queda ilustrada por su habilidad para emular las interfaces ipfwadm e ipchains. La emulación hace un poco más sencilla la transición a la nueva generación del software de cortafuegos.

Los dos módulos de netfilter del núcleo denominados ipfwadm.o e ipchains.o proporcionan la compatibilidad hacia atrás para ipfwadm e ipchains. Sólo puede cargarse uno de estos módulos a la vez, y utilizarlo sólo si el módulo ip_tables.o no está cargado. Cuando se ha cargado el módulo apropiado, entonces netfilter funciona exactamente de la misma forma que la anterior implementación del cortafuegos.

netfilter imita la interfaz de ipchains con las siguientes órdenes:
    rmmod ip_tables
    modprobe ipchains
    ipchains ...

9.8.2. Uso de iptables

La utilidad iptables se utiliza para configurar las reglas de filtrado de netfilter. Su sintaxis se apoya fuertemente en la de la orden ipchains, pero difiere en un aspecto muy importante: es extensible. Esto quiere decir que su funcionalidad puede extenderse sin tener que recompilar. Consigue este truco utilizando bibliotecas compartidas. Hay extensiones estándares de las que se explorarán algunas dentro de un momento.

Antes de que se pueda utilizar la orden iptables, se debe cargar el módulo del núcleo de netfilter que proporciona el soporte para ello. La forma más fácil de hacerlo es con la orden modprobe:
    modprobe ip_tables

La orden iptables se utiliza para configurar tanto el filtrado de IP como la traducción de direcciones de red. Para facilitar esto, existen dos tablas de reglas denominadas filter y nat. Por defecto, se asume la tabla 'filter' salvo que se especifique la opción -t. También se proporciona cinco cadenas predefinidas. Las cadenas INPUT y FORWARD están disponibles para la tabla filter, las cadenas PREROUTING y POSTROUTING están disponbiles para la tabla nat , y la cadena OUTPUT está disponible para ambas tablas. En este capítulo se discutirá solamente la tabla filter. Se contemplará la tabla nat en el Capítulo 11

La sintaxis general de la mayoría de las órdenes de iptables es:
    iptables orden especificación_de_regla extensiones
Veamos alguna de las opciones con detalle, y después se revisarán algunos ejemplos.

9.8.2.1. Órdenes

Existen varias formas de manipular las reglas y los conjuntos de reglas con la orden iptables. Las relevantes para la función de cortafuegos de IP son:

-A cadena

Añade una o más reglas al final de la cadena especificada. Si se proporciona un nombre de 'host' tanto como origen como destino y se resuelve a más de una dirección IP, se añadirá una regla por cada una de esas direcciones.

-I cadena número_de_regla

Inserta una o más reglas al comienzo de la cadena especificada. De nuevo, si se proporciona un nombre de 'host' en la especificación de la regla, se añadirá una regla por cada una de las direcciones que se resuelvan.

-D cadena

Borra de la cadena especificada una o más reglas que coincidan con la especificación de regla de la cadena especificada.

-D cadena número_de_regla

Borra la regla que ocupa la posición número_de_regla en la cadena especificada. Las posiciones de reglas comienzan en el 1 para la primera regla de la cadena.

-R cadena número_de_regla

Reemplaza la regla que ocupa la posición número_de_regla en la cadena especificada por la regla proporcionada en la especificación.

-C cadena

Comprueba el datagrama descrito por la especificación de la regla contra la cadena especificada. Esta orden devolverá un mensaje que describe cómo el datagrama procesa la cadena. Resulta de la mayor utilidad para las pruebas de la configuración del cortafuegos por lo que se contemplará con más detalle más adelante.

-L [cadena]

Muestra las reglas de la cadena especificada, o de todas las cadenas si no se especifica ninguna.

-F [cadena]

Borra todas las reglas de la cadena especificada, o de todas las cadenas si no se especifica ninguna.

-Z [cadena]

Pone a cero los contadores de datagramas y bytes en todas las reglas de la cadena especificada, o de todas las cadenas si no se especifica ninguna.

-N cadena

Crea una nueva cadena con el nombre especificado. No puede existir antes una cadena del mismo nombre. Así es cómo se crean las cadenas de usuario.

-X [cadena]

Borra la cadena de usuario especificada, o todas las cadenas de usuario si no se especifica ninguna. Para que este comando tenga éxito, no deben existir referencias a la cadena especificada desde cualquier otra cadena de reglas.

-P cadena política

Establece la política por defecto de la cadena especificada a la política especificada. Las políticas válidas de cortafuegos son ACCEPT, DROP, QUEUE, y RETURN. ACCEPT permite pasar a los datagramas. DROP causa que el datagrama sea descartado. QUEUE causa que el datagrama sea pasado al espacio de usuario para posterior procesamiento. La política RETURN causa que el código del cortafuegos de IP vuelva a la cadena que llamó a la que contenía esta regla, y que continúe con la regla situada tras la regla desde la que se hizo la llamada.

9.8.2.2. Parámetros de especificación de reglas

Existe una serie de parámetros de iptables que constituyen la especificación de una regla. Donde se requiera la especificación de una regla, se debe proporcionar algunos de esos parámetros o se asumirá sus valores por defecto.

-p [!]protocolo

Especifica el protocolo del datagrama que buscar coincidencias coná con esta regla. Los nombres válidos de protocolos son tcp, udp, icmp, o un número, si se conoce el número del protocolo de IP.[2] Por ejemplo, podría utilizarse un 4 para buscar coincidencias con el protocolo de encapsulamiento ipip. Si se proporciona el signo!, entonces se niega la regla y el datagrama buscar coincidencias coná con cualquier protocolo diferente del especificado. Si no se proporciona este parámetro, se asume por omisión la coincidencia con todos los protocolos.

-s [!]dirección[/máscara]

Especifica la dirección de origen del datagrama que buscar coincidencias coná con esta regla. Se puede proporcionar la dirección como un nombre de 'host', como un nombre de red o como una dirección de IP. El parámetro opcional máscara es la máscara de red que se utilizará y puede proporcionarse tanto en la forma tradicional (e.g., /255.255.255.0) como en la forma moderna (e.g., /24).

-d [!]dirección[/máscara]

Especifica la dirección de destino del datagrama que buscar coincidencias coná con esta regla. La codificación de este parámetro es la misma que la del parámetro -s.

-j blanco

Especifica qué acción se tomará cuando se coincida con esta regla. Puede pensarse en este parámetro como con el significado de “salta a”. Los blancos válidos son ACCEPT, DROP, QUEUE, y RETURN. Se describieron sus significados más arriba. Sin embargo, también puede proporcionarse el nombre de una cadena de usuario, que sería por donde el proceso continuaría. También puede proporcionarse el nombre de un blanco complementado con el de una extensión. Se hablará acerca de las extensiones en breve. Si se omite este parámetro, no se realizará ninguna acción sobre los datagramas coincidentes, excepto la actualización de los contadores de datagramas y bytes de esta regla.

-i [!]nombre_de_interfaz

Especifica la interfaz por la que se recibió el datagrama. De nuevo, el signo “! invierte el resultado de la coincidencia. Si el nombre de la interfaz acaba con un signo “+”entonces cualquier interfaz que comience con la cadena proporcionada buscar coincidencias coná. Por ejemplo, -i ppp+ buscar coincidencias coná con cualquier dispositivo de red de PPP y -i ! eth+ con todas las interfaces excepto las correspondientes a dispositivos de Ethernet.

-o [!]nombre_de_interfaz

Especifica la interfaz por la que se enviará el datagrama. Este argumento tiene la misma codificación que el argumento -i.

[!] -f

Especifica que esta regla se aplica al segundo y restantes fragmentos de un datagrama fragmentado, y no al primer fragmento.

9.8.2.4. Extensiones

Se dijo antes que la utilidad iptables es extensible a través de módulos de bibliotecas compartidas. Existen alguna extensiones estándares que proporciona algunas de las características que ipchains proporcionaba. Para utilizar una extensión, se debe especificar su nombre con el argumento -m nombre de iptables. La lista siguiente muestra las opciones -m y -p que establecen el contexto de la extensión, y las opciones proporcionadas por esa extensión.

9.8.2.4.1. Extensiones de TCP: utilizadas con -m tcp -p tcp

- -sport [!] [puerto[:puerto]]

Especifica el puerto que debe utilizar el origen del datagrama para buscar coincidencias con esta regla. Se pueden especificar los puertos en la forma de un rango, especificando los límites inferior y superior con un signo : como delimitador. Por ejemplo, 20:25 describe todos los puertos que van desde el 20 hasta el 25 incluyendo ambos. De nuevo, el signo ! puede utilizarse para negar los valores.

- -dport [!] [puerto[:puerto]]

Especifica el puerto que el datagrama de destino utilizará para buscar coincidencias con este regla. Este argumento se codifica de forma idéntica a la opción - -sport.

- -tcp-flags [!] máscara comp

Especifica que esta regla debe buscar coincidencias con cuando los indicadores de TCP del datagrama coincidan con los especificados por máscara y comp. máscara es una lista separada por comas de los indicadores que deben examinarse en la comprobación. comp es una lista separada por comas de indicadores cuyo valores han de ser todos 1 para que la regla coincida. Los indicadores válidos son: SYN, ACK, FIN, RST, URG, PSH, ALL o NONE. Esto constituye una opción avanzada: consúltese una buena descripción del protocolo de TCP, como el documento RFC-793, para la explicación del significado y la implicación de cada uno de estos indicadores. El signo ! niega la regla.

[!] - -syn

Especifica que la regla buscar coincidencias coná sólo con los datagramas cuyo bit SYN valga 1 y cuyos bits ACK y FIN valgan ambos 0. Los datagramas con estos valores de los indicadores se utilizan para abrir las conexiones de TCP, por tanto esta opción puede ser utilizada para gestionar las solicitudes de conexión. Esta opción es una abreviatura de:
    - -tcp-flags SYN,RST,ACK SYN
Cuando se utilice el operador de negación, la regla buscar coincidencias coná con todos los datagramas cuyos bits SYN y ACK no valgan 1 simultáneamente.

9.8.3. Nuestro ejemplo simple revisado otra vez

Para implementar nuestro ejemplo simple con netfilter, simplemente se podría cargar el módulo ipchains.o para que se comporte como la versión ipchains. En cambio, se volverá a realizar una implementación con iptables para ilustrar lo similar que es.

De nuevo, supóngase que se dispone en nuestra organización de una red y que se está utilizando una máquina cortafuegos basada en GNU/Linux para permiir a nuestros usuarios que sean capaces de acceder a servidores WWW de Internet, y para impedir el paso de cualquier otro tipo de tráfico.

Si nuestra red tiene una máscara de red de 24 bits (clase C) y tiene como dirección 172.16.1.0, entonces se podría utilizar las siguientes reglas de iptables:
    # modprobe ip_tables
    # iptables -F FORWARD
    # iptables -P FORWARD DROP
    # iptables -A FORWARD -m tcp -p tcp -s 0/0 --sport 80 -d 172.16.1.0/24 /
    --syn -j DROP
    # iptables -A FORWARD -m tcp -p tcp -s 172.16.1.0/24 --sport /
    80 -d 0/0 -j ACCEPT
    # iptables -A FORWARD -m tcp -p tcp -d 172.16.1.0/24 --dport 80 -s 0/0 -j /
    ACCEPT

En este ejemplo, las órdenes de iptables se interpretan exactamente como sus equivalentes de ipchains. La diferencia más importante es que debe cargarse el módulo ip_tables.o. Nótese que iptables no soporta la opción -b, por lo que debe proporcionarse una regla para cada sentido.

Notas

[1]

N. del T.: 'Network Address Translation' en el original en inglés

[2]

Véase el fichero /etc/protocols para buscar los nombres y números de los protocolos.