Introduction à Packet Filter

From Deimos.fr / Bloc Notes Informatique
Jump to: navigation, search

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:

Configuration File /etc/rc.conf
pf_enable="YES"                 # Charger PF
pf_rules="/etc/pf.conf"         # Règles, chemin par défaut par défaut.
pf_flags=""                     # Bonus
pflog_enable="YES"              # démarrer le journal
pflog_logfile="/var/log/pflog"  # où trouver le journal
pflog_flags=""                  # Bonus

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...) :

Configuration File /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 :

Configuration File /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 :

Configuration File /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 :

Configuration File /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 :

Command pfctl
pfctl -t spammers -T add 218.70.0.0/16
pfctl -t spammers -T delete 218.70.0.12

Pour tout supprimer :

Command 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 :

Configuration File /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 :

Configuration File /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 :
    • une adresse IP
    • un CIDR
    • un DNS qui sera résolu par PF lors du chargement des règles
    • une interface réseau
    • une Table ou une Liste
    • n'importe laquelle de ces notations, précédée d'un ! pour signifier la négation
    • enfin any pour n'importe quelle adresse
  • 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 :

Configuration File /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 :

Configuration File /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 :

Configuration File /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 :

Configuration File /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 :

Configuration File /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).

Configuration File /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 :

Command pfctl
pfctl -e

ou ajouter pf=YES au niveau du /etc/rc.conf.local.

Pour désactiver pf, il suffit de faire :

Command pfctl
pfctl -d

Si l'on souhaite faire une analyse syntaxique du fichier sans charger les règles de filtrage, utilisez l'argument -n :

Command 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 :

Command 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
Configuration File /etc/pf.conf
 # Notre interface réseau
 iface = "bge0"
 
 # Les machines de confiance pour les connexions SSH :
 trusted_hosts = "{ 131.25.4.12, 88.12.74.5, 207.124.20.9 }"
 
 # On s'occupe pas de l'interface de loopback utilisée par plusieurs services en interne de la machine :
 set skip on lo
 
 # On active la normalisation des paquets en entrée. PF va réassembler les paquets fragmentés et faire des vérifications supplémentaires dessus :
 match in all scrub (no-df)
 
 # Par défaut, on bloque tous les paquets :
 block all
 
 # On autorise les paquets ICMP de type "echo request" pour les pings venant de l'extérieur, et echo reply/time exceeded/destination
 # unreachable pour les réponses aux pings que l'on avait initié vers l'extérieur :
 pass in inet proto icmp form any to $iface icmp-type { echoreq, echorep, timex, unreach }
 
 # On autorise les connexions au serveur Apache et on les enregistre dans la table d'état :
 pass in inet proto tcp from any to $iface port www flags S/SA keep state
 
 # On autorise les connexions SSH uniquement à partir des machines de confiance :
 pass in inet proto tcp from $trusted_hosts to $iface port ssh flags S/SA keep state
 
 # Enfin on autorise tout le traffic sortant :
 pass out inet proto tcp from $iface to any flags S/SA keep state
 pass out inet proto { udp, icmp } from $iface to any keep state

Maintenant on active notre nouvelle configuration :

Command 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
Configuration File /etc/pf.conf
# /etc/pf.conf
 
# Déclaration des interfaces réseau
ext="r10"
int="ne3"
 
# Déclaration des ports à ne pas logguer
ports_not_logged = "{ netbios-ssn, microsoft-ds, epmap, ms-sql-s, 5900 }"
 
# Déclaration des hôtes sur mon réseau local
diane = "192.168.1.2"
tommy = "192.168.1.20"
sickboy = "192.168.1.21"
 
# Déclaration du port utilisé à l'extérieur pour accéder au SSH de diane
ssh_diane = "65322"
 
set skip on lo
match in all scrub (no-df)
 
# Tout d'abord, la règle principale de NAT pour le réseau local. Ici, je ne mets pas "pass" car après j'autorise explicitement tout le
# traffic sortant. Le suffix :network est utilisé pour dire "le sous-réseau correspondant à l'adresse de cette interface"
nat on $ext from $int:network to any -> $ext
 
# Ensuite, les règles de redirection. J'ai mis le mot-clé 'pass' pour ne pas faire de filtrage supplémentaire sur ces connexions, sinon
# il aurait fallu rajouter les ports correspondants dans la règle de filtrage sur le trafic venant de l'extérieur.
 
# Le flux rtsp du multiposte de free est de l'udp provenant de freeplayer.freebox.fr
rdr pass on $ext protoudp from 212.27.38.253 -> $sickboy
 
# On redirige le port 8010 utilisé par Jabber pour l'ouverture des transferts de fichiers entrant
rdr pass on $ext proto tcp to port 8010 -> $tommy
 
# Enfin on redirige le SSH (sur le port particulier utilisé à l'extérieur) et le https vers diane
rdr pass on $ext proto tcp to port $ssh_diane -> $diane port ssh
rdr pass on $ext proto tcp to port https -> $diane
 
# On active l'antispoofing sur l'interface externe afin de bloquer les paquets venant de l'extérieur essayent d'utiliser froduleusement
# notre adresse pour passer à travers le filtre
antispoof for $ext
 
# On ne filtre pas les paquets sur l'interface interne
pass quick on $int
 
# Maintenant les règles de filtrage
# Par défaut on bloque et on loggue tous les paquets venant de l'extérieur
block in on $ext log all
 
# On n'a pas envie de remplir nos logs en 5 min avec les quelques vers bien connus qui traient sur le net. Donc on bloque certains
# paquets sans les logger
block in on $ext inet proto tcp from any to any port $ports_not_logged
 
# On autorise les pings en provenance de l'extérieur
pass in on $ext inet proto icmp from any to any icmp-type { chorep, echoreq, timex, unreach }
 
# On autorise le SSH venant de l'extérieur
pass in on $ext inet $proto tcp from any to any port ssh flags S/SA keep state
 
# On autorise tout le traffic sortant (le NATé du réseau local passera par ces règles)
pass out inet proto tcp from $iface to any flags S/SA keep state
pass out inet proto { udp, icmp } from $iface to any keep state

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
Configuration File /etc/pf.conf
#       $OpenBSD: pf.conf,v 1.37 2008/05/09 06:04:08 reyk Exp $
#
# See pf.conf(5) for syntax and examples.
# Remember to set net.inet.ip.forwarding=1 and/or net.inet6.ip6.forwarding=1
# in /etc/sysctl.conf if packets are to be forwarded between interfaces.
 
#-------------------------------------------------------------------------
# Physical and virtual interfaces definitions
#-------------------------------------------------------------------------
 
# Physical Interfaces
wan_if="sis0"
lan_if="vr0"
dmz_if="sis1"
wifi_if="vr1"
# VLAN Interfaces
vlan99_if="vlan99"
vlan110_if="vlan110"
vlan120_if="vlan120"
# TUN Interfaces
openvpn_if="tun0"
sshvpn_if="tun1"
 
#-------------------------------------------------------------------------
# Networks definitions
#-------------------------------------------------------------------------
 
# Wan
wan_net="192.168.10.0/24"
 
# Trusted networks
lan_net="192.168.0.0/24"
vlan99_net="192.168.99.0/24"
wifi_net="192.168.200.0/24"
 
# Remote trusted networks
openvpn_net="192.168.20.0/24"
openvpn_net_nat="10.0.0.0/24"
sshvpn_net="192.168.30.0/24"
 
# DMZ
dmz_net="192.168.100.0/24"
vlan110_net="192.168.110.0/24"
vlan120_net="192.168.120.0/24"
 
# OpenVPN Shenzi network
openvpn_shenzi_net="192.168.90.0/24"
 
#-------------------------------------------------------------------------
# IP definitions
#-------------------------------------------------------------------------
 
# Router IP interfaces
wan_sks_ip="192.168.10.254"
dmz_sks_ip="192.168.100.254"
vlan110_sks_ip="192.168.110.254"
vlan120_sks_ip="192.168.120.254"
 
# Others IP
dedibox_ip="x.x.x.x"
work_ip="x.x.x.x"
freebox_tv_ip="212.27.38.253"
 
# Services IP
dmz_mail_ip="192.168.100.3"
dmz_web_ip="192.168.110.2"
dmz_dns_ip="192.168.100.3"
dmz_sftp_ip="192.168.100.6"
apt_cacher_ip="192.168.120.2"
 
#-------------------------------------------------------------------------
# Ports definitions and options
#-------------------------------------------------------------------------
 
# Port descriptions
imaps_ports="143, 993"
smtps_ports="25, 465"
ssh_ports="22, 222"
dns_port="53"
webs_ports="80, 443"
openvpn_port="1194"
proxy_port="3128"
apt_cacher_port="3142"
mysql_port="3306"
git_port="9418"
free_multiposte="31336, 31337"
 
# Whitelist / Blacklist table
table <blacklist> persist
table <whitelist> persist file "/etc/ssh/whitelist"
 
# Do not touch lo interface
set skip on lo0
 
#-------------------------------------------------------------------------
# Packet Normalization: reassemble fragments
#-------------------------------------------------------------------------
 
match in all scrub (no-df)
 
#-------------------------------------------------------------------------
# Nat for all internal interfaces
#-------------------------------------------------------------------------
 
match out on $wan_if from !($wan_if) nat-to ($wan_if)
 
#-------------------------------------------------------------------------
# block in all with no usurpation
#-------------------------------------------------------------------------
 
block in log all
block in log quick from urpf-failed
 
#-------------------------------------------------------------------------
# Redirections for incoming connections (wan)
#-------------------------------------------------------------------------
 
# From WAN
pass in on $wan_if proto tcp from any to $wan_if port 25 rdr-to $dmz_mail_ip port 25
pass in on $wan_if proto udp from any to $wan_if port $dns_port rdr-to $dmz_dns_ip port $dns_port
pass in on $wan_if proto tcp from any to $wan_if port $dns_port rdr-to $dmz_dns_ip port $dns_port
pass in on $wan_if proto tcp from any to $wan_if port 80 rdr-to $dmz_web_ip port 80
pass in on $wan_if proto tcp from any to $wan_if port 143 rdr-to $dmz_mail_ip port 143
pass in on $wan_if proto tcp from any to $wan_if port 222 rdr-to $dmz_sftp_ip port 22
pass in on $wan_if proto tcp from any to $wan_if port 443 rdr-to 127.0.0.1 port 443
pass in on $wan_if proto tcp from any to $wan_if port 465 rdr-to $dmz_mail_ip port 465
pass in on $wan_if proto tcp from any to $wan_if port 993 rdr-to $dmz_mail_ip port 993
pass in on $wan_if proto tcp from any to $wan_if port 9418 rdr-to $dmz_web_ip port 9418
 
# Apt-cacher-ng
pass in on $vlan120_if proto tcp from any to $vlan120_if port $apt_cacher_port rdr-to $apt_cacher_ip port $apt_cacher_port
 
pass in on $wan_if proto tcp from $dedibox_ip to $wan_if port $mysql_port rdr-to $dmz_web_ip port $mysql_port
 
pass in on $wan_if proto udp from $freebox_tv_ip to $wan_if rdr-to 192.168.0.100
pass in on $wan_if proto tcp from $freebox_tv_ip to $wan_if rdr-to 192.168.0.100
 
#-------------------------------------------------------------------------
# Global Rules pass and block
#-------------------------------------------------------------------------
 
# Allow Free Multiposte
pass in quick on $wan_if proto udp from $freebox_tv_ip to 192.168.0.100
pass out quick on $wan_if proto udp from $freebox_tv_ip to 192.168.0.100
pass in quick on $wan_if proto tcp from $freebox_tv_ip to 192.168.0.100
pass out quick on $wan_if proto tcp from $freebox_tv_ip to 192.168.0.100
 
# Allow all outgoing from $lan_net, $wifi_net and $openvpn_net
pass in on { $lan_if, $vlan99_if, $wifi_if, $openvpn_if, $sshvpn_if } from { $lan_net, $vlan99_net, $wifi_net, $openvpn_net, $sshvpn_net } to any
pass out on { $lan_if, $vlan99_if, $wifi_if, $openvpn_if, $sshvpn_if } from { $lan_net, $vlan99_net, $wifi_net, $openvpn_net, $sshvpn_net } to any
pass out on $wan_if from $wan_net to any
antispoof quick for { $wan_if, $dmz_if, $vlan110_if, $vlan120_if }
 
# block all incoming on lan_if, wifi_if and openvpn_if
block out log on { $lan_if, $vlan99_if, $wifi_if, $openvpn_if, $sshvpn_if } from { !$lan_if, !$vlan99_if, !$wifi_if, !($openvpn_if), !($sshvpn_if) } to any
 
#-------------------------------------------------------------------------
# VPN Access
#-------------------------------------------------------------------------
 
# Allow to access shenzi VE
pass out on $openvpn_if from any to $openvpn_shenzi_net
 
#-------------------------------------------------------------------------
# Specific ports on dmz
#-------------------------------------------------------------------------
 
# DNS
pass in on $dmz_if proto tcp from $dmz_dns_ip to $dmz_sks_ip port $dns_port
pass in on $dmz_if proto udp from $dmz_dns_ip to $dmz_sks_ip port $dns_port
 
# Apt-cacher
pass out on $vlan120_if proto tcp to ($vlan120_if) port $apt_cacher_port
pass in on $vlan120_if proto tcp to ($vlan120_if) port $apt_cacher_port
 
# DMZ and Vlan autorisations
#pass in on $vlan110_if from $vlan110_net to { !$lan_net, !$wifi_net, !$openvpn_net, !$sshvpn_net, !$vlan120_net, !$vlan110_sks_ip }
# Arrive pas a avoir juste any -> 3142
pass in on $vlan110_if from $vlan110_net to { !$lan_net, !$wifi_net, !$openvpn_net, !$sshvpn_net, !$vlan110_sks_ip }
pass in on $vlan120_if from $vlan120_net to { !$lan_net, !$wifi_net, !$openvpn_net, !$sshvpn_net, !$vlan120_sks_ip }
pass in on $dmz_if from $dmz_net to { !$lan_net, !$wifi_net, !$openvpn_net, !$sshvpn_net, !$dmz_sks_ip }
 
#-------------------------------------------------------------------------
# Specific Rules pass and block
#-------------------------------------------------------------------------
 
# Allow all incoming ICMP
pass in on $wan_if proto icmp to any
 
# Autoblacklist on SSH
pass in on $wan_if proto tcp from !<whitelist> to ($wan_if) port { $ssh_ports } \
        flags S/SA keep state \
        (max-src-conn-rate 3/60, \
        overload <blacklist> flush global)
pass in on $wan_if proto tcp from <whitelist> to $wan_if port { $ssh_ports } flags S/SA keep state
 
# block the ssh bruteforce bastards
block drop in on $wan_if from <blacklist>
pass in on $wan_if proto tcp from <whitelist> port { $ssh_ports }
 
# Allow OpenVPN
pass in on $wan_if proto tcp to $wan_if port $openvpn_port
 
# Allow DNS TCP from DMZ (Bind SRV) to secondary (Soekris)
#pass in quick on $dmz_if proto tcp from $dmz_dns_ip to $dmz_sks_ip port $dns_port
 
# Allow on wan interface from wan for tcp
pass out on $wan_if proto tcp to ($wan_if) port { $ssh_ports, $smtps_ports, $imaps_ports, $dns_port, $webs_ports, $git_port, $mysql_port }
# Allow on dmz interface from wan for udp
pass out on $wan_if proto udp to ($dmz_if) port { $dns_port }
 
# Allow all outbound traffic
pass out inet from !($wan_if) to any flags S/SA keep state

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 :

Command
ifconfig pflog0 up && pflogd

On peut vérifier que tout va bien :

Command
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 :
Command tcpdump
tcpdump -n -e -ttt -i pflog0

Il ira directement lire ce qui ce passe en direct sur pflog0, pflogd sera donc utile.

  • Passif :
Command
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 :

Command 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 :

Command 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 :

Configuration File /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 :

Command 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 :

Configuration File /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