<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Opnsense on Mook’s Lab Chronicles</title><link>https://blog.homeblack.fr/tags/opnsense/</link><description>Recent content in Opnsense on Mook’s Lab Chronicles</description><generator>Hugo -- gohugo.io</generator><language>fr-FR</language><lastBuildDate>Mon, 24 Aug 2026 15:20:00 +0200</lastBuildDate><atom:link href="https://blog.homeblack.fr/tags/opnsense/index.xml" rel="self" type="application/rss+xml"/><item><title>J’ai enfin activé Hyperscan sur mes deux OPNsense</title><link>https://blog.homeblack.fr/p/hyperscan-suricata-opnsense/</link><pubDate>Mon, 24 Aug 2026 15:20:00 +0200</pubDate><guid>https://blog.homeblack.fr/p/hyperscan-suricata-opnsense/</guid><description>&lt;img src="https://blog.homeblack.fr/p/hyperscan-suricata-opnsense/cover.png" alt="Featured image of post J’ai enfin activé Hyperscan sur mes deux OPNsense" /&gt;&lt;p&gt;Tout est parti d’un article publié sur le blog de Suricata : &lt;a class="link" href="https://suricata.io/2025/10/07/faster-suricata-startups-with-hyperscan-caching/" target="_blank" rel="noopener"
&gt;&lt;em&gt;Faster Suricata startups with Hyperscan caching&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Le sujet était le cache MPM (&lt;em&gt;Multi-Pattern Matching&lt;/em&gt;) introduit avec Suricata 8. Au lieu de recompiler toutes les bases de motifs Hyperscan à chaque démarrage ou rechargement des règles, Suricata peut les conserver sur disque et les réutiliser. L’article annonçait un temps de démarrage réduit jusqu’à 50 %.&lt;/p&gt;
&lt;p&gt;En le lisant, je me suis surtout posé une question beaucoup plus basique : est-ce qu’Hyperscan tournait réellement sur mes firewalls ?&lt;/p&gt;
&lt;p&gt;La réponse était non.&lt;/p&gt;
&lt;p&gt;Suricata fonctionnait déjà sur mes deux OPNsense, les événements remontaient bien dans VictoriaLogs et mon cluster HA faisait son travail. Mais le moteur de recherche de motifs capable de profiter des instructions vectorielles du processeur n’était pas actif.&lt;/p&gt;
&lt;p&gt;Le verrou ne se trouvait pas vraiment dans Suricata. Mes deux VM utilisaient encore le modèle de CPU générique &lt;code&gt;kvm64&lt;/code&gt;, pratique pour la compatibilité, mais trop conservateur pour exposer les instructions que je voulais utiliser. Avant de jouer avec le cache, il fallait donc commencer par activer le moteur lui-même.&lt;/p&gt;
&lt;h2 id="donner-le-bon-cpu-virtuel-à-suricata"&gt;Donner le bon CPU virtuel à Suricata
&lt;/h2&gt;&lt;p&gt;J’ai passé &lt;code&gt;fw-core-p1&lt;/code&gt; et &lt;code&gt;fw-core-p2&lt;/code&gt; en &lt;code&gt;x86-64-v3&lt;/code&gt;. FreeBSD voyait alors correctement AVX2, AES et FMA, et je pouvais enfin demander explicitement Hyperscan à Suricata :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-hcl" data-lang="hcl"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;cpu&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;x86-64-v3&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;opnsense_ids_mpm_algo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;hs&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Le &lt;code&gt;hs&lt;/code&gt; sélectionne Hyperscan comme moteur MPM. C’est la partie de Suricata qui cherche plusieurs motifs à la fois dans le trafic et écarte rapidement les signatures qui ne peuvent pas correspondre. La &lt;a class="link" href="https://docs.opnsense.org/manual/ips.html" target="_blank" rel="noopener"
&gt;documentation OPNsense&lt;/a&gt; le présente d’ailleurs comme le meilleur choix sur les plateformes compatibles.&lt;/p&gt;
&lt;p&gt;Le changement posait tout de même une question importante : mes hôtes Proxmox mélangent des processeurs Intel et AMD. J’ai donc fait migrer une VM de test d’Intel vers AMD, puis de nouveau vers Intel, avant de considérer &lt;code&gt;x86-64-v3&lt;/code&gt; comme un socle suffisamment portable chez moi.&lt;/p&gt;
&lt;p&gt;Je n’ai pas touché aux règles de placement HA. Les deux firewalls restent volontairement séparés sur deux hôtes différents.&lt;/p&gt;
&lt;h2 id="le-test-qui-comptait-vraiment"&gt;Le test qui comptait vraiment
&lt;/h2&gt;&lt;p&gt;Voir &lt;code&gt;mpm-algo = hs&lt;/code&gt; sur les deux nœuds était une bonne nouvelle, mais cela ne disait encore rien sur le comportement du cluster pendant une vraie bascule.&lt;/p&gt;
&lt;p&gt;J’ai donc laissé &lt;code&gt;p2&lt;/code&gt; reprendre les neuf VIP CARP. Pendant qu’il était MASTER avec Hyperscan actif, la sortie Internet continuait de fonctionner. Je n’ai observé aucune perte de paquets pendant le failover, avec une latence maximale de 13 ms.&lt;/p&gt;
&lt;p&gt;Après le test, la situation normale était revenue : &lt;code&gt;p1&lt;/code&gt; MASTER, &lt;code&gt;p2&lt;/code&gt; BACKUP et neuf VIP au bon endroit. Le moteur de détection avait changé sans perturber le cluster.&lt;/p&gt;
&lt;h2 id="seize-heures-plus-tard"&gt;Seize heures plus tard
&lt;/h2&gt;&lt;p&gt;Le premier contrôle montrait déjà Suricata autour de 356 à 359 Mio de mémoire, contre environ 548 Mio auparavant. J’ai laissé tourner avant de regarder de nouveau les compteurs.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mesure&lt;/th&gt;
&lt;th&gt;Après environ 16 heures&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CPU Suricata&lt;/td&gt;
&lt;td&gt;0 à 0,1 % sur les deux nœuds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mémoire sur P1&lt;/td&gt;
&lt;td&gt;233 Mio, soit −57,5 % par rapport au repère de 548 Mio&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mémoire sur P2&lt;/td&gt;
&lt;td&gt;356 Mio, stable depuis le premier contrôle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Capture sur P1&lt;/td&gt;
&lt;td&gt;51 044 239 paquets, 4 669 drops, soit 0,0091 %&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Capture sur P2&lt;/td&gt;
&lt;td&gt;1 078 301 paquets, aucun drop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Événements TLS&lt;/td&gt;
&lt;td&gt;22 056 événements centralisés dans VictoriaLogs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Les 4 669 drops de capture observés sur &lt;code&gt;p1&lt;/code&gt; n’avaient pas augmenté depuis plus de deux heures lors du dernier relevé. Aucun drop Suricata n’apparaissait dans les échantillons contrôlés et la qualité de capture restait supérieure à 99,99 %.&lt;/p&gt;
&lt;p&gt;Surtout, l’enrichissement n’avait pas disparu au passage : 926 SNI, 370 empreintes JA4, 55 empreintes de certificats et 23 émetteurs distincts remontaient toujours dans VictoriaLogs.&lt;/p&gt;
&lt;p&gt;Ce n’est pas un benchmark. &lt;code&gt;p1&lt;/code&gt; et &lt;code&gt;p2&lt;/code&gt; n’ont ni le même rôle ni le même volume de trafic, et je ne vais pas attribuer toute la baisse à une seule option. La tendance observée reste néanmoins assez nette : CPU négligeable, mémoire en baisse et aucune dégradation visible de la détection ou du failover.&lt;/p&gt;
&lt;p&gt;La configuration est maintenant décrite dans Terraform et Ansible. Le plan Terraform ne montrait aucun drift, le second passage Ansible terminait avec &lt;code&gt;changed=0&lt;/code&gt;, et les sept tests du rôle passaient avec le &lt;em&gt;syntax-check&lt;/em&gt; et &lt;code&gt;ansible-lint&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Assez ironiquement, un article consacré au cache m’aura d’abord fait découvrir que je n’utilisais même pas le moteur. Le cache pourra venir ensuite. Pour l’instant, Hyperscan tourne enfin sur mes deux firewalls — et c’était déjà le vrai chantier.&lt;/p&gt;</description></item><item><title>Je voulais juste mettre à jour mes OPNsense</title><link>https://blog.homeblack.fr/p/revision-cluster-opnsense/</link><pubDate>Sat, 15 Aug 2026 20:00:00 +0200</pubDate><guid>https://blog.homeblack.fr/p/revision-cluster-opnsense/</guid><description>&lt;img src="https://blog.homeblack.fr/p/revision-cluster-opnsense/cover.png" alt="Featured image of post Je voulais juste mettre à jour mes OPNsense" /&gt;&lt;p&gt;J’étais parti pour une petite révision tranquille de mes deux OPNsense. Une sauvegarde, une mise à jour du premier firewall, quelques contrôles, puis la même chose sur le second.&lt;/p&gt;
&lt;p&gt;Le premier venait de terminer. J’ai lancé la mise à jour du second, puis contrôlé l’état du cluster pendant qu’elle avançait.&lt;/p&gt;
&lt;h2 id="un-cluster-qui-fonctionnait-presque"&gt;Un cluster qui fonctionnait… presque
&lt;/h2&gt;&lt;p&gt;Mon réseau repose sur deux firewalls, &lt;code&gt;fw-core-p1&lt;/code&gt; et &lt;code&gt;fw-core-p2&lt;/code&gt;, avec neuf adresses IP virtuelles CARP. En temps normal, &lt;code&gt;p1&lt;/code&gt; porte les neuf VIP et &lt;code&gt;p2&lt;/code&gt; attend sagement en secours.&lt;/p&gt;
&lt;p&gt;Après la mise à jour du premier nœud, l’état était un peu plus créatif :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;8 VIP : p1 BACKUP / p2 MASTER
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;VIP LAN : p1 MASTER / p2 MASTER
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Le réseau fonctionnait encore. Les interfaces répondaient, pfsync synchronisait toujours les connexions et rien ne semblait cassé à la maison. Mais la VIP du LAN était portée par les deux firewalls en même temps.&lt;/p&gt;
&lt;p&gt;C’était le genre de problème assez discret pour passer inaperçu, jusqu’au jour où il décide de ne plus l’être.&lt;/p&gt;
&lt;p&gt;Je n’ai rien tenté au milieu de l’opération. &lt;code&gt;p1&lt;/code&gt; a repris les neuf VIP avant le redémarrage du second, puis j’ai simplement surveillé le trafic jusqu’à son retour. Quelques minutes plus tard, les deux nœuds étaient bien en OPNsense &lt;code&gt;26.7.2_2&lt;/code&gt; sur FreeBSD 15.1 et tous les services répondaient.&lt;/p&gt;
&lt;p&gt;La mise à jour était terminée. Restait à comprendre ce petit double-MASTER.&lt;/p&gt;
&lt;h2 id="p2-discutait-avec-lui-même"&gt;P2 discutait avec lui-même
&lt;/h2&gt;&lt;p&gt;Le CARP du LAN utilisait l’unicast. Chaque firewall devait envoyer ses annonces directement à l’adresse réelle de l’autre :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;P1 devait cibler 10.0.0.3
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;P2 devait cibler 10.0.0.2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Dans la configuration réelle, les deux ciblaient &lt;code&gt;10.0.0.3&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Pour &lt;code&gt;p1&lt;/code&gt;, c’était correct. Pour &lt;code&gt;p2&lt;/code&gt;, beaucoup moins : il envoyait ses annonces CARP à sa propre adresse.&lt;/p&gt;
&lt;p&gt;Le plus amusant, c’est que la synchronisation HA faisait exactement ce que je lui demandais. &lt;code&gt;p1&lt;/code&gt; copiait sa configuration vers &lt;code&gt;p2&lt;/code&gt;, y compris ce pair unicast qui aurait justement dû être différent sur chaque machine. Corriger uniquement le second nœud n’aurait servi à rien : la synchronisation suivante aurait remis la mauvaise valeur.&lt;/p&gt;
&lt;p&gt;Mes huit autres VIP fonctionnaient déjà avec le multicast CARP classique. J’ai donc vérifié que les annonces envoyées vers &lt;code&gt;224.0.0.18&lt;/code&gt; traversaient bien le bridge Proxmox et arrivaient sur &lt;code&gt;p2&lt;/code&gt;. C’était le cas.&lt;/p&gt;
&lt;p&gt;J’ai supprimé le pair unicast du LAN et laissé CARP revenir à son fonctionnement normal :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;P1 ─┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ├── 224.0.0.18
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;P2 ─┘
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Cette fois, les deux firewalls pouvaient partager exactement la même configuration sans que l’un d’eux finisse par se parler à lui-même.&lt;/p&gt;
&lt;h2 id="le-petit-test-qui-évite-les-grandes-suppositions"&gt;Le petit test qui évite les grandes suppositions
&lt;/h2&gt;&lt;p&gt;Une fois la correction appliquée, j’ai placé &lt;code&gt;p1&lt;/code&gt; en maintenance. &lt;code&gt;p2&lt;/code&gt; a repris les neuf VIP, le DNS a continué de répondre et mes routes applicatives sont restées accessibles. En quittant le mode maintenance, &lt;code&gt;p1&lt;/code&gt; est redevenu MASTER par préemption et &lt;code&gt;p2&lt;/code&gt; est retourné en BACKUP.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;fw-core-p1 : MASTER 9/9
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;fw-core-p2 : BACKUP 9/9
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;demotion : 0
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pfsync : synchronisé
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Au final, la mise à jour vers la &lt;a class="link" href="https://docs.opnsense.org/releases/CE_26.7.html" target="_blank" rel="noopener"
&gt;série OPNsense 26.7&lt;/a&gt; s’est passée sans incident. Elle m’a surtout donné une bonne excuse pour regarder un peu plus loin que le voyant vert.&lt;/p&gt;
&lt;p&gt;Je voulais faire une petite révision. J’ai retiré une adresse dans un champ, relancé un vrai failover et mon cluster est maintenant plus simple qu’avant.&lt;/p&gt;
&lt;p&gt;C’est déjà pas mal pour une matinée de maintenance.&lt;/p&gt;</description></item><item><title>J’ai doublé mon firewall pour pouvoir en éteindre un</title><link>https://blog.homeblack.fr/p/opnsense-ha-carp-fw-core-p1-fw-core-p2/</link><pubDate>Tue, 31 Mar 2026 00:00:00 +0000</pubDate><guid>https://blog.homeblack.fr/p/opnsense-ha-carp-fw-core-p1-fw-core-p2/</guid><description>&lt;img src="https://blog.homeblack.fr/p/opnsense-ha-carp-fw-core-p1-fw-core-p2/cover.png" alt="Featured image of post J’ai doublé mon firewall pour pouvoir en éteindre un" /&gt;&lt;p&gt;Mon premier OPNsense fonctionnait très bien. C’était justement le problème.&lt;/p&gt;
&lt;p&gt;Tout le cœur du réseau reposait sur &lt;code&gt;fw-core-p1&lt;/code&gt; : le LAN, huit VLANs, l’accès Internet et les routes entre mes différentes zones. Tant que la VM tournait, je n’avais aucune raison d’y penser. Dès qu’une mise à jour demandait un redémarrage, la question revenait : qu’est-ce qui reste accessible si ce firewall s’arrête ?&lt;/p&gt;
&lt;p&gt;Pas grand-chose.&lt;/p&gt;
&lt;p&gt;Je ne cherchais pas à transformer mon homelab en infrastructure bancaire. Je voulais simplement pouvoir maintenir un firewall sans couper volontairement tout ce qui passait derrière lui. C’est comme ça que &lt;code&gt;fw-core-p2&lt;/code&gt; est arrivé.&lt;/p&gt;
&lt;h2 id="une-seconde-vm-ne-fait-pas-encore-un-cluster"&gt;Une seconde VM ne fait pas encore un cluster
&lt;/h2&gt;&lt;p&gt;Le plus facile aurait été de cloner &lt;code&gt;fw-core-p1&lt;/code&gt;, de démarrer la copie et d’appeler ça de la haute disponibilité. Deux firewalls mal coordonnés savent surtout produire deux fois plus de problèmes.&lt;/p&gt;
&lt;p&gt;J’ai donc commencé par leur donner la même forme. Les deux VM vivent sur deux nœuds Proxmox différents et possèdent exactement le même ordre d’interfaces : le WAN, le LAN, les huit VLANs du homelab, puis une dernière interface réservée à la synchronisation.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pve-forum pve-fuji
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;fw-core-p1 fw-core-p2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; MASTER ─── VLAN 999 SYNC ─── BACKUP
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ pfsync │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ XML-RPC │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └────── 9 VIP CARP partagées ──┘
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Cette symétrie n’est pas seulement jolie dans Terraform. &lt;code&gt;pfsync&lt;/code&gt; associe les états aux interfaces : si leur ordre ou leur nom diffère entre les deux machines, la réplication ne raconte plus la même histoire des deux côtés.&lt;/p&gt;
&lt;h2 id="carp-pfsync-et-xml-rpc-ne-font-pas-le-même-travail"&gt;CARP, pfsync et XML-RPC ne font pas le même travail
&lt;/h2&gt;&lt;p&gt;J’avais tendance à ranger tout cela dans la boîte « HA ». En pratique, trois mécanismes distincts coopèrent.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CARP&lt;/strong&gt; (&lt;em&gt;Common Address Redundancy Protocol&lt;/em&gt;) détermine quel firewall porte les adresses IP virtuelles. Les machines du réseau utilisent ces VIP comme passerelles et n’ont pas besoin de savoir si &lt;code&gt;p1&lt;/code&gt; ou &lt;code&gt;p2&lt;/code&gt; répond derrière.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;pfsync&lt;/strong&gt; réplique la table d’états. Quand &lt;code&gt;p2&lt;/code&gt; reprend le trafic, il connaît déjà les connexions suivies par &lt;code&gt;p1&lt;/code&gt; au lieu de repartir avec une feuille blanche.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;XML-RPC&lt;/strong&gt; synchronise la configuration depuis le primaire : règles, alias, NAT et objets partagés. Le second nœud reste ainsi un vrai pair, pas une vieille copie que j’espère encore correcte six mois plus tard.&lt;/p&gt;
&lt;p&gt;La &lt;a class="link" href="https://docs.opnsense.org/manual/how-tos/carp.html" target="_blank" rel="noopener"
&gt;documentation HA d’OPNsense&lt;/a&gt; recommande justement une interface dédiée à &lt;code&gt;pfsync&lt;/code&gt;. J’ai appliqué ce principe avec le VLAN 999, utilisé également pour la synchronisation de configuration.&lt;/p&gt;
&lt;p&gt;Dans Terraform, ce choix tient dans quelques lignes :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-hcl" data-lang="hcl"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# vtnet10 -&amp;gt; SYNC (pfsync + xmlrpc HA)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;network_device&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; bridge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;fw_core_lan_bridge&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; vlan_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;fw_core_sync_vlan&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;virtio&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Ce petit &lt;code&gt;vtnet10&lt;/code&gt; est pourtant l’une des pièces les plus importantes du montage. Le trafic utilisateur et les échanges qui permettent au cluster de rester cohérent ne se disputent pas la même interface logique.&lt;/p&gt;
&lt;h2 id="le-moment-où-neuf-vip-ont-changé-de-côté"&gt;Le moment où neuf VIP ont changé de côté
&lt;/h2&gt;&lt;p&gt;Une interface verte dans OPNsense ne prouve pas grand-chose. Le cluster est devenu réel le jour où j’ai placé &lt;code&gt;fw-core-p1&lt;/code&gt; en maintenance et regardé &lt;code&gt;fw-core-p2&lt;/code&gt; reprendre les neuf VIP.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;fw-core-p1 : BACKUP
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;fw-core-p2 : MASTER
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;VIP CARP : 9/9 sur p2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Le DNS continuait de répondre et mes routes applicatives restaient accessibles. En quittant le mode maintenance, &lt;code&gt;p1&lt;/code&gt; a repris son rôle par préemption et &lt;code&gt;p2&lt;/code&gt; est retourné attendre en secours.&lt;/p&gt;
&lt;p&gt;C’est un test très simple, mais il change complètement la confiance que j’accorde au montage. Avant lui, j’avais deux VM configurées pour faire du CARP. Après lui, j’avais un cluster que je pouvais réellement utiliser pendant une intervention.&lt;/p&gt;
&lt;h2 id="la-haute-disponibilité-synchronise-aussi-les-erreurs"&gt;La haute disponibilité synchronise aussi les erreurs
&lt;/h2&gt;&lt;p&gt;Le piège le plus intéressant est arrivé plus tard. Une VIP utilisait un pair CARP unicast propre à &lt;code&gt;p1&lt;/code&gt;. XML-RPC a parfaitement synchronisé cette valeur vers &lt;code&gt;p2&lt;/code&gt;, qui s’est alors mis à envoyer ses annonces… à sa propre adresse.&lt;/p&gt;
&lt;p&gt;La synchronisation n’était pas cassée. Elle exécutait fidèlement une configuration qui n’aurait pas dû être identique sur les deux nœuds.&lt;/p&gt;
&lt;p&gt;J’ai raconté cette petite surprise dans &lt;a class="link" href="https://blog.homeblack.fr/p/revision-cluster-opnsense/" &gt;ma révision du cluster OPNsense&lt;/a&gt;. Elle m’a rappelé qu’un cluster ne rend pas une configuration correcte par magie. Il propage aussi très efficacement les mauvaises idées.&lt;/p&gt;
&lt;p&gt;Le retour au multicast CARP a supprimé cette exception locale et rendu les deux configurations réellement partageables. Depuis, mon test ne consiste plus seulement à vérifier que les deux firewalls affichent du vert : je provoque la bascule et je contrôle ce qui continue à fonctionner.&lt;/p&gt;
&lt;h2 id="ce-que-ce-second-firewall-a-réellement-changé"&gt;Ce que ce second firewall a réellement changé
&lt;/h2&gt;&lt;p&gt;Je n’ai pas supprimé tous les points de panne. Les deux VM dépendent toujours du réseau physique, des bridges Proxmox et de ce qui se trouve en amont. La haute disponibilité du firewall ne rend pas magiquement le reste du homelab redondant.&lt;/p&gt;
&lt;p&gt;Elle a en revanche retiré un blocage très concret : je peux éteindre un OPNsense sans considérer la coupure comme une étape normale de la maintenance.&lt;/p&gt;
&lt;p&gt;Le bénéfice n’est pas spectaculaire quand tout va bien. &lt;code&gt;fw-core-p2&lt;/code&gt; passe l’essentiel de son temps en BACKUP, ce qui est exactement ce que je lui demande. Sa valeur apparaît quand je mets à jour &lt;code&gt;p1&lt;/code&gt;, quand je redémarre une VM ou quand je veux vérifier une modification sans jouer toute la connectivité du homelab sur un seul clic.&lt;/p&gt;
&lt;p&gt;Au fond, j’ai doublé mon firewall pour pouvoir enfin en éteindre un sereinement. C’était tout le projet.&lt;/p&gt;</description></item></channel></rss>