Introduction à Packet Filter
Contents
1 Introduction
Packet Filter (ou PF) est le pare-feu logiciel et officiel de OpenBSD, écrit à l'origine par Daniel Hartmeier. C'est un Logiciel Libre gratuit.
Il remplace IPFilter de Darren Reed depuis la version 3.0 de OpenBSD, suite à des problèmes de licence mais aussi des refus systématiques de Reed d'incorporer des modifications de code venant de développeurs OpenBSD.
Il a été porté sur DragonflyBSD 1.2 et NetBSD 3.0 ; il est fourni en standard sur FreeBSD (version 5.3 et ultérieures).
Un port gratuit de PF a également été réalisé pour les systèmes d'exploitation Windows 2000 et XP par la communauté Core FORCE. Ce port n'est cependant qu'un firewall personnel : il n'implémente pas les fonctions de PF permettant de faire du NAT ou d'utiliser ALTQ
2 Installation
2.1 FreeBSD
Insérez dans /etc/rc.conf les entrées suivantes:
2.2 OpenBSD
Vive OpenBSD :-), pas besoin d'installer quoi que ce soit, il est par défaut sur le système. Il suffit juste de le configurer et l'activer. Pour le configurer éditez le fichier /etc/pf.conf.
3 Configuration
Nous allons voir ici plusieurs exemples pour apprendre à l'utiliser.
3.1 Macros & Listes
3.1.1 Macros
On peu définir des macros pour remplacer des variables revenant souvent (IP, interfaces...) :
/etc/pf.conf |
if_ext="ne0" ssh="192.168.0.1" |
3.1.2 Listes
Les listes permettent de rassembler des critères dans une variable :
/etc/pf.conf |
block out on $if_ext proto tcp from { 192.168.0.1, 10.8.0.6 } to any port { 22 80 } |
Cette ligne permet d'exécuter 4 règles d'un coup ! Pour les plus newbies cette ligne va bloquer :
- 192.168.0.1 sur le port 80
- 192.168.0.1 sur le port 22
- 10.8.0.6 sur le port 80
- 10.8.0.6 sur le port 22
3.1.3 Mix des Macros et Listes
Pour mixer le tout :
/etc/pf.conf |
if_ext="ne0" trusted="{ 192.168.0.1, 10.8.0.6 }" pass in on $if_ext inet proto tcp from { 10.10.0.0/24 $trusted } to port 22 |
3.2 Les Tables
Les tables permettent de stocker un grand nombre d'adresses (50 ou 50000, même combat), et sont ensuite utilisées directement dans les règles de filtrage/NAT/redirection. La recherche d'une adresse dans une table en mémoire est beaucoup plus rapide, et moins coûteuse en temps processeur/mémoire que la recherche dans un jeu de règles correspondant chacune à une valeur d'une liste d'adresses.
Il existe plusieurs mots clefs pour les tables avec des fonctions différentes :
- cont : utilisé lorsqu'on veut que la table ne puisse pas être modifiée
- persist : indique à PF de ne pas supprimer une table non référencée par une règle.
L'avantage d'une table non const par rapport aux listes est que l'on peut ajouter/supprimer à la volée des adresses ou sous-réseaux dans une table, utile pour bloquer un certain temps l'adresses d'un spammeur, d'un script-kiddie ou gérer la redirection vers un ensemble de serveurs faisant de la haute disponibilité.
Enfin cerise sur le gâteau, on peut initialiser une table avec un fichier contenant une liste d'adresses :
/etc/pf.conf |
table <privateip> const { 192.168.0.0/24, 10.8.0.0/8 } table <spammers> persist file "/etc/spammers" block in on $if_ext from { <privateip>, <spammer> } to any |
Et donc maintenant, si nous voulons ajouter ou supprimer des IP :
pfctl |
pfctl -t spammers -T add 218.70.0.0/16 pfctl -t spammers -T delete 218.70.0.12 |
Pour tout supprimer :
pfctl |
pfctl -t spammers -T flush |
3.3 NAT
Je vais pas expliquer ce que fais la NAT ici, mais comment l'utiliser sous PF. On va donc d'abord activer le forwarding de paquet, il faut mettre dans /etc/sysctl.conf :
/etc/sysctl.conf |
net.inet.ip.forwarding=1 |
Ceci rendra persistant la NAT.
Il ne faut pas oublier que les paquets vont passer à travers le filtre de paquets après avoir été modifés, sauf si l'on utilise le mot-clé pass. Et ceci s'applique aussi à RDR que nous verrons plus bas. Voici une règle NAT :
/etc/pf.conf |
nat [pass] on interface [address_family] from src_addr [port src_port] to dst_addr [port dst_port] -> ext_addr |
Si on décortique (entre crochets : variable, italique : facultatif) :
- nat : indique que c'est une règle nat
- pass : le paquet est NATé, et envoyé directement sans passer par le filtre de paquet
- on interface : le paquet est arrivé sur telle interface réseau ($if_ext, ne0...)
- address_family : inet ou niet6, c'est un détail qui peut avoir son importance
- from src_addr' : le paquet provient de telle adresse. Ici pour l'adresse, on peut spécifier plein de trucs :
- port src_port : si on veut NATer uniquement un certain port ou une plage de ports...rarement utiliser
- to dst_addr : le paquet est destiné à telle adresse. Mêmes possibilités que pour src_addr
- -> ext : remplacer l'adresse source par cette adresse. Le reoutr se fera tout seul par magie. Et si cette adresse change (attribuée par DHCP), on peut ici spécifier le nom de l'interface réseau entre parenthèses (rl0), et l'adresse sera automatiquement mise à jour dans la règle.
Maintenant un petit exemple. Si on souhaites partager sa connexion internet avec son réseau local :
/etc/pf.conf |
nat on r10 inet from ne3 : network to any -> (r10) |
Ouala, c'est pas la même avec Iptables hein !
3.4 Redirections de paquets
RDR est le petit frère caché de NAT, à savoir qu'il fait tout l'inverse de son grand frère : il prend les paquets arrivant de l'extérieur pour les rediriger sur le réseau local. Voici un exemple de syntaxe :
/etc/pf.conf |
rdr pass on interface [address_family] from src_addr [port src_port] to dst_addr [port dst_port] -> int_addr [port int_port] |
C'est la même syntaxe que pour la NAT à quelques exceptions près du genre : Je redirige ce qui était à destination de dst_addr:dst_port version int_addr:int_port et comme pour la NAT, le retour se fera tout seul.
Si par exemple je veux accéder depuis l'extérieur sur le SSH d'une de mes machines sur le réseau local :
/etc/pf.conf |
rdr pass on $external proto tcp to port 35422 -> $diane port ssh |
Et maintenant, on peut accéder depuis l'extérieur à la machine dans mon lan via mon adresse ip externe et le port 35422.
Vous remarquerez que j'ai utiliser ssh comme nom. Tous les noms présents dans /etc/services peuvent être utilisés.
3.5 Les règles de filtrage
Les règles de filtrage sont évaluées de façon séquentielle de la prmière à la dernière (du haut vers le bas dans les fichiers de règles utilisés). Ce qui veut dire que chaque paquet va être évalué oar chaque règle, et la dernière règle correspondant au paquet emporte la décision (block ou pass). Au contraire, si on utilise le mot clef quick, l'évaluation s'arrête dès qu'une règle correspond au paquet. La prmière règle (implicite) est un "tout laisser passer' de sorte que si aucune règle n'est applicable à un paquet, celui-ci est accepté. C'est pourquoi généralement la première règle explicite est un block all.
Voici la syntaxe :
/etc/pf.conf |
action [direction] [log] [quick] [on interface] [address_family] [proto protocol] [from src_addr [port src_port]] [to dst_addr [port dst_port]] [flags tcp_flags] [state] |
- action : au choix, block ou pass. La politique pour les paquets bloqués sera drop ou return en fonction de l'option block-policy. Par défaut c'est drop.
- direction : in ou out, si on veut filtrer le trafic entrant ou sortant de l'interface. Si on ne met rien, la règle sera évaluée pour les deux sens.
- log : si ce flag est présent, on enregistre la décision prise pour cette règle concernant le paquet. Pour analyser ceci, pflog sera notre ami.
- quick : J'en ai déjà parlé, si ce flag est aussi présent et que le paquet matche la règle, il ne sera plus analysé/trituré, et la décision prise par cette règle sera notre dernié mot Jean-Pierre (ok elle est pas terrible celle là m'enfin bon...)
- proto protocol : un protocol de niveau 4, généralement tcp,udp ou icmp, mais on peut aussi rencontrer n'importe quel protocole de niveau 4 référencé dans /etc/protocols. On peut même l'appeller par son petit numéro !
- port dst_port : On peut ici spécifier un intervalle complexe de ports avec les opérateurs <, <=, >=, >, <> et :, voyez avec man pf.conf.
- flags tcp_flags_check/mask : on peut spécifier des vérifications supplémentaires sur les drapeaux d'un paquet TCP, par exemple pour traiter les ouvertures de session TCP. On utilise souvent flag S/SA que je traduirais par "cette règle s'applique sur les paquets TCP qui ont, sur les deux drapeaux SYN et ACK, eulement SYN positionné". Si les 2 drapeaux sont positionnés, le paquet ne matchera pas la règle. Pour les autres flags, RTFM un peu.
- state : ici on utilise généralement deux possibilité :
- keep state : utilisé quand on veut créer une entrée dans la table des états de connexions lorsqu'un paquet matche la règle, et appliquer la même politique aux paquets suivants prenant part à la connexion. Tous ces packets sont donc rattachés à cette entrée, et on peut aussi du coup vérifier que la séquence des paquets TCP est bien respectée.
- synproxy state est utilisé quand on veut quePF joue le rôle d'un proxy TCP pour l'établissement d'une connexion. Dans ce cas, PF va traiter la demande en lieu et place du destinataire et ne transfèrera qu'ensuite les paquets à ce dernier. Aucun paquet n'est transmis au destinataire avant que le client n'ait terminé l'échange initial. Cette technique permet de protéger le destinataire des attaques de type TCP SYN flood, lorsqu'on demande un grand nombre d'ouvertures de connexions en vue de provoquer un deni de service.
Voici un petit exemple pour éclairsir tout ça :
/etc/pf.conf |
pass in quick on $external inet proto tcp from any to any port { http, https, smtp, imaps } flags S/SA keep state |
J'autorise tous les paquets TCP/IPv4 arrivant sur l'interface externe à destination des ports http/https/smtp/imaps à passer. Je vérifie que ce sont des ouvertures de connexion TCP, j'enregistre leur état dans la table, et j'arrête l'analyse de ces paquets sur cette règle (quick).
/etc/pf.conf |
block in log on $external from { 192.168.0.0/16, 172.16.0.0/12 } to any. |
Ici, je bloque les paquets arrivant sur l'interface externe avec une adresse de source privée, et j'enregistre les information du paquet bloqué. Ca permet de parer certaines attques de type spoofing où l'on envoie des paquets usurpés dans le but d'induire en erreur les équipements réseau.
Bon évidement, il y a encore pleins d'options de détails et de particularités (comme les ancres, le scrubbing, l'antispoofing...) dont je n'ai pas parlé. Pour plus d'informations, se reporter à la doc de pf.conf.
4 Utilisation
Le binaire d'utilisation de pf est pfctl. Avant de commencer, il faut activer PF au niveau du noyau. 2 solutions :
pfctl |
pfctl -e |
ou ajouter pf=YES au niveau du /etc/rc.conf.local.
Pour désactiver pf, il suffit de faire :
pfctl |
pfctl -d |
Si l'on souhaite faire une analyse syntaxique du fichier sans charger les règles de filtrage, utilisez l'argument -n :
pfctl |
pfctl -n |
Si l'on souhaite optimiser PF, il faut ajouter l'option -O, ce qui permettra de supprimer les doublons et réordonner les règles.
4.1 Modifications à la volée
- -T (kill/flush/add/delete/show/test..) : utilisé avec -t table, permet de manipuler une table : la supprimer, vider, ajouter une adresse, supprimer, l'affichier, vérifier qu'elle est dans la table. Exemple :
pfctl -t blocked-hosts -T show
Ceci va m'afficher les adresses de toutes les machines ayant été ajoutées dans la table blocked-hosts, déclarée un peu plus haut dans /etc/pf.conf.
- -F (nat/rules/state/Tables/..) : remet respectivement à zero les règles de NAT, de filtrage, les états des connexions ouvertes ou les tables. Pratique si l'on veut faire un peu de ménage, remettre à zéro les compteurs ou des connexions, désactiver la NAT, supprimer toutes les entrées de toutes les tables, etc...
- -k (host/network)' : Permet de tuer toutes les entrées dans la table d'état concernant les connexions venant d'une machine/d'un réseau. Si on utilise 2 fois cette option, on supprime les états des connexiosn venant de la première adresse vers la deuxième. Exemple :
pfctl -k 192.168.1.0/24 -k 172.16.0.0/16
Ceci supprimera tous les états des connexions entre ces 2 sous réseaux.
- -s modifieur : Cette option permet d'obtenir beaucoup d'informations sur le status de PF. Si on l'utilise avec -r, PF fera du reverse-dns lookup pour les adresses qu'il affiche. Les valeurs les plus intéressantes pour modifieur sont :
- rules: afficher respectivement les règles de filtrage chargées en mémoire
- nat : celle de NAT
- state : les connexions ouvertes
- info : les statistiques globales sur PF
- all : affichera tout ce que PF a à nous dire
ex :
pfctl |
pfctl -sr |
- -v, -vv, -g, -x, -q : pour avoir des modes un peu plus verbeux, et même le mode debug (-v -> -x). Le -q le repassera en mode quiet.
5 Exemples pratique
5.1 Simple
Cahier des charges :
- On a une machine connectée à internet via une interface réseau en IP fixe appellée bge0
- Elle doit être accessible depuis l'extérieur via 3 IP de confiance
- Le serveur Apache doit être accessible à tous
- Elle doit répondre aux ping depuis l'extérieur
- On veut pouvoir accéder à internet à partir de cette mahcine
- On bloquera silencieusement tous les autres packets
Maintenant on active notre nouvelle configuration :
pfctl |
pfctl -e -f /etc/pf.conf |
Cette conf est assez restrictive dans le sens ou en générale, on met all à la place de "from any to $iface" et on utilise "quick" à outrance.
5.2 Avancé
Cahier des charges :
- Notre passerelle (renton) dispose de 2 interfaces, r10 côté internet et ne3 côté réseau local
- La première carte possède une IP fixe (celle de notre FAI)
- La seconde interface côté réseau local est configurée avec l'adresse 192.168.1.1 sur le réseau local en 192.168.1.0/24
- On protègera tout ceci du monde extérieur, et comme on est parano, on fera un peu de logging
- On veut pouvoir accéder à internet à partir de notre réseau local sans limitations
- Une machine particulière (diane) devra être accessible via ssh et https
- On voudra accéder au multiposte de Free sur la machine (sickboy), c'est juste un flux RTSP/UDP classique
- Notre machine principale (tommy) aimerait bien faire marcher des trucs clicka-compliants comme le transfert de fichier Jabber
- On autorisera le SSH depuis l'extérieur
5.3 Ma config
J'ai donc une Soekris avec plusieurs interfaces réseaux :
- Wan
- DMZ
- Lan
- Wifi
- VPN
Et je veux que la configuration soit la suivante :
- Le wifi et le lan ont accès à tout
- La DMZ n'a accès qu'au Wan
- Il y a du nat sur toutes les interfaces sauf wan évidement
- Une protection contre le bruteforce SSH
- Le VPN est accessible sur la passerelle depuis l'extérieur
6 Les Logs
Quand PF aura envie de cafter un peu, il va envoyer des informations en binaire (histoire que ce soit plus rigolo, c'est du PCAP/TCPdump standard) sur une pseudo interface (pflog0), et un de ses bon potes pflogd qui va tout stocker dans /var/log/pflog.
Tout d'abord, il faut activer/lancer le démon pflogd. Normalement il doit se lancer tout seul si PF est activé au démarrage de la machine. Si ce n'est pas le cas :
ifconfig pflog0 up && pflogd |
On peut vérifier que tout va bien :
ps waux |
Maintenant que PF cause sur pflog0, lorsqu'un paquet matche une règle où le mot clé log est utilisé, passons à tcpdump. On peut l'utiliser selon 2 modes :
- Interactif :
tcpdump |
tcpdump -n -e -ttt -i pflog0 |
Il ira directement lire ce qui ce passe en direct sur pflog0, pflogd sera donc utile.
- Passif :
tcpdump -r /var/log/pflog |
Il lira ce qui a été enregistré par pflogd dans son fichier de sortie.
On peut aussi passer une expression à tcpdump pour qu'il filtre sa sortie selon des critères précis :
tcpdump |
tcpdump -ttt -r /var/log/pflog port 80 and host 192.168.1.2 |
Enfin tcpdump comprend aussi la syntaxe de configuration de PF. On peut donc lui demander des choses de ce style :
tcpdump |
tcpdump -ttt -i pflog0 inbound and action pass and on wi0 |
Avec cette commande, il n'affichera que les paquets autorisés à passer, logués et entrants sur l'interface wi0. tcpdump est également capable de lire des infos tel que le passive OS fingerprint.
7 Fonctions avancées de PF
Si l'on souhaite bloquer par exemple les machines sour Windows 95/98 :
/etc/pf.conf |
block in on $ext proto tcp from any os { "Windows 95", "Windows 98" } to any port smtp |
Pour obtenir la liste des OS que PF reconnait :
pfctl -so
Pour faire de l'analyse d'OS avec tcpdump :
tcpdump |
tcpdump -o -r /var/log/pflog |
Evidement ces techniques bouffent du CPU et ne sont pas en plus infaillibles.
8 FAQ
8.1 no IP address found for tun0
Si vous avez ce genre de messages, c'est parce qu'une interface (ici tun0) essaye d'être initialisée avec PF, alors que le service associé (sensé créer ce device) n'a pas encore démarrer.
Pour éviter que ce soit donc trop le dawa, il suffit d'essayer de mettre vos devices entre parenthèses (!($vpn_if)) ex :
/etc/pf.conf |
block out quick on { $wifi_if } from { !$lan_if, !$wifi_if, !($vpn_if) } to any |
Et si des parenthèses existent déjà, essayez de les retirer.
9 Ressources
Documentation pour Sécuriser sa BSD avec PF
Firewalling with OpenBSD’s PF packet Fillter
Filtrage des paquets avec le logiciel IPFilter
http://www.openbsd.org/faq/pf/filter.html