Étant le firewall open source le plus souvent utilisé au sein des systèmes *BSD, Packet Filter est un concurrent intéressant de NetFilter/IPTables.
1. Origines, concepts et comparaisons à NetFilter/IPTables
Packet Filter est un firewall né avec la version 3.0 du système d'exploitation OpenBSD, il a été avant tout conçu de manière à remplacer IPFilter : le firewall « historique » de la plupart des distributions *BSD. Il conserve donc une syntaxe relativement similaire à celle d'IPFilter tout en résolvant les soucis de licences que posait IPFilter. Il a ensuite été porté sur divers OS tels que les distributions *BSD principales (NetBSD, FreeBSD, DragonFlyBSD) mais aussi sous Debian (version kFreeBSD) et plus récemment sous Mac OS X 10.7 (Lion).
À l’exception du cas particulier d'OpenBSD, Packet Filter se place systématiquement en concurrence vis-à-vis d'IPFilter. Contrairement à l'hégémonie de NetFilter/IPtables sous Linux, cela signifie qu'il existe une saine concurrence propice à la remise en question et à l'amélioration de la solution.
D'un point de vue plus technique, Packet Filter se limite à du filtrage stateful (avec suivi des sessions) ou stateless (sans suivi de sessions) au niveau 4 de la couche OSI. Contrairement à NetFilter/IPtables ou à nombre d'appliances propriétaires de firewalling, aucune analyse du contenu des paquets n'est effectuée ce qui signifie qu'aucun mécanisme de suivi de session FTP ou SIP n'est présent au sein de Packet Filter, il faut dans ces cas précis utiliser un démon externe tel que ftp-proxy s'occupant de la partie applicative et ajoutant au fur et à mesure les règles niveau 4 nécessaires au bon fonctionnement des sessions FTP.
Néanmoins, Packet Filter supporte nativement la version 6 du protocole IP, il est donc possible de mixer IPv4 et IPv6 au sein d'une même configuration, à la différence d'un NetFilter/IPTables qui nécessite l'utilisation de « ip6tables » en lieu et place de « iptables » pour les règles IPv6.
Du point de vue de la configuration, même si les ancres permettent une modification dynamique du jeu de règles du firewall, la configuration d'un firewall Packet Filter se fait avant tout via un fichier de configuration monolithique divisé en cinq parties distinctes et ordonnées : la définition des macros, la déclaration des tables, la configuration des options, les règles de contrôle de bande passante (queueing), et enfin, les règles de filtrage. A contrario, NetFilter/IPTables permettra plus de souplesse et n'imposera pas de contraintes particulières sur l'ordre d'ajout des différentes règles. Dans le cas de jeux de règles complexes, cette souplesse pourra rapidement devenir un obstacle à la lisibilité.
2. Un premier exemple de configuration Packet Filter
Comme brièvement exposé ci-dessus, une configuration Packet Filter se divise en cinq sections distinctes, nous allons maintenant étudier par l'exemple la configuration de chacune de ces sections. Nous prendrons l'exemple relativement classique d'un firewall ayant une connectivité publique sur l'une de ses interfaces (em0) et un LAN de machines en adressage privé (RFC 1918) sur son autre interface (em1). L'objectif de la configuration sera de fournir une connectivité Internet au LAN. Bien que Packet Filter soit depuis longtemps totalement compatible IPv6, dans un souci de simplicité, nous limiterons nos exemples à IPv4 (l'utilisation d’adresses IPv6 ne nécessiterait aucune adaptation, tout champ acceptant une adresse IPv4 pourrait contenir une adresse IPv6).
2.1. Macros
Dans le cadre d'une configuration Packet Filter, les macros correspondent à des variables statiques définies par l'utilisateur de manière à réduire la complexité du jeu de règles et limiter la complexité des modifications nécessaires au fil du temps. Il est commun de déclarer trois types d'objets :
- les interfaces utilisées : sachant que sur les systèmes *BSD, les interfaces prennent leur nom en fonction du driver réseau utilisé (e.g. em0 correspond à la première interface gérée par le driver em, si le driver utilisé avait été fxp, elle aurait été nommée fxp0), il est relativement commun que lors d'un changement matériel le nom de l'interface change. Il est donc primordial de ne pas utiliser directement le nom de l'interface mais une variable dans la suite du jeu de règles.
- les réseaux ou groupes d'hôtes réseau utilisés : les valeurs de ces variables changeront généralement peu lors des évolutions du jeu de règles, cependant cela améliore grandement la lisibilité.
- les hôtes réseau nécessitant des règles spécifiques : il s'agit encore une fois d'une question de lisibilité.
Une variable peut être composée d'un seul élément ou d'une liste d'éléments. Au sein d'une liste, il est possible d'utiliser le symbole « ! » afin d'exprimer l'exclusion. Les macros utilisent une syntaxe proche de celle des variables BASH, elles sont définies sans être précédées d'un symbole « $ » mais sont utilisées ensuite uniquement en étant précédées de ce symbole « $ ».
Dans notre exemple, la section macro pourrait être rédigée ainsi (les symboles « # » permettent d'ajouter des commentaires) :
#définition des trois interfaces
ext_if = "em0"
in_if = "em1"
loopback="lo0"
#définition du réseau interne
internal_lan = "172.16.64.0/23"
#définition des serveurs nécessitant des règles spécifiques
webserver = "172.16.64.201"
mailserver = "172.16.64.202"
code
Il n'est pas nécessaire de préciser l’adresse d'une interface réseau, l'ajout de parenthèses autour d'un nom d'interface réseau permet d'obtenir l’adresse IP principale de l'interface, ainsi dans notre exemple ($ext_if) correspondra à l’adresse IP publique de notre firewall.
2.2. Tables
À la différence des macros, les tables ne peuvent contenir que des adresses ou blocs d’adresses et sont par défaut dynamiques : il est donc possible d'ajouter ou retirer des éléments à une table dynamiquement (via une règle de filtrage, une ancre ou l'outil en ligne de commandes pfctl). Il est possible de définir une table statique via le mot-clé const. Par défaut, les tables ne sont conservées en mémoire que si elles sont référencées par au moins une règle, ce qui peut poser problème en cas de changement du jeu de règles, le mot-clé persist permet de rendre les tables persistantes.
Pour définir une table dynamique et persistante qui sera remplie au fur et à mesure afin de blacklister des IP malveillantes et une table contenant l'ensemble des réseaux privés de la RFC 1918, il suffit de rédiger la configuration suivante :
table <blacklist> persist
table <rfc1918> const { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 }
2.3. Options
Un certain nombre d'options globales permettent d'influencer le comportement de Packet Filter, la documentation officielle de Packet Filter [1] permet d'en obtenir la liste exhaustive (attention les nouvelles options sont généralement d'abord disponibles sous OpenBSD puis portées sur les différents autres systèmes d'exploitation sur lesquels Packet Filter est disponible).
Dans notre exemple, nous allons nous contenter de définir notre politique de blocage (drop des paquets) et de collecter des statistiques sur l'interface publique.
set block-policy drop
set loginterface $ext_if
2.4. Règles de contrôle de la bande passante
Le contrôle de la bande passante n'est pas directement effectué par Packet Filter mais par le module kernel ALTQ (Alternate Queueing). Il faudra donc s'assurer d'avoir ce module kernel lancé en plus du module kernel PF si l'on souhaite utiliser les fonctionnalités de contrôle de la bande passante.
Le mot-clé altq permet de réguler la bande passante sur une interface, il est ensuite possible de définir des queue qui se partagent la bande passante via une réservation d'une partie de la bande passante (mot-clé bandwidth) et éventuellement des priorités (mot-clé priority de 0 : le moins prioritaire, à 7 ou 15 : le plus prioritaire ; par défaut la priorité est à 1). Il faudra aussi définir le scheduler, en général CBQ donnera des résultats satisfaisants sans complexifier la configuration. Dans le cas de CBQ, la priorité maximum utilisable est 7. La classification du trafic réseau dans une queue ou une autre n'est pas effectuée dans cette section mais directement dans les règles de filtrage.
Dans notre exemple, si l'on souhaite sur une interface publique limitée à 20mbps réserver la moitié de la bande passante pour le trafic HTTP entrant tout en conservant 10 % pour le SSH, on rédigerait ainsi notre configuration :
altq on $ext_if cbq bandwidth 20Mb queue { http, ssh }
queue qhttp bandwidth 50 % cbq(default)
queue qssh bandwidth 10 % priority 5 cbq(default)
2.5. Règles de filtrage
Une fois l'ensemble des bases posées au sein des sections précédentes, il est maintenant temps de définir les règles de filtrage. Une règle de filtrage peut être définie par la syntaxe simplifiée suivante :
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] {queue queue_name]
Les principaux mot-clés seront l'action (généralement block ou pass), la direction (in ou out), et le protocole (possibilité d'utiliser tout protocole défini dans /etc/protocols, en plus des classiques « udp », « tcp », « icmp » et « icmp6 »).
Dans le cadre de la translation d'adresses, l'action rdr permet d'effectuer une translation de ports (PAT) tandis que la bien nommée action nat permet d'effectuer un NAT.
Les règles sont évaluées séquentiellement, le mot-clé quick permet, lorsqu'un paquet vérifie une règle, d'éviter d’exécuter la suite du jeu de règles pour le paquet.
Le mot-clé queue ajouté en fin de règle permet la classification du trafic nécessaire au contrôle de la bande passante.
Dans le cadre de notre exemple, nous allons bloquer la blacklist et les adresses IP privées en entrée sur l'interface publique, effectuer une redirection de port pour les serveurs HTTP et mail, et configurer un NAT sortant pour le réseau interne :
#Definition du NAT sortant
nat on $ext_if from $internal_lan to any -> ($ext_if)
#Definition des translation de port
rdr on $ext_if proto tcp from any to ($ext_if) port 80 -> $webserver port 80
rdr on $ext_if proto tcp from any to ($ext_if) port 995 -> $mailserver port 995
rdr on $ext_if proto tcp from any to ($ext_if) port 993 -> $mailserver port 993
#Pas de filtrage sur le trafic sortant (quick est nécessaire ici)
pass out quick on $ext_if,
pass quick on $in_if
#On ignore la loopback (127.0.0.0/8)
set skip on $loopback
#Blocage de la blacklist et des Ips privées
block in quick on $ext_if from <blacklist>
block in quick on $ext_if from <rfc1918>
#Par défaut on bloque le trafic entrant (ne pas mettre de quick ici, sinon il ne serait
#pas possible de définir des exceptions)
block in
#On autorise le SSH sur le firewall avec la queue associée (port ssh est valide car le
#service ssh est définie dans /etc/services)
pass in quick on $ext_if proto tcp to ($ext_if) port ssh queue qssh
#On autorise le HTTP vers le serveur web, comme le PAT a été effectué plus haut, il faut
# utiliser l'adresse IP privée du serveur
pass in quick on $ext_if proto tcp to $webserver port 80 queue qhttp
#Même chose pour le serveur de mail
pass in quick on $ext_if proto tcp to $mailserver port 993
pass in quick on $ext_if proto tcp to $mailserver port 995
2.6. Mise en place du jeu de règles
Le jeu de règles est généralement enregistré dans le fichier /etc/pf.conf, mais ce n'est pas une obligation. Il peut ensuite être activé via pfctl avec l'option -e (enable) et l'option -f fichier_de_regles. Dans notre cas, le fichier de règles complet serait le suivant :
Fichier /etc/pf.conf
ext_if = "em0"
in_if = "em1"
loopback="lo0"
internal_lan = "172.16.64.0/23"
webserver = "172.16.64.201"
mailserver = "172.16.64.202"
table <blacklist> persist
table <rfc1918> const { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 }
set block-policy drop
set loginterface $ext_if
altq on $ext_if cbq bandwidth 20Mb queue { http, ssh }
queue qhttp bandwidth 50 % cbq(default)
queue qssh bandwidth 10 % priority 5 cbq(default)
nat on $ext_if from $internal_lan to any -> ($ext_if)
rdr on $ext_if proto tcp from any to ($ext_if) port 80 -> $webserver port 80
rdr on $ext_if proto tcp from any to ($ext_if) port 995 -> $mailserver port 995
rdr on $ext_if proto tcp from any to ($ext_if) port 993 -> $mailserver port 993
pass out quick on $ext_if
pass quick on $in_if
set skip on $loopback
block in quick on $ext_if from <blacklist>
block in quick on $ext_if from <rfc1918>
block in
pass in quick on $ext_if proto tcp to ($ext_if) port ssh queue qssh
pass in quick on $ext_if proto tcp to $webserver port 80 queue qhttp
pass in quick on $ext_if proto tcp to $mailserver port 993
pass in quick on $ext_if proto tcp to $mailserver port 995
Il suffirait ensuite d’appeler la commande pfctl -e -f /etc/pf.conf pour activer cette configuration. Afin de blacklister une IP externe (e.g. 1.2.3.4), il suffit d’appeler la commande pfctl -t blacklist -T add 1.2.3.4.
2.7. pfctl
Lors de l'administration d'un firewall PF, il est plus que souvent nécessaire d'utiliser l'outil pfctl afin d'obtenir des informations sur l'état actuel de Packet Filter, pousser des modification dynamiquement ou tout simplement charger une nouvelle version du jeu de règles. Le tableau ci-dessous liste les commandes pfctl les plus utiles en les comparant avec leurs équivalent NetFilter/IPTables.
Action |
Commande pfctl |
Équivalent NetFilter/IPTables |
Lister les règles appliquées |
pfctl -s rules |
iptables -L -n -v |
Lister les règles de NAT appliquées |
pfctl -s nat |
iptables -L -n -v |
Lister les « states » actuellement présents dans la table du firewall |
pfctl -s state |
conntrack -L (package conntrack-tool) |
Flusher les règles de NAT |
pfctl -F nat |
iptables -t nat -F |
Flusher l'ensemble des règles de filtrage et NAT |
pfctl -F all |
iptables -F |
Ajouter une entrée à une table |
pfctl -t blacklist -T add 1.2.3.4 |
iptables -A INPUT -s 1.2.3.4 -j blacklist |
Lister le contenu d'une table |
pfctl -t blacklist -T show |
iptables -L blacklist -n -v |
Supprimer une entrée d'une table |
pfctl -t blacklist -T delete 1.2.3.4 |
iptables -D -A INPUT -s 1.2.3.4 -j blacklist |
Vérifier la validité d'un fichier de configuration |
pfctl -n -f /etc/pf.conf |
Pas de commande directement équivalente |
Consulter les statistiques de Packet Filter (compteurs et vitesse de modification des compteurs) |
pfctl -s info |
Pas de commande directement équivalente |
3. Mise en redondance
Afin de mettre en redondance un firewall Packet Filter, deux problématiques doivent être résolues. Il faut tout d'abord s'assurer que l'équipement passif recoupera les sessions enregistrées sur l'équipement actif. Cela évite toute coupure de flux lors d'une bascule de l'actif vers le passif. L'outil dévoué à cette tâche est pfsync. Il faut ensuite mettre en place un système de bascule d'adresse IP virtuelle de manière à ce que la défaillance du nœud actif soit transparente pour le reste des équipements réseau, plusieurs solutions existent, la plus aboutie sous systèmes *BSD étant CARP.
3.1. Mise en place de pfsync
pfsync est en fait une interface réseau virtuelle qui sera chargée de faire transiter les sessions et donc in fine permettre à l'équipement passif d'entretenir une copie de l'ensemble de la table des sessions de l'équipement actif. En fonction de la charge du firewall, la synchronisation des sessions peut consommer du trafic réseau de manière significative. Il est donc plus que conseillé d'utiliser une interface réseau dédiée pour pfsync. Dans notre exemple, nous utiliserons l'interface fxp0. Par défaut, la communication se fait en multicast, il est néanmoins possible en pair à pair en utilisant l'option syncpeer lors de la configuration de l'interface réseau virtuelle.
Sur le firewall actif, il faudrait donc mettre une configuration de ce type :
ifconfig fxp0 inet 10.0.0.1 netmask 255.255.255.0
ifconfig pfsync0 syncdev fxp0 syncpeer 10.0.0.2
Sur le firewall passif, ensuite configuré :
ifconfig fxp0 inet 10.0.0.2 netmask 255.255.255.0
ifconfig pfsync0 syncdev fxp0 syncpeer 10.0.0.1
Il faudra faire attention à bien autoriser les flux pfsync avec par exemple la règle de filtrage suivante :
pass on $sync_if proto pfsync
Il est par ailleurs possible de s'appuyer sur un tunnel IPsec, cependant l'utilisation d'un câble croisé entre les deux firewalls garantit déjà un niveau satisfaisant de confidentialité des échanges de sessions.
3.2. Configuration de CARP
« CARP » se présente aussi sous la forme d'une interface réseau virtuelle. Une communication entre le nœud actif et le nœud passif est effectuée en multicast afin que le nœud passif puisse détecter la défaillance du nœud et actif et récupérer automatiquement l'adresse IP virtuelle. Il est impératif de préciser un « vhid » unique pour chaque interface de manière à éviter tout risque de collision. Il est par ailleurs possible de sécuriser les échanges via l'utilisation d'une passphrase : option pass lors de la configuration de l'interface. L'adresse IP virtuelle sera celle de l'interface CARP. Afin de favoriser un nœud à passer actif, il est possible de jouer sur les priorités avec l'option advskew qui est par défaut à 0 (le plus prioritaire) et peut prendre toute valeur jusqu'à 254 (le moins prioritaire).
Dans le cadre de notre exemple, il faudrait mettre en place deux interfaces CARP, une pour l'interface publique et une pour l'interface privée. Ce qui donnerait pour le nœud actif :
ifconfig carp1 vhid 1 pass MaPassPhr4s3Sup3rS3cUr3 carpdev em0 1.2.3.4 netmask 255.255.255.240
ifconfig carp2 vhid 2 pass MaPassPhr4s3Sup3rS3cUr3 carpdev em1 172.16.64.254 netmask 255.255.254.0
et pour le nœud passif :
ifconfig carp1 vhid 1 pass MaPassPhr4s3Sup3rS3cUr3 carpdev em0 advskew 128 1.2.3.4 \
netmask 255.255.255.240
ifconfig carp2 vhid 2 pass MaPassPhr4s3Sup3rS3cUr3 carpdev em1 advskew 128 \
172.16.64.254 netmask 255.255.254.0
3.3. Comparaison avec les équivalents Linux
La configuration exposée lors des exemples se limite à une typologie actif/passif relativement conventionnelle. Il est néanmoins possible de mettre en place une configuration actif/actif très simplement. Côté conntrackd (démon équivalent à pfsync pour NetFilter/IPTables), le support des configurations actif/actif est pour l'instant relativement limité et imposera donc plus de contraintes.
De manière similaire, CARP se démarque des solutions Linux équivalentes telles que KeepAlived et HeartBeat par le support de configurations actif/actif, l'ensemble des nœuds répond alors sur la même adresse IP virtuelle. Il évite par ailleurs l'utilisation d'un lien dédié tout en proposant une solution sécurisée pour la vérification de la disponibilité des nœuds du cluster CARP.
4. Optimisations en cas de forte charge
Dans le cas de forte charge ou d'attaque dirigée à destination du firewall, il est nécessaire d'être en mesure de bloquer efficacement tout en conservant un niveau de performance satisfaisant pour les connexions légitimes.
4.1. Taille des tables
De manière analogue à /proc/sys/net/ipv4/ip_conntrack_max pour un firewall NetFilter/IPTables, la taille de la table des sessions est limitée. Au sein de Packet Filter, il existe trois limites importantes :
- states : il s'agit de la limite de taille pour la table des sessions ;
- src-nodes : nombre maximum d’adresses IP distinctes au sein des différentes tables ;
- table-entries : il s'agit de la limite pour l'ensemble des tables, par exemple dans le cas d'une connexion NATée, une occurrence sera créée dans la table des sessions state et une autre dans la table de NAT, il faut donc s'assurer d'avoir toujours la limite de taille de tables-entries supérieure ou a minima égale à limite de taille de states.
La modification des limites se fait directement dans la section set limit accompagnée de la liste des limitations pour lesquelles on souhaite imposer une limite différente de celle par défaut (visible via pfctl -s memory).
du fichier de configuration via la directivePar exemple sous FreeBSD, les limites par défaut sont :
# pfctl -s memory
No ALTQ support in kernel
ALTQ related functions disabled
states hard limit 10000
src-nodes hard limit 10000
frags hard limit 5000
tables hard limit 1000
table-entries hard limit 200000
Il est possible de les augmenter significativement en ajoutant la ligne suivante au jeu de règles :
set limit { states 750000, src-nodes 12500000, table-entries 200000000 }
4.2. Antispoofing
Dans le cas d'une topologie réseau simple, il peut être intéressant d'activer l'antispoofing. Cependant, cette fonctionnalité possède deux inconvénients majeurs : elle ne sera efficace que contre les attaques locales et elle ne supportera aucune exception (e.g. autoriser le passage de certains paquets d'un sous-réseau différent de celui utilisé par l'interface). Dans le cas de notre exemple, la règle de filtrage à ajouter pour activer l'antispoofing sur l'interface réseau privé serait :
antispoof quick for em1
4.3. Cas d'un SYN Flood
Le SYN Flood via paquets spoofées étant malheureusement une des attaques les plus simples à réaliser tout en conservant un relatif anonymat, c'est une attaque préférée par un grand nombre d'attaquants.
L'attaque étant généralement réalisée depuis l'Internet, les fonctionnalités d'antispoofing ne seront d'aucune utilité.
L'attaque visant à saturer la table des sessions, augmenter les limites est un bon début mais ne sera pas suffisant, dans la plupart des cas, on saturera la mémoire disponible sur le firewall avant d'avoir réussi à obtenir le moindre résultat concluant.
La solution va être d'adopter une politique agressive sur l'expiration des sessions en diminuant au maximum la durée des timeout de sessions (2 secondes) tout en demandant à Packet Filter d'être le plus agressif possible sur l'expiration des sessions via les deux options suivantes dans le fichier de configuration :
set optimization aggressive
set timeout interval 2
Sur NetFilter/IPTables, un comportement similaire pourra être obtenu en agissant sur la configuration IPv4 et/ou IPv6 via /proc/sys/net/ipv4 et /proc/sys/net/ipv6. Dans les deux cas, l'activation des SYN Cookies au niveau du système d'exploitation est une deuxième contre-mesure très efficace.
Dans les cas les plus difficiles, il est possible de passer en stateless (laisser passer le paquet sans créer de session dans la table des sessions) sur les règles ciblées par les attaques, il suffit pour cela d'ajouter le mot-clé « no state » à chaque règle devant passer en stateless. Par exemple :
pass in quick on $ext_if proto tcp to $webserver port 80 queue qhttp no state
5. Pour aller plus loin
Pour l'instant, nous nous sommes contentés de considérer Packet Filter uniquement sur le niveau 4 de la couche OSI, même si Packet Filter reste intrinsèquement un firewall de niveau 4, il est possible de l'étendre.
5.1. ftp-proxy et les ancres
Dans le cas d'un protocole tel que FTP, la communication se fait via un canal d'échange entre le client et le serveur sur port TCP 21 et via des canaux de « DATA » via un port TCP devant être ouvert à la volée. Sauf à ouvrir énormément de ports, il n'est pas possible pour un firewall niveau 4 de filtrer efficacement ce protocole. De plus, dans le cadre d'une connexion avec NAT, la partie natée proposera généralement à l'autre partie de se connecter sur son IP non-natée.
Nous allons donc devoir utiliser un démon disponible sur les OS *BSD nommé ftp-proxy afin de mettre en place les modifications nécessaires au niveau applicatif. Sa configuration se fait de manière assez rapide, il suffit de préciser le port d'écoute ainsi que l'adresse IP et le port du « vrai » serveur FTP interne. ftp-proxy étant situé juste devant le serveur, il s'agit d'une configuration reverse proxy, il faudra donc utiliser l'option -R pour spécifier cette adresse IP. Par défaut, ftp-proxy impose une limite à 100 sessions concurrentes, il est possible de la modifier via l'option -m.
Afin de permettre à ftp-proxy, qui a été conçu de manière à fonctionner uniquement avec Packet Filter, d'autoriser dynamiquement l'ouverture des ports « DATA » du protocole FTP. Il est nécessaire d'utiliser une ancre : mot-clé anchor et ses dérivés anchor-rdr et anchor-nat. Une ancre est une règle de filtrage destinée à être mise à jour dynamiquement. Dans notre cas, ftp-proxy s'occupera de la mise à jour dynamique de l'ancre. Dans un cas plus spécifique, il est possible de mettre à jour une ancre en fournissant une règle de filtrage valide à pfctl avec l'option -a.
Si l'on souhaite dans notre exemple ajouter un serveur FTP interne ayant comme adresse IP interne 172.16.64.203 et acceptant un maximum de 200 sessions concurrentes, il faudrait d'abord lancer ftp-proxy :
/usr/sbin/ftp-proxy -p 8021 -R 172.16.64.203 -P 21 -m 200
puis modifier notre jeu de règles Packet Filter de manière à ajouter au niveau de la définition des règles de redirection (début de la partie dédiée aux règles de filtrage) :
rdr-anchor "ftp-proxy/*"
rdr pass quick on $ext_if proto tcp from any to $ext_if port 21 -> 127.0.0.1 port 8021
(On remarquera l'utilisation simultanée de rdr et pass de manière à se limiter à une seule et unique règle au lieu de deux règles redondantes).
Il faudra ensuite ajouter juste avant les règles de NAT :
nat-anchor "ftp-proxy/*"
et enfin, au niveau des règles de filtrage à proprement parler :
anchor "ftp-proxy/*"
5.2. authpf
Dans le cas où l'on ne souhaite pas se contenter d'authentifier les utilisateurs par une adresse IP fluctuante au cours du temps mais par un couple identifiant/mot de passe, il est possible d'utiliser authpf. authpf est en fait un shell utilisateur qui, une fois l'utilisateur correctement logué sur le système (via SSH), ajoutera via une table et une ancre spécifiques de nouvelles règles spécifiques à l'utilisateur nouvellement authentifié.
Les détails de la configuration variant d'un OS à un autre, la suite de l'exemple est basée sur un OS FreeBSD. Dans notre exemple, nous souhaiterions que l'utilisateur « zerocool » s'authentifie en loguant en SSH sur le firewall afin d’accéder au port TCP 1337 du serveur 172.16.64.212 pour lequel aucune règle de filtrage n'autorise l'accès en temps normal.
Afin d'utiliser authpf, il faut d'abord le référencer comme un « shell » utilisable sur le firewall en ajoutant la ligne suivante au fichier /etc/shells :
/usr/sbin/authpf
Il est aussi nécessaire de créer les répertoires /etc/authpf pour la configuration globale et /etc/authpf/users/zerocool pour la configuration spécifique de l'utilisateur. La configuration globale se limite à définir le nom de la table et de l'ancre à utiliser pour authpf. Ce qui dans notre cas donnerait le fichier /etc/pf/authpf.conf suivant :
anchor=authpf
table=authpf_users
Afin de nous permettre de rédiger des règles spécifiques, authpf fournit la macro $user_ip contenant l'adresse IP de l'utilisateur authentifié. Il est donc possible de définir simplement le jeu de règles utilisateurs /etc/authpf/users/zerocool/authpf.rules suivant :
pass in quick proto tcp from $user_ip to 172.16.64.212 port 1337
Afin de rendre opérationnel authpf, il faut ajouter à notre jeu de règles la table (dans la section tables) :
table <authpf_users> persist
ainsi que l'ancre (au niveau des règles de filtrage) :
anchor "authpf/*"
Il suffit maintenant de créer l'utilisateur « zerocool » en lui attribuant comme shell utilisateur authpf pour rendre la solution totalement fonctionnelle.
Conclusion
Nous avons vu au cours de cet article que Packet Filter reste une très bonne alternative à NetFilter/IPTables, l'approche assez différente nécessitera quand même un temps d'adaptation pour quelqu'un habitué à NetFilter/IPTables. Cependant, certaines fonctionnalités comme la mise en place de cluster actif/actif peuvent être une bonne raison de franchir le pas.