Sous macOS, un interrupteur d’urgence ne vaut que ce que vaut la politique du filtre de paquets derrière lui, et pf est la pièce qui applique cette politique. La politique doit faire deux choses opposées à la fois : laisser le client atteindre son serveur pour qu’un tunnel puisse exister, et empêcher tout le reste de sortir directement. La plupart des jeux de règles publiés réussissent la seconde et oublient la première, si bien que la machine perd la connectivité au lieu de gagner une protection. La version qui fonctionne nomme le point de terminaison, le résolveur et le tunnel, et rien de plus.

Ce qu’un filtre de paquets peut décider, et ce qu’il ne peut pas

pf examine les interfaces, les adresses, les numéros de protocole et les ports, et s’arrête là. Il n’apprend jamais quel processus a ouvert une socket, donc une autorisation écrite pour un programme devient une autorisation pour tout programme capable d’atteindre le même port. C’est ainsi qu’une exception UDP généreuse retransforme un interrupteur d’urgence en réseau ordinaire. Le blocage conscient de l’application appartient au client ou à une extension système, pas à ce fichier.

Le filtre ne crée pas de routes non plus. Quand le client tombe, la route par défaut peut encore pointer vers un tunnel mort, et pf transforme cela en paquets bloqués plutôt qu’en un retour silencieux au Wi-Fi. Le blocage est le résultat voulu, pas un dysfonctionnement qu’il faudrait contourner.

L’ordre décide du résultat plus souvent que le texte de la règle. Sans quick, pf continue d’évaluer après une correspondance et la dernière règle correspondante l’emporte, si bien qu’un passage large près du bas annule les exceptions soignées du dessus. Marquer les passages avec quick rend la première correspondance définitive, ce qu’un auteur de politique veut généralement dire.

Lisez d’abord les faits sur l’interface et le point de terminaison

Les interfaces sans fil ne sont pas numérotées de la même façon sur chaque Mac, et le périphérique de tunnel reçoit un numéro qui peut changer entre deux démarrages. Une règle qui nomme un périphérique inexistant échoue à l’analyse, ce qui est bruyant et facile à repérer, tandis qu’une règle qui nomme le mauvais périphérique existant s’analyse proprement et autorise un trafic que vous vouliez arrêter.

Recueillez les faits pendant que le client est connecté, car le périphérique de tunnel n’existe qu’à ce moment-là. Il vous faut l’interface qui porte la route par défaut, le périphérique créé par le client, ainsi que l’adresse et le port du serveur qu’il appelle. C’est cette dernière valeur que la plupart des jeux de règles omettent, et c’est pourquoi un jeu recopié bloque son propre tunnel.

route -n get default
ifconfig -l
scutil --nc list

La première commande nomme l’interface qui l’emporte actuellement, la deuxième liste chaque périphérique connu du noyau, et la troisième montre les configurations VPN que le système peut lancer. Notez ces réponses, puis lisez l’adresse et le port du serveur dans la configuration propre du client.

Écrivez le jeu de règles autour du chemin du tunnel

Le fichier ci-dessous refuse par défaut et réadmet cinq choses : la boucle locale, le périphérique de tunnel, le chemin du client vers son serveur, le résolveur utilisé avant que le tunnel existe, et les deux échanges qui gardent une liaison sans fil utilisable. Les noms d’interface et l’adresse du serveur viennent de votre machine, alors remplacez les valeurs que vous avez confirmées. Le numéro de tunnel ne correspondra pas non plus au vôtre.

set skip on lo0
block all
pass out quick on utun0
# maintain this line when the server address changes
pass out quick on en1 inet proto udp to 203.0.113.10 port 1194
# only the resolver the client uses before the tunnel exists
pass out quick on en1 inet proto udp to 192.0.2.53 port 53
pass out quick on en1 inet proto udp from any port 68 to any port 67
pass out quick on en1 inet proto udp to 224.0.0.251 port 5353

La ligne DHCP existe parce qu’un Mac demande une adresse avant d’en avoir une, si bien que la requête quitte le port 68 vers le port 67 sans adresse source utilisable. La ligne multicast permet au système de juger si la liaison sans fil vaut la peine d’être utilisée, et la retirer produit une machine qui signale aucune connexion sur un réseau qui fonctionne. La ligne du point de terminaison est ce qui permet à OpenVPN d’atteindre son serveur tout court ; sans elle, block all jette la poignée de main extérieure et le tunnel ne monte jamais. La ligne du résolveur est plus étroite qu’une autorisation DNS générale, car elle nomme une adresse et un port au lieu de chaque requête que fait la machine.

Ces exceptions de point de terminaison et de résolveur sont les deux qui doivent exister ; tout le reste reste bloqué. Si le client appelle une adresse littérale, supprimez la ligne du résolveur et gardez celle du point de terminaison. Si l’adresse du serveur change, mettez cette ligne à jour avant la connexion suivante, ou remplissez une table pf depuis le crochet propre au client et faites correspondre la table à la place.

Coupez le tunnel, puis redémarrez

Analysez avant de charger, car une erreur de syntaxe coûte peu lors d’un essai à blanc et cher en session distante. Chargez ensuite le fichier, activez le filtre s’il était désactivé, et relisez ce qui est réellement en vigueur. La relecture n’est pas une cérémonie : une autre ancre peut ajouter des règles que vous n’avez pas écrites, alors comparez l’ensemble imprimé avec votre fichier.

sudo pfctl -n -v -f /etc/pf.conf
sudo pfctl -f /etc/pf.conf
sudo pfctl -e
sudo pfctl -s rules
sudo pfctl -s Anchors
sudo pfctl -a <anchor> -s rules
sudo pfctl -s info

Une fois les règles chargées, coupez le tunnel et essayez de charger une page. Le trafic ordinaire doit s’arrêter, et il doit rester arrêté après que la machine s’est endormie puis réveillée, car c’est à cette transition que l’état périmé revient. Reconnectez-vous et confirmez que le trafic reprend sans toucher au jeu de règles. Si un navigateur fonctionne encore tunnel coupé, une règle est plus large que vous ne le pensiez, et la relecture la nomme généralement.

Le redémarrage est la partie que les gens sautent. macOS lit /etc/pf.conf au démarrage, mais lire le fichier n’est pas la même chose qu’activer le filtre, donc un jeu de règles qui fonctionne maintenant peut être inactif après un redémarrage. Lancez ensuite sudo pfctl -s info et confirmez que la ligne d’état indique le filtre comme activé.

Gardez la sortie et le chemin de redémarrage honnêtes

Un jeu de règles qui bloque tout peut aussi vous verrouiller hors d’une machine que vous administrez à distance. Modifiez depuis une session locale quand vous pouvez, gardez le fichier précédent à côté du nouveau, et connaissez le retour arrière avant d’en avoir besoin. Les commandes ci-dessous désactivent le filtre et rechargent la copie que vous avez gardée, et c’est la séquence à lancer depuis une console, pas par le tunnel que vous venez de couper. Un retour en cinq secondes bat une règle astucieuse à chaque fois.

sudo pfctl -d
sudo pfctl -f /etc/pf.conf.previous
sudo pfctl -s rules

La persistance demande un démon de lancement qui exécute pfctl -e et charge votre fichier au démarrage. Traitez-le comme toute autre tâche privilégiée : chemins absolus, un journal que vous pouvez relire ensuite, et un test sur une machine que vous pouvez atteindre physiquement. Un démon qui échoue en silence laisse la machine sans protection et d’apparence normale.

Documentez les lacunes, en commençant par un second client VPN qui crée son propre périphérique de tunnel. Vos règles nomment une interface de tunnel, donc une seconde reste bloquée ou est capturée par une règle large ajoutée plus tard. Les interfaces de partage de connexion et de point d’accès n’apparaissent que parfois et méritent la même note. Aucune de ces lacunes n’est une raison de sauter la politique ; ce sont les conditions dans lesquelles elle tient encore.

Articles similaires