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
(ou192.0.2.0/24
, idem pour les adresses ip6) ; - définitivement,
echo 192.0.2.3 >> /etc/pf.blackhole
puispfctl -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 : Ceci est faux, ceci remplace les
règles actuelles par la règle donnée.
echo "block
in proto icmp" | pfctl -mf -
.
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.