Si vous êtes comme moi, vous aimez bien savoir précisément ce que votre firewall (aussi appelé pare-feu par les anglophobes, ou coupe-feu par l'équipe de traduction de Leopard) laisse ou ne laisse pas passer, et vous aimez tout autant pouvoir lui spécifier très clairement vos choix. Or, il se trouve que seule une partie des capacités de firewalling de MacOS 10.5 (a.k.a. Leopard) sont directement accessibles par les menus. Voyons donc comment ces capacités se découpent, et comment les exploiter simplement dans leur ensemble.

Les deux firewalls de Leopard

IPFW

Le firewall IPFW, issu du projet FreeBSD, est un firewall traditionnel, intervenant sur les datagrammes IP avant qu'ils ne remontent vers les couches applicatives, en fonction notamment du contenu des en-têtes IP, TCP ou UDP. Ces fonctionnalités ne sont accessibles que par le biais de la commande ipfw, pas dans l'environnement graphique.

Application Layer Firewall

Leopard intègre une nouveauté, que l'on appelle généralement un firewall applicatif. Il travaille au niveau des sockets, et est donc capable d'autoriser ou non des accès en fonction de l'application qui écoute sur un port donné. En pratique, il utilise un dictionnaire de signatures pour vérifier que le binaire de l'application est bien celui qui est autorisé : quand une nouvelle application est signalée au firewall comme autorisée, une signature est générée et ajoutée au dictionnaire. Le firewall applicatif est paramétrable dans les préférences système, dans la section sécurité, dans l'onglet coupe-feu.

Bien que très utile pour les néophytes, ce firewall présente plusieurs défauts notables (liste non exhaustive) :

  • la granularité offerte est très faible : on peut soit autoriser toutes les connexions entrantes, soit uniquement les "services essentiels" (d'après Apple, configd pour DHCP, mDNSResponder pour Bonjour, et racoon pour IPSec), avec en option l'ajout d'autres applications au choix de l'utilisateur
  • bien que cela ne soit pas indiqué, tout service écoutant sur le réseau et disposant des droits root sera autorisé à recevoir des paquets
  • il est impossible de bloquer les "services essentiels", ni de gérer d'autres protocoles que TCP ou UDP (à part les paquets ICMP echo-request, que l'on peut activé dans la partie "Avancé..." de la configuration du coupe-feu en activant le mode furtif)
  • il est impossible de travailler sur les paquets sortants
Notez que ce firewall travaille plus haut dans la pile réseau qu'IPFW : tout paquet bloqué par IPFW ne sera pas transmis plus loin, même si le firewall applicatif l'aurait autorisé.

Mise en oeuvre d'une protection maison

On vient de le voir, le firewall applicatif a ses limites, et, en utilisateur soucieux de protéger un peu mieux ma machine, j'ai défini un jeu de règles IPFW qui sont lancées automagiquement au démarrage de l'OS.

Créer un script de démarrage

Pour créer un script qui démarre avec l'OS sous Leopard, la solution que j'ai retenue est celle du launchd. Pour cela, il faut créer (avec les droits d'administration) un sous-répertoire dans /Library/StartupItems :

# mkdir -p /Library/StartupItems/Firewall

On y crée ensuite le fichier StartupParameters.plist avec le contenu suivant :

Description My Firewall Rules OrderPreference Late Provides Custom IPFW firewall rules RunAtLoad

Cela définit notre StartupItem comme étant à charger au démarrage de l'OS, et plutôt vers la fin du démarrage. Cela dit, pour l'instant, il ne fait strictement rien! Il nous reste à créer un script exécutable, portant le même nom que le répertoire, qui contiendra nos commandes :

# touch /Library/StartupItems/Firewall/Firewall # chmod 755 /Library/StartupItems/Firewall/Firewall

Ce script va réaliser la mise en oeuvre des règles IPFW qui nous intéressent. Sa structure globale est la suivante :

#!/bin/sh . /etc/rc.common # d'éventuelles définitions de variables StartService() { # le code exécuté au lancement } StopService() { # le code exécuté à l'arrêt } RunService "$1"

Un exemple de script IPFW

Ce billet n'ayant pas vocation a être une introduction à IPFW, je me contenterai de présenter un exemple de ce que j'utilise sur mon portable. Pour plus d'informations, on peut se référer à la page man d'IPFW, la page IPFW sur le site de FreeBSD déjà mentionnée plus haut, ou poser ses questions à l'ami Google (ou tout autre moteur de recherche de votre choix, d'ailleurs).

Voici donc le script, très basique, que j'ai mis en place. Pas de filtrage des connexions sortantes, et un filtrage assez massif des connexions entrantes, mis à part quelques protocoles servant à découvrir ou utiliser des services disponibles sur le réseau du coin (partages CIFS, services Bonjour, DHCP), et bien sûr les connexions déjà établies, via les fonctionnalités de stateful filtering d'IPFW.

#!/bin/sh . /etc/rc.common IPFW="/sbin/ipfw -q" StartService() { ConsoleMessage "Lancement des regles IPFW maison" # vidage de la table de règles ${IPFW} -f flush # ouverture locale uniquement de l'interface loopback ${IPFW} add 1000 allow all from any to any via lo0 ${IPFW} add 1001 deny all from any to 127.0.0.0/8 # autorisation des paquets appartenant à des connexions identifiées # (pour le stateful filtering) ${IPFW} add 2000 check-state # autorisation des connexions TCP sortantes (paquet SYN) et # mise en place de l'identification de la connexion (stateful) ${IPFW} add 2001 allow tcp from me to any out setup keep-state # autorisation des connexions UDP sortantes et mise # en place de l'identification de la connexion (stateful) ${IPFW} add 2003 allow udp from me to any out keep-state # autorisation des connexions ICMP sortantes et mise # en place de l'identification de la connexion (stateful) ${IPFW} add 2004 allow icmp from me to any out keep-state # autorisation de certains protocoles de résolution ${IPFW} add 04000 allow udp from any to any 5353 in # mDNS (Bonjour) ${IPFW} add 04010 allow udp from any to any 427 in # svrloc (Bonjour) ${IPFW} add 04020 allow udp from any to any 68 in # DHCP ${IPFW} add 04030 allow udp from any to any 137 in # Samba (NetBIOS) # toi, t'as des baskets, tu rentres pas! # (si on n'a pas encore matché une règle, c'est que c'est non) ${IPFW} add 9999 deny all from any to any } StopService() { ConsoleMessage "Stopping custom IPFW rules" ${IPFW} -f flush } RunService "$1"