Introduction
Par le passé, j’ai travaillé avec des firewalls propriétaires (Stormshield, Arkoon, Netasq). L’expérience a été plutôt mitigée : il est compliqué de debugger l’ensemble du fonctionnement, les mises à jour sont conditionnées à des licences, les prix sont excessifs et il y a de nombreuses limitations techniques.
Autant de raisons d’écarter le propriétaire pour ce genre de design, au profit d’une solution basée sur Linux (FRR, BGP, nftables) : open source, innovation et indépendance.
L’idée : un design en hub and spoke où les firewalls jouent le rôle de hub, et personne ne se parle sans passer par eux.
Généralités
Voila un schéma de ce design simplifié :
Sur les différents routeurs du réseau, on instancie une VRF hub services (HUBSVC dans la configuration). Cette VRF possède uniquement les default routes originées par les différents firewalls.
Une seconde VRF est présente, il s’agit de la VRF spoke. Cette VRF servira de VRF d’accueil pour les interfaces clientes que l’on souhaite isoler.
Elle va nous permettre d’exporter en EVPN les différentes destinations joignables pour le firewall. Quand des machines présentes dans cette VRF voudront parler à d’autres machines, le flux sera attiré grâce à un leak des default routes de hub services.
Dans notre schéma, l’interface entre r1 et riri est dans la VRF spoke : quand riri veut communiquer avec loulou, il suit la default route de hub services.
Le firewall monte une session eBGP unicast IPv4 et IPv6 pour spoke services (SPKSVC) et pour hub services : l’EVPN s’arrête au spine. On aurait pu faire une seule session EVPN entre le firewall et le spine ; garder des sessions unicast séparées permet de découpler la gestion du réseau de celle des firewalls, par exemple quand ce ne sont pas les mêmes équipes qui s’en occupent.
Voici un exemple de flux entre riri et loulou :
Dans une optique de redondance, ce design peut comporter plusieurs firewalls : il arrive alors que le flux retour n’emprunte pas le même firewall qu’à l’aller. Ce genre de cas empêche de faire du firewall stateful : il faudrait soit synchroniser le conntrack entre tous les firewalls (avec conntrackd), soit renoncer à l’état. C’est la seconde option qui est retenue ici, détaillée dans la section Filtrage.
La mécanique EVPN
Toute l’astuce de l’isolation tient dans les route-targets de la VRF spoke. Voici l’extrait de la configuration FRR sur r1 :
| |
Trois choses à remarquer :
- La VRF spoke exporte ses réseaux avec le route-target
65010:110. Le spine les importe dans une VRFspoke serviceset les annonce en eBGP au firewall : le firewall connaît donc toutes les destinations des spokes. - La VRF spoke importe
65010:65000, un route-target “sentinelle” qui ne match volontairement rien. Sans lui, FRR importerait automatiquement son propre route-target d’export, et chaque spoke apprendrait les réseaux des autres spokes. C’est cette ligne qui garantit qu’il n’existe aucune route directe entre r1 et r2. - La default route arrive par le
import vrf HUBSVC, filtrée par une route-map qui ne laisse passer que la default :0.0.0.0/0en IPv4 (LEAK_DEFAULT4) et::/0en IPv6 (LEAK_DEFAULT6).
Résultat sur r1, la table de routage de la VRF spoke est minimaliste :
| |
Les réseaux de loulou (198.51.100.16/28 et 2001:db8:b::/64) n’apparaissent nulle part : la seule porte de sortie est la default route, et elle mène au firewall.
Filtrage
Ici, le choix est de travailler uniquement en filtrage sans état. On peut ainsi ajouter autant de firewalls que souhaité, sans installer de solution de synchronisation de conntrack : comme le flux retour peut emprunter un autre firewall que l’aller, un filtrage stateful obligerait à répliquer les états entre toutes les machines.
L’inconvénient est qu’il faut écrire chaque règle dans les deux sens : inverser les sources / destinations pour les flux retour.
En chaînant les règles nftables de façon classique, on rencontre vite des soucis de performances, avec une gigue importante : chaque paquet parcourt la chaîne règle par règle, et le temps de traitement dépend de la position de la règle qui match.
À l’inverse, les sets nftables (les héritiers d’ipset) sont bien plus adaptés à ce besoin : le coût du lookup reste quasi constant, quel que soit le nombre d’entrées.
| |
Les deux premières règles autorisent l’ICMP et l’ICMPv6 : pratique pour les tests de joignabilité. Le reste du filtrage passe par les sets, un par famille d’adresses.
Une connexion TCP de riri vers loulou sur le port 443 génère deux trames aux en-têtes symétriques :
| |
Le set ne stocke qu’une seule orientation, celle de l’aller : riri . loulou . 443 (client, serveur, port du serveur). Le port éphémère (40000) n’y figure pas.
Pour que la trame retour retombe sur cette même entrée, la seconde règle lit ses champs à l’envers :
| |
Les champs saddr et daddr sont permutés (la source et la destination s’échangent au retour), et on match sur sport au lieu de dport (le port du serveur passe de la destination à la source). Une seule entrée dans le set couvre donc les deux sens.
Deux sets, quelques règles fixes, et la chaîne forward ne grossit jamais : seuls les sets évoluent.
Autre avantage opérationnel : les sets se modifient à chaud, sans recharger le ruleset.
| |
Vérification
Depuis riri, on vérifie que loulou est joignable :
| |
Et sur le firewall, un tcpdump montre que le flux passe bien par lui, dans les deux sens :
| |
Le paquet entre par la VRF hub services (la default route) et ressort par la VRF spoke services. Si la règle nftables n’existe pas, le paquet meurt ici : policy drop.
Conclusion
Avec ce fonctionnement, tous les flux entre machines sont attirés par les firewalls. La redondance et la mise à l’échelle sont faciles.
Le tout avec du Linux, FRR et nftables. Zéro boîtier propriétaire, zéro licence, et un debug qui se fait avec les outils qu’on connaît déjà. Et quand quelque chose nous manque ou nous bloque, on peut lire le code et contribuer, plutôt que d’ouvrir un ticket chez un éditeur et attendre.
Le déploiement de la configuration reste simple : pas besoin de contrôleurs propriétaires ni d’interfaces web pour gérer un grand nombre d’équipements.
Un lab containerlab complet qui reproduit ce design (en dual-stack IPv4/IPv6) est disponible sur GitHub.
Pour aller plus loin
- Hub-and-Spoke VPN Topology (Ivan Pepelnjak, ipSpace.net) : le même design (pousser le trafic inter-spoke à travers un firewall central via un couple de VRF ingress/egress).
- RFC 7024, Virtual Hub-and-Spoke in BGP/MPLS VPNs : la formalisation du même mécanisme de routage par route-targets, côté BGP/MPLS et orientée scalabilité plutôt que filtrage.