Tutorial

Comment configurer Packet Filter (PF) sous FreeBSD 12.1

SecurityFirewallFreeBSD

L'auteur a choisi le COVID-19 Relief Fund pour recevoir un don dans le cadre du programme Write for DOnations.

Introduction

Le pare-feu est sans doute l'une des plus importantes lignes de défense contre les cyberattaques. La capacité de configurer un pare-feu à partir de zéro est une compétence qui donne à l'administrateur le pouvoir de prendre le contrôle de ses réseaux.

Packet Filter (PF) est une application de pare-feu renommée qui est maintenue en amont par le projet OpenBSD axé sur la sécurité. Il s'exprime plus précisément comme un outil de filtrage de paquets, d'où son nom, et il est connu pour sa syntaxe simple, sa facilité d'utilisation et ses nombreuses fonctionnalités. PF est un pare-feu à état par défaut, qui stocke des informations sur les connexions dans une table d'état accessible à des fins d'analyse. PF fait partie du système de base de FreeBSD et est soutenu par une forte communauté de développeurs. Bien qu'il existe des différences entre les versions FreeBSD et OpenBSD de PF concernant les architectures de noyau, en général leur syntaxe est similaire. En fonction de leur complexité, les règlements communs peuvent être modifiés pour fonctionner sur l'une ou l'autre distribution avec relativement peu d'efforts.

Dans ce tutoriel, vous allez construire un pare-feu à partir de zéro sur un serveur FreeBSD 12.1 avec PF. Vous concevrez un ensemble de règles de base qui pourra servir de modèle pour de futurs projets. Vous explorerez également certaines des fonctionnalités avancées de PF telles que l'hygiène des paquets, la prévention de la force brute, la surveillance et l'enregistrement, ainsi que d'autres outils tiers.

Conditions préalables

Avant de débuter ce tutoriel, vous aurez besoin des éléments suivants :

  • Un serveur 1G FreeBSD 12.1 (soit ZFS ou UFS). Vous pouvez utiliser notre tutoriel Comment démarrer avec FreeBSD pour configurer votre serveur selon vos préférences.
  • FreeBSD n'a pas de pare-feu activé par défaut - la personnalisation est une caractéristique de l'éthique de FreeBSD. Par conséquent, lorsque vous lancez votre serveur pour la première fois, vous avez besoin d'une protection temporaire pendant que PF est en cours de configuration. Si vous utilisez DigitalOcean, vous pouvez activer votre pare-feu dans le nuage immédiatement après avoir fait tourner le serveur. Reportez-vous au tutoriel Démarrage rapide du pare-feu de DigitalOcean pour obtenir des instructions sur la configuration d'un pare-feu en nuage. Si vous utilisez un autre fournisseur de services en ligne, déterminez le chemin le plus rapide vers une protection immédiate avant de commencer. Quelle que soit la méthode que vous choisissez, votre pare-feu temporaire doit autoriser uniquement le trafic SSH entrant et peut autoriser tous les types de trafic sortant.

Étape 1 - Établissement de votre règlement préliminaire

Vous commencerez ce tutoriel en rédigeant un ensemble de règles préliminaires qui assurent une protection de base et l'accès aux services essentiels sur l'internet. À ce stade, vous avez un serveur FreeBSD 12.1 en cours d'exécution avec un pare-feu en nuage actif.

Il existe deux approches pour construire un pare-feu : le refus par défaut et le permis par défaut. L'approche de refus par défaut bloque tout le trafic, et n'autorise que ce qui est spécifié dans une règle. L'approche du permis par défaut fait exactement le contraire : il passe tout le trafic, et ne bloque que ce qui est spécifié dans une règle. Vous utiliserez l'approche de refus par défaut.

Les règlements PF sont écrits dans un fichier de configuration nommé /etc/pf.conf, qui est également son emplacement par défaut. Il est possible de stocker ce fichier ailleurs, à condition que cela soit spécifié dans le Fichier de configuration /etc/rc.conf. Dans ce tutoriel, vous utiliserez l'emplacement par défaut.

Connectez-vous à votre serveur avec votre utilisateur non racine :

  • ssh freebsd@your_server_ip

Créez ensuite votre fichier /etc/pf.conf :

  • sudo vi /etc/pf.conf

Remarque : Si vous souhaitez voir l'ensemble complet du règlement de base à un moment quelconque du tutoriel, vous pouvez vous référer aux exemples de l'étape 4 ou de l'étape 8.

PF filtre les paquets en fonction de trois actions principales : bloquer, passer et faire correspondre. Lorsqu'elles sont combinées avec d'autres options, elles forment des règles. Une mesure est prise lorsqu'un paquet répond aux critères spécifiés dans une règle. Comme vous pouvez vous en douter, les règles de passage et de blocage passeront et bloqueront le trafic. Une règle de correspondance effectue une action sur un paquet lorsqu'elle trouve un critère de correspondance, mais ne le passe pas ou ne le bloque pas. Par exemple, vous pouvez effectuer une traduction d'adresse réseau (NAT) sur un paquet correspondant sans le transmettre ou le bloquer, et il restera là jusqu'à ce que vous lui demandiez de faire quelque chose dans une autre règle, comme le router vers une autre machine ou une autre passerelle.

Ensuite, ajoutez la première règle à votre fichier /etc/pf.conf :

/etc/pf.conf
block all

Cette règle bloque toute forme de trafic dans toutes les directions. Comme il ne spécifie pas de direction, il indique par défaut les entrées et les sorties. Cette règle est légitime pour un poste de travail local qui doit être isolé du monde, mais elle est largement inapplicable et ne fonctionnera pas sur un serveur distant car elle ne permet pas le trafic SSH. En fait, si vous aviez activé PF, vous vous seriez enfermé hors du serveur.

Révisez votre fichier /etc/pf.conf pour autoriser le trafic SSH avec la ligne surlignée suivante :

/etc/pf.conf
block all
pass in proto tcp to port 22

Remarque : vous pouvez également utiliser le nom du protocole :

/etc/pf.conf
block all
pass in proto tcp to port ssh

Par souci de cohérence, nous utiliserons les numéros de port, à moins qu'il n'y ait une raison valable de ne pas le faire. Vous trouverez une liste détaillée des protocoles et de leurs numéros de port respectifs dans le fichier /etc/services, que nous vous invitons à consulter.

PF traite les règles de manière séquentielle de haut en bas, donc votre règlement actuel bloque initialement tout le trafic, mais le passe ensuite si les critères de la ligne suivante sont respectés, ce qui dans ce cas est le trafic SSH.

Vous pouvez maintenant utiliser le SSH sur votre serveur, mais vous continuez à bloquer toute forme de trafic sortant. Cela pose problème car vous ne pouvez pas accéder à des services essentiels depuis l'internet pour installer des paquets, mettre à jour vos paramètres de temps, etc.

Pour y remédier, ajoutez la règle surlignée suivante à la fin de votre fichier /etc/pf.conf :

/etc/pf.conf
block all
pass in proto tcp to port { 22 }
pass out proto { tcp udp } to port { 22 53 80 123 443 }

Votre ensemble de règles autorise désormais le trafic sortant SSH, DNS, HTTP, NTP et HTTPS, ainsi que le blocage de tout trafic entrant (à l'exception du SSH). Vous placez les numéros de port et les protocoles entre parenthèses, ce qui forme une liste dans la syntaxe PF, vous permettant d'ajouter d'autres numéros de port si nécessaire. Vous ajoutez également une règle de distribution pour le protocole UDP sur les ports 53 et 123 car le DNS et le NTP basculent souvent entre les protocoles TCP et UDP.   Vous avez presque terminé l'ensemble des règles préliminaires, et il vous suffit d'ajouter quelques règles pour obtenir une fonctionnalité de base.

Complétez le règlement préliminaire avec les règles mises en évidence :

Preliminary Ruleset /etc/pf.conf
set skip on lo0
block all
pass in proto tcp to port { 22 }
pass out proto { tcp udp } to port { 22 53 80 123 443 }
pass out inet proto icmp icmp-type { echoreq }

Enregistrez et quittez le fichier.

Vous créez une règle de saut définie pour le dispositif de bouclage car il n'a pas besoin de filtrer le trafic et risquerait d'amener votre serveur à un crawl. Vous ajoutez une règle pass out inet pour le protocole ICMP, qui vous permet d'utiliser l'utilitaire ping(8) pour le dépannage. L'option inet représente la famille d'adresses IPv4.

L'ICMP est un protocole de messagerie polyvalent utilisé par les appareils en réseau pour divers types de communication. L'utilitaire ping, par exemple, utilise un type de message appelé echo request, que vous avez ajouté à votre liste icmp_type. Par mesure de précaution, vous n'autorisez que les types de messages dont vous avez besoin pour éviter que des appareils indésirables n'entrent en contact avec votre serveur. À mesure que vos besoins augmentent, vous pouvez ajouter d'autres types de messages à votre liste.

Vous disposez désormais d'un règlement de fonctionnement qui fournit des fonctionnalités de base à la plupart des machines. Dans la section suivante, nous allons confirmer que tout fonctionne correctement en activant PF et en testant votre règlement préliminaire.

Étape 2 - Test de votre règlement préliminaire

Au cours de cette étape, vous testerez votre règlement préliminaire et ferez la transition de votre pare-feu dans le nuage à votre pare-feu PF, permettant à PF de prendre complètement le relais. Vous activerez votre règlement avec l'utilitaire pfctl, qui est l'outil de ligne de commande intégré de PF, et la principale méthode d'interface avec PF. 

Les règlements de la PF ne sont rien d'autre que des fichiers texte, ce qui signifie que le chargement de nouveaux règlement n'implique aucune procédure délicate. Vous pouvez charger un nouveau règlement, et l'ancien est parti. Il est rarement, voire jamais, nécessaire de vider un règlement existant.

FreeBSD utilise un réseau de scripts shell connu sous le nom de système rc pour gérer la façon dont les services sont démarrés au démarrage ; nous spécifions ces services dans divers fichiers de configuration rc. Pour les services globaux tels que PF, vous utilisez le fichier /etc/rc.conf. Comme les fichiers rc sont essentiels au bien-être d'un système FreeBSD, ils ne doivent pas être édités directement. A la place, FreeBSD fournit un utilitaire en ligne de commande connu sous le nom de sysrc, conçu pour vous aider à modifier ces fichiers en toute sécurité.

Activons PF en utilisant l'utilitaire de ligne de commande sysrc :

  • sudo sysrc pf_enable="YES"
  • sudo sysrc pflog_enable="YES"

Vérifiez ces changements en imprimant le contenu de votre fichier /etc/rc.conf :

  • sudo cat /etc/rc.conf

Vous verrez le résultat suivant :

Output
pf_enable="YES" pflog_enable="YES"

Vous activez également le service pflog, qui, à son tour, active le démon pflogd pour se connecter à PF. Vous travaillerez sur la connexion dans une étape ultérieure.

Vous spécifiez deux services globaux dans votre fichier /etc/rc.conf, mais ils ne s'initialiseront pas tant que vous n'aurez pas redémarré le serveur ou que vous ne les aurez pas démarrés manuellement. Redémarrez le serveur afin de pouvoir également tester votre accès SSH.

Démarrez PF en redémarrant le serveur :

  • sudo reboot

La connexion sera interrompue. Donnez-lui quelques minutes pour mettre à jour.

Maintenant, SSH est de retour sur le serveur :

  • ssh freebsd@your_server_ip

Bien que vous ayez initialisé vos services PF, vous n'avez pas encore chargé votre règlement /etc/pf.conf, ce qui signifie que votre pare-feu n'est pas encore actif.

Chargez le règlement avec pfctl :

  • sudo pfctl -f /etc/pf.conf

S'il n'y a pas d'erreurs ou de messages, cela signifie que votre règlement ne contient pas d'erreurs et que le pare-feu est actif.

Maintenant que PF fonctionne, vous pouvez détacher votre serveur de votre pare-feu dans le nuage. Cela peut se faire dans le panneau de contrôle de votre compte DigitalOcean en retirant votre Droplet du portail de votre pare-feu dans le nuage. Si vous utilisez un autre fournisseur de services en nuage, assurez-vous que ce que vous utilisez pour la protection temporaire est désactivé. Faire fonctionner deux pare-feu différents sur un serveur posera presque certainement des problèmes.

Pour une bonne mesure, redémarrez votre serveur :

  • sudo reboot

Après quelques minutes, SSH revient sur votre serveur :

  • ssh freebsd@your_server_ip

PF est maintenant votre pare-feu. Vous pouvez vous assurer qu'il fonctionne en accédant à certaines données avec l'utilitaire pfctl.

Voyons quelques statistiques et compteurs avec pfctl -si :

  • sudo pfctl -si

Vous passez devant les drapeaux -si, qui signifient show info. C'est l'une des nombreuses combinaisons de paramètres de filtrage que vous pouvez utiliser avec pfctl pour analyser les données concernant l'activité de votre pare-feu.

Vous verrez les données tabulaires suivantes (les valeurs varient d'une machine à l'autre) :

Output
Status: Enabled for 0 days 00:01:53 Debug: Urgent State Table Total Rate current entries 5 searches 144 1.3/s inserts 11 0.1/s removals 6 0.1/s Counters match 23 0.2/s bad-offset 0 0.0/s fragment 0 0.0/s short 0 0.0/s normalize 0 0.0/s memory 0 0.0/s bad-timestamp 0 0.0/s congestion 0 0.0/s ip-option 0 0.0/s proto-cksum 0 0.0/s state-insert 0 0.0/s state-limit 0 0.0/s src-limit 0 0.0/s synproxy 0 0.0/s map-failed 0 0.0/s

Comme vous venez d'activer votre ensemble de règles, vous ne verrez pas encore beaucoup d'informations. Cependant, ce résultat montre que PF a déjà enregistré 23 règles correspondantes, ce qui signifie que les critères de votre ensemble de règles ont été adaptés 23 fois. La sortie confirme également que votre pare-feu fonctionne.

Votre règlement permet également au trafic sortant d'accéder à certains services essentiels de l'internet, y compris l'utilitaire ping.

Vérifions la connectivité internet et le service DNS avec un ping contre google.com : 

  • ping -c 3 google.com

Depuis que vous avez lancé le drapeau du compte-c 3, vous verrez trois réponses de connexion réussies :

Output
PING google.com (172.217.0.46): 56 data bytes 64 bytes from 172.217.0.46: icmp_seq=0 ttl=56 time=2.088 ms 64 bytes from 172.217.0.46: icmp_seq=1 ttl=56 time=1.469 ms 64 bytes from 172.217.0.46: icmp_seq=2 ttl=56 time=1.466 ms --- google.com ping statistics --- 3 packets transmitted, 3 packets received, 0.0% packet loss round-trip min/avg/max/stddev = 1.466/1.674/2.088/0.293 ms

Assurez-vous que vous pouvez accéder au dépôt pkgs avec la commande suivante :

  • sudo pkg upgrade

S'il y a des paquets à mettre à niveau, allez-y, mettez-les à niveau.

Si ces deux services fonctionnent, cela signifie que votre pare-feu fonctionne et que vous pouvez maintenant continuer. Bien que votre ensemble de règles préliminaires offre une protection et des fonctionnalités, il reste un ensemble de règles élémentaires et pourrait bénéficier de quelques améliorations. Dans les autres sections, vous compléterez votre règlement de base et utiliserez certaines des fonctionnalités avancées de PF.

Étape 3 - Comment compléter votre règlement de base

Au cours de cette étape, vous vous appuierez sur l'ensemble de règles préliminaires pour compléter votre règlement de base. Vous réorganiserez certaines de vos règles et travaillerez avec des concepts plus avancés.

Incorporation de macros et de tableaux

Dans votre ensemble de règles préliminaires, vous avez codé en dur tous vos paramètres dans chaque règle, c'est-à-dire les numéros de port qui composent les listes. Cela peut devenir ingérable à l'avenir, en fonction de la nature de vos réseaux. Pour des raisons d'organisation, PF comprend des macros, des listes et des tableaux. Vous avez déjà inclus des listes directement dans vos règles, mais vous pouvez également les séparer de vos règles et les affecter à une variable à l'aide de macros.

Ouvrez votre fichier pour transférer certains de vos paramètres dans des macros :

  • sudo vi /etc/pf.conf

Ajoutez maintenant le contenu suivant tout en haut du règlement :

/etc/pf.conf
vtnet0 = "vtnet0"
icmp_types = "{ echoreq }"
. . .

Modifiez vos anciennes règles SSH et ICMP avec vos nouvelles variables :

/etc/pf.conf
. . .
pass in on $vtnet0 proto tcp to port { 22 }
. . .
pass inet proto icmp icmp-type $icmp_types
. . .

Vos anciennes règles SSH et ICMP utilisent désormais des macros. Les noms des variables sont désignés par la syntaxe du signe du dollar de PF. Vous attribuez votre interface vtnet0 à une variable portant le même nom juste comme une formalité, ce qui vous donne la possibilité de la renommer à l'avenir si nécessaire. D'autres noms de variables courantes pour les interfaces publiques comprennent $pub_if ou $ext_if.

Ensuite, vous instaurerez une table, qui est similaire à une macro, mais conçu pour contenir des groupes d'adresses IP. Créons une table pour les adresses IP non routables, qui jouent souvent un rôle dans les attaques par déni de service (DOS). Vous pouvez utiliser les adresses IP spécifiées dans la RFC6890, qui définit les registres d'adresses IP à usage spécifique. Votre serveur ne devrait pas envoyer ou recevoir de paquets à destination ou en provenance de ces adresses via l'interface publique.

Créez cette table en ajoutant le contenu suivant directement sous la macro icmp_types :

/etc/pf.conf
. . .
table <rfc6890> { 0.0.0.0/8 10.0.0.0/8 100.64.0.0/10 127.0.0.0/8 169.254.0.0/16          \
                  172.16.0.0/12 192.0.0.0/24 192.0.0.0/29 192.0.2.0/24 192.88.99.0/24    \
                  192.168.0.0/16 198.18.0.0/15 198.51.100.0/24 203.0.113.0/24            \
                  240.0.0.0/4 255.255.255.255/32 }
. . .

Ajoutez maintenant vos règles pour la table <rfc6890> sous la règle set skip on lo0 :

/etc/pf.conf
. . .
set skip on lo0
block in quick on egress from <rfc6890>
block return out quick on egress to <rfc6890>
. . .

Vous introduisez ici l'option de retour, qui complète votre règle de blocage. Les paquets sont alors déposés et un message RST est envoyé à l'hôte qui a essayé d'établir ces connexions, ce qui est utile pour analyser l'activité de l'hôte.   Ensuite, vous ajoutez le mot-clé egress, qui trouve automatiquement la (les) route(s) par défaut sur une (des) interface(s) donnée(s). C'est généralement une méthode plus propre pour trouver des itinéraires par défaut, surtout avec des réseaux complexes. Le mot-clé quick exécute les règles immédiatement sans tenir compte du reste de l'ensemble des règles. Par exemple, si un paquet avec une adresse IP illogique tente de se connecter au serveur, vous voulez immédiatement interrompre la connexion et vous n'avez aucune raison de faire passer ce paquet par le reste du règlement.

Protéger vos ports SSH

Comme votre port SSH est ouvert au public, il est sujet à l'exploitation. L'un des signes d'alerte les plus évidents d'un agresseur est la quantité massive de tentatives de connexion. Par exemple, si la même adresse IP essaie de se connecter à votre serveur dix fois en une seconde, vous pouvez supposer que cela n'a pas été fait par des mains humaines, mais par un logiciel informatique qui a essayé de craquer votre mot de passe de connexion. Ces types d'exploits systématiques sont souvent appelés “attaques par force brute” et réussissent généralement si le serveur a des mots de passe faibles.

Avertissement : Nous recommandons vivement l'utilisation d'une authentification par clé publique sur tous les serveurs. Consultez le tutoriel de DigitalOcean sur l'authentification par clé.

PF dispose de fonctions intégrées pour gérer la force brute et d'autres attaques similaires. Avec PF, vous pouvez limiter le nombre de tentatives de connexion simultanées autorisées par un seul hôte. Si un hôte dépasse ces limites, la connexion sera interrompue et il sera banni du serveur. Pour ce faire, vous utiliserez le mécanisme de surcharge de PF qui gère une table d'adresses IP interdites.

Modifiez votre ancienne règle SSH pour limiter le nombre de connexions simultanées à partir d'un seul hôte comme suit :

/etc/pf.conf
. . .
pass in on $vtnet0 proto tcp to port { 22 } \
    keep state (max-src-conn 15, max-src-conn-rate 3/1, \
        overload <bruteforce> flush global)
. . .

Vous ajoutez l'option keep state qui vous permet de définir les critères d'état pour la table de surcharge. Vous passez le paramètre max-src-conn pour spécifier le nombre de connexions simultanées autorisées à partir d'un seul hôte par seconde, et le paramètre max-src-conn-rate pour spécifier le nombre de nouvelles connexions autorisées à partir d'un seul hôte par seconde. Vous spécifiez 15 connexions pour max-src-conn, et 3 connexions pour max-src-conn-rate. Si ces limites sont dépassées par un hôte, le mécanisme de surcharge ajoute l'IP source à la table <bruteforce>, ce qui les interdit au serveur. Enfin, l'option flush global supprime immédiatement la connexion.

Vous avez défini une table de surcharge dans votre règle SSH, mais vous n'avez pas déclaré cette table dans votre règlement.

Ajoutez la table <bruteforce> sous la macro icmp_types :

/etc/pf.conf
. . .
icmp_types = "{ echoreq }"
table <bruteforce> persist
. . .

Le mot-clé persist permet à une table vide d'exister dans le règlement. Sans lui, PF se plaindra qu'il n'y a pas d'adresses IP dans la table.

Ces mesures garantissent que votre port SSH est protégé par un puissant mécanisme de sécurité. PF vous permet de configurer des solutions rapides pour vous protéger contre des formes d'exploitation désastreuses. Dans les sections suivantes, vous allez prendre des mesures pour nettoyer les paquets lorsqu'ils arrivent sur votre serveur.

Assainir votre trafic

Remarque : Les sections suivantes décrivent les principes fondamentaux de la suite de protocoles TCP/IP.   Si vous envisagez de créer des applications web ou des réseaux, il est dans votre intérêt de maîtriser ces concepts. Consultez le tutoriel Introduction à la terminologie, aux interfaces et aux protocoles de réseau de DigitalOcean.

En raison de la complexité de la suite de protocoles TCP/IP et de la persistance d'acteurs malveillants, les paquets arrivent souvent avec des divergences et des ambiguïtés telles que des fragments d'IP qui se chevauchent, de fausses adresses IP, etc. Il est impératif que vous assainissiez votre trafic avant qu'il n'entre dans le système. Le terme technique pour ce processus est la normalisation.

Lorsque les données circulent sur Internet, elles sont généralement divisées en petits fragments à leur source pour tenir compte des paramètres de transmission de l'hôte cible où elles sont réassemblées en paquets complets. Malheureusement, un intrus peut détourner ce processus de plusieurs façons qui vont au-delà de la portée de ce tutoriel. Cependant, avec PF, vous pouvez gérer la fragmentation avec une seule règle. PF comprend un mot-clé scrub qui normalise les paquets.

Ajoutez le mot-clé scrub précédant directement votre règle block all : 

/etc/pf.conf
. . .
set skip on lo0
scrub in all fragment reassemble max-mss 1440
block all
. . .

Cette règle s'applique à l'épuration de tout le trafic entrant. Vous incluez l'option scrub in all fragment reassemble qui empêche les fragments d'entrer dans le système. Au lieu de cela, elles sont mises en cache en mémoire jusqu'à ce qu'elles soient réassemblées en paquets complets, ce qui signifie que vos règles de filtrage n'auront à faire face qu'à des paquets uniformes. Vous incluez également l'option max-mss 1440, qui représente la taille maximale du segment des paquets TCP réassemblés, également connue sous le nom de charge utile.   Vous spécifiez une valeur de 1440 octets, ce qui établit un équilibre entre la taille et les performances, laissant beaucoup de place pour les en-têtes.

Un autre aspect important de la fragmentation est un terme connu sous le nom d’unité de transmission maximale (MTU). Les protocoles TCP/IP permettent aux appareils de négocier la taille des paquets pour établir des connexions. L'hôte cible utilise les messages ICMP pour informer l'IP source de son MTU, un processus connu sous le nom de découverte de chemin MTU. Le type de message ICMP spécifique concerne la destination inaccessible. Vous activerez la découverte du chemin MTU en ajoutant le type de message unreach à votre liste icmp_types.

Vous utiliserez le MTU par défaut de votre serveur de 1500 octets qui peut être déterminé avec la commande ifconfig : 

  • ifconfig

Vous verrez la sortie suivante qui inclut votre MTU actuelle :

Output
vtnet0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500 options=6c07bb<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,JUMBO_MTU,VLAN_HWCSUM,TSO4,TSO6,LRO,VLAN_HWTSO,LINKSTATE,RXCSUM_IPV6,TXCSUM_IPV6> . . .

Mettez à jour la liste icmp_types pour y inclure le type de message de destination inaccessible :

/etc/pf.conf
vtnet0 = "vtnet0"
icmp_types = "{ echoreq unreach}"
. . .

Maintenant que vous avez mis en place des politiques pour gérer la fragmentation, les paquets qui entrent dans votre système seront uniformes et cohérents. C'est souhaitable car il existe de nombreux appareils qui échangent des données sur l'internet.

Vous allez maintenant vous efforcer d'éviter un autre problème de sécurité connu sous le nom de IP spoofing. Les attaquants changent souvent leurs IP sources pour faire croire qu'ils résident sur un nœud de confiance au sein d'une organisation. L'IP comprend une directive antispoofing pour le traitement des IPs de source usurpés Lorsqu'il est appliqué à une ou plusieurs interfaces spécifiques, l'antispoofing bloque tout le trafic provenant du réseau de cette interface (sauf s'il provient de cette interface). Par exemple, si vous appliquez l'antispoofing à une (des) interface(s) résidant au 5.5.5.1/24, tout le trafic du réseau 5.5.5.0/24 ne peut pas communiquer avec le système, sauf s'il provient de cette (ces) interface(s).

Ajoutez le contenu surligné suivant pour appliquer l'antispoofing à votre interface vtnet0 :

/etc/pf.conf
. . .
set skip on lo0
scrub in
antispoof quick for $vtnet0
block all
. . .

Enregistrez et quittez le fichier.

Cette règle antispoofing stipule que tout le trafic du (des) réseau(x) vtnet0 ne peut passer que par l'interface vtnet0, ou il sera immédiatement supprimé avec le mot-clé quick. Les mauvais éléments ne pourront pas se cacher dans le réseau de vtnet0 et communiquer avec d'autres nœuds. 

Pour démontrer votre règle antispoofing, vous afficherez votre règlement à l'écran sous forme verbeuse. Les règles en PF sont généralement écrites sous une forme abrégée, mais elles peuvent également être écrites de manière verbeuse. Il n'est généralement pas pratique d'écrire les règles de cette manière, mais cela peut être utile à des fins de test.

Imprimez le contenu de /etc/pf.conf en utilisant pfctl avec la commande suivante :

  • sudo pfctl -nvf /etc/pf.conf

Cette commande pfctl prend les drapeaux -nvf, qui permettent d'imprimer le règlement et de le tester sans rien charger, ce qu'on appelle aussi un essai. Vous allez maintenant voir le contenu entier de /etc/pf.conf dans sa forme détaillée.

Vous verrez quelque chose de similaire à la sortie suivante dans la partie antispoofing :

Output
. . . block drop in quick on ! vtnet0 inet from your_server_ip/20 to any block drop in quick on ! vtnet0 inet from network_address/16 to any block drop in quick inet from your_server_ip to any block drop in quick inet from network_address to any block drop in quick on vtnet0 inet6 from your_IPv6_address to any . . .

Votre règle antispoofing a découvert qu'elle faisait partie du réseau your_server_ip/20. Il a également détecté que (pour l'exemple de ce tutoriel) le serveur fait partie d'un réseau adresse_réseau/16, et possède une adresse IPv6 supplémentaire. L'antispoofing empêche tous ces réseaux de communiquer avec le système, sauf si leur trafic passe par l'interface vtnet0. 

Votre règle antispoofing est le dernier ajout à votre règlement de base. Dans l'étape suivante, vous lancerez ces changements et effectuerez quelques tests.

Étape 4 - Tester votre règlement de base

Au cours de cette étape, vous examinerez et testerez votre règlement de base pour vous assurer que tout fonctionne correctement. Il est préférable d'éviter de mettre en œuvre trop de règles à la fois sans les tester. La meilleure pratique consiste à commencer par l'essentiel, à développer progressivement et à remonter tout en modifiant la configuration.

Voici votre règlement de base complet :

Base Ruleset /etc/pf.conf
vtnet0 = "vtnet0"
icmp_types = "{ echoreq unreach }"
table <bruteforce> persist
table <rfc6890> { 0.0.0.0/8 10.0.0.0/8 100.64.0.0/10 127.0.0.0/8 169.254.0.0/16          \
                  172.16.0.0/12 192.0.0.0/24 192.0.0.0/29 192.0.2.0/24 192.88.99.0/24    \
                  192.168.0.0/16 198.18.0.0/15 198.51.100.0/24 203.0.113.0/24            \
                  240.0.0.0/4 255.255.255.255/32 }

set skip on lo0
scrub in all fragment reassemble max-mss 1440
antispoof quick for $vtnet0
block in quick on $vtnet0 from <rfc6890>
block return out quick on egress to <rfc6890>
block all
pass in on $vtnet0 proto tcp to port { 22 } \
    keep state (max-src-conn 15, max-src-conn-rate 3/1, \
        overload <bruteforce> flush global)
pass out proto { tcp udp } to port { 22 53 80 123 443 }
pass inet proto icmp icmp-type $icmp_types

Assurez-vous que votre fichier /etc/pf.conf est identique au règlement de base complet ici avant de continuer. Puis, enregistrez et quittez le fichier.

Vous disposez d'un règlement de base complet :

  • Une collection de macros qui peuvent définir les services et appareils clés.
  • Des politiques d'hygiène du réseau pour remédier à la fragmentation des paquets et aux adresses IP illogiques.
  • Une structure de filtrage de refus par défaut qui bloque tout et n'autorise que ce que vous spécifiez.
  • Accès SSH entrant avec des limites sur le nombre de connexions simultanées qui peuvent être effectuées par un hôte.
  • Des politiques de trafic sortant qui vous permettent d'accéder à certains services essentiels sur internet.
  • Les politiques de l'ICMP qui donnent accès à l'utilitaire ping et à la découverte du chemin MTU.

Exécutez la commande pfctl suivante pour faire un essai :

  • sudo pfctl -nf /etc/pf.conf

Vous passez les drapeaux -nf qui indiquent à pfctl d'exécuter le règlement sans le charger, ce qui entraînera des erreurs si quelque chose ne va pas.

Maintenant, si vous ne rencontrez aucune erreur, chargez le règlement :

  • sudo pfctl -f /etc/pf.conf

S'il n'y a pas d'erreurs, cela signifie que votre règlement de base est actif et fonctionne correctement. Comme précédemment dans le tutoriel, vous allez effectuer quelques tests sur votre ensemble de règles.

Premier test pour la connectivité internet et le service DNS :

  • ping -c 3 google.com

Vous verrez le résultat suivant :

Output
PING google.com (172.217.0.46): 56 data bytes 64 bytes from 172.217.0.46: icmp_seq=0 ttl=56 time=2.088 ms 64 bytes from 172.217.0.46: icmp_seq=1 ttl=56 time=1.469 ms 64 bytes from 172.217.0.46: icmp_seq=2 ttl=56 time=1.466 ms --- google.com ping statistics --- 3 packets transmitted, 3 packets received, 0.0% packet loss round-trip min/avg/max/stddev = 1.466/1.674/2.088/0.293 ms

Ensuite, vérifiez que vous atteignez le dépôt pkgs : 

  • sudo pkg upgrade

Encore une fois, améliorez les paquets si nécessaire.

Enfin, redémarrez votre serveur :

  • sudo reboot

Donnez à votre serveur quelques minutes pour redémarrer. Vous avez achevé et mis en œuvre votre règlement de base, ce qui constitue une étape importante en termes de progrès. Vous êtes maintenant prêt à explorer certaines des fonctionnalités avancées de PF. Dans la prochaine étape, vous continuerez à prévenir les attaques par la force brute.

Étape 5 - Gérer votre table de surcharge

Avec le temps, la table de surcharge <bruteforce> se remplira d'adresses IP malveillantes et devra être nettoyé périodiquement. Il est peu probable qu'un attaquant continue à utiliser la même adresse IP, il est donc contre-intuitif de les stocker dans la table de surcharge pendant de longues périodes.

Vous utiliserez pfctl pour effacer manuellement les adresses IP qui ont été stockées dans la table de surcharge pendant 48 heures ou plus avec la commande suivante :

  • sudo pfctl -t bruteforce -T expire 172800

Vous verrez une sortie semblable à :

Output
0/0 addresses expired.

Vous passez le drapeau -t bruteforce, qui signifie table bruteforce, et le drapeau -T, qui vous permet d'exécuter une poignée de commandes intégrées. Dans ce cas, vous dirigez la commandeexpire pour effacer toutes les entrées de -t bruteforce avec une valeur de temps représentée en secondes. Comme vous travaillez sur un nouveau serveur, il n'y a probablement pas encore d'adresses IP dans la table de surcharge.

Cette règle fonctionne pour les solutions rapides, mais une solution plus robuste serait d'automatiser le processus avec cron, le planificateur de tâches de FreeBSD.   Créons plutôt un script shell qui exécute cette séquence de commandes.

Créez un fichier de script shell dans le répertoire /usr/local/bin :

  • sudo vi /usr/local/bin/clear_overload.sh

Ajoutez le contenu suivant au script shell :

/usr/local/bin/clear_overload.sh
#!/bin/sh

pfctl -t bruteforce -T expire 172800

Rendez le fichier exécutable avec la commande suivante :

  • sudo chmod 755 /usr/local/bin/clear_overload.sh

Ensuite, vous allez créer une tâche cron. Il s'agit de tâches répétitives qui se déroulent selon une durée que vous spécifiez. Ils sont couramment utilisés pour les sauvegardes, ou tout processus qui doit s'exécuter à la même heure chaque jour. Vous créez des tâches cron avec des fichiers crontab. Veuillez consulter les pages du manuel pour en savoir plus surcron(8) et crontab(5). 

Créez un fichier crontab pour l'utilisateur root ou racine avec la commande suivante :

  • sudo crontab -e

Ajoutez maintenant le contenu suivant au fichier crontab :

crontab
# minute    hour    mday    month   wday    command

  *             0     *       *     *     /usr/local/bin/clear_overload.sh

Enregistrez et quittez le fichier.

Remarque : Veuillez aligner chaque valeur sur l'entrée de table correspondante pour des raisons de lisibilité si les choses ne s'alignent pas correctement lorsque vous ajoutez le contenu.

Cette tâche cron exécute le script clear_overload.sh tous les jours à minuit, en supprimant les adresses IP vieilles de 48 heures du tableau de surcharge <bruteforce>. Ensuite, vous ajouterez des ancres à votre règlement.

Étape 6 - Introduction des ancres dans vos règlements

Au cours de cette étape, vous introduisez des ancres, qui sont utilisées pour l'approvisionnement des règles dans le règlement principal, soit manuellement, soit à partir d'un fichier texte externe. Les ancres peuvent contenir des bribes de règles, des tables et même d'autres ancres, appelées ancres imbriquées. Montrons comment fonctionnent les ancres en ajoutant une table à un fichier externe, et en l'intégrant dans votre règlement de base. Votre table comprendra un groupe d'hôtes internes que vous voulez empêcher de se connecter au monde extérieur.

Créez un fichier nommé /etc/blocked-hosts-anchor :

  • sudo vi /etc/blocked-hosts-anchor

Ajoutez le contenu suivant au fichier :

/etc/blocked-hosts-anchor
table <blocked-hosts> { 192.168.47.1 192.168.47.2 192.168.47.3 }

block return out quick on egress from <blocked-hosts>

Enregistrez et quittez le fichier.

Ces règles déclarent et définissent la table <blocked-hosts>, puis empêchent chaque adresse IP de la table <blocked-hosts> d'accéder aux services depuis le monde extérieur. Vous utilisez le mot-clé egress comme méthode privilégiée pour trouver l'itinéraire par défaut, ou la sortie vers internet.

Vous devez encore déclarer l'ancre dans votre fichier /etc/pf.conf :

  • sudo vi /etc/pf.conf

Ajoutez maintenant les règles d'ancrage suivantes après la règle block all :

/etc/pf.conf
. . .
block all
anchor blocked_hosts
load anchor blocked_hosts from "/etc/blocked-hosts-anchor"
. . .

Enregistrez et quittez le fichier.

Ces règles déclarent les blocked_hosts et chargent les règles d'ancrage dans votre règlement principal à partir de /etc/blocked-hosts-anchorde l'entreprise. 

Maintenant, initiez ces changements en rechargeant votre règlement avec pfctl :

  • sudo pfctl -f /etc/pf.conf

S'il n'y a pas d'erreurs, cela signifie qu'il n'y a pas d'erreurs dans votre règlement et que vos modifications sont actives.

Utilisez pfctl pour vérifier que votre ancre fonctionne :

  • sudo pfctl -s Anchors

Le drapeau -s Anchors signifie show anchors (montrer les ancres). Vous verrez la sortie suivante :

Output
blocked_hosts

L'utilitaire pfctl peut également analyser les règles spécifiques de votre ancre avec les drapeaux -a et -s : 

  • sudo pfctl -a blocked_hosts -s rules

Vous verrez le résultat suivant :

Output
block return out quick on egress from <blocked-hosts> to any

Une autre caractéristique des ancres est qu'elles vous permettent d'ajouter des règles à la demande sans avoir à recharger le règlement. Cela peut être utile pour les tests, les corrections rapides, les situations d'urgence, etc. Par exemple, si un hôte interne agit de manière particulière et que vous voulez l'empêcher d'établir des connexions vers l'extérieur, vous pouvez avoir une ancre en place qui vous permet d'intervenir rapidement depuis la ligne de commande.

Ouvrons /etc/pf.conf et ajoutons une autre ancre :

  • sudo vi /etc/pf.conf

Vous nommerez l'ancre rogue_hosts, et la placerez sous la règle block all :

/etc/pf.conf
. . .
block all
anchor rogue_hosts
. . .

Enregistrez et quittez le fichier.

Pour initier ces changements, rechargez le règlement avec pfctl : 

  • sudo pfctl -f /etc/pf.conf

Encore une fois, utilisez pfctl pour vérifier que l'ancre est en marche :

  • sudo pfctl -s Anchors

Cela donnera le résultat suivant :

Output
blocked_hosts rogue_hosts

Maintenant que l'ancre est en marche, vous pouvez y ajouter des règles à tout moment. Testez ceci en ajoutant la règle suivante :

  • sudo sh -c 'echo "block return out quick on egress from 192.168.47.4" | pfctl -a rogue_hosts -f -'

Ceci invoque la commande echo et son contenu de chaîne de caractères, qui est ensuite acheminé dans l'utilitaire pfctl avec le symbole |, où il est traité en une règle d'ancrage. Vous ouvrez une autre session shell avec la commande sh -c. En effet, vous établissez un conduit entre deux processus, mais vous avez besoin de privilèges sudo pour persister pendant toute la séquence de commande. Il existe plusieurs façons de résoudre ce problème ; ici, vous ouvrez un processus shell supplémentaire avec des privilèges sudo en utilisant sudo sh -c.

Maintenant, utilisez à nouveau pfctl pour vérifier que ces règles sont actives :

  • sudo pfctl -a rogue_hosts -s rules

Cela donnera le résultat suivant :

Output
block return out quick on egress inet from 192.168.47.4 to any

L'utilisation des ancres est totalement situationnelle et souvent subjective. Comme toute autre caractéristique, il y a des avantages et des inconvénients à utiliser des ancres. Certaines applications telles que l'interface blacklistd avec les ancres par conception. Ensuite, vous vous concentrerez sur la journalisation avec PF, qui est un aspect essentiel de la sécurité du réseau. Votre pare-feu n'est pas utile si vous ne pouvez pas voir ce qu'il fait.

Étape 7 - Enregistrer les activités de votre pare-feu

Dans cette étape, vous travaillerez avec l'enregistrement PF, qui est géré par une pseudo-interface nommée pflog. La journalisation est activée au démarrage en ajoutant pflog_enabled=YES au fichier /etc/rc.conf, ce que vous avez fait à l'étape 2. Cela active le démon pflogd qui fait apparaître une interface nommée pflog0 et écrit les journaux au format binaire dans un fichier nommé /var/log/pflog. Les journaux peuvent être analysés en temps réel à partir de l'interface, ou lus dans le fichier /var/log/pflog avec l'utilitaire tcpdump(8).

Accédez d'abord à certains journaux à partir du fichier /var/log/pflog :

  • sudo tcpdump -ner /var/log/pflog

Vous passez les drapeaux -ner qui formatent la sortie pour la lisibilité, et spécifiez également un fichier à lire, qui dans votre cas est /var/log/pflog. 

Vous verrez le résultat suivant :

Output
reading from file /var/log/pflog, link-type PFLOG (OpenBSD pflog file)

Dans ces premières étapes, il se peut qu'il n'y ait aucune donnée dans le fichier /var/log/pflog. Dans un court laps de temps, le fichier journal commencera à se développer.

Vous pouvez également consulter les journaux en temps réel à partir de l'interface pflog0 en utilisant la commande suivante :

  • sudo tcpdump -nei pflog0

Vous passez les drapeaux -nei, qui formatent également la sortie pour la lisibilité, mais cette fois-ci vous spécifiez une interface, qui dans votre cas est pflog0.

Vous verrez le résultat suivant :

Output
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on pflog0, link-type PFLOG (OpenBSD pflog file), capture size 262144 bytes

Vous verrez maintenant les connexions en temps réel. Si possible, effectuez un ping sur votre serveur à partir d'une machine distante et vous verrez les connexions se produire. Le serveur restera dans cet état jusqu'à ce que vous en sortiez.

Pour sortir de cet état et revenir à la ligne de commande, appuyez sur CTRL + Z. 

Il existe sur Internet une multitude d'informations sur le tcpdump(8), y compris le site officiel. 

Accéder aux fichiers journaux avec pftop

L'utilitaire pftop est un outil permettant de visualiser rapidement l'activité du pare-feu en temps réel. Son nom provient de l'utilitaire de pointe bien connu d'Unix.

Pour l'utiliser, vous devez installer le paquet pftop :

  • sudo pkg install pftop

Maintenant, lancez le binaire pftop : 

  • sudo pftop

Cela produira le résultat suivant (vos IP seront différents) :

Output
PR DIR SRC DEST STATE AGE EXP PKTS BYTES tcp In 251.155.237.90:27537 157.225.173.58:22 ESTABLISHED:ESTABLISHED 00:12:35 23:59:55 1890 265K tcp In 222.186.42.15:25884 157.225.173.58:22 TIME_WAIT:TIME_WAIT 00:01:25 00:00:06 22 3801 udp Out 157.245.171.59:4699 67.203.62.5:53 MULTIPLE:SINGLE 00:00:14 00:00:16 2 227

Création d'interfaces de journal supplémentaires

Comme toute autre interface, plusieurs interfaces de journalisation peuvent être créées et nommées à l'aide d'un fichier /etc/hostname. Vous pouvez trouver cela utile à des fins d'organisation, par exemple si vous souhaitez enregistrer certains types d'activité séparément.

Créer une interface de journalisation supplémentaire appelée pflog1 : 

  • sudo vi /etc/hostname.pflog1

Ajoutez le contenu suivant au fichier /etc/hostname.pflog1 :

/etc/hostname.pflog1
up

Activez maintenant le périphérique au moment du démarrage dans votre fichier /etc/rc.conf :

  • sudo sysrc pflog1_enable="YES"

Vous pouvez maintenant surveiller et enregistrer l'activité de votre pare-feu. Cela vous permet de voir qui établit des connexions à votre serveur et les types de connexions effectuées.

Tout au long de ce tutoriel, vous avez intégré certains concepts avancés dans votre ensemble de règles PF. Il est seulement nécessaire de mettre en œuvre des fonctionnalités avancées au fur et à mesure de vos besoins. Cela dit, dans la prochaine étape, vous reviendrez au règlement de base.

Étape 8 - Retour à votre règlement de base

Dans cette dernière section, vous reviendrez à votre règlement de base. C'est une étape rapide qui vous ramènera à un état de fonctionnalité minimaliste.

Ouvrez le règlement de base avec la commande suivante :

  • sudo vi /etc/pf.conf

Supprimez le règlement actuel dans votre fichier et remplacez-le par le règlement de base suivant :

/etc/pf.conf
vtnet0 = "vtnet0"
icmp_types = "{ echoreq unreach }"
table <bruteforce> persist
table <rfc6890> { 0.0.0.0/8 10.0.0.0/8 100.64.0.0/10 127.0.0.0/8 169.254.0.0/16          \
                  172.16.0.0/12 192.0.0.0/24 192.0.0.0/29 192.0.2.0/24 192.88.99.0/24    \
                  192.168.0.0/16 198.18.0.0/15 198.51.100.0/24 203.0.113.0/24            \
                  240.0.0.0/4 255.255.255.255/32 }

set skip on lo0
scrub in all fragment reassemble max-mss 1440
antispoof quick for $vtnet0
block in quick on $vtnet0 from <rfc6890>
block return out quick on egress to <rfc6890>
block all
pass in on $vtnet0 proto tcp to port { 22 } \
    keep state (max-src-conn 15, max-src-conn-rate 3/1, \
        overload <bruteforce> flush global)
pass out proto { tcp udp } to port { 22 53 80 123 443 }
pass inet proto icmp icmp-type $icmp_types

Enregistrez et quittez le fichier.

Rechargez le règlement :

  • sudo pfctl -f /etc/pf.conf

S'il n'y a pas d'erreurs de la commande, alors il n'y a pas d'erreurs dans votre règlement et votre pare-feu fonctionne correctement.

Vous devez également désactiver l'interface pflog1 que vous avez créée. Comme vous ne savez peut-être pas encore si vous en avez besoin, vous pouvez désactiver pflog1 avec l'utilitaire sysrc : 

  • sudo sysrc pflog1_enable="NO"

Supprimez maintenant le fichier /etc/hostname.pflog1 du répertoire /etc :

  • sudo rm /etc/hostname.pflog1

Avant de vous déconnecter, redémarrez le serveur une fois de plus pour vous assurer que toutes vos modifications sont actives et persistantes :

  • sudo reboot

Attendez quelques minutes avant de vous connecter à votre serveur.

Si vous souhaitez mettre en œuvre la PF avec un serveur web, voici un ensemble de règles pour ce scénario. Cet ensemble de règles constitue un point de départ suffisant pour la plupart des applications web.

Simple Web Server Ruleset
vtnet0 = "vtnet0"
icmp_types = "{ echoreq unreach }"
table <bruteforce> persist
table <webcrawlers> persist
table <rfc6890> { 0.0.0.0/8 10.0.0.0/8 100.64.0.0/10 127.0.0.0/8 169.254.0.0/16          \
                  172.16.0.0/12 192.0.0.0/24 192.0.0.0/29 192.0.2.0/24 192.88.99.0/24    \
                  192.168.0.0/16 198.18.0.0/15 198.51.100.0/24 203.0.113.0/24            \
                  240.0.0.0/4 255.255.255.255/32 }

set skip on lo0
scrub in all fragment reassemble max-mss 1440
antispoof quick for $vtnet0
block in quick on $vtnet0 from <rfc6890>
block return out quick on egress to <rfc6890>
block all
pass in on $vtnet0 proto tcp to port { 22 } \
    keep state (max-src-conn 15, max-src-conn-rate 3/1, \
        overload <bruteforce> flush global)
pass in on $vtnet0 proto tcp to port { 80 443 } \
    keep state (max-src-conn 45, max-src-conn-rate 9/1, \
        overload <webcrawlers> flush global)
pass out proto { tcp udp } to port { 22 53 80 123 443 }
pass inet proto icmp icmp-type $icmp_types

Cela crée une table de surcharge appelée <webcrawlers>, qui a une politique de surcharge plus libérale que votre port SSH basée sur les valeurs de max-src-conn 45 et max-src-conn-rate. C'est parce que toutes les surcharges ne sont pas le fait de mauvais acteurs. Elles peuvent également provenir de netbots non malveillants, ce qui vous permet d'éviter des mesures de sécurité excessives dans les ports 80 et 443. Si vous décidez de mettre en œuvre le règlement du serveur web, vous devez ajouter la table <webcrawlers> au fichier /etc/pf.conf, et supprimer les PA de la table périodiquement. Pour cela, reportez-vous à l'étape 5.

Conclusion

Dans ce tutoriel, vous avez configuré PF sous FreeBSD 12.1. Vous disposez maintenant d'un ensemble de règles de base qui peut servir de point de départ pour tous vos projets FreeBSD. Pour plus d'informations sur PF, consultez les pages de manuel pf.conf(5).

Visitez notre page thématique sur FreeBSD pour plus de tutoriels et de questions-réponses.

0 Comments

Creative Commons License