pf dans FreeBSD

FreeBSD contient trois pare-feu dans le système de base, pf, ipfw (IPFIREWALL) et ipf (IP Filter).

1. Tableau comparatif

  pf ipfw ipf
Origine OpenBSD FreeBSD Darren Reed
Version pf 4.1 (dans FreeBSD 8.1)    
Documentation pf.conf(5), pfctl(8) ipfw(8) ipf(5), ipf(4)
Écriture des règles Langage avec macro dans /etc/pf.conf Appels à ipfw(8) depuis /etc/rc.firewall (rc.firewall6 intégré dans rc.firewall depuis 8.1) Langage dans /etc/ipf.rules
Suivi des connexions Avec état par défaut En utilisant keep-state Avec l'option keep state
Décision Dernière règle qui correspond Première règle qui correspond Dernière règle
NAT Avec nat Avec divert puid natd(8), ou avec nat Avec map
Limitation de BP Avec ALTQ(4), nécessite de recompiler le noyau Avec dummynet(4) Pas d'intégration avec ipf

2. Commérages

J'ai choisi pf parce que c'est ce que les gens conseillent quand quelqu'un pose la question. Plus sérieusement, les trois paquets semblent raisonnablement fonctionnels. La syntaxe de pf est celle qui semble la plus claire (sans avoir écrit de règles dans les autres pare-feu).

3. Configuration de pf

3.1. Activation

Ajouter pf_enable=YES dans rc.conf. Le fichier de configuration par défaut est /etc/pf.conf. Utiliser /etc/rc.d/pf reload pour recharger les règles sans couper la connexion SSH courante.

Pour vérifier la syntaxe des règles, et la traduction des macros : pfctl -nvf /etc/pf.conf.

Utiliser set skip on lo pour ne pas filtrer ce qui passe sur lo0 ou lo1.

3.2. Premier goût

Syntaxe générale d'une règle :

action [direction:in|out] [log] [quick] [on interface] [af] [proto protocol] \
   [from src_addr [port src_port]] [to dst_addr [port dst_port]] \
   [flags tcp_flags] [state] 

action est typiquement block ou pass (rdr pour la redirection de ports, nat pour du NAT).

Les adresses sont des adresses (ip4 ou ip6) toutes seules, ou des listes écrites entre accolades, ou des sous-réseaux (notation CIDR classique, du style 2001:0db8:ab01::/48).

Bloquer tout ce qui rentre :

block in all

Et même, bloquer tout :

block all

Ceci va typiquement en tant que première règle, pour définir la politique par défaut. Les exceptions seront précisées plus loin. Par exemple, pour autoriser un port en entrée :

pass in proto tcp to any port ssh

Le proto tcp (ou UDP) est obligatoire lorsqu'un numéro de port est précisé.

3.3. Bloquer plein de monde

Blocage d'IP :

table <blackhole> persist file "/etc/pf.blackhole"
block quick from <blackhole>
block quick to   <blackhole>

quick pour dire qu'il ne sert à rien d'examiner les règles suivantes (i.e. on abandonne temporairement la règle du dernier gagnant).

persist ne signifie pas que les ajouts à la table blackhole lorsque le pare-feu est en court d'utilisation sont écrits dans le fichier à l'arrêt du pare-feu, mais qu'elle est gardée en mémoire lors d'un flush. La persistence dans ce premier sens pourrait être faite avec un cron qui lance régulièrement un pfctl -t blackhole -T show > /etc/pf.blackhole (ou voir le port security/expiretable pour une solution plus raffinée, non testé).

Pour ajouter une adresse à la table :

  • jusqu'au prochain redémarrage du pare-feu, pfctl -t blackhole -T add 192.0.2.3 (ou 192.0.2.0/24, idem pour les adresses ip6) ;
  • définitivement, echo 192.0.2.3 >> /etc/pf.blackhole puis pfctl -t blackhole -T add -f /etc/pf.blackhole.

Remplacer le -T add par -T replace pour vider la table actuellement utilisée. Les adresses ajoutée de la première façon ne survivent pas à un /etc/rc.d/pf reload.

Les gens actuellement dans la table : pfctl -t blackhole -T show.

3.4. Autoriser des connexions en entrée

ns_ip    = "{ 2001:41d0:1:8248::53  87.98.132.43  }"
www_ip   = "{ 2001:41d0:1:8248::80  87.98.132.43  }"
pass in  proto tcp         to   $www_ip          port { http https }
pass in  proto { tcp udp } to   $ns_ip           port domain
pass out proto { tcp udp } from $ns_ip    to any port domain

Le protocole est obligatoire dès qu'il y a des numéros de ports.

Pour spécifier une plage de ports, utiliser port début:fin.

3.5. Pare-feu pour un serveur seul sur Internet

Extrait du pare-feu de papillon :

host_ip  = "{ 2001:db8:1:8248::1   203.0.113.72  }"
www_ip   = "{ 2001:db8:1:8248::80  198.51.100.43 }"
ns_ip    = "{ 2001:db8:1:8248::53  198.51.100.43 }"

psnap_ip     = "{ 204.109.56.116 208.83.221.214 212.101.4.241 93.158.155.199 204.9.55.80 2001:4978:1:420::cc09:3750 }"
paudit_ip    = "{ 2001:4f8:fff6::24 69.147.83.36 }"
rtm_ip       = "{ 91.121.77.251 }"

table <blackhole> persist file "/etc/pf.blackhole"

set skip on lo

block in  all
block out all
block quick from <blackhole>
block quick to   <blackhole>

# Permettre tout ICMP : j'ai envie de pinguer mon serveur, et de toute
# façon c'est nécessaire pour ip6.
pass proto { icmp icmp6 }

pass out proto udp         from $host_ip port ntp
pass in  proto tcp         to   $www_ip          port { http https }
pass in  proto { tcp udp } to   $ns_ip           port domain
pass out proto { tcp udp } from $ns_ip    to any port domain

# Logiciel RTM d'OVH
pass out proto udp         from $host_ip  to $rtm_ip     port 6100:6199
# MaJ de la base de portaudit
pass out proto tcp         from $host_ip  to $paudit_ip  port http
# Portsnap
pass out proto tcp         from $host_ip  to $psnap_ip   port http

4. Manipulation du pare-feu en cours d'exécution

4.1. Directement

  • vi /etc/pf.conf ;
  • /etc/rc.d/pf reload.

L'action reload nettoie toute la configuration et recharge les règles à partir du fichier de configaration. Les connexions en cours ne sont pas cassées, même si les nouvelles règles interdiraient son établissement. Typiquement, si on enlève pass in de SSH, la connexion SSH courante n'est pas coupée. Les statistiques attachées à chaque règle sont remises à zéro.

On peut utiliser pfctl -nf /etc/pf.conf pour vérifier si le fichier contient des erreurs de syntaxe.

4.2. Avec pfctl

Pour lister les règles : pfctl -s rules. Ajouter un -v pour voir en même temps le nombre de fois où la règle a été appliquée.

Pour ajouter une règle à la fin des règles actuelles : echo "block in proto icmp" | pfctl -mf -. Ceci est faux, ceci remplace les règles actuelles par la règle donnée.

Par contre, il n'y a pas de moyen pour supprimer des règles. Je cherchais un moyen pour pouvoir autoriser mes jails (au moins certaines d'entre elles) à télécharger sur le WWW ou par FTP les sources des ports, mais seulement lorsqu'elles en ont besoin. Il faut donc pouvoir supprimer les règles lorsqu'on a fini les mises à jour. Une méthode pour faire cela est d'utiliser des ancres. On ajoute à la fin de pf.conf une ancre :

% echo anchor portupdt >> /etc/pf.conf
% /etc/rc.d/pf reload

Ensuite, on utilise l'option -a pour dire à pfctl que les règles qu'on ajoute sont dans l'ancre portudpt. Par exemple :

echo 'pass out proto tcp from $www_ip to any port www' | pfctl -a portupdt -mf-

Ensuite, pour supprimer les règles associées à cette ancre, on fera un simple pfctl -a portupdt -F rules. Attention, dans les deux cas, si l'option -a est oubliée, les règles sont ajoutées aux règles principales, ou alors toutes les règles sont supprimées. On voudra peut-être scripter cela…

Le problème… C'est que ceci ne marche pas sous cette : en effet, les macros définies dans pf.conf ont été remplacées au moment de l'analyse, et ne sont donc plus accessibles dans notre commande plus haut. La solution est de remplacer ces macros par des tables qui sont marquées avec persist. Par rapport à l'extrait de pf.conf, on supprime la définition de $www_ip et ses utilisations pour les remplacer par :

table <www_ip> persist { 2001:db8:1:8248::80  198.51.100.43 }
...
pass in proto tcp to <www_ip> port { http https }
...
anchor portupdt

On ajoutera peut-être l'option const après le persist pour éviter les modifications involontaires de la table.

Puis pour autoriser ponctuellement le téléchargement de ports par HTTP :

echo 'pass out proto tcp from <www_ip> to any port http' | pfctl -a portupdt -mf-

Pour enlever cette autorisation, comme plus haut, pfctl -a portupdt -Fr. Les règles qui sont attachées à une ancre ne sont pas supprimées lors d'un /etc/rc.d/pf reload.

5. Liens

La FAQ d'OpenBSD pf, qui est en réalité un guide. Sur la première page, il y a un avertissement concernant des modifications importantes dans pf 4.7. Dans pf 4.1 (celui de FreeBSD), je ne suis pas tombé sur ces modifications, qui d'après les changelogs concernent principalement les règles de NAT et de redirection.

Auteur : Frédéric Perrin

Date : lundi 29 novembre 2010, modifié le lundi 11 mars 2024

Sauf mention contraire, les textes de ce site sont sous licence Creative Common BY-SA.

Ce site est produit avec des logiciels libres 100% élevés au grain et en plein air.