Comment configurer le filtre de paquets (PF) sur FreeBSD 12.1
L'auteur a sélectionné 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 lignes de défense les plus importantes contre les cyberattaques. La possibilité de configurer un pare-feu à partir de zéro est une compétence habilitante qui permet à l'administrateur 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 de sécurité OpenBSD. Il est plus précisément exprimé comme un outil de filtrage de paquets, d'où son nom, et il est connu pour sa syntaxe simple, sa convivialité et ses fonctionnalités étendues. PF est un pare-feu avec état par défaut, stockant des informations sur les connexions dans une table d'état accessible à des fins d'analyse. PF fait partie du système de base FreeBSD et est pris en charge par une solide communauté de développeurs. Bien qu'il existe des différences entre les versions FreeBSD et OpenBSD de PF liées aux architectures du noyau, en général leur syntaxe est similaire. En fonction de leur complexité, les ensembles de règles communs peuvent être modifiés pour fonctionner sur l'une ou l'autre des distributions avec relativement peu d'effort.
Dans ce didacticiel, vous allez créer un pare-feu à partir de zéro sur un serveur FreeBSD 12.1 avec PF. Vous allez concevoir un ensemble de règles de base pouvant être utilisé comme 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 la journalisation, ainsi que d'autres outils tiers.
Conditions préalables
Avant de commencer ce didacticiel, 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 votre configuration préférée.
- FreeBSD n'a pas de pare-feu activé par défaut - la personnalisation est la marque de fabrique de FreeBSD. Par conséquent, lorsque vous lancez votre serveur pour la première fois, vous avez besoin d'une protection temporaire pendant la configuration de PF. Si vous utilisez DigitalOcean, vous pouvez activer votre pare-feu cloud immédiatement après avoir démarré le serveur. Reportez-vous au Firewall Quickstart de DigitalOcean pour obtenir des instructions sur la configuration d'un pare-feu cloud. Si vous utilisez un autre fournisseur de cloud, déterminez le chemin le plus rapide vers une protection immédiate avant de commencer. Quelle que soit la méthode choisie, votre pare-feu temporaire doit autoriser uniquement le trafic SSH entrant et peut autoriser tous les types de trafic sortant.
Étape 1 - Construire votre ensemble de règles préliminaires
Vous commencerez ce didacticiel en rédigeant un ensemble de règles préliminaires qui fournit une protection de base et un accès aux services critiques à partir d'Internet. À ce stade, vous avez un serveur FreeBSD 12.1 en cours d'exécution avec un pare-feu cloud actif.
Il existe deux approches pour créer un pare-feu : refuser par défaut et autoriser 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 d'autorisation par défaut fait exactement le contraire : elle laisse passer 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 ensembles de règles PF sont écrits dans un fichier de configuration nommé /etc/pf.conf
, qui est également son emplacement par défaut. Il est acceptable de stocker ce fichier ailleurs tant qu'il est spécifié dans le fichier de configuration /etc/rc.conf
. Dans ce didacticiel, vous utiliserez l'emplacement par défaut.
Connectez-vous à votre serveur avec votre utilisateur non root :
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 de règles de base complet à tout moment du didacticiel, vous pouvez vous référer aux exemples de l'Étape 4 ou de l'Étape 8.
PF filtre les paquets selon trois actions principales : block
, pass
et match
. Lorsqu'ils sont combinés avec d'autres options, ils forment des règles. Une action est entreprise lorsqu'un paquet répond aux critères spécifiés dans une règle. Comme vous pouvez vous y attendre, les règles pass
et block
génèreront du trafic pass
et block
. Une règle match
exécute une action sur un paquet lorsqu'elle trouve un critère correspondant, mais ne le transmet pas ou ne le bloque pas. Par exemple, vous pouvez effectuer traduction d'adresse réseau (NAT) sur un paquet correspondant sans le transmettre ni le bloquer, et il restera là jusqu'à ce que vous lui disiez de faire quelque chose dans une autre règle, comme le router vers un autre machine ou passerelle.
Ajoutez ensuite la première règle à votre fichier /etc/pf.conf
:
/etc/pf.conf
block all
Cette règle bloque toutes les formes de trafic dans toutes les directions. Puisqu'il ne spécifie pas de direction, il est par défaut à la fois in
et out
. Cette règle est légitime pour un poste de travail local qui doit être isolé du monde, mais elle est largement peu pratique et ne fonctionnera pas sur un serveur distant car elle n'autorise pas le trafic SSH. En fait, si vous aviez activé PF, vous vous seriez verrouillé hors du serveur.
Modifiez votre fichier /etc/pf.conf
pour autoriser le trafic SSH avec la ligne en surbrillance 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. Il existe une liste détaillée des protocoles et de leurs numéros de port respectifs dans le fichier /etc/services
, que nous vous encourageons à consulter.
PF traite les règles séquentiellement de haut en bas, donc votre ensemble de règles actuel bloque initialement tout le trafic, mais le transmet ensuite si les critères de la ligne suivante correspondent, ce qui dans ce cas est le trafic SSH.
Vous pouvez maintenant vous connecter en SSH à votre serveur, mais vous bloquez toujours toutes les formes de trafic sortant. Ceci est problématique car vous ne pouvez pas accéder à des services critiques à partir d'Internet pour installer des packages, mettre à jour vos paramètres d'heure, etc.
Pour résoudre ce problème, ajoutez la règle en surbrillance 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 le trafic entrant, ( à l'exception de SSH). Vous placez les numéros de port et les protocoles entre accolades, ce qui forme une liste dans la syntaxe PF, vous permettant d'ajouter plus de numéros de port si nécessaire. Vous ajoutez également une règle d'exclusion pour le protocole UDP sur les ports 53
et 123
car DNS et NTP basculent souvent entre les protocoles TCP et UDP. Vous avez presque terminé avec l'ensemble de règles préliminaires et n'avez besoin que d'ajouter quelques règles pour obtenir les fonctionnalités de base.
Complétez le jeu de règles préliminaire avec les règles en surbrillance :
Ensemble de règles préliminaires /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 set skip
pour le périphérique de bouclage, car il n'a pas besoin de filtrer le trafic et entraînerait probablement une analyse de votre serveur. 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.
ICMP est un protocole de messagerie polyvalent utilisé par les périphériques réseau pour divers types de communication. L'utilitaire ping, par exemple, utilise un type de message appelé requête d'écho, 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 empêcher les appareils indésirables de contacter votre serveur. Au fur et à mesure que vos besoins augmentent, vous pouvez ajouter d'autres types de messages à votre liste.
Vous disposez maintenant d'un ensemble de règles de travail qui fournit des fonctionnalités de base à la plupart des machines. Dans la section suivante, confirmons que tout fonctionne correctement en activant PF et en testant votre ensemble de règles préliminaires.
Étape 2 - Test de votre ensemble de règles préliminaires
Au cours de cette étape, vous testerez votre ensemble de règles préliminaires et effectuerez la transition de votre pare-feu cloud vers votre pare-feu PF, permettant à PF de prendre complètement le relais. Vous activerez votre ensemble de règles 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 ensembles de règles PF ne sont rien de plus que des fichiers texte, ce qui signifie qu'il n'y a pas de procédures délicates impliquées dans le chargement de nouveaux ensembles de règles. Vous pouvez charger un nouveau jeu de règles et l'ancien est parti. Il est rarement, voire jamais, nécessaire de vider un ensemble de règles existant.
FreeBSD utilise un réseau de scripts shell connu sous le nom de rc system pour gérer le démarrage des services 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
. Étant donné que les fichiers rc
sont essentiels au bon fonctionnement d'un système FreeBSD, ils ne doivent pas être modifiés directement. Au lieu de cela, FreeBSD fournit un utilitaire de ligne de commande connu sous le nom de sysrc
conçu pour vous aider à éditer ces fichiers en toute sécurité.
Activons PF à l'aide de l'utilitaire de ligne de commande sysrc
:
sudo sysrc pf_enable="YES" sudo sysrc pflog_enable="YES"
Vérifiez ces modifications en imprimant le contenu de votre fichier /etc/rc.conf
:
sudo cat /etc/rc.conf
Vous verrez la sortie suivante :
Outputpf_enable="YES" pflog_enable="YES"
Vous activez également le service pflog
, qui à son tour active le démon pflogd
pour la connexion à PF. (Vous travaillerez avec la connexion à 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 démarré 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.
Revenez maintenant en SSH sur le serveur :
ssh freebsd@your_server_ip
Bien que vous ayez initialisé vos services PF, vous n'avez pas réellement chargé votre jeu de règles /etc/pf.conf
, ce qui signifie que votre pare-feu n'est pas encore actif.
Chargez le jeu de règles avec pfctl
:
sudo pfctl -f /etc/pf.conf
S'il n'y a pas d'erreurs ou de messages, cela signifie que votre ensemble de règles n'a pas d'erreurs et que le pare-feu est actif.
Maintenant que PF est en cours d'exécution, vous pouvez détacher votre serveur de votre pare-feu cloud. Cela peut être accompli sur le panneau de configuration de votre compte DigitalOcean en supprimant votre Droplet du portail de votre pare-feu cloud. Si vous utilisez un autre fournisseur de cloud, assurez-vous que tout ce que vous utilisez pour la protection temporaire est désactivé. L'exécution de deux pare-feu différents sur un serveur causera presque certainement des problèmes.
Pour faire bonne mesure, redémarrez à nouveau votre serveur :
sudo reboot
Après quelques minutes, revenez en SSH sur votre serveur :
ssh freebsd@your_server_ip
PF est maintenant votre pare-feu actif. Vous pouvez vous assurer qu'il est en cours d'exécution en accédant à certaines données avec l'utilitaire pfctl.
Voyons quelques statistiques et compteurs avec pfctl -si
:
sudo pfctl -si
Vous passez les drapeaux -si
, qui signifient show info. C'est l'une des nombreuses combinaisons de paramètres de filtre que vous pouvez utiliser avec pfctl pour analyser les données sur l'activité de votre pare-feu.
Vous verrez les données tabulaires suivantes (les valeurs varient d'une machine à l'autre) :
OutputStatus: 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
Puisque vous venez d'activer votre ensemble de règles, vous ne verrez pas encore beaucoup d'informations. Cependant, cette sortie 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é mis en correspondance 23 fois. La sortie confirme également que votre pare-feu fonctionne.
Votre ensemble de règles permet également au trafic sortant d'accéder à certains services critiques à partir d'Internet, y compris l'utilitaire ping.
Vérifions la connectivité Internet et le service DNS avec ping contre google.com
:
ping -c 3 google.com
Depuis que vous avez exécuté l'indicateur de comptage -c 3
, vous verrez trois réponses de connexion réussie :
OutputPING 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 packages à mettre à niveau, continuez et 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éliminaire fournisse une protection et des fonctionnalités, il s'agit toujours d'un ensemble de règles élémentaires qui pourrait bénéficier de certaines améliorations. Dans les sections restantes, vous compléterez votre ensemble de règles de base et utiliserez certaines des fonctionnalités avancées de PF.
Étape 3 - Compléter votre ensemble de règles de base
Dans cette étape, vous vous baserez sur l'ensemble de règles préliminaire pour compléter votre ensemble de règles de base. Vous réorganiserez certaines de vos règles et travaillerez avec des concepts plus avancés.
Incorporer des macros et des 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, selon la nature de vos réseaux. À des fins d'organisation, PF inclut macros, listes et tables. 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 de l'ensemble de règles :
/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 règles SSH et ICMP précédentes utilisent désormais des macros. Les noms de variables sont indiqués par la syntaxe du signe dollar de PF. Vous affectez votre interface vtnet0
à une variable avec le même nom juste comme une formalité, ce qui vous donne la possibilité de la renommer à l'avenir si nécessaire. Les autres noms de variables courants pour les interfaces publiques incluent $pub_if
ou $ext_if
.
Ensuite, vous allez implémenter une table, qui est similaire à une macro, mais conçue 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 RFC6890, qui définit des registres d'adresses IP à usage spécifique. Votre serveur ne doit pas envoyer ou recevoir de paquets vers ou depuis ces adresses via l'interface publique.
Créez ce tableau 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> . . .
Ici, vous introduisez l'option return
, qui complète votre règle block out
. Cela supprimera les paquets et enverra également un message RST à l'hôte qui a tenté 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 ou les routes par défaut ' sur une ou plusieurs interfaces données. Il s'agit généralement d'une méthode plus propre pour trouver des routes par défaut, en particulier avec des réseaux complexes. Le mot clé quick
exécute les règles immédiatement sans tenir compte du reste de l'ensemble de règles. Par exemple, si un paquet avec une adresse IP illogique tente de se connecter au serveur, vous souhaitez interrompre immédiatement la connexion et n'avez aucune raison d'exécuter ce paquet à travers le reste de l'ensemble de règles.
Protéger vos ports SSH
Étant donné que votre port SSH est ouvert au public, il est susceptible d'être exploité. L'un des signes avant-coureurs les plus évidents d'un attaquant 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 avec des mains humaines, mais avec un logiciel informatique qui tentait de déchiffrer votre mot de passe de connexion. Ces types d'exploits systématiques sont souvent appelés attaques brute force et réussissent généralement si le serveur a des mots de passe faibles.
Avertissement : Nous vous recommandons vivement d'utiliser l'authentification par clé publique sur tous les serveurs. Reportez-vous au didacticiel de DigitalOcean sur l'authentification par clé how-to-configure-ssh-key-based-authentication-on-a-freebsd-server.
PF a des fonctionnalités 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 maintient une table d'adresses IP interdites.
Modifiez votre règle SSH précédente 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 permet de définir les critères d'état de 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 les connexions 15
pour max-src-conn
et les connexions 3
pour max-src-conn-rate
. Si ces limites sont dépassées par un hôte, le mécanisme overload
ajoute l'IP source à la table <bruteforce>
, ce qui les bannit du serveur. Enfin, l'option flush global
coupe 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 ensemble de règles.
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 l'ensemble de règles. Sans cela, 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 des formes d'exploitation désastreuses. Dans les sections suivantes, vous prendrez des mesures pour nettoyer les paquets à mesure qu'ils arrivent sur votre serveur.
Assainir votre trafic
Remarque : Les sections suivantes décrivent les principes de base 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 didacticiel Introduction à la terminologie, aux interfaces et aux protocoles de mise en réseau de DigitalOcean.
En raison de la complexité de la suite de protocoles TCP/IP et de la persévérance des acteurs malveillants, les paquets arrivent souvent avec des divergences et des ambiguïtés telles que des fragments 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 normalisation.
Lorsque les données transitent par Internet, elles sont généralement divisées en fragments plus petits à leur source pour s'adapter aux 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 manières qui dépassent le cadre de ce didacticiel. Cependant, avec PF, vous pouvez gérer la fragmentation avec une seule règle. PF inclut un mot-clé scrub
qui normalise les paquets.
Ajoutez le mot-clé scrub
juste avant 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 applique le nettoyage à tout le trafic entrant. Vous incluez l'option fragment reassemble
qui empêche les fragments d'entrer dans le système. Au lieu de cela, ils sont mis en cache en mémoire jusqu'à ce qu'ils soient réassemblés en paquets complets, ce qui signifie que vos règles de filtrage n'auront à gérer que des paquets uniformes. Vous incluez également l'option max-mss 1440
, qui représente la taille de segment maximale des paquets TCP réassemblés, également connue sous le nom de charge utile. Vous spécifiez une valeur de 1440 octets, 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 appelé 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 des messages ICMP pour informer l'IP source de son MTU, un processus connu sous le nom de MTU path discovery. Le type de message ICMP spécifique est destination inaccessible. Vous activerez la découverte de chemin MTU en ajoutant le type de message unreach
à votre liste icmp_types
.
Vous utiliserez le MTU par défaut de votre serveur de 1 500 octets, qui peut être déterminé avec la commande ifconfig
:
ifconfig
Vous verrez la sortie suivante qui inclut votre MTU actuel :
Outputvtnet0: 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 inclure le type de message 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. Ceci est souhaitable car de nombreux appareils échangent des données sur Internet.
Vous allez maintenant vous efforcer d'éviter un autre problème de sécurité appelé IP spoofing. Les attaquants modifient souvent leurs adresses IP source pour donner l'impression qu'ils résident sur un nœud de confiance au sein d'une organisation. PF inclut une directive antispoofing pour gérer les adresses IP source usurpées. Lorsqu'il est appliqué à une ou plusieurs interfaces spécifiques, l'anti-usurpation bloque tout le trafic provenant du réseau de cette interface (sauf s'il provient de cette interface). Par exemple, si vous appliquez l'anti-usurpation à une ou plusieurs interfaces qui résident sur 5.5.5.1/24
, tout le trafic provenant du réseau 5.5.5.0/24
ne peut pas communiquer avec le système à moins qu'il ne provienne de cette ou ces interfaces.
Ajoutez le contenu en surbrillance suivant pour appliquer l'anti-usurpation à 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 anti-usurpation indique que tout le trafic provenant du ou des réseaux de vtnet0
ne peut passer que par l'interface vtnet0
, sinon il sera supprimé immédiatement avec le mot-clé quick
. Les mauvais acteurs ne pourront pas se cacher dans le réseau de vtnet0
et communiquer avec d'autres nœuds.
Pour démontrer votre règle anti-usurpation, vous allez imprimer votre ensemble de règles à l'écran sous sa forme détaillée. Les règles dans PF sont généralement écrites sous une forme abrégée, mais elles peuvent également être écrites sous une forme détaillée. Il n'est généralement pas pratique d'écrire des règles de cette façon, mais à des fins de test, cela peut être utile.
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 impriment l'ensemble de règles et le testent sans rien charger, également connu sous le nom de dry run. Vous verrez maintenant tout le contenu de /etc/pf.conf
sous sa forme détaillée.
Vous verrez quelque chose de similaire à la sortie suivante dans la partie anti-usurpation :
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 fait partie du réseau your_server_ip/20
. Il a également détecté que (pour l'exemple de ce didacticiel) le serveur fait partie d'un réseau network_address/16
et possède une adresse IPv6 supplémentaire. L'anti-usurpation empêche tous ces réseaux de communiquer avec le système, à moins que leur trafic ne passe par l'interface vtnet0
.
Votre règle anti-usurpation est le dernier ajout à votre ensemble de règles de base. À l'étape suivante, vous initierez ces modifications et effectuerez des tests.
Étape 4 - Test de votre ensemble de règles de base
Au cours de cette étape, vous examinerez et testerez votre ensemble de règles de base pour vous assurer que tout fonctionne correctement. Il est préférable d'éviter d'implémenter trop de règles à la fois sans les tester. La meilleure pratique consiste à commencer par l'essentiel, à développer progressivement et à revenir en arrière tout en apportant des modifications à la configuration.
Voici votre ensemble de règles de base complet :
Ensemble de règles de base /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 jeu de règles de base complet ici avant de continuer. Ensuite, enregistrez et quittez le fichier.
Votre jeu de règles de base complet vous fournit :
- Une collection de macros qui peuvent définir des services et des appareils clés.
- Politiques d'hygiène du réseau pour lutter contre la fragmentation des paquets et les adresses IP illogiques.
- Une structure de filtrage default deny 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 pouvant être établies par un hôte.
- Politiques de trafic sortant qui vous donnent accès à certains services critiques à partir d'Internet.
- Politiques ICMP qui permettent d'accéder à l'utilitaire ping et à la découverte du chemin MTU.
Exécutez la commande pfctl
suivante pour effectuer un essai :
sudo pfctl -nf /etc/pf.conf
Vous passez les drapeaux -nf
qui indiquent à pfctl
d'exécuter l'ensemble de règles sans le charger, ce qui générera des erreurs si quelque chose ne va pas.
Maintenant, sans erreur rencontrée, chargez l'ensemble de règles :
sudo pfctl -f /etc/pf.conf
S'il n'y a pas d'erreurs, cela signifie que votre ensemble de règles de base est actif et fonctionne correctement. Comme précédemment dans le didacticiel, vous effectuerez 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 la sortie suivante :
OutputPING 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, mettez à niveau les packages si nécessaire.
Enfin, redémarrez votre serveur :
sudo reboot
Donnez quelques minutes à votre serveur pour redémarrer. Vous avez terminé et implémenté votre ensemble de règles de base, ce qui est une étape importante en termes de progression. Vous êtes maintenant prêt à explorer certaines des fonctionnalités avancées de PF. À l'étape suivante, vous continuerez à empêcher les attaques par force brute.
Étape 5 - Gestion de votre table de surcharge
Au fil du temps, la table de surcharge <bruteforce>
deviendra pleine d'adresses IP malveillantes et devra être effacée 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 similaire à :
Output0/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 exécutez la commande expire
pour effacer toutes les entrées de -t bruteforce
avec une valeur de temps représentée en secondes. Puisque 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 correctifs rapides, mais une solution plus robuste consisterait à 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 '. Ce sont des travaux qui s'exécuteront de manière répétitive selon une heure 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 de manuel pour en savoir plus sur cron(8) et crontab(5).
Créez un fichier crontab utilisateur root 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 tableau correspondante pour plus de lisibilité si les éléments 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, supprimant les adresses IP datant de 48 heures de la table de surcharge <bruteforce>
. Ensuite, vous ajouterez des ancres à votre ensemble de règles.
Étape 6 - Présentation des ancres à vos ensembles de règles
Dans cette étape, vous allez introduire les ancres, qui sont utilisées pour approvisionner les règles dans l'ensemble de règles principal, soit manuellement, soit à partir d'un fichier texte externe. Les ancres peuvent contenir des extraits de règles, des tableaux et même d'autres ancres, appelées ancres imbriquées. Démontrons comment les ancres fonctionnent en ajoutant une table à un fichier externe et en l'intégrant à votre ensemble de règles de base. Votre table comprendra un groupe d'hôtes internes que vous souhaitez 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-ancre
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 du monde extérieur. Vous utilisez le mot-clé egress
comme méthode préférée pour trouver la route par défaut, ou sortie, vers Internet.
Il vous reste à 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 le blocked_hosts
et chargent les règles d'ancrage dans votre jeu de règles principal à partir du fichier /etc/blocked-hosts-anchor
.
Initiez maintenant ces modifications en rechargeant votre jeu de règles 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 ensemble de règles et que vos modifications sont actives.
Utilisez pfctl
pour vérifier que votre ancre fonctionne :
sudo pfctl -s Anchors
Le drapeau -s Anchors
signifie « afficher les ancres ». Vous verrez le résultat suivant :
Outputblocked_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 la sortie suivante :
Outputblock 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 l'ensemble de règles. Cela peut être utile pour les tests, les correctifs rapides, les urgences, etc. Par exemple, si un hôte interne agit de manière particulière et que vous souhaitez l'empêcher d'établir des connexions vers l'extérieur, vous pouvez mettre en place une ancre qui vous permet d'intervenir rapidement à partir de la ligne de commande.
Ouvrons /etc/pf.conf
et ajoutons une autre ancre :
sudo vi /etc/pf.conf
Vous allez nommer l'ancre rogue_hosts
et la placer dans la règle block all
:
/etc/pf.conf
. . . block all anchor rogue_hosts . . .
Enregistrez et quittez le fichier.
Pour initier ces modifications, rechargez le jeu de règles avec pfctl
:
sudo pfctl -f /etc/pf.conf
Encore une fois, utilisez pfctl
pour vérifier que l'ancre fonctionne :
sudo pfctl -s Anchors
Cela générera la sortie suivante :
Outputblocked_hosts rogue_hosts
Maintenant que l'ancre est en cours d'exécution, 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 -'
Cela appelle la commande echo
et son contenu de chaîne, qui est ensuite redirigé vers l'utilitaire pfctl
avec le symbole |
, où il est traité dans une règle d'ancrage. Vous ouvrez une autre session shell avec la commande sh -c
. En effet, vous établissez un canal entre deux processus, mais vous avez besoin des privilèges sudo
pour persister tout au long de la séquence de commandes. Il existe plusieurs façons de résoudre ce problème ; ici, vous ouvrez un processus shell supplémentaire avec les 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 générera la sortie suivante :
Outputblock return out quick on egress inet from 192.168.47.4 to any
L'utilisation des ancres est complètement situationnelle et souvent subjective. Comme toute autre fonctionnalité, il y a des avantages et des inconvénients à utiliser des ancres. Certaines applications telles que blacklistd s'interfacent avec des 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 - Journalisation de l'activité de votre pare-feu
Dans cette étape, vous travaillerez avec la journalisation PF, qui est gérée 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 affiche 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 à partir du fichier /var/log/pflog
avec l'utilitaire tcpdump(8).
Accédez d'abord à certains journaux 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 la sortie suivante :
Outputreading from file /var/log/pflog, link-type PFLOG (OpenBSD pflog file)
Dans ces premiers stades, 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 à croître.
Vous pouvez également afficher 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 spécifiez une interface, qui dans votre cas est pflog0
.
Vous verrez la sortie suivante :
Outputtcpdump: 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, envoyez un ping à 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 une mine d'informations sur Internet à propos de tcpdump(8), y compris le site Web officiel [1].
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 est influencé par le célèbre utilitaire Unix top
.
Pour l'utiliser, vous devez installer le package pftop
:
sudo pkg install pftop
Exécutez maintenant le binaire pftop
:
sudo pftop
Cela générera la sortie suivante (vos adresses IP seront différentes) :
OutputPR 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 journal peuvent être créées et nommées avec un fichier /etc/hostname
. Cela peut vous être utile à des fins d'organisation, par exemple si vous souhaitez consigner séparément certains types d'activités.
Créez une interface de journalisation supplémentaire nommé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 démarrage dans votre fichier /etc/rc.conf
:
sudo sysrc pflog1_enable="YES"
Vous pouvez maintenant surveiller et consigner l'activité de votre pare-feu. Cela vous permet de voir qui se connecte à votre serveur et les types de connexions en cours.
Tout au long de ce didacticiel, vous avez intégré des concepts avancés dans votre jeu de règles PF. Il suffit d'implémenter les fonctionnalités avancées lorsque vous en avez besoin. Cela dit, à l'étape suivante, vous reviendrez à l'ensemble de règles de base.
Étape 8 - Revenir à votre ensemble de règles de base
Dans cette dernière section, vous reviendrez à votre ensemble de règles de base. Il s'agit d'une étape rapide qui vous ramènera à un état de fonctionnalité minimaliste.
Ouvrez l'ensemble de règles de base avec la commande suivante :
sudo vi /etc/pf.conf
Supprimez l'ensemble de règles actuel dans votre fichier et remplacez-le par l'ensemble de règles 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 l'ensemble de règles :
sudo pfctl -f /etc/pf.conf
S'il n'y a pas d'erreur dans la commande, il n'y a pas d'erreur dans votre ensemble de règles 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.
Facultativement, si vous souhaitez implémenter PF avec un serveur Web, voici un ensemble de règles pour ce scénario. Cet ensemble de règles est un point de départ suffisant pour la plupart des applications Web.
Ensemble de règles de serveur Web simple
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 nommé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
. En effet, toutes les surcharges ne proviennent pas de mauvais acteurs. Ils peuvent également provenir de netbots non malveillants, vous évitez ainsi des mesures de sécurité excessives sur les ports 80
et 443
. Si vous décidez d'implémenter l'ensemble de règles du serveur Web, vous devez ajouter la table <webcrawlers>
à /etc/pf.conf
et effacer périodiquement les IP de la table. Reportez-vous à Étape 5 pour cela.
Conclusion
Dans ce tutoriel, vous avez configuré PF sur FreeBSD 12.1. Vous avez maintenant 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 du sujet FreeBSD pour plus de tutoriels et de questions-réponses.