--- categories: openbsd network firewall pare-feu title: Howto PacketFilter --- Documentation : * * * PacketFilter est le pare-feu par défaut sous OpenBSD. ## Général Activer PacketFilter : > Note : Attention, comme les règles qui seront activées n'auront pas d'état, pf va couper **toutes** les connexions. ~~~ # pfctl -e ~~~ Désactiver PacketFilter : ~~~ # pfctl -d ~~~ Vérifier la configuration sans la charger : ~~~ # pfctl -nf /etc/pf.conf ~~~ Recharger la configuration : ~~~ # pfctl -f /etc/pf.conf ~~~ ## Utilisation détaillée ### Options Certaines options peuvent être configurées. Par exemple : ~~~ set limit states set limit src-nodes ~~~ La limite `states` est le nombre d'état que le pare-feu peut créer au maximum. La limite `src-nodes` est le nombre total d'IP sources pouvant être "trackées", et generées par les options `sticky-address` et `source-track`. ### Observation Voir la QoS en temps réel : ~~~ # systat queue # pfctl -s queue -vv ~~~ Voir toutes les règles actuellement en place : ~~~ # pfctl -sr ~~~ Voir toutes les règles actuellement en place, avec les statistiques : ~~~ # pfctl -vsr ~~~ Obtenir la règle numéro 42 : ~~~ # pfctl -sr -R42 ~~~ Liste des états : ~~~ # pfctl -s states ~~~ Liste des états avec statistiques, notamment l'âge et l'expiration de l'état : ~~~ # pfctl -vs states ~~~ Vérifier les statistiques et les compteurs : ~~~ # pfctl -si ~~~ Avec notamment : * `current entries` : le nombre d'états actuels * `half-open tcp` : le nombre de connexions TCP à moitié ouvertes, c'est-à-dire dont le three-way handshake n'est pas terminé * `memory` : le nombre de fois où la mémoire n'a pas pu être allouée, et ayant pu causer des paquets perdus ; ce paramètre est intéressant à suivre après une augmentation des valeurs des options (telles que `limit states` ou `limit src-nodes`) * `state-limit` : le nombre de fois où l'une des limites concernant les états a été atteinte Toutes les infos sur PF : ~~~ # pfctl -sa | less ~~~ ### Action Flush des états : ~~~ # pfctl -F states ~~~ Gestion des tables : ~~~ # pfctl -t -T show/flush/kill/add/delete ~~~ En remplaçant `
` par le nom de la table sans les chevrons `<` et `>`. ### Logs Des logs sont visibles lorsqu'une règle contenant le mot clef `log` est matchée. Par exemple, la règle `block log all` permettra de voir tous les flux bloqués. Voir les logs : ~~~ # tcpdump -n -e -ttt -r /var/log/pflog ~~~ Voir les vieux logs : ~~~ # zcat /var/log/pflog.0.gz |tcpdump -ne -ttt -r - ~~~ Voir les logs en temps réel : ~~~ # tcpdump -n -e -ttt -i pflog0 ~~~ On peut par exemple filtrer pour ne voir que les paquets bloqués, ou que ceux autorisés : ~~~ # tcpdump -n -e -ttt -i pflog0 action block # tcpdump -n -e -ttt -i pflog0 action pass ~~~ ## Cas d'utilisation ### Autoriser une machine à communiquer avec une autre sur un port particulier Imaginons la politique de filtrage par défaut suivante : - Tout est bloqué en entrée sur l'interface WAN - Tout est autorisé en sortie sur les interfaces WAN et LAN - Tout est autorisé en entrée sur l'interface LAN, pour les IP provenant du LAN Equivalent dans la configuration : ~~~ # Interfaces lan_if = "em0" ext_if = "em1" # Politique par défaut block all pass out pass in quick on $lan_if from ($lan_if:network) to any ~~~ Si on veut autoriser en entrée sur l'interface WAN l'IP `203.0.113.10` à communiquer vers les ports `22` et `443` de l'IP `198.51.100.50 : ~~~ pass in quick on $ext_if proto tcp from 203.0.113.10 to 198.51.100.50 port { 22, 443 } ~~~ ### Limite d'état spécifique à une règle Si une machine est souvent la cible d'attaques pouvant remplir la table d'états, une limite spécifique à une règle peut être mise en place afin de ne pas atteindre la limite globale qui bloquerait la création de nouveaux états. ~~~ pass in quick on $if proto tcp from any to 192.0.2.10 port { 80, 443 } keep state (max 50000) ~~~ Ainsi, au maximum 50 000 états pourront être créés pour 192.0.2.10:80 et 192.0.2.10:443, soit un total de 100 000 états pour l'ensemble. Attention, les 2 limites (la globale et celle spécifique à la règle) ne sont pas séparées. Si la limite globale est configuré à 150 000 (`set limit states 150000`) et que 50 000 états sont atteints pour 192.0.2.10:80, alors il ne restera que 100 000 états possible globalement. #### Empêcher le brute force Il est possible d'empêcher le brute force vers un service particulier. Pour cela, la configuration suivante est nécessaire : ~~~ table persist block in quick on $ext_if proto tcp from to port 2222 pass in quick on $ext_if proto tcp to port 2222 rdr-to 192.0.2.5 port ssh \ keep state (max-src-conn 10, max-src-conn-rate 5/30, overload flush) ~~~ Il est également possible d'avoir une whitelist avec cette configuration : ~~~ table const persist { 198.51.100.12, 203.0.113.56 } table persist pass in quick on $ext_if proto tcp from to port 2222 rdr-to 192.0.2.5 port ssh block in quick on $ext_if proto tcp from to port 2222 pass in quick on $ext_if proto tcp from ! to port 2222 rdr-to 192.0.2.5 port ssh \ keep state (max-src-conn 10, max-src-conn-rate 5/30, overload flush) ~~~ Ces règles signifient que : - Une IP peut établir jusqu'à 10 connexion simultanées maximum vers le port 2222 (`max-src-conn 10`). - Seules 5 connexions peuvent se faire sur un laps de temps de 30 secondes par une même IP (`max-src-conn-rate 5/30`). - Au-delà des 5 connexions échouées durant ces 30 secondes, l'IP en question est insérée dans la table (`overload `). - Les autres connexions établies par cette même IP sur cette même régle (connexion SSH vers le port 2222) sont tuées (`flush`). - L'IP est bannie. Elle peut toujours tenter toute autre connexion, mais ne peut plus tenter de connexion SSH vers le port 2222 (`block in quick on $ext_if proto tcp from to port 2222`). Cette règle peut être adaptée selon ce que l'on veut bloquer à l'IP bannie. - Les IPs contenues dans la table peuvent toujours se connecter, sans jamais être soumis aux limites contre le brute force. La table ne se vide pas avec un rechargement de pf (`pfctl -f /etc/pf.conf`). * On peut voir les IPs contenues dans cette table avec `pfctl -t blacklist-ssh -T show`. * On peut entièrement vider la table avec `pfctl -t blacklist-ssh -T flush`. * On peut lever une IP spécifique de la table avec `pfctl -t blacklist-ssh -T delete X.X.X.X`. ### Routage particulier (avec route-to) Pour avoir un routage spécifique, c'est-à-dire différent de celui donné par la table de routage, on peut agir avec le mot-clé `route-to`. Ça se configure sous la forme suivante : ~~~ pass in on $lan_if ... to ! route-to 192.0.2.254 ~~~ Cela ne matche que pour les paquets provenants du LAN et non pas pour ceux provenants du routeur lui-même, ni pour une réponse à un paquet provenant de l'extérieur. Dans ce cas, on peut utiliser une règle du type : ~~~ pass out from $egress:0 ... keep state route-to 192.0.2.254 nat-to 192.0.2.253 ~~~ La partie nat-to est nécessaire, sinon les paquets vont partir sur l'interface secondaire avec l'IP de l'interface principale. Aussi, l'opérateur réseau en face risque de voir cela comme une usurpation et refuser le paquet. Attention également, cette règle peut casser le NAT du client ayant émis le paquet dont la route est changée (exemple avec OpenVPN où le client ne reçoit pas le paquet de réponse, et la connexion ne s'établit plus). ### Options nat-to vs rdr-to vs binat-to L'option nat-to permet de traduire l'adresse IP d'une machine depuis un réseau local vers l'extérieur. Utile par exemple pour qu'une machine ayant une IP privée puisse joindre Internet en utilisant une IP publique. ~~~ match out on $ext_if from 10.0.2.1 nat-to 192.0.2.1 ~~~ L'option rdr-to permet de rediriger le trafic arrivant depuis Internet sur une IP et un port particulier du pare-feu vers une autre IP et un autre port d'une machine interne à un réseau. Utile par exemple pour qu'une machine ayant une IP privée puisse être joignable depuis Internet avec une IP publique. ~~~ pass in proto tcp from 192.0.2.1 to self rdr-to 10.0.2.1 ~~~ L'option binat-to combine les effets de nat-to et rdr-to en une seule règle. Une machine ayant une IP privée dans un réseau local peut alors sortir avec une IP publique et être joignable depuis l'extérieur par cette même IP publique, qui n'est pourtant pas attribuée directement à la machine. ~~~ match on $ext_if from 10.0.2.1 binat-to 192.0.2.1 ~~~ ### Répondre par la même interface par laquelle une requête arrive Si un routeur a deux sorties et qu'on le joint par la connexion secondaire, la réponse se fera par la principale en suivant la route par défaut. On peut modifier ce comportement, pour que la réponse sorte forcément par là où elle est arrivée : ~~~ pass in quick on $wan1 from … to self reply-to ($wan1) pass in quick on $wan2 from … to self reply-to ($wan2) ~~~ > Note : Attention, l'IP autorisée par cette règle aura un accès complet au routeur. ### Exclure une IP d'une règle autorisant un préfixe plus large Imaginons la règle suivante, permettant de ne rien bloquer en entrée sur l'interface WAN, depuis n'importe où et à destination du préfixe 192.0.2.0/24 : ~~~ pass in quick on $wan from any to 192.0.2.0/24 ~~~ Si dans ce préfixe, il y a l'IP 192.0.2.5 en particulier pour laquelle on ne veut pas avoir cette large autorisation, on pourrait vouloir l'exclure. PacketFilter permet la négation avec le point d'exclamation `!`. On pourrait donc penser à modifier la règle précédente pour y ajouter `!192.0.2.5`, mais attention ! Cette règle n'indiquerait pas de tout autoriser vers tout le préfixe 192.0.2.0/24 sauf vers l'IP 192.0.2.5. Elle serait découpée en 2 règles indépendantes : la première indiquant de tout autoriser vers 192.0.2.0/24, et la deuxième indiquant de tout autoriser vers tout le monde, sauf vers 192.0.2.5. On se retrouverait alors avec une autorisation depuis n'importe qui et vers l'ensemble de notre réseau, sauf 192.0.2.5. Pour exclure une IP d'une autorisation plus large, il faut créer une règle `block` en amont : ~~~ block in quick on $wan from any to 192.0.2.5 pass in quick on $wan from any to 192.0.2.0/24 ~~~ Ainsi, la première règle de `block` permettra de ne rien autoriser à destination de 192.0.2.5, et la deuxième règle permettra, pour le reste du préfixe 192.0.2.0/24, de tout autoriser. Si, pour cette même IP 192.0.2.5, on ne veut pas tout bloquer mais autoriser seulement certains ports, il faut encore ajouter une autre règle : ~~~ pass in quick on $wan proto tcp from any to 192.0.2.5 port 443 block in quick on $wan from any to 192.0.2.5 pass in quick on $wan from any to 192.0.2.0/24 ~~~ On aura correctement notre autorisation précise vers 192.0.2.5 sur son port TCP/443, les autres ports seront bloqués, et tout le reste du préfixe sera entièrement autorisé. ### Tuer un état selon son ID On peut voir l'ensemble des états d'un firewall et leur ID : ~~~ # pfctl -vvss all udp 2001:db8:1::1[48110] -> 2001:db8:2::1[53] MULTIPLE:SINGLE age 00:00:00, expires in 00:00:30, 1:1 pkts, 88:231 bytes, rule 15 id: 57dbc2163a63f98b creatorid: 3a22c108 ~~~ Puis tuer l'état en spécifiant son ID : ~~~ # pfctl -k id -k 57dbc2163a63f98b ~~~ On peut ainsi tuer par exemple tous les états UDP : ~~~ for _ID in $(pfctl -vvss | grep -A2 'all udp' | grep id | awk '{print $2}') ; do echo "${_ID}" ; pfctl -k id -k "${_ID}" ; done ~~~ ### Tuer tous les états correspondant à des IP source et/ou destination Tuer tous les états dont les IP sources sont dans le range 192.0.2.0/24 : ~~~ # pfctl -k 192.0.2.0/24 ~~~ Tuer tous les états dont les IP sources sont dans le range 192.0.2.0/24 et dont l'IP destination est 198.51.100.10 : ~~~ # pfctl -k 192.0.2.0/24 -k 198.51.100.10 ~~~ Tuer tous les états dont l'IP destination est 198.51.100.10, peu importe la source : ~~~ # pfctl -k 0.0.0.0/0 -k 198.51.100.10 ~~~ ## FAQ ### pfctl: warning: namespace collision with \ global table. Il faut a priori effacer la table avec ~~~ # pfctl -t
-T kill ~~~ ### Valeurs des timeout D'après le man : ~~~ set optimization environment Optimize state timeouts for one of the following network environments: aggressive Aggressively expire connections. This can greatly reduce the memory usage of the firewall at the cost of dropping idle connections early. conservative Extremely conservative settings. Avoid dropping legitimate connections at the expense of greater memory utilization (possibly much greater on a busy network) and slightly increased processor utilization. high-latency A high-latency environment (such as a satellite connection). normal A normal network environment. Suitable for almost all networks. satellite Alias for high-latency. ~~~ Comparaison des valeurs des modes *aggressif* et *normal* ~~~ Agressive Normal tcp.first 30s 120s tcp.opening 5s 30s tcp.established 18000s 86400s tcp.closing 60s 900s tcp.finwait 30s 45s tcp.closed 30s 90s tcp.tsdiff 10s 30s udp.first 60s 60s udp.single 30s 30s udp.multiple 60s 60s icmp.first 20s 20s icmp.error 10s 10s other.first 60s 60s other.single 30s 30s other.multiple 60s 60s frag 60s 60s interval 10s 10s adaptive.start 6000 states 6000 states adaptive.end 12000 states 12000 states src.track 0s 0s ~~~ À titre d'indication, en mode *satellite* ~~~ tcp.first 180s tcp.opening 35s tcp.established 86400s tcp.closing 905s tcp.finwait 50s tcp.closed 95s tcp.tsdiff 60s udp.first 60s udp.single 30s udp.multiple 60s icmp.first 20s icmp.error 10s other.first 60s other.single 30s other.multiple 60s frag 60s interval 10s adaptive.start 6000 states adaptive.end 12000 states src.track 0s ~~~