PF
Filtrage de paquets






PF, de Daniel Hartmeier, est le logiciel de firewalling IP d'OpenBSD. Il est assez jeune; son prédécesseur, IPF de Darren Reed a été retiré du système à la version 3.0 pour une histoire de licence.
Malgré ou, grâce à sa jeunesse, PF progresse rapidement...[Encore une fois, étant dans l'incapacité la plus totale d'analyser le source de PF, j'en reste à voir évoluer ses fonctionnalités et à lire les commentaires des spécialistes ]

PF sait faire plein de choses :

- C'est un Firewall de type filtre de paquet (Couches réseau et transport)
- C'est un FireWall stateful (à états). C'est à dire qu'il est capable de suivre les différents datagrammes (oui, oui UDP aussi!!) ou segments TCP échangés par le client et le serveur lors d'une requête quelconque et de savoir qu'ils font partie d'une seule et même session; Avantage immédiat,  si le premier paquet est autorisé à passer, les suivants passeront automatiquement sans repasser par les filtres. Avantages : Simplicité d'écriture des règles (donc moins d'erreurs humaines), Charge CPU diminuée.
    Il intègre les numéros de séquence TCP dans  la gestion des états. Ca parait simple, mais, apparemment, des pointures du monde commercial comme CheckPoint FW1 en sont incapable, bravo !
    Quand il voit passer une séquence d'initialisation de connexion TCP, il sait qu'il doit voir passer un SYN, puis un SYN/ACK dans l'autre sens, en un ACK de retour. Point barre pour la séquence d'initialisation! Et contrairement à d'autres (NetFilter, par ex..) PF est intraitable sur ces flags TCP.
- Il intègre maintenant la fonction de NAT, (Translation d'adresse réseau ) pour permettre à plusieurs machines d'aller faire un tour sur Internet avec une seule adresse IPV4 publique.
- Désormais, il intègre la possibilité de faire de l'allocation de bande passante ( trafic shaping) avec altq. Pour se réserver 10% de la bande passante, ou alors interdire au téléchargement sauvage de manger toute la bande passante.
- Il est capable de protéger la pile TCP/IP un peu faiblarde de certains OS qui sont derrière lui, en réécrivant au vol, le numéro de séquence TCP.
- Il peut, réassembler les paquets qui sont intentionnellement fragmentés afin d'échapper aux IDS.(Systèmes de détection d'intrusions)
- Il sait rendre aveugle les vilains qui voudraient savoir combien de machines sont translatées (nattées ?) dans votre LAN. Et oui, ça existe.
- Il peut, désormais, faire du load balancing (répartition de charge) entre plusieurs machines.On peut ainsi allèger la charge d'une machine en en mettant plusieurs en parallèle.

PF ne sait pas encore faire ou ne fera jamais certaines choses :

- PF n'est pas et ne sera pas un Firewall applicatif; ie Squid, fait très bien le proxy (mandataire), chacun son boulot.
- Quelques applis mal foutues , Netmeeting pour ne pas la citer, utilisent des tas de ports qui ont la mauvaise idée d'être à la fois dynamiques et de se trouver côtés clients et serveurs...bref c'est un casse-tête pour un FireWall, qui va se retrouver en pass all dans les 2 sens. Selon des sources bien informées, PF ne gèrera jamais NetMeeting..Dommage, NetFilter sait faire.

- Table mangle mais altq.

Performances :

- Et, en plus avec tout ça, vous êtes  en train de vous dire que vous allez casser votre tirelire pour le dernier CPU gonflé aux GHz ??? 
Que nenni, PF est très chiche en ressources. Avec, à peu près tout d'activé ici, un P166 avec 32 Mo de RAM dépasse rarement 5 % sur une ligne ADSL 512kb/s avec des requêtes nombreuses et variées.
Quelques remarques néanmoins :
Les règles sont toutes chargées en mémoire et parcourues sous une forme arborescente lors de l'évaluation des règles.
Vous pouvez atteindre une diminution de 20 % des ressources  nécessaires si vous écrivez en bloc les règles ayant des paramètres identiques, cad :
- Par sens de passage (in/out)
- Par interface
- Par adresse
...
Lors du parcours de l'arbre, le logiciel éliminera en bloc toutes les règles inutilisées; sinon, il doit les parcourir une par une.

Les règles de base

Authpf : Le firewall avec authentification
Pour aller plus loin
    Filtrage sur les flags
    State modulation
    Normaliser les paquets
    ICMP
    IPV6
    Le problème du FTP (ftp-proxy)
Exemple simple
La commande pfctl
Surveiller les logs
La translation d'adresse : Nat

L'ordre des règles dans pf.conf

Les modifications apportées depuis la 3.0
nat.conf et altq.conf intègrent pf.conf. En fait nat.conf a intégré pf.conf depuis la 3.2 et altq.conf depuis la 3.3.

Authpf

Vous voudriez bien, pouvoir utiliser des règles de firewall qui seraient dépendantes de VOUS, utilisateur et de VOTRE IP, dans le cas d'une connexion itinérante.
Vous voudriez éviter d'investir dans l'agent d'authentification de Checkpoint FW-1 qui fait exactement ça.
Authpf est fait pour vous !
Il vous suffit d'avoir un compte et un shell valide sur la passerelle ou le serveur. Vous vous connectez en SSH sur cette machine et des règles spécifiques vous sont attribuées dans pf. Ces rèlgles qui peuvent concerner les directives rdr, nat, pass ou block, entre autres, sont détruites lors de la déconnexion de SSH.

Afin d'éviter que l'utilisateur ne se fasse des règles un peu trop "ouvertes", celles-ci sont stockées à plusieurs endroits possibles qui ne devraint pas être ouverts en RW à l'utilisateur :
/etc/authpf/users/$USER/authpf.rules        Puis
/etc/authpf/authpf.rules        

authpf.conf DOIT exister


Nous allons utiliser le merveilleux outil fourni par l'équipe OpenBSD : authpf
Ajoutez simplement la ligne suivante au fichier /etc/authpf/authpf.rules :
pass in log quick on $interface_ext proto tcp from $user_ip/32 to IP_Publique_du serveur_POP3/32 port = 110 flags S/SA keep state
   
Attention, la variable $user_ip est d'usage obligatoire, elle récupère l'adresse IP du client SSH..
Ensuite, vous fermez le port 110 et vous ouvrez le port 22 sur tout  l'internet :
pass in log quick on $interface_ext proto tcp from any to IP_Publique_du serveur_POP3/32 port = 22 flags S/SA keep state
block  in log on $interface_ext proto tcp from any to IP_Publique_du serveur_POP3/32 port = 110 
   
Attention, n'ajoutez pas le mot clef quick à la fermeture du port 110, car authpf ajoute, par défaut, les règles de filtrage à la fin du fichier de règle, votre règle d'ouverture par authpf ne serait jamais lue.
Il est probable qu'utiliser le compte root pour récupérer son courrier soit une assez mauvaise idée ;-); De plus, être obligé de taper son mot de passe SSH à chaque relevé de courrier risque de devenir vite pénible. Pour cela, créez un compte utilisateur non-privilégié sur le serveur, 

CLE PUB, PAS DE PHRASE DE PASS.....


Documentations

Les règles de base

- Tout d'abord, sa syntaxe est limpide quand on la compare à celle d'un IPchains; ça limitera d'autant les erreurs humaines.
- PF ne s'arrête pas à la première règle correspondante trouvée, il lit le fichier complètement du haut vers le bas jusqu'au bout..
De fait la dernière trouvée l'emporte.
Comme l'indique la page de man :  "last matching rule wins". Attention, ceci est l'inverse des règles de NAT !
Si aucune règle ne provoque de correspondance, l'action par défaut est pass.Notez-le c'est important.
- Afin d'accélérer l'analyse on peut ajouter quick dans une règle lorsque l'on veut que l'analyse des règles s'arrête là, et que la règle soit immédiatement mise en oeuvre.
- Rappelons que pour optimiser pf,  il est préférable de ranger les règles dans un ordre donné, en commençant par mettre à la suite toutes les règles qui portent sur la même interface, puis par protocole, puis adresse et port source pour finir par adresse et port destination.
- Il est possible d'utiliser dans les règles des variables que l'on définit en début de fichier. 
- Si vous utilisez NAT et pf gardez à l'esprit que NAT est interprété le premier, puis pf.(Très important pour débugger, NAT PUIS pf)
- Pour lancer pf au démarrage vous devez vérifier que vous avez bien pf=YES dans /etc/rc.conf. Vous pouvez aussi indiquer quel fichier de règles utiliser. Par défaut /etc/pf.conf . Cela se modifie dans /etc/rc.conf.

Syntaxe

La syntaxe des règles de filtrage est la suivante (pour les règles de translation):

Action    Sens    Paramètres

Action Signification
scrub Le paquet suit la phase de normalisation/défragmentation. La directive scrub ne peut pas constituer une dernière règle. Les paquets IPV6 ne sont pas défragmentés.
pass Le paquet est transmis.
block Le paquet est bloqué. Une option permet de renvoyer un Paquet TCP RST ou un ICMP UNREACHABLE à l'expéditeur. Le paquet ICMP peut avoir un code indiqué par son nom ou son numéro. Le paquet TCP peut avoir un TTL déterminé.

 

Sens Signification
in  
out  

 

Parma Signification
scrub Le paquet suit la phase de normalisation/défragmentation. La directive scrub ne peut pas constituer une dernière règle. Les paquets IPV6 ne sont pas défragmentés.
pass Le paquet est transmis.
block Le paquet est bloqué. Une option permet de renvoyer un Paquet TCP RST ou un ICMP UNREACHABLE à l'expéditeur. Le paquet ICMP peut avoir un code indiqué par son nom ou son numéro. Le paquet TCP peut avoir un TTL déterminé.

 

log : seulement le premier paquet en cas de keep state, sinon log-all...attention aux D.o.S.
donc block in all et block out all en premier

 

block in all

On bloque tout par defaut en entrée (si on veut bloquer en sortie mettre out à la place de in)

pass out all

On autorise tout en sortie

pass in proto tcp from any to 195.98.248.1/32 port = 80

On autorise le monde entier à entrer sur cette machine (qui a comme adresse 195.98.246.1) sur le serveur Web. On peut utiliser www à la palce de 80 du moment que cela existe dans /etc/services

pass in from 1.2.3.4/32 to 195.98.248.1/32

On autorise la machine 1.2.3.4 à venir sur notre machine sur tous les protocoles.

pass in proto tcp from any to 195.98.248.1/32 port = 80 keep state

On conserve l'état (voir la commande pfctl -ss), Ce qui permet d'accélérer les connexions. En fait il y a création d'une nouvelle table, dans laquelle est placée la règle. Si un autre paquet ayant la "même origine " se présente on utilise cette table.

pass in quick proto tcp from any to 195.98.248.1/32 port = 80

On a "quick" en plus. En fait lorsque ce mot est rencontré, la règle est appliquée immédiatement. On ne continue pas à lire le fichier de règles.

block in quick from 172.16.0.0/16 to 195.98.248.1/32 port = 80

On bloque toutes les machines qui se présentent avec une adresse dans 172.16.0.0/16 et qui essayent de se connecter au port 80 de notre machine. On peut imaginer que la ligne qui peut suivre est :
pass in proto tcp from any to 195.98.248.1/32 port = 80
On autorise alors tout le monde sauf le groupe 172.16.0.0/16

block in log quick from 10.0.0.0/8 to 195.98.248.1/32 port = www

On bloque tous les paquets en provenance de 10.0.0.0/8 vers le serveur web en 195.98.248.1/32 et en plus on log les tentatives. Voir plus bas sur la lecture des logs.

pass in quick on fxp0 proto tcp from any to 195.98.248.1/32 port = 80 

On précise la carte réseau. fxp0 correspond au nom de ma carte, qui dépend de la marque de celle-ci. Si vous avez une deuxième carte vous allez avoir fxp1 à la place de fxp0.

pass in proto tcp from {1.2.3.4/32 , 10.0.0.0/8 } to 195.98.248.1/32 port = 80

On autorise la machine 1.2.3.4 et le réseau 10.0.0.0 à venir sur le serveur Web de votre machine. On peut aussi écrire deux fois la ligne.

pass in proto tcp from any to 195.98.248.1/32 port = { 80 , 22}

On autorise tout le monde à venir sur votre machine sur le serveur Web et en SSH. Un point de notation. On peut aussi utiliser deux lignes pour faire la même chose:
pass in proto tcp from any to 195.98.248.1/32 port =  80 
pass in proto tcp from any to 195.98.248.1/32 port = 22

pass in on fxp0 proto tcp from any to fxp0 port > 50000 keep state

On autorise tous les paquets à entrer sur le port supérieur à 50000 vers la carte réseau fxp0. Ce nom dépend de votre carte.
Pour autoriser entre deux valeurs indiquer port { 50000 >< 55000 } Paquets entre 50000 et 55000.

L'expansion de variable:
Vous pouvez si vous avez une même règle qui s'applique sur des IP identiques, utiliser l'expansion de variable afin de n'avoir qu'une règle à écrire.
Ex : Vous voulez autoriser SSH uniquement depuis votre machine d'administration (IP1), depuis une partie de votre LAN (10.0.0.0/24), et depuis votre IP fixe perso (IPperso).

TRUST_IP="{ IP1/32, 10.0.0.0/24, IPperso/32 }"            ##Attention aux virgules, accolades, .... c'est assez sensible à la rédaction.
# Autorise SSH
pass in quick on xl0 proto tcp from $TRUST_IP to any port = 22 flags S keep state

En revanche lors d'une visualisation des stats de pf, vous aurez une ligne par variable (3 lignes dans notre exemple)

Pour aller plus loin

Filtrage sur les drapeaux la VO
   
Les paquets TCP échangés pendant une connexion disposent de flags (drapeaux):

Un établissement de connection TCP se fait en trois temps (le Three Way Handshake : Poignée de main en trois temps):
S/SA
1 : Le client envoie un paquet TCP avec le drapeau SYN vers le serveur.
2. Le serveur répond avec un SYN/ACK.
3. Le client répond avec un ACK, jusqu'au dernier paquet qui contiendra à la place un drapeau F.

Rien n'interdit d'envoyer à une machine un paquet avec un flag, puis de ne plus donner suite à l'échange. Cela est une façon de savoir si cette machine répond. Les produits de scan utilisent ce type de méthode, qui les rendent souvent plus difficiles à voir. 
    Afin de limiter ce type de scan et de forcer le "scanneur" à utiliser des méthodes plus voyantes, vous pouvez bloquer les scans qui utilisent certains types scans en utilisant les flags. 

En fait, dans les règles de filtrage vous pouvez( ou devriez!) 


    Ce qui nous donne la règle suivante pass in proto tcp from any to 195.98.250.1/32 port = ssh flags S/SA keep state. Ce qui veut dire que seul les paquets qui disposent du flag S parmi les paquets qui disposent du flag SA sont pris en compte ou s'ils disposent déjà d'une entrée dans la table d'état. Ce cas laissera passer les paquets ayant le un flag de FIN ou les autres.

NONONONONONNN S, mais pas SA......

 


Autre possibilité mettre le flag S/AUPRFS ce qui implique que seul les paquets ayant le flag S (poignet de main comme étant le premier paquet), ou les paquets disposants déjà d'un état pourront passer. Ce qui dans ce cas donne la ligne :
pass in proto tcp from any to 195.98.250.1/32 port = ssh flags S/AUPRFS keep state
Pour résumer, cette ligne veut donc dire : j'autorise les paquets venant du monde entier à entrer sur la machine 195.98.250.1 sur le port tcp 22 (ssh) seulement si ce paquet se présente avec le flag S ( première demande de connexion ou poignet de main), si c'est le cas, alors cela est mis dans la table d'état, permettant par la suite à tous les paquets venant de la même machine à pouvoir continuer à envoyer des paquets, alors que les paquets envoyés peuvent avoir n'importent quel flag. 

State modulation 
   
Lors de l'échange de paquets ceci disposent d'un numéro (ISN) qui s'incrémente normalement en fonction du temps et aléatoirement  à chaque paquet échangé. Toutefois certains OS (pas de nom ;-)) ne génèrent pas de numéros aussi aléatoires que prévu, permettant ainsi de déterminer les numéros des paquets, et donc éventuellement d'usurper une connexion existante. On peut avec pf modifier cela en ajoutant modulate state dans la règle.Très utile si vous protégez derrière des OS pas costauds (les Win9X, en particulier) ! 

pass out quick on fxp0 from $INSIDE_IP to any modulate state

Un excellent article sur les séquences TCP.

Normalisation des paquets mal formés
   
Certains paquets sont mal formés lorsqu'ils arrivent sur votre machine; Ils représentent au mieux un système mal configuré, au pire une attaque. Avec pf, il est possible de vérifier cela est de les normaliser avant de les faire suivre à destination. Pour cela ajouter simplement la ligne scrub in all avant les autres règles. Attention toutefois cela nécessite des ressources machines.

scrub random-id deadly.org 9/2/3

 


ICMP
Mieux connu avec les pings, il peut être utile de les bloquer, ou de les laisser passer (cas d'un serveur DNS par exemple), sans laisser passer les autres ICMP. 

Autoriser les pings sans autoriser les autres icmp
pass in quick on fxp0 inet proto icmp all icmp-type 8 code 0 keep state    (echo request )
pass in quick on fxp0 inet proto icmp all icmp-type 11keep state                (time exceeded)

Puis on bloque tous les autres
block in log quick on fxp0 proto icmp from any to any 
Attention il faut tenir compte de l'ordre.
Bien sur on peut tous les bloquer en ne mettant que la dernière ligne.

IPV 6
Pour le moment ipv6 est implémenté, mais pas couramment utilisé. Par prudence, on ne sait jamais, le mieux est de le bloquer. On peut aussi le désactiver en recompilant le noyau. La règle est donc block in quick inet6 all à placer en début du fichier de règles, en général pf.conf. On peut aussi le faire pour les paquets sortants : block out quick inet6 all. (pour ipv4 on utilise inet).

Exemples d'architectures réseau :

Client avec une seule carte réseau
.
Serveur avec une seule carte réseau.
Passerelle filtrante avec deux cartes.
FireWall dédié avec 3 cartes dont une pour la DMZ.

La commande pfctl

pfctl est la commande qui permet de contrôler les règles de filtrage. J'indique ici les options qu'il faut absolument connaître. Pour les autres reportez vous à man pfctl.

Commande

Résultat

pfctl -d

Désactive les règles en place

pfctl -e -f /etc/pf.conf

Lance les règles qui se trouvent dans le fichier /etc/pf.conf (voir aussi rc.conf pour le lancement au démarrage de la machine)

pfctl -s rules ou pfctl -sr  

Montre les règles de filtrage actives

pfctl -s nat ou pfctl -sn

Montre les règles de NAT actives

pfctl  -s info ou pfctl -si

Montre les statistiques par paquet, par octet pour chaque interface

   

pfctl  -s state ou pfctl -ss

Montre le contenu de la table des états. Il faut avoir un keep state (pour avoir une entrée dans la table des états!)

pfctl -F rules

Permet de flusher les règles de filtrage

pfctl -F nat

Permet de flusher les règles de NAT

pfctl -f /etc/pf.conf

Re force la lecture du fichier des règles

Ajouter -v afin d'avoir plus d'information. par exemple pfctl -v -ss pour avoir plus d'information sur les états (state). 

Un exemple de sortie de pfctl -sa sur une machine (toute seule, et non reliée au réseau, ce qui rend l'analyse, pour le moins, inexploitable...)

Surveiller les logs

Bloquer c'est bien. Surveiller les logs régulièrement ce n'est pas mal non plus!!
Alors, attention : les logs de pf NE SONT PAS au format syslog, mais au format tcpdump. d'où l'utilisation de ce dernier pour la visualisation et ce, malgré les pbs que cela pose. un grand débat ici ou ..

Pour que cette surveillance soit active, plusieurs conditions doivent etre réunies :
- Que la règle contienne le mot clé log, afin qu'elle provoque lors de son action, une entrée dans les logs de pf.
- Que l'interface des logs fonctionne : ifconfig pflog0 up
- Que le démon de log tourne : pflogd

tcpdump -n -e -ttt -r /var/log/pflog

Affiche les logs du fichier /var/log/pflog. Surveillance après coup.

tcpdump -i pflog0

Affiche les logs en temps réels

tcpdump -e -i pflog0 port 80

Idem mais plus fin, puisque ne surveille les logs que sur le port 80.

Par défaut les logs se placent dans /var/log/pf.log.

Ordre des règles dans pf.conf
Attention, l'intégration des règles de NAT à celles de PF ne se fait pas n'importe comment, il y a un ordre à respecter absolument :
- D'abord les options.(man pf.conf) de pf.conf.
- Puis la directive  scrub
- Puis les règles de NAT.
- Enfin, les règles de filtrage .

La directive antispoof.
antispoof for interface [inet|inet6]
Elle limite le spoofing IP (IPV4 ou IPV6, au choix), 




© philippe schwarz - Philippe Chadefaux   - $Id: pf.htm,v 1.16 2003/01/19 20:36:14 phil Exp $ -