Packet Filter sur les systèmes BSD : les tables
Date de publication : 29/10/2007
Par
julp (Autres articles)
Que sont les tables ? Pourquoi et comment les utiliser ?
Celles-ci sont utilisées par certains programmes externes pour interagir
avec Packet Filter afin de traiter différemment de nouvelles adresses sans
avoir à relire les règles. Vous vous rendrez vite compte de leur intérêt en
les mettant à profit par exemple pour la constitution de listes noires (ou
blanches) dynamiques.
1. Présentation
1.1. Une amélioration notable des listes et macros
1.2. Syntaxe
1.2.1. Déclaration d'une table
1.2.2. Utilisation dans les règles
2. Manipulation en ligne de commande avec pfctl
2.1. Limitations de pfctl
2.2. Lister les adresses d'une table
2.3. Ajouter des adresses à une table
2.4. Supprimer des adresses à une table
2.5. Recharger le contenu de la table
2.6. Remplacer l'ensemble des adresses
2.7. Tester si une adresse fait partie de la table
2.8. Les statistiques d'une table
2.9. Vider/Supprimer une table
3. Les différentes propriétés apposables aux tables
3.1. Les tables immuables
3.2. Les tables persistantes
3.3. Les tables dont le contenu réside dans un fichier
3.4. Combinaison de plusieurs propriétés
4. Autres éléments syntaxiques liés aux tables
4.1. Le mot-clé "self"
4.2. Les négations
4.3. Les listes
5. Utilitaires autour des tables
5.1. expiretable : supprimer des entrées obsolètes à intervalle régulier
6. Conclusion
6.1. Epilogue
6.2. Remerciements
1. Présentation
1.1. Une amélioration notable des listes et macros
Une table permet le regroupement d'adresses au sein d'une même
entité. En quoi sont-elles différentes aux listes que l'on peut
définir avec Packet Filter et que l'on peut réutiliser grâce aux
macros (voir exemple ci-dessous) ?
me = "sis0"
tcpflags = "flags S/SFRA"
workstations = "{ 192.168.0.1 192.168.0.2 192.168.0.3 192.168.0.4 192.168.0.5 }"
block all
pass in quick inet proto tcp from $workstations to $me port ssh $tcpflags keep state
pass in quick inet proto tcp from $workstations to $me port http $tcpflags keep state
|
Les spécificités des tables par rapport aux listes standard sont les
suivantes :
- Utilisation directe et multiple dans les règles, comparable à une macro
- Les recherches d'une adresse sont beaucoup plus rapides (utilise moins de mémoire et de temps CPU qu'une simple liste)
- Existence qui va au-delà du fichier de configuration où elles sont définies puisqu'elles résident ensuite en mémoire ce qui permet de leur appliquer des modifications au niveau de leur contenu. Ces modifications sont immédiatement prises en compte par Packet Filter sans avoir à recharger les règles ou à procéder à une quelconque manipulation.
1.2. Syntaxe
1.2.1. Déclaration d'une table
Le contenu d'une table est sensiblement identique à celui que
l'on peut fournir comme adresse source ou de destination dans les
règles en temps normal. Il peut prendre les différentes formes
suivantes :
- Une adresse IP (version 4 comme 6), exemples : 192.168.0.1, 66.80.97.134
- Une adresse en notation CIDR (version 4 comme 6) : 192.168.0.0/24
- Un nom de machine, comme www.developpez.com, qui sera automatiquement remplacé par les adresses IP correspondantes (versions 4 et 6) à condition de pouvoir effectuer cette résolution (ne pas oublier de construire des règles visant à autoriser le trafic DNS)
- Le nom d'une interface (sis1, lo0, etc), qui sera substituée par la liste des adresses qui lui sont attribuées
- Le nom d'une interface suivie de la notation CIDR, exemple sis0/8
- Le nom d'une interface suivie de :network, :broadcast, :peer ou :0 correspondant respectivement à l'adresse réseau, l'adresse de broadcast, l'adresse d'un pair sur un lien point à point ou l'adresse principale affectée à une carte réseau (ne tient pas compte des alias)
- Le mot clé self
- L'une des formes antérieures précédée d'une négation
Exemples de déclarations de quelques tables :
table <sites_http_autorises> { www.developpez.net, www.developpez.com, www.developpez.be }
table <serveurs_dns_internes> { 192.168.0.1, 192.168.0.101 }
table <blacklist> { 125.60.86.214 208.159.106.199 81.237.54.31 76.89.10.213 }
table <reseaux_internes> { sis1:network sis2:network }
|
Les signes < et > ne sont pas le résultat d'une erreur
typographique et encadrent systématiquement le nom d'une table.
On peut ainsi reconnaître d'un simple coup d'oeil qu'il s'agit
d'une table, tout comme les macros sont précédées du signe dollar
($).
Les virgules séparant les différents éléments de la liste sont
facultatives. Libre à vous donc de les omettre ou de les faire
figurer.
 |
L'utilisation des noms de machine (DNS) est à éviter autant que
possible pour des raisons de sécurité.
L'utilisation d'adresses dynamiques (protocole DHCP) n'est ici
pas gérée puisque les éléments d'une table sont systématiquement
traduits en une adresse IP à leur intégration dans la table.
|
1.2.2. Utilisation dans les règles
Les tables peuvent être employées dans les différentes règles
de manière quasi similaire à une adresse. Voici une liste
exhaustive des emplacements où elles peuvent figurer :
- Comme ensemble d'adresses source ou de destination dans les règles de filtrage (pass et block)
- Comme ensemble d'adresses source ou de destination dans les règles de traduction d'adresse (nat et binat)
- Comme ensemble d'adresses source ou de destination dans les règles de redirection (rdr)
- Pour constituer une liste d'attaquants à l'aide du mot-clé overload suivant le comportement de l'hôte distant (la mesure est effectuée en nombre de connexions sur un certain laps de temps)
Lors de l'emploi d'une table dans une règle, il suffit de
reprendre son nom à l'endroit souhaité sans oublier de l'encadrer
de ses signes distinctifs < et >. Petit exemple pour
illustrer :
if = "sis0"
tcpflags = "flags S/SFRA"
table <blacklist> { 125.60.86.214 208.159.106.199 81.237.54.31 76.89.10.213 }
table <serveurs_http> { 192.168.0.102, 192.168.0.103 }
block in quick on $if from <blacklist> to any
pass in quick on $if inet proto tcp from any \
to <serveurs_http> port { http https } $tcpflags keep state
|
Les tables ne font l'objet d'aucune restriction au niveau de leur
emplacement. Elles doivent cependant être "déclarées" avant une
quelconque référence dans vos règles.
2. Manipulation en ligne de commande avec pfctl
 |
La commande pfctl accepte en paramètre exactement les mêmes types
d'informations comme adresse (adresses IP, noms DNS, interfaces, ...)
que cité précédemment. Ajoutons que celle-ci n'est d'ailleurs pas
cantonnée à l'unique manipulation des tables.
|
Les exemples qui suivent seront basés sur la déclaration d'une table
nommée blacklist visant à interdire les paquets de certaines
adresses IP suite à des activités plus ou moins obscures :
table <blacklist> { 125.60.86.214 208.159.106.199 81.237.54.31 76.89.10.213 }
|
2.1. Limitations de pfctl
Il faut savoir que pfctl est soumis à certaines restrictions de la
part du système et de l'implémentation de la commande elle-même :
- La commande pfctl n'altère que le contenu des tables en
mémoire et ne répercute en aucun cas les différents changements
demandés sur un quelconque fichier de configuration. Si vous avez
besoin de synchroniser ces modifications en vue de les réappliquer
au prochain démarrage de Packet Filter vous devrez le faire
manuellement ou éventuellement à l'aide d'un script.
- Il vous sera impossible d'agir sur les tables à l'aide de
pfctl si le niveau de sécurité du noyau atteint la valeur 2 sur
NetBSD ou OpenBSD et la valeur 3 pour FreeBSD. Cette restriction
concerne de manière plus générale les règles de filtrage qui sont
ainsi chargées une bonne fois pour toutes avant le changement de ce
niveau de sécurité.
2.2. Lister les adresses d'une table
Vous pouvez à tout moment consulter le contenu d'une table à l'aide
de pfctl. Illustration avec notre table blacklist :
pfctl (-v) -t blacklist -T show
|
Qui produira la sortie suivante :
76.89.10.213
81.237.54.31
125.60.86.214
208.159.106.199
|
2.3. Ajouter des adresses à une table
Imaginons maintenant que nous voulions ajouter de nouvelles adresses
à notre liste noire :
pfctl (-v) -t blacklist -T add 216.194.109.203 88.240.211.27
|
Notre table sera désormais composée des six adresses suivantes :
76.89.10.213
81.237.54.31
88.240.211.27
125.60.86.214
208.159.106.199
216.194.109.203
|
 |
La table sera créée si celle-ci n'existe pas déjà.
|
2.4. Supprimer des adresses à une table
Nous avons besoin de supprimer deux adresses de celle-ci :
pfctl (-v) -t blacklist -T delete 125.60.86.214 76.89.10.213
|
Cette table contiendra de nouveau quatre adresses :
81.237.54.31
88.240.211.27
208.159.106.199
216.194.109.203
|
2.5. Recharger le contenu de la table
Nous souhaitons réinitialiser une table à partir de sa déclaration :
pfctl (-v) -t blacklist -T load -f /root/pf.conf
|
L'option -f est nécessaire pour indiquer le fichier où est définie la
table en question.
Nous obtenons bien le contenu tel que la table a été déclarée :
76.89.10.213
81.237.54.31
125.60.86.214
208.159.106.199
|
2.6. Remplacer l'ensemble des adresses
Il est possible d'aller un peu plus loin en remplaçant l'intégralité
des adresses actuelles d'une liste par de nouvelles. Pour cela on
utilise la sous-commande replace, exemple :
pfctl (-v) -t blacklist -T replace 115.79.79.48 143.59.174.112 139.102.183.50
|
Le contenu sera désormais le suivant :
115.79.79.48
139.102.183.50
143.59.174.112
|
 |
La sous-commande replace, comme add, crée la table si cette dernière
n'existe pas.
|
2.7. Tester si une adresse fait partie de la table
pfctl vous permet même de savoir si une adresse est actuellement
définie dans la table donnée :
pfctl (-vv) -t blacklist -T test 115.79.79.48 www.developpez.com
|
Indiquera qu'il y a correspondance avec l'une des deux adresses
(sur 115.79.79.48 et aucune avec www.developpez.com).
2.8. Les statistiques d'une table
Vous pouvez obtenir différentes informations (nombre de paquets
bloqués ou passés) sur une table en particulier avec la commande
suivante :
pfctl -vv -t blacklist -T show
|
Ou bien sur l'ensemble des tables avec :
Pour réinitialiser les statistiques d'une table vous pouvez utiliser
la sous-commande zero. Exemple avec notre table blacklist :
pfctl -t blacklist -T zero
|
2.9. Vider/Supprimer une table
Vous pouvez bien évidemment vider la table :
pfctl -t blacklist -T flush
|
Ou encore la supprimer mais celle-ci n'aura plus aucune existence
en mémoire et ne sera plus, par conséquent, manipulable via pfctl :
 |
Les règles faisant référence à une table supprimée subsistent mais
deviennent donc, par conséquent, inutiles. Vous pouvez la recréer
à tout moment à l'aide des commandes add ou replace
(voir plus haut).
|
3. Les différentes propriétés apposables aux tables
3.1. Les tables immuables
Il est possible de rendre une table insensible à toute modification.
Cet état est introduit par le mot-clé const. En effet, par
défaut (en son absence) une table peut être modifiée par l'utilitaire
pfctl, que nous avons étudié en détail ci-dessus.
Voici un exemple d'une telle table qu'il ne nous sera pas possible
d'altérer même pour y ajouter une adresse :
table <serveurs_dns> const { 192.168.0.1 192.168.0.101 }
|
3.2. Les tables persistantes
Par défaut, le noyau se débarrasse automatiquement des tables
inutilisées dans le sens où elles ne sont pas référencées par une
quelconque règle.
Cela peut s'avérer particulièrement gênant lorsque cette table est
amenée à être utilisée par un programme externe et dont des règles
sont ajoutées ou supprimées en fonction des besoins, bien souvent à
l'aide du mécanisme appelé ancre (exemples courants : authpf ou
encore certains mandataires FTP intégrés à pf).
La solution consiste à utiliser le mot-clé persist, rendant
ainsi la table résidente en mémoire quoiqu'il advienne. Exemple de
déclaration d'une table vide dont la persistance est assurée :
table <blacklist> persist
|
Cette table, blacklist, ici vide vous sera donc toujours accessible
avec pfctl. Notez que vous pouvez également initialiser cette table
à l'aide d'une ou plusieurs valeurs :
table <blacklist> persist { 67.80.122.54 67.80.122.55 }
|
3.3. Les tables dont le contenu réside dans un fichier
Packet Filter vous offre également la possibilité de stocker les
adresses de votre table dans un fichier annexe, introduisant de ce
fait un nouveau mot-clé : file. Voyons tout d'abord comment
déclarer ce genre de tables quelque peu particulier à l'aide d'un
exemple :
table <whitelist> file "/root/whitelist"
|
Puis on place les adresses, pouvant prendre l'une des formes citées
lors de la présentation, à raison d'une adresse par ligne, dans le
fichier indiqué. Ce dernier devra impérativement exister même vide
sous peine de voir apparaître des erreurs fatales lors du chargement
des règles. A noter, qu'il est même possible d'y insérer des
commentaires, en les faisant précéder d'un caractère dièse (#).
Voici le contenu de la table whitelist dont on prendra soin de
mettre dans le fichier /root/whitelist tel que convenu :
| /root/whitelist : |
www.developpez.com
www.developpez.net
www.developpez.be
43.204.225.199
75.186.215.37
|
Quand l'utilisation de ce type de table est-il justifié ? Lorsque le
nombre d'adresses devient conséquent ou bien lorsque vous avez besoin
d'ajouter ou de supprimer des adresses et de mémoriser ce changement
d'état pour le prochain démarrage de Packet Filter, ce format étant
fortement pratique pour des manipulations à l'aide de commandes shell
par exemple. Ainsi pour ajouter une nouvelle adresse on pourrait
utiliser un script semblable à celui-ci :
progname=`basename $0`
args=`getopt t:f: $*`
usage() {
echo "Usage: $progname -t table -f fichier adresse1 ... adresseN"
exit 2
}
[ $? -eq 0 ] || usage
set -- $args
for i
do
case "$i"
in
-t)
table="$2"
shift; shift;;
-f)
fichier="$2"
shift; shift;;
--)
shift; break;;
esac
done
[ -z "$fichier" -o -z "$table" -o $# -lt 1 ] && usage
pfctl -t $table -T add $*
[ $? -eq 0 ] || exit $?
for arg in $*
do
echo "$arg" >> $fichier
done
|
3.4. Combinaison de plusieurs propriétés
Il est parfaitement possible de combiner plusieurs voir les trois
états (persist, const et file) abordés dans cette partie à la fois.
Ainsi, l'exemple qui présente le plus d'intérêt serait de réaliser
une table dont le contenu est chargé à partir d'un fichier annexe
et déclarée constante pour qu'elle ne puisse pas être modifiée par
l'utilitaire pfctl :
table <blackist> const file "/root/blacklist"
table <blacklist> file "/root/blacklist" const
|
4. Autres éléments syntaxiques liés aux tables
4.1. Le mot-clé "self"
Ce mot-clé particulier est substitué par l'ensemble des adresses IP
utilisées par votre machine. Ceci inclut également votre interface
réseau de bouclage (également appelée loopback sous l'identifiant
lo0 ou lo). Exemple, soit la configuration réseau
suivante :
[root@freebsd ~]# ifconfig
de0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
inet 192.168.0.1 netmask 0xffffff00 broadcast 192.168.0.255
ether 00:03:ff:3d:94:f9
media: Ethernet autoselect (100baseTX)
status: active
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
inet6 ::1 prefixlen 128
inet 127.0.0.1 netmask 0xff000000
|
self retournera les adresses suivantes :
- l'adresse IP attribuée à la carte réseau de0 : 192.168.0.1
- les adresses IP respectivement en version 4 et 6 affectées à la boucle locale : 127.0.0.1 et ::1
4.2. Les négations
Les négations sont utilisées pour exclure un sous-ensemble (adresse
ou sous-réseau) d'un ensemble plus important (réseau) qui peut
éventuellement figurer dans la même table. Afin d'exclure une
adresse ou un ensemble (notation CIDR), il suffit de faire précéder
cet élément d'un point d'exclamation (!). Prenons un exemple pour y
voir plus clair :
On dispose d'un important réseau interne privé (rfc1918) découpé en
plusieurs sous-réseaux dont un exclusivement destiné au service
comptabilité dont on souhaite interdire l'accès à Internet, en ayant
recours à une table, à l'exception d'un serveur situé dans ce service.
Les affectations sont les suivantes :
- 192.168.0.0/16 : le réseau interne dans son intégralité
- 192.168.2.0/24 : le réseau interne réservé à la comptabilité
- 192.168.2.1 : un serveur (dans la partie comptabilité) effectuant des transferts programmés avec l'extérieur
La table qui découle de cette politique est alors la suivante :
table <internet_autorise> { 192.168.0.0/16 !192.168.2.0/24 192.168.2.1 }
|
Et les règles semblables à celles-ci :
int_if = "sis0"
tcpflags = "flags S/SFRA"
block all
pass in quick on $int_if inet proto tcp from <internet_autorise> \
to any port { http https ... } $tcpflags keep state
|
La correspondance se fera sur l'adresse la plus spécifique. Examinons
quelques cas :
- 192.168.3.63 : la correspondance sera établie sur l'entrée 192.168.0.0/16 de la table, le paquet sera autorisé à passer
- 192.168.2.47 : aucune correspondance car toute la tranche 192.168.2.X est exclue à l'aide de la négation, le paquet sera bloqué
- 192.168.2.1 : la correspondance aura lieu sur 192.168.2.1 - la négation du sous-réseau 192.168.2 sera ignoré car son poids est moins important que celui de l'adresse elle-même, le paquet passera
4.3. Les listes
Ces mêmes listes qui nous ont permis d'introduire les tables peuvent
parfaitement contenir une ou plusieurs tables comme éléments. Encore
mieux elles peuvent contenir à la fois des tables et des adresses. En
voici un exemple :
ext_if = "de0"
maison = "83.241.141.246"
table <partenaires> const { 73.184.228.146 24.1.3.149 186.220.104.61 88.240.211.27 }
table <collegues> { 142.80.125.240 131.254.222.237 }
pass in quick on $ext_if from { <partenaires> $maison <collegues> } to $ext_if
|
La règle pass pourrait très bien être écrite sous des formes moins
condensées équivalentes :
pass in quick on $ext_if from { <partenaires> <collegues> } to $ext_if
pass in quick on $ext_if from $maison to $ext_if
pass in quick on $ext_if from <partenaires> to $ext_if
pass in quick on $ext_if from <collegues> to $ext_if
pass in quick on $ext_if from $maison to $ext_if
|
5. Utilitaires autour des tables
5.1. expiretable : supprimer des entrées obsolètes à intervalle régulier
Ce petit programme permet de retirer toutes les entrées qui ont
passées plus d'un certain temps, que vous aurez fixé, au sein d'une
table.
Son exécution requiert les droits de lecture et d'écriture sur le
fichier spécial /dev/pf.
Installation :
-
FreeBSD, il ne nous sera pas nécessaire d'aller bien
loin puisque vous pouvez trouver expiretable parmi les
logiciels portés :
cd /usr/ports/security/expiretable
make install
|
-
Il en est de même pour OpenBSD :
cd /usr/ports/sysutils/expiretable
make install
|
-
En revanche il en est tout autrement sur NetBSD
puisque ce logiciel ne figure pas parmi les paquetages :
cd ~
ftp http://expiretable.fnord.se/expiretable-0.6.tar.gz
tar xzf expiretable-0.6.tar.gz
ftp http://julp.developpez.com/bsd/packet-filter/tables/fichiers/netbsd-expiretable-0.6.patch
patch < netbsd-expiretable-0.6.patch
cd expiretable-0.6
make
make install
|
Options de lancement :
- -a ancre : spécifie l'ancre contenant la table (privée).
- -d : lancement en tâche de fond. Implique l'option -p.
- -n : indique seulement les adresses qui vont être effacées sur la sortie standard mais ne procède pas à cette suppression.
- -p : modifie le comportement normal du programme qui, par défaut, est de quitter. Il s'exécute ainsi périodiquement (fonction de la durée fournie).
- -t durée : définit la durée de validité maximale d'une entrée dans cette table. Celle-ci peut être exprimée en secondes ou à l'aide de suffixes d (jour), h (heure), m (minute) ou s (seconde).
- -v : sortie verbeuse. Doublez la pour obtenir un maximum d'informations.
Exemple d'utilisation : supprimer régulièrement (exécution en
tâche de fond) toutes les entrées datant de plus d'une heure de la
table utilisateurs_authpf.
expiretable -d -p -t 1h utilisateurs_authpf
|
6. Conclusion
6.1. Epilogue
Toutes les particularités liées de façon directe ou non aux tables
ont été abordées par le présent article. Il ne vous reste qu'à mettre
en pratique ces connaissances au sein de vos différentes règles et
pourquoi pas rédiger des scripts pour profiter de toute leur
dynamicité.
Liens Developpez :
6.2. Remerciements
J'adresse mes plus vifs remerciements à mon relecteur
Biglo.
Sans oublier les membres de la section Linux pour leurs conseils
avisés et leur soutien (
ovh et
Biglo).


Copyright © 2007 julp. Aucune reproduction, même partielle, ne peut être faite
de ce site et de l'ensemble de son contenu : textes, documents, images, etc
sans l'autorisation expresse de l'auteur.
Sinon vous encourez selon la loi jusqu'à 3 ans de prison et jusqu'à 300 000 E
de dommages et intérêts.
Cette page est déposée à la
SACD.