[Anterior: Tablas] [Contenido] [Siguiente: Traducción de Direcciones de Red]
La acción de filtrar paquetes es bloquear o permitir el paso a los paquetes de datos de forma selectiva, según van llegando a una interfaz de red. Los criterios que usa pf(4) para inspeccionar los paquetes los toma de la información existente en la capa 'Layer 3' (IPv4 and IPv6) y en la capa 'Layer 4' (TCP, UDP, ICMP y ICMPv6) de las cabeceras de los paquetes. Los criterios que más se utilizan son los de la dirección de origen y de destino, el puerto de origen y de destino, y el protocolo.
Las reglas de filtrado especifican los criterios con los que debe concordar un paquete y la acción a seguir, bien sea bloquearlo o permitir que pase, que se toma cuando se encuentra una concordancia. Las reglas de filtrado se evalúan por orden de secuencia, de la primera a la última. A menos que el paquete concuerde con una regla que contenga la clave quick, se evaluará el paquete comparándolo con todas las reglas de filtrado antes de decidir una acción final. La última regla que concuerde será la «ganadora» y la que dictamine qué acción se tomará con el paquete. Al principio del grupo de reglas de filtrado hay un pass all implícito que indica que si algún paquete no concuerda con ninguna de las reglas de filtrado, la acción a seguir será pass, o sea permitirle el paso.
La sintaxis general, muy simplificada, para las reglas de filtrado es:
action [direction] [log] [quick] [on interface] [af] [proto protocol] \
[from src_addr [port src_port]] [to dst_addr [port dst_port]] \
[flags tcp_flags] [state]
La práctica recomendada para configurar un cortafuegos es la de tomar una aproximación de «denegación predeterminada»; o sea, denegar el paso a todo y a partir de ahí ir permitiendo el paso a través del cortafuegos de forma selectiva a cierto tráfico. Esta aproximación es la recomendada ya que los posibles fallos se cometerían a favor de la seguridad, y también porque hace más fácil la creación de grupos de reglas.
Para crear una política de filtrado de denegación predeterminada, las primeras dos reglas deben ser:
block in all
block out all
Con esto se bloquea todo el tráfico en todas las interfaces en cualquier dirección, y desde cualquier origen, hasta cualquier destino.
Ahora hay que permitir de forma explícita y selectiva el paso del tráfico a través del cortafuegos, o de lo contrario será bloqueado por la política de denegación predeterminada. Aquí es donde entran en juego los criterios del paquete, como son el puerto de origen/destino, la dirección de origen/destino, y el protocolo. Siempre que se permita el paso de cierto tráfico a través del cortafuegos hay que escribir las reglas de un modo tan restrictivo como sea posible. Esto es para asegurarse de que sólo pasará el tráfico que se permita, y ningún otro.
Algunos ejemplos:
# Permitir el paso al tráfico entrante en la interfaz dc0 de la red local,
# 192.168.0.0/24, hacia la dirección IP 192.168.0.1 de la máquina de OpenBSD.
# También permitir el paso al tráfico saliente que es enviado de vuelta en dc0.
pass in on dc0 from 192.168.0.0/24 to 192.168.0.1
pass out on dc0 from 192.168.0.1 to 192.168.0.0/24
# Permitir el paso al tráfico entrante TCP en la interfaz fxp0 del servidor
# de web que se encuentra en la máquina de OpenBSD. El nombre de la
# interfaz, fxp0, se usa como la dirección de destino para que los paquetes
# sólo concuerden con esta regla si tienen como destino la máquina de OpenBSD.
pass in on fxp0 proto tcp from any to fxp0 port www
Como se ha indicado anteriormente, cada paquete se evalúa con el grupo de reglas de filtrado, desde la primera hasta la última. El resultado predeterminado es el de marcar el paquete para que se le permita el paso; esto puede cambiar con cualquiera de las reglas por las que pasa, y podría cambiar varias veces antes de llegar al final de las reglas de filtrado. La última regla con la que concuerde marcará el resultado. Existe una excepción para esto: la opción quick en una regla de filtrado tiene el efecto de cancelar el procesamiento de cualquier regla consiguiente, y provoca que se ejecute la acción especificada sin más dilación. Veamos un par de ejemplos:
Mal:
block in on fxp0 proto tcp to port ssh
pass in all
En este caso, la línea block puede ser evaluada, pero nunca tendrá ningún efecto, ya que va seguida por una línea que permite el paso de todo.
Mejor:
block in quick on fxp0 proto tcp to port ssh
pass in all
Estas reglas se evalúan de una forma algo diferente. Si un paquete concuerda con la línea block, debido a la naturaleza de la opción quick, se bloqueará el paso a dicho paquete y se ignorará el resto del grupo de reglas.
Una de las funcionalidades importantes de PF es la del «mantenimiento del estado» (keeping state) o «inspección completa del estado» (stateful inspection). La inspección del estado se refiere a la capacidad de PF de llevar un seguimiento del estado, o del progreso, de una conexión de red. Almacenando información sobre cada conexión en una tabla de estado, PF puede determinar rápidamente si un paquete que está pasando a través del cortafuegos pertenece a una conexión ya establecida. Si es así, se le permite pasar a través del cortafuegos sin tener que pasar a través de la evaluación del grupo de reglas.
El mantenimiento del estado tiene muchas ventajas, entre otras que los grupos de reglas son más simples y se obtiene un rendimiento más alto del filtrado de paquetes. PF puede puede hacer que los paquetes que vayan en cualquier dirección concuerden con entradas en la tabla de estado, lo que quiere decir que no es necesario escribir reglas de filtrado que permitan el paso del tráfico de vuelta. Y, como los paquetes que concuerdan con conexiones stateful no pasan a través de la evaluación del grupo de reglas, el tiempo que tarda PF en procesarlos puede reducirse considerablemente.
Cuando una regla crea el estado, el primer paquete que concuerda con ella crea un estado entre el remitente y el destinatario. A partir de ahí, los paquetes que vayan desde el remitente hacia el destinatario no serán los únicos que concuerden con la entrada de estado y que circunvalen la evaluación de las reglas, sino que también lo harán los paquetes de respuesta desde el destinatario hacia el remitente.
Todas las reglas pass crean automáticamente una entrada en la tabla de estado cuando un paquete concuerda con la regla. Esto puede deshabilitarse de manera explícita mediante la opción no state
pass out on fxp0 proto tcp from any to any
Esto permite el paso de cualquier tráfico TCP saliente en la interfaz fxp0, y también permite que el tráfico de respuesta pase de vuelta a través del cortafuegos. El mantenimiento del estado mejora de forma significativa el rendimiento del cortafuegos, ya que las búsquedas de estados son mucho más rápidas que la evaluación de un paquete a través de todas las reglas de filtrado.
La opción de «modulación del estado» (modulate state), funciona como keep state, con la diferencia que solo es válida para paquetes TCP. Con modulate state, el ISN de las conexiones salientes es aleatorio. Esta opción es útil para proteger conexiones que hayan sido iniciadas por ciertos sistemas operativos que realizan un pobre trabajo al escoger ISNs. Para simplificar las reglas, la opción modulate state puede usarse en reglas que especifican protocolos diferentes de TCP; en estos casos es tratada como keep state.
Mantenimiento del estado en paquetes TCP, UDP e ICMP salientes y ISN TCP modulados:
pass out on fxp0 proto { tcp, udp, icmp } from any \
to any modulate state
Otra ventaja del mantenimiento del estado es que el tráfico ICMP correspondiente también pasará por el cortafuegos. Por ejemplo, si una conexión TCP pasa a través del cortafuegos, como se mantiene el estado, y llega una señal de congestión (source quench) ICMP, se buscará su concordancia con la entrada apropiada de la tabla de estado y pasará a través del cortafuegos.
El ámbito de una entrada en la tabla de estados es controlado globalmente por la opción en tiempo de ejecución state-policy y al nivel de cada regla por las palabras-clave de opciones de estado if-bound y floating. Estas palabras-clave usadas en las reglas tienen el mismo efecto que cuando se usan con la opción state-policy. Ejemplo:
pass out on fxp0 proto { tcp, udp, icmp } from any \
to any modulate state (if-bound)
Esta regla dictaminaría que para que los paquetes concuerden con la entrada de estado deben transitar por la interfaz fxp0.
Algunos dicen que «no se puede crear estado con UDP, ya que UDP es un protocolo sin estado». Aunque es cierto que una sesión de comunicación de UDP no tiene ningún concepto de estado (un comienzo y un final de las comunicaciones explícito), esto no tiene ningún impacto en la capacidad de PF para crear estado para una sesión de UDP. En el caso de protocolos sin paquetes de «inicio» ni «final», PF se limita a mantener un seguimiento del tiempo transcurrido desde que ha pasado un paquete que concuerde. Los valores del tiempo agotado (timeout) se pueden configurar en la sección de opciones del fichero pf.conf.
Las opciones se especifican entre paréntesis e inmediatamente después de una de las palabras-clave de estado (keep state, modulate state, or synproxy state). Si hay múltiples opciones, deben ir separadas por comas. En OpenBSD 4.1 y versiones más recientes, la opción keep state se utiliza de forma predeterminada implícitamente para todas las reglas de filtrado. A pesar de ello, cuando se especifican opciones de mantener estado, debe especificarse todavía una de las palabras-clave por delante de las opciones.
Una regla de ejemplo:
pass in on $ext_if proto tcp to $web_server \
port www keep state \
(max 200, source-track rule, max-src-nodes 100, max-src-states 3)
La regla anterior define el siguiente comportamiento:
Puede ponerse un conjunto separado de restricciones en conexiones TCP con estado que han completado la negociación en tres pasos (3-way handshake).
Ambas opciones invocan automáticamente la opción source-track rule y son incompatibles con source-track global
Dado que estos límites solo son colocados en conexiones TCP que han completado la negociación en tres pasos, se pueden adoptar acciones más agresivas en direcciones IP ofensivas.
Un ejemplo:
table <abusive_hosts> persist
block in quick from <abusive_hosts>
pass in on $ext_if proto tcp to $web_server \
port www flags S/SA keep state \
(max-src-conn 100, max-src-conn-rate 15/5, overload <abusive_hosts> flush)
Esto hace lo siguiente:
La concordancia de paquetes TCP basada en indicadores es algo que se suele usar para filtrar paquetes TCP que estén intentando abrir una nueva conexión. Aquí se puede ver una lista de indicadores TCP y sus significados:
Para que PF inspeccione los indicadores TCP durante la evaluación de una regla se usa la clave flags con la sintaxis siguiente:
flags check/mask
flags any
La parte mask indica a PF que sólo inspeccione los indicadores especificados, y la parte check especifica qué indicadores deben estar activos ("on") en la cabecera para que ocurra una concordancia. EL uso de la palabra-clave any permite que cualquier combinación de indicadores (flags) sea definido en la cabecera.
pass in on fxp0 proto tcp from any to any port ssh flags S/SA pass in on fxp0 proto tcp from any to any port ssh
Como los indicadores S/SA se definen de forma prederminada, las reglas anteriores son equivalentes. Cada una de estas reglas permite el paso de tráfico TCP con el indicador SYN activo, y sólo mira a los indicadores SYN y ACK. Un paquete con los indicadores SYN y ECE concordaría con la regla anterior, mientras que un paquete con SYN y ACK, o sólo con ACK, no concordaría.
Los indicadores predeterminados pueden sobreescribirse mediante la opción flags tal como se ha descrito anteriormente.
Hay que tener cuidado con el uso de indicadores; hay que entender qué es lo que se está haciendo y por qué, y tener cuidado con los consejos recibidos de otros ya que muchos suelen ser erróneos. Algunas personas han sugerido la creación de estado «solo si está activado el indicador SYN, y no otros». Una regla de este tipo terminaría así
. . . flags S/FSRPAUEW mala idea!!
La teoría es crear estado solo en el inicio de la sesión TCP, y la sesión debería iniciarse con un indicador SYN, y ningún otro. El problema es que algunos sitios están empezando a usar el indicador ECN, y cualquier sitio que use ECN e intentara conectar con nosotros sería rechazado por una regla de ese tipo. Un enfoque mucho mejor sería no especificar indicador alguno y dejar a PF que aplique los indicadores predeterminados a las reglas de usted. Si realmente necesita usted especificar indicadores, entonces esta combinación debería ser segura:
. . . flags S/SAFR
Aunque esto es práctico y seguro, también es necesario comprobar los indicadores FIN y RST si se está normalizando (scrub) el tráfico. El proceso de normalización de paquetes hará que PF descarte cualquier paquete entrante que lleve una combinación ilegal del indicador TCP (como SYN y RST) y normalice combinaciones potencialmente ambiguas (como SYN y FIN).
Normalmente, cuando un cliente inicia una conexión TCP a un servidor, PF pasa los paquetes del saludo inicial ( handshake) entre los dos extremos según llegan. Sin embargo, PF también puede hacer de proxy para el saludo inicial. Con el modo proxy, PF completará el saludo inicial con el cliente, iniciará un saludo inicial con el servidor, y pasará los paquetes entre los dos. La ventaja de este proceso es que no se enviará ningún paquete al servidor antes de que el cliente complete el saludo inicial. Esto elimina la amenaza de que desbordamientos TCP SYN falseados puedan afectar al servidor, debido a que una conexión de un cliente falseado no podrá completar el saludo inicial.
El proxy TCP SYN se activa usando la clave synproxy state en las reglas de filtrado. Ejemplo:
pass in on $ext_if proto tcp to $web_server port www synproxy state
En este ejemplo, PF hará de proxy TCP para las conexiones del servidor web.
Debido al modo en que funciona synproxy state, también incluye la misma funcionalidad que keep state y modulate state.
El proxy SYN no funcionará si PF está funcionando sobre un bridge(4).
La falsificación de direcciones (spoofing) es cuando un usuario con malas intenciones falsifica la dirección IP de origen en los paquetes que se transmiten, con el objetivo de esconder su dirección real o de suplantar otro nodo en la red. Una vez que el usuario ha falsificado su dirección, puede lanzar un ataque a nivel de red sin revelar la dirección real de origen del ataque, o intentar obtener acceso a servicios de la red que estén restringidos para ciertas direcciones IP.
PF ofrece cierto nivel de protección contra la falsificación de direcciones mediante la clave antispoof:
antispoof [log] [quick] for interface [af]
Ejemplo:
antispoof for fxp0 inet
Cuando se carga un grupo de reglas, cualquier suceso de la clave antispoof se expandirá en dos reglas de filtrado. Asumiendo que la interfaz fxp0 tuviera una dirección IP 10.0.0.1 y una máscara de subred de 255.255.255.0 (o sea, un /24), la regla antispoof anterior se expandiría así:
block in on ! fxp0 inet from 10.0.0.0/24 to any
block in inet from 10.0.0.1 to any
Estas reglas realizan dos funciones:
NOTA: Las reglas de filtrado resultantes de la expansión de la regla antispoof también bloquearán los paquetes que se envíen por la interfaz de loopback hacia direcciones locales. Es recomendable desactivar el filtrado en las interfaces de loopback, pero esto se convierte en necesidad cuando se utilizan reglas contra falsificaciones:
set skip on lo0
antispoof for fxp0 inet
El uso de antispoof se debe restringir a las interfaces a las que se les haya asignado una dirección IP. El uso de antispoof en una interfaz sin una dirección IP resultará en reglas de filtrado como:
block drop in on ! fxp0 inet all
block drop in inet all
Con estas reglas existe el riesgo de bloquear todo el tráfico entrante en todas las interfaces.
PF ofrece la funcionalidad Unicast Reverse Path Forwarding (uRPF). Cuando un paquete se somete a la comprobación uRPF, se busca la dirección IP de origen del paquete en la tabla de enrutamiento. Si la interfaz de salida hallada en la la tabla de enrutamiento es la misma por la que el paquete acaba de entrar, entonces la comprobación uRPF permite el paso del paquete. Si las interfaces no coinciden, entonces es posible que el paquete tenga su dirección de origen falsificada.
La comprobación uRPF puede realizarse en los paquetes utilizando la palabra-clave urpf-failed en las reglas de filtrado:
block in quick from urpf-failed label uRPF
Nótese que la comprobación uRPF solo tiene sentido en un entorno donde el enrutado es simétrico.
uRPF proporciona la misma funcionalidad que las reglas antifalsificación
La identificación pasiva del sistema operativo (OS Fingerprinting u OSFP por sus siglas en inglés) es un método para detectar pasivamente el sistema operativo de una máquina remota basado en ciertas características contenidas en sus paquetes SYN TCP. Esta información puede usarse después como criterio en las reglas de filtrado.
PF determina el sistema operativo remoto comparando las características de un paquete SYN TCP con el archivo de huellas digitales (fingerprints), que por omisión es /etc/pf.os. Una vez que PF está habilitado, la lista actual de huellas digitales puede verse con la siguiente instrucción:
# pfctl -s osfp
Dentro de una regla de filtrado, una huella digital puede especificarse mediante una clase de sistema operativo, versión o nivel de subtipo/parche. Cada uno de estos elementos se lista en la salida de la instrucción pfctl de arriba. Para especificar una huella digital en una regla de filtrado se usa la palabra clave os:
pass in on $ext_if from any os OpenBSD keep state
block in on $ext_if from any os "Windows 2000"
block in on $ext_if from any os "Linux 2.4 ts"
block in on $ext_if from any os unknown
La clase especial de sistema operativo unknown hace que los paquetes concuerden con la regla si las huellas digitales del sistema operativo son desconocidas.
TOME NOTA de lo siguiente:
Por definición, PF bloquea los paquetes con las opciones IP activadas. Esto puede hacer las cosas más difíciles para utilidades de detección de sistemas operativos (OS fingerprinting) como nmap. Si se tiene una aplicación que requiere el paso de estos paquetes, como multidifusión o IGMP, se puede usar la directiva allow-opts:
pass in quick on fxp0 all allow-opts
A continuación tenemos un ejemplo de un grupo de reglas de
filtrado. La máquina en la que está funcionando PF
actúa como cortafuegos entre una red interna pequeña e
Internet. Sólo se muestran las reglas de filtrado; las reglas
de queueing,
nat,
rdr,
etc.. se han omitido en este ejemplo.
ext_if = "fxp0"
int_if = "dc0"
lan_net = "192.168.0.0/24"
# tabla que contiene todas las direcciones IP asignadas al cortafuegos
table <firewall> const { self }
# no filtrar en el interfaz loopback
set skip on lo0
# normalizar los paquetes entrantes
match in all scrub (no-df)
# configurar una política de denegación predeterminada
block all
# activar la protección contra la falsificación de direcciones
# para todas las interfaces
block in quick from urpf-failed
# permitir sólo conexiones por ssh si provienen
# desde la máquina de confianza, 192.168.0.15;
# usar "block return" para que se envíe un TCP RST
# para cerrar inmediatamente las conexiones bloqueadas;
# usar "quick" para que las reglas "pass" que
# vienen a continuación no anulen esta regla.
block return in quick on $int_if proto tcp from ! 192.168.0.15 \
to $int_if port ssh
# permitir el paso del tráfico hacia y desde la red local
# estas reglas crearán entradas de estado debido a la opción
# por omisión "keep state" que se aplica automáticamente
pass in on $int_if from $lan_net
pass out on $int_if to $lan_net
# permitir el paso de paquetes tcp, udp e icmp
# salientes en la interfaz externa (Internet);
# modular el estado en conexiones tcp y mantener el estado en udp/icmp.
pass out on $ext_if proto { tcp udp icmp } all modulate state
# permitir el paso de las conexiones entrantes de ssh
# en la interfaz externa siempre que su destino NO sea
# el cortafuegos (o sea, aquellas cuyo destino sea
# una máquina en la red local); registrar el paquete inicial
# para que podamos ver más tarde quién intenta conectar.
# Usar el proxy tcp syn para la conexión.
# PF aplicará automáticamente los indicadores por omisión "S/SA"
# a la regla.
pass in log on $ext_if proto tcp to ! <firewall> \
port ssh synproxy state
|
[Anterior: Tablas] [Contenido] [Siguiente: Traducción de Direcciones de Red]