OPNsense HA Umsetzung: Unterschied zwischen den Versionen
Zur Navigation springen
Zur Suche springen
Keine Bearbeitungszusammenfassung |
Keine Bearbeitungszusammenfassung |
||
| Zeile 1: | Zeile 1: | ||
=Plan= | |||
{{#drawio:ha-opnsense-kit}} | |||
{{#drawio: | |||
== Platzhalter in diesem Artikel == | |||
{| class="wikitable" | {| class="wikitable" | ||
! | ! Platzhalter !! Bedeutung !! Beispiel | ||
|- | |- | ||
| | | '''HS''' || Drittes Oktett des Schulungsnetzes (WAN), wird vom Trainer vorgegeben || 4 | ||
|- | |- | ||
| | | '''X''' || Deine Teilnehmernummer, wird vom Trainer vorgegeben || 13 | ||
|} | |} | ||
* Beispiel für X=13 und HS=4: WAN Master 192.168.4.53, WAN Backup 192.168.4.73, WAN-VIP 192.168.4.33, VHID 13 | |||
* '''Alle Passwörter in diesem Artikel sind Laborwerte und dürfen so niemals in eine produktive Umgebung übernommen werden.''' | |||
== | =Wichtig= | ||
* Die Netzwerkkarten für LAN und WAN müssen '''im Hypervisor''' in den Promiscuous-Modus geschaltet werden – nicht in OPNsense. | |||
** VirtualBox: Maschine => Ändern => Netzwerk => Adapter => Erweitert => Promiscuous-Modus: '''Alle erlauben''' | |||
** Proxmox: Bridge ohne MAC-Filter bzw. entsprechende Bridge-Einstellung | |||
* Das gilt sowohl für den Master als auch für das Backup. | |||
;Begründung | |||
* CARP verwendet virtuelle MAC-Adressen für die gemeinsame VIP. In virtualisierten Umgebungen werden Frames mit diesen MAC-Adressen vom Hypervisor standardmäßig verworfen, da sie nicht der echten VM-NIC zugeordnet sind. Ohne aktivierten Promiscuous-Modus erreichen CARP-Advertisements den Partner nicht, was zu Fehldetektion, unerwünschtem Failover und HA-Inkonsistenzen führt. | |||
* | |||
== | = Vorbereitung = | ||
* Zwei OPNsense-Installationen mit jeweils mind. 3 Netzwerkkarten | |||
* Auf beiden Maschinen muss die '''gleiche OPNsense-Version''' laufen | |||
* Die Anzahl der Netzwerkkarten muss auf beiden Maschinen gleich sein | |||
* '''Die Reihenfolge und Anzahl der Netzwerkschnittstellen muss auf beiden Maschinen identisch sein!''' | |||
* Empfehlung für beide Maschinen: | |||
** em0: LAN | |||
** em1: WAN | |||
** em2: SYN | |||
* Am Anfang sollten so wenige Dienste wie möglich laufen | |||
== | == IP-Adressen der Master Firewall == | ||
* em0 (LAN): 192.168.1.1/24 | |||
* em1 (WAN): 192.168.HS.X+40/24 | |||
* em2 (SYN): 100.64.64.1/30 | |||
== IP-Adressen der Backup Firewall == | |||
* em0 (LAN): 192.168.1.2/24 | |||
* em1 (WAN): 192.168.HS.X+60/24 | |||
* em2 (SYN): 100.64.64.2/30 | |||
== Übersicht == | |||
{| class="wikitable" | {| class="wikitable" | ||
! | ! !! Master (opns-ha1) !! Backup (opns-ha2) !! VIP !! VHID | ||
|- | |- | ||
| | | LAN (em0) || 192.168.1.1 || 192.168.1.2 || 192.168.1.3 || 100 | ||
|- | |- | ||
| | | WAN (em1) || 192.168.HS.X+40 || 192.168.HS.X+60 || 192.168.HS.X+20 || X | ||
|- | |||
| SYN (em2) || 100.64.64.1 || 100.64.64.2 || – || – | |||
|} | |||
* Die VHID muss nur innerhalb einer Broadcast-Domäne eindeutig sein, der Wertebereich ist 1 bis 255. | |||
* Da sich alle Teilnehmer das WAN-Segment teilen, wird dort die Teilnehmernummer X verwendet (1 bis 13). | |||
* Das LAN ist pro Teilnehmer ein eigenes, isoliertes Segment. Dort steht deshalb bei allen Teilnehmern fest die 100. Der Abstand zum WAN-Bereich ist Absicht: dreistellig = LAN, zweistellig = WAN. | |||
= Firewall Regeln (beide Firewalls) = | |||
* Auf allen Schnittstellen '''außer''' dem SYN-Interface muss das Protokoll CARP akzeptiert werden | |||
* Dazu muss im Menü '''Firewall => Rules => {WAN, LAN}''' je eine Allow-Regel erstellt werden, die auf das CARP-Protokoll matcht | |||
* Für das SYN-Interface kann man eine Standard-Allow-Regel erstellen, die alles erlaubt | |||
= Virtuelle IPs (Master Firewall) = | |||
* Die virtuellen IPs werden von den Knoten des HA-Clusters geteilt, sobald ein Ausfall erkannt wird | |||
* Somit wird dafür gesorgt, dass die virtuelle IP immer erreichbar ist | |||
* Die virtuelle IP wird '''nur''' auf der Master Firewall eingestellt und an die Backups mitgeteilt | |||
* Dazu muss man im Menü '''Interfaces => Virtual IPs => Settings''' an allen Schnittstellen bis auf das SYN-Interface die gewünschten IPs vergeben | |||
* Über das '''[+]''' auf der rechten Seite muss man folgende Einträge machen: | |||
== virtuelle WAN IP == | |||
{| class="wikitable" | |||
! Parameter !! Wert | |||
|- | |- | ||
| | | Mode || CARP | ||
|- | |- | ||
| | | Interface || WAN | ||
|- | |- | ||
| | | Address || 192.168.HS.X+20/24 | ||
|- | |- | ||
| | | Virtual Password || radler | ||
|- | |- | ||
| | | VHID Group || X | ||
|- | |- | ||
| | | Advertising Frequency || 1 | ||
|- | |- | ||
| | | Description || Virtual WAN IP | ||
|} | |} | ||
== virtuelle LAN IP == | |||
=== | |||
{| class="wikitable" | {| class="wikitable" | ||
! | ! Parameter !! Wert | ||
|- | |- | ||
| | | Mode || CARP | ||
|- | |- | ||
| | | Interface || LAN | ||
|- | |- | ||
| | | Address || 192.168.1.3/24 | ||
|- | |- | ||
| | | Virtual Password || radler | ||
|- | |- | ||
| | | VHID Group || 100 | ||
|- | |- | ||
| | | Advertising Frequency || 1 | ||
|- | |- | ||
| | | Description || Virtual LAN IP | ||
|} | |} | ||
=== | = Source NAT (Master Firewall) = | ||
* Da die virtuelle IP für ausgehende Verbindungen benutzt werden soll, muss die automatische Regelgenerierung ausgeschaltet werden | |||
'' | * Im Menü '''Firewall => NAT => Outbound''' muss die Option "Manual outbound NAT rule generation" ausgewählt werden | ||
* Dann die folgende Regel erstellen: | |||
** Interface: WAN | |||
** Source address: LAN net | |||
** Translation / target: '''Virtual WAN IP (192.168.HS.X+20)''' aus der Auswahlliste | |||
;Achtung | |||
* Als Ziel muss der VIP-Eintrag aus der Auswahlliste gewählt werden, nicht das Netz 192.168.HS.0/24. Trägt man ein ganzes Netz ein, baut OPNsense daraus einen NAT-Pool. | |||
= High Availability Konfiguration (Master Firewall) = | |||
* Die eigentliche HA-Einstellung erfolgt über das Menü '''System => High Availability => Settings''': | |||
{| class="wikitable" | {| class="wikitable" | ||
! | ! Parameter !! Wert | ||
|- | |- | ||
| | | Synchronize all states via || SYN | ||
|- | |- | ||
| | | Sync compatibility || OPNsense 24.7 or above | ||
|- | |- | ||
| | | Synchronize Peer IP || 100.64.64.2 | ||
|- | |- | ||
| | | Synchronize Config || 100.64.64.2 | ||
|- | |- | ||
| | | Verify peer || deaktiviert | ||
|- | |- | ||
| | | Remote System Username || root | ||
|- | |- | ||
| | | Remote System Password || 123Start$ | ||
|- | |- | ||
| | | Services || Aliases, Firewall Rules, NAT, Virtual IPs | ||
|} | |} | ||
* "Verify peer" bleibt deaktiviert, weil die Web-GUI der Gegenstelle ein selbstsigniertes Zertifikat verwendet. | |||
= High Availability Konfiguration (Backup Firewall) = | |||
* Hier darf im Menü '''System => High Availability => Settings''' nur folgendes eingestellt werden: | |||
{| class="wikitable" | {| class="wikitable" | ||
! | ! Parameter !! Wert | ||
|- | |- | ||
| | | Synchronize all states via || SYN | ||
|- | |- | ||
| | | Sync compatibility || OPNsense 24.7 or above | ||
|- | |- | ||
| | | Synchronize Peer IP || 100.64.64.1 | ||
|- | |- | ||
| | | Synchronize Config || leer | ||
| | |- | ||
| Verify peer || deaktiviert | |||
|- | |||
| Remote System Username || leer | |||
|- | |- | ||
| | | Remote System Password || leer | ||
|- | |- | ||
| | | Services || Nothing selected | ||
|} | |} | ||
'' | * '''Synchronize Config''' muss auf dem Backup leer bleiben. Trägt man hier den Master ein, schieben sich beide Knoten gegenseitig ihre Konfiguration zu. | ||
''' | = Client im LAN = | ||
* Der Windows-Client (192.168.1.50) muss als '''Gateway''' und als '''DNS-Server''' die LAN-VIP '''192.168.1.3''' erhalten – nicht die 192.168.1.1 | |||
* Wird die echte IP des Masters eingetragen, ist der Client beim Ausfall des Masters trotz funktionierendem Cluster offline | |||
* Bei Verwendung eines DHCP-Servers muss dieser die VIP als Gateway und DNS verteilen | |||
=== | = Endergebnis = | ||
==Master== | |||
* System | |||
** High Availability | |||
*** Status | |||
* Hier müssen beide Spalten gefüllt sein und auf beiden Knoten die gleichen Dienste erscheinen: | |||
{| class="wikitable" | {| class="wikitable" | ||
! | ! Service !! Description | ||
|- | |- | ||
| | | configd || System Configuration Daemon | ||
|- | |- | ||
| | | cron || Cron | ||
| | |- | ||
| hostwatch || Host discovery service | |||
|- | |||
| login || Users and Groups | |||
|- | |||
| ntpd || Network Time Daemon | |||
|- | |||
| openssh || Secure Shell Daemon | |||
|- | |- | ||
| | | pf || Packet Filter | ||
|- | |- | ||
| | | routing || System routing | ||
|- | |- | ||
| | | sysctl || System tunables | ||
|- | |- | ||
| | | syslog-ng || Syslog-ng Daemon | ||
|- | |- | ||
| | | unbound || Unbound DNS | ||
|- | |- | ||
| | | webgui || Web GUI | ||
|} | |} | ||
==Slave== | |||
* System | |||
** High Availability | |||
auf | *** Status | ||
* Auf der Backup-Firewall erscheint hier die Warnung, dass die XMLRPC-Konfiguration unvollständig sei. | |||
* Diese Meldung kann ignoriert werden: Das Backup empfängt nur passiv, deshalb sind die XMLRPC-Felder dort absichtlich leer und werden fälschlicherweise als "unvollständig" markiert. Solange die Master-Firewall auf Version 24.7 eingestellt ist und die Daten erfolgreich überträgt, arbeitet der Cluster technisch einwandfrei. | |||
'' | = Failover testen = | ||
* Auf dem Client einen Dauerping nach draußen starten: | |||
ping -t 192.168.HS.254 | |||
* Auf dem '''Master''': System => High Availability => Status => '''Enter persistent CARP maintenance mode''' | |||
* Erwartetes Verhalten: | |||
** Das Backup wechselt für beide VHIDs von BACKUP auf MASTER | |||
** Der Ping läuft weiter oder verliert maximal ein bis zwei Pakete | |||
* Kontrolle auf beiden Knoten: | |||
ifconfig em0 ; ifconfig em1 | |||
* Zurückschalten über '''Leave persistent CARP maintenance mode''' auf dem Master | |||
* Zweiter Test: Master hart ausschalten (VM stoppen) und das gleiche Verhalten prüfen | |||
= | =Debugging= | ||
== Vorgehen == | |||
* Von unten nach oben prüfen: erst CARP (Layer 2/3), dann pfsync (States), dann XMLRPC (Konfiguration) | |||
* Ein Cluster kann sauber synchronisieren und trotzdem kein Failover können – und umgekehrt | |||
== Schritt 1: CARP-Status == | |||
* Auf beiden Knoten: | |||
ifconfig em0 ; ifconfig em1 | |||
* Erwartetes Bild: | |||
{| class="wikitable" | {| class="wikitable" | ||
! | ! Knoten !! LAN (vhid 100) !! WAN (vhid X) | ||
|- | |- | ||
| | | Master || MASTER, advskew 0 || MASTER, advskew 0 | ||
|- | |- | ||
| | | Backup || BACKUP, advskew 100 || BACKUP, advskew 100 | ||
|} | |} | ||
* In der GUI: '''Interfaces => Virtual IPs => Status''' | |||
* CARP global ein-/ausgeschaltet: | |||
sysctl net.inet.carp.allow | |||
* Ausführlichere CARP-Meldungen im Log einschalten: | |||
sysctl net.inet.carp.log=2 | |||
== Schritt 2: Beide Knoten sind MASTER (Split Brain) == | |||
* Das häufigste Fehlerbild im Labor. Beide Knoten sehen den Partner nicht und übernehmen die VIP. | |||
* Prüfen, ob die Advertisements des Partners überhaupt ankommen: | |||
tcpdump -n -i em1 proto 112 | |||
tcpdump -n -i em0 proto 112 | |||
* Man muss die Pakete '''beider''' Knoten sehen (Quelle = jeweils die echte Interface-IP) | |||
{| class="wikitable" | {| class="wikitable" | ||
! | ! Beobachtung !! Ursache | ||
|- | |- | ||
| | | Nur eigene Pakete sichtbar || Promiscuous-Modus im Hypervisor fehlt | ||
|- | |- | ||
| | | Gar keine Pakete sichtbar || VIP nicht angelegt oder Interface down | ||
|- | |- | ||
| | | Pakete kommen an, trotzdem beide MASTER || CARP-Allow-Regel auf dem Interface fehlt | ||
|- | |- | ||
| | | Pakete mit fremder VHID || Anderer Teilnehmer nutzt die gleiche VHID | ||
|} | |} | ||
* Weitere Ursachen bei Split Brain: | |||
** Unterschiedliches '''Virtual Password''' auf den beiden Knoten | |||
** Unterschiedliche '''VHID''' auf den beiden Knoten | |||
** Unterschiedliche '''Advertising Frequency''' | |||
== Schritt 3: Kein Failover trotz korrektem Status == | |||
* Demotion-Zähler prüfen – ist er größer als 0, bleibt der Knoten absichtlich BACKUP: | |||
sysctl net.inet.carp.demotion | |||
* Ursachen für einen erhöhten Zähler: ein Interface ist down, ein Dienst startet nicht sauber, der Knoten befindet sich noch im Maintenance Mode | |||
* Maintenance Mode verlassen: '''System => High Availability => Status => Leave persistent CARP maintenance mode''' | |||
== Schritt 4: Sicht des Clients == | |||
* Auf dem Windows-Client prüfen, welche MAC hinter dem Gateway steckt: | |||
arp -a | |||
* Die LAN-VIP muss auf eine virtuelle MAC zeigen, nicht auf die echte NIC des Masters: | |||
192.168.1.3 00-00-5e-00-01-64 | |||
* Das letzte Byte ist die VHID in hex – 0x64 = 100 | |||
* Steht dort eine 08:00:27:... Adresse, arbeitet der Client an CARP vorbei direkt mit einer echten Firewall-IP | |||
* Nach einem Failover darf sich der ARP-Eintrag '''nicht''' ändern – genau das ist der Sinn der virtuellen MAC | |||
=== | == Schritt 5: Zustandssynchronisation (pfsync) == | ||
* Läuft der pfsync-Verkehr über das SYN-Netz? | |||
tcpdump -n -i em2 proto 240 | |||
* Anzahl der States auf beiden Knoten vergleichen – die Werte müssen ähnlich sein: | |||
pfctl -s state | wc -l | |||
* Ist die Zahl auf dem Backup dauerhaft 0, überträgt pfsync nicht: | |||
** '''Synchronize Peer IP''' zeigt auf die falsche Adresse | |||
** Die Allow-Regel auf dem SYN-Interface fehlt | |||
** SYN-Interface auf einem der Knoten nicht aktiviert | |||
== Schritt 6: Konfigurationssynchronisation (XMLRPC) == | |||
* Auf der Master-Firewall: '''System => Log Files => General''', im Filter-Feld '''xmlrpc''' eingeben | |||
* Gesucht wird ein Eintrag wie: '''[OK] Configuration synchronized to''' https://100.64.64.2:443 | |||
* Manuell anstoßen: '''System => High Availability => Status => Synchronize and reconfigure all (Push)''' | |||
* Gleichzeitig auf dem Backup mitlesen: | |||
tail -f /var/log/configd/latest.log | |||
{| class="wikitable" | {| class="wikitable" | ||
! | ! Meldung !! Ursache | ||
|- | |- | ||
| | | Connection refused || Web-GUI des Backups nicht erreichbar oder falscher Port | ||
|- | |- | ||
| | | Authentication failed || Falsches Passwort unter "Remote System Password" | ||
|- | |- | ||
| | | Certificate verify failed || "Verify peer" ist aktiviert, Zertifikat ist selbstsigniert | ||
|- | |||
| Endlose Sync-Schleife || Auf dem Backup ist "Synchronize Config" gefüllt – muss leer sein | |||
|} | |} | ||
== | == Schritt 7: Ausgehende Verbindungen == | ||
* Prüfen, mit welcher Quelladresse der Client nach draußen geht: | |||
tcpdump -n -i em1 host 192.168.HS.254 | |||
* Es muss die Virtual WAN IP (192.168.HS.X+20) erscheinen, nicht die echte IP des Masters | |||
* Erscheint die echte IP: Outbound-NAT steht noch auf automatisch, oder in der manuellen Regel wurde als Target nicht die VIP gewählt | |||
== Nützliche Befehle im Überblick == | |||
{| class="wikitable" | {| class="wikitable" | ||
! | ! Befehl !! Zweck | ||
|- | |||
| ifconfig em0 ; ifconfig em1 || CARP-Rolle und VIPs anzeigen | |||
|- | |||
| tcpdump -n -i em1 proto 112 || CARP-Advertisements mitlesen | |||
|- | |||
| tcpdump -n -i em2 proto 240 || pfsync-Verkehr mitlesen | |||
|- | |- | ||
| | | pfctl -s state {{!}} wc -l || Anzahl der Firewall-States | ||
|- | |- | ||
| | | sysctl net.inet.carp.demotion || Demotion-Zähler | ||
|- | |- | ||
| | | sysctl net.inet.carp.log=2 || Ausführliches CARP-Logging | ||
|- | |- | ||
| | | tail -f /var/log/configd/latest.log || Konfigurationssync mitlesen | ||
|- | |- | ||
| | | configctl filter reload || Regelwerk neu laden | ||
|} | |} | ||
''' | ==Netzwerkschnittstellen== | ||
=== Master === | |||
em0: flags=1008943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST,LOWER_UP> metric 0 mtu 1500 | |||
description: LAN (lan) | |||
options=48500b8<VLAN_MTU,VLAN_HWTAGGING,JUMBO_MTU,VLAN_HWCSUM,VLAN_HWFILTER,VLAN_HWTSO,HWSTATS,MEXTPG> | |||
ether 08:00:27:c4:a2:6f | |||
inet 192.168.1.1 netmask 0xffffff00 broadcast 192.168.1.255 | |||
'''inet 192.168.1.3 netmask 0xffffff00 broadcast 192.168.1.255 vhid 100''' | |||
'''carp: MASTER vhid 100 advbase 1 advskew 0''' | |||
'''peer 224.0.0.18 peer6 ff02::12''' | |||
media: Ethernet autoselect (1000baseT <full-duplex>) | |||
status: active | |||
nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL> | |||
em1: flags=1008943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST,LOWER_UP> metric 0 mtu 1500 | |||
description: WAN (wan) | |||
options=48500b8<VLAN_MTU,VLAN_HWTAGGING,JUMBO_MTU,VLAN_HWCSUM,VLAN_HWFILTER,VLAN_HWTSO,HWSTATS,MEXTPG> | |||
ether 08:00:27:61:6f:6d | |||
inet 192.168.HS.X+40 netmask 0xffffff00 broadcast 192.168.HS.255 | |||
'''inet 192.168.HS.X+20 netmask 0xffffff00 broadcast 192.168.HS.255 vhid X''' | |||
'''carp: MASTER vhid X advbase 1 advskew 0''' | |||
'''peer 224.0.0.18 peer6 ff02::12''' | |||
media: Ethernet autoselect (1000baseT <full-duplex>) | |||
status: active | |||
nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL> | |||
=== Backup === | |||
em0: flags=1008943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST,LOWER_UP> metric 0 mtu 1500 | |||
description: LAN (lan) | |||
options=48500b8<VLAN_MTU,VLAN_HWTAGGING,JUMBO_MTU,VLAN_HWCSUM,VLAN_HWFILTER,VLAN_HWTSO,HWSTATS,MEXTPG> | |||
ether 08:00:27:1c:44:8b | |||
inet 192.168.1.2 netmask 0xffffff00 broadcast 192.168.1.255 | |||
'''inet 192.168.1.3 netmask 0xffffff00 broadcast 192.168.1.255 vhid 100''' | |||
'''carp: BACKUP vhid 100 advbase 1 advskew 100''' | |||
'''peer 224.0.0.18 peer6 ff02::12''' | |||
media: Ethernet autoselect (1000baseT <full-duplex>) | |||
status: active | |||
nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL> | |||
em1: flags=1008943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST,LOWER_UP> metric 0 mtu 1500 | |||
description: WAN (wan) | |||
options=48500b8<VLAN_MTU,VLAN_HWTAGGING,JUMBO_MTU,VLAN_HWCSUM,VLAN_HWFILTER,VLAN_HWTSO,HWSTATS,MEXTPG> | |||
ether 08:00:27:95:9b:4b | |||
inet 192.168.HS.X+60 netmask 0xffffff00 broadcast 192.168.HS.255 | |||
'''inet 192.168.HS.X+20 netmask 0xffffff00 broadcast 192.168.HS.255 vhid X''' | |||
'''carp: BACKUP vhid X advbase 1 advskew 100''' | |||
'''peer 224.0.0.18 peer6 ff02::12''' | |||
media: Ethernet autoselect (1000baseT <full-duplex>) | |||
status: active | |||
nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL> | |||
* Der Master hat '''advskew 0''', das Backup '''advskew 100'''. Diesen Wert setzt OPNsense beim Sync automatisch – er wird nicht von Hand vergeben. | |||
* | ==Wird Synchronisiert?== | ||
;Master | |||
* System | |||
** High Availability | |||
*** Status | |||
**** Synchronize and reconfigure all (Push) | |||
;Backup | |||
tail -f /var/log/configd/latest.log | |||
* Hier sollte man die Übertragung sehen. | |||
Version vom 3. September 2026, 15:41 Uhr
Plan
Platzhalter in diesem Artikel
| Platzhalter | Bedeutung | Beispiel |
|---|---|---|
| HS | Drittes Oktett des Schulungsnetzes (WAN), wird vom Trainer vorgegeben | 4 |
| X | Deine Teilnehmernummer, wird vom Trainer vorgegeben | 13 |
- Beispiel für X=13 und HS=4: WAN Master 192.168.4.53, WAN Backup 192.168.4.73, WAN-VIP 192.168.4.33, VHID 13
- Alle Passwörter in diesem Artikel sind Laborwerte und dürfen so niemals in eine produktive Umgebung übernommen werden.
Wichtig
- Die Netzwerkkarten für LAN und WAN müssen im Hypervisor in den Promiscuous-Modus geschaltet werden – nicht in OPNsense.
- VirtualBox: Maschine => Ändern => Netzwerk => Adapter => Erweitert => Promiscuous-Modus: Alle erlauben
- Proxmox: Bridge ohne MAC-Filter bzw. entsprechende Bridge-Einstellung
- Das gilt sowohl für den Master als auch für das Backup.
- Begründung
- CARP verwendet virtuelle MAC-Adressen für die gemeinsame VIP. In virtualisierten Umgebungen werden Frames mit diesen MAC-Adressen vom Hypervisor standardmäßig verworfen, da sie nicht der echten VM-NIC zugeordnet sind. Ohne aktivierten Promiscuous-Modus erreichen CARP-Advertisements den Partner nicht, was zu Fehldetektion, unerwünschtem Failover und HA-Inkonsistenzen führt.
Vorbereitung
- Zwei OPNsense-Installationen mit jeweils mind. 3 Netzwerkkarten
- Auf beiden Maschinen muss die gleiche OPNsense-Version laufen
- Die Anzahl der Netzwerkkarten muss auf beiden Maschinen gleich sein
- Die Reihenfolge und Anzahl der Netzwerkschnittstellen muss auf beiden Maschinen identisch sein!
- Empfehlung für beide Maschinen:
- em0: LAN
- em1: WAN
- em2: SYN
- Am Anfang sollten so wenige Dienste wie möglich laufen
IP-Adressen der Master Firewall
- em0 (LAN): 192.168.1.1/24
- em1 (WAN): 192.168.HS.X+40/24
- em2 (SYN): 100.64.64.1/30
IP-Adressen der Backup Firewall
- em0 (LAN): 192.168.1.2/24
- em1 (WAN): 192.168.HS.X+60/24
- em2 (SYN): 100.64.64.2/30
Übersicht
| Master (opns-ha1) | Backup (opns-ha2) | VIP | VHID | |
|---|---|---|---|---|
| LAN (em0) | 192.168.1.1 | 192.168.1.2 | 192.168.1.3 | 100 |
| WAN (em1) | 192.168.HS.X+40 | 192.168.HS.X+60 | 192.168.HS.X+20 | X |
| SYN (em2) | 100.64.64.1 | 100.64.64.2 | – | – |
- Die VHID muss nur innerhalb einer Broadcast-Domäne eindeutig sein, der Wertebereich ist 1 bis 255.
- Da sich alle Teilnehmer das WAN-Segment teilen, wird dort die Teilnehmernummer X verwendet (1 bis 13).
- Das LAN ist pro Teilnehmer ein eigenes, isoliertes Segment. Dort steht deshalb bei allen Teilnehmern fest die 100. Der Abstand zum WAN-Bereich ist Absicht: dreistellig = LAN, zweistellig = WAN.
Firewall Regeln (beide Firewalls)
- Auf allen Schnittstellen außer dem SYN-Interface muss das Protokoll CARP akzeptiert werden
- Dazu muss im Menü Firewall => Rules => {WAN, LAN} je eine Allow-Regel erstellt werden, die auf das CARP-Protokoll matcht
- Für das SYN-Interface kann man eine Standard-Allow-Regel erstellen, die alles erlaubt
Virtuelle IPs (Master Firewall)
- Die virtuellen IPs werden von den Knoten des HA-Clusters geteilt, sobald ein Ausfall erkannt wird
- Somit wird dafür gesorgt, dass die virtuelle IP immer erreichbar ist
- Die virtuelle IP wird nur auf der Master Firewall eingestellt und an die Backups mitgeteilt
- Dazu muss man im Menü Interfaces => Virtual IPs => Settings an allen Schnittstellen bis auf das SYN-Interface die gewünschten IPs vergeben
- Über das [+] auf der rechten Seite muss man folgende Einträge machen:
virtuelle WAN IP
| Parameter | Wert |
|---|---|
| Mode | CARP |
| Interface | WAN |
| Address | 192.168.HS.X+20/24 |
| Virtual Password | radler |
| VHID Group | X |
| Advertising Frequency | 1 |
| Description | Virtual WAN IP |
virtuelle LAN IP
| Parameter | Wert |
|---|---|
| Mode | CARP |
| Interface | LAN |
| Address | 192.168.1.3/24 |
| Virtual Password | radler |
| VHID Group | 100 |
| Advertising Frequency | 1 |
| Description | Virtual LAN IP |
Source NAT (Master Firewall)
- Da die virtuelle IP für ausgehende Verbindungen benutzt werden soll, muss die automatische Regelgenerierung ausgeschaltet werden
- Im Menü Firewall => NAT => Outbound muss die Option "Manual outbound NAT rule generation" ausgewählt werden
- Dann die folgende Regel erstellen:
- Interface: WAN
- Source address: LAN net
- Translation / target: Virtual WAN IP (192.168.HS.X+20) aus der Auswahlliste
- Achtung
- Als Ziel muss der VIP-Eintrag aus der Auswahlliste gewählt werden, nicht das Netz 192.168.HS.0/24. Trägt man ein ganzes Netz ein, baut OPNsense daraus einen NAT-Pool.
High Availability Konfiguration (Master Firewall)
- Die eigentliche HA-Einstellung erfolgt über das Menü System => High Availability => Settings:
| Parameter | Wert |
|---|---|
| Synchronize all states via | SYN |
| Sync compatibility | OPNsense 24.7 or above |
| Synchronize Peer IP | 100.64.64.2 |
| Synchronize Config | 100.64.64.2 |
| Verify peer | deaktiviert |
| Remote System Username | root |
| Remote System Password | 123Start$ |
| Services | Aliases, Firewall Rules, NAT, Virtual IPs |
- "Verify peer" bleibt deaktiviert, weil die Web-GUI der Gegenstelle ein selbstsigniertes Zertifikat verwendet.
High Availability Konfiguration (Backup Firewall)
- Hier darf im Menü System => High Availability => Settings nur folgendes eingestellt werden:
| Parameter | Wert |
|---|---|
| Synchronize all states via | SYN |
| Sync compatibility | OPNsense 24.7 or above |
| Synchronize Peer IP | 100.64.64.1 |
| Synchronize Config | leer |
| Verify peer | deaktiviert |
| Remote System Username | leer |
| Remote System Password | leer |
| Services | Nothing selected |
- Synchronize Config muss auf dem Backup leer bleiben. Trägt man hier den Master ein, schieben sich beide Knoten gegenseitig ihre Konfiguration zu.
Client im LAN
- Der Windows-Client (192.168.1.50) muss als Gateway und als DNS-Server die LAN-VIP 192.168.1.3 erhalten – nicht die 192.168.1.1
- Wird die echte IP des Masters eingetragen, ist der Client beim Ausfall des Masters trotz funktionierendem Cluster offline
- Bei Verwendung eines DHCP-Servers muss dieser die VIP als Gateway und DNS verteilen
Endergebnis
Master
- System
- High Availability
- Status
- High Availability
- Hier müssen beide Spalten gefüllt sein und auf beiden Knoten die gleichen Dienste erscheinen:
| Service | Description |
|---|---|
| configd | System Configuration Daemon |
| cron | Cron |
| hostwatch | Host discovery service |
| login | Users and Groups |
| ntpd | Network Time Daemon |
| openssh | Secure Shell Daemon |
| pf | Packet Filter |
| routing | System routing |
| sysctl | System tunables |
| syslog-ng | Syslog-ng Daemon |
| unbound | Unbound DNS |
| webgui | Web GUI |
Slave
- System
- High Availability
- Status
- High Availability
- Auf der Backup-Firewall erscheint hier die Warnung, dass die XMLRPC-Konfiguration unvollständig sei.
- Diese Meldung kann ignoriert werden: Das Backup empfängt nur passiv, deshalb sind die XMLRPC-Felder dort absichtlich leer und werden fälschlicherweise als "unvollständig" markiert. Solange die Master-Firewall auf Version 24.7 eingestellt ist und die Daten erfolgreich überträgt, arbeitet der Cluster technisch einwandfrei.
Failover testen
- Auf dem Client einen Dauerping nach draußen starten:
ping -t 192.168.HS.254
- Auf dem Master: System => High Availability => Status => Enter persistent CARP maintenance mode
- Erwartetes Verhalten:
- Das Backup wechselt für beide VHIDs von BACKUP auf MASTER
- Der Ping läuft weiter oder verliert maximal ein bis zwei Pakete
- Kontrolle auf beiden Knoten:
ifconfig em0 ; ifconfig em1
- Zurückschalten über Leave persistent CARP maintenance mode auf dem Master
- Zweiter Test: Master hart ausschalten (VM stoppen) und das gleiche Verhalten prüfen
Debugging
Vorgehen
- Von unten nach oben prüfen: erst CARP (Layer 2/3), dann pfsync (States), dann XMLRPC (Konfiguration)
- Ein Cluster kann sauber synchronisieren und trotzdem kein Failover können – und umgekehrt
Schritt 1: CARP-Status
- Auf beiden Knoten:
ifconfig em0 ; ifconfig em1
- Erwartetes Bild:
| Knoten | LAN (vhid 100) | WAN (vhid X) |
|---|---|---|
| Master | MASTER, advskew 0 | MASTER, advskew 0 |
| Backup | BACKUP, advskew 100 | BACKUP, advskew 100 |
- In der GUI: Interfaces => Virtual IPs => Status
- CARP global ein-/ausgeschaltet:
sysctl net.inet.carp.allow
- Ausführlichere CARP-Meldungen im Log einschalten:
sysctl net.inet.carp.log=2
Schritt 2: Beide Knoten sind MASTER (Split Brain)
- Das häufigste Fehlerbild im Labor. Beide Knoten sehen den Partner nicht und übernehmen die VIP.
- Prüfen, ob die Advertisements des Partners überhaupt ankommen:
tcpdump -n -i em1 proto 112 tcpdump -n -i em0 proto 112
- Man muss die Pakete beider Knoten sehen (Quelle = jeweils die echte Interface-IP)
| Beobachtung | Ursache |
|---|---|
| Nur eigene Pakete sichtbar | Promiscuous-Modus im Hypervisor fehlt |
| Gar keine Pakete sichtbar | VIP nicht angelegt oder Interface down |
| Pakete kommen an, trotzdem beide MASTER | CARP-Allow-Regel auf dem Interface fehlt |
| Pakete mit fremder VHID | Anderer Teilnehmer nutzt die gleiche VHID |
- Weitere Ursachen bei Split Brain:
- Unterschiedliches Virtual Password auf den beiden Knoten
- Unterschiedliche VHID auf den beiden Knoten
- Unterschiedliche Advertising Frequency
Schritt 3: Kein Failover trotz korrektem Status
- Demotion-Zähler prüfen – ist er größer als 0, bleibt der Knoten absichtlich BACKUP:
sysctl net.inet.carp.demotion
- Ursachen für einen erhöhten Zähler: ein Interface ist down, ein Dienst startet nicht sauber, der Knoten befindet sich noch im Maintenance Mode
- Maintenance Mode verlassen: System => High Availability => Status => Leave persistent CARP maintenance mode
Schritt 4: Sicht des Clients
- Auf dem Windows-Client prüfen, welche MAC hinter dem Gateway steckt:
arp -a
- Die LAN-VIP muss auf eine virtuelle MAC zeigen, nicht auf die echte NIC des Masters:
192.168.1.3 00-00-5e-00-01-64
- Das letzte Byte ist die VHID in hex – 0x64 = 100
- Steht dort eine 08:00:27:... Adresse, arbeitet der Client an CARP vorbei direkt mit einer echten Firewall-IP
- Nach einem Failover darf sich der ARP-Eintrag nicht ändern – genau das ist der Sinn der virtuellen MAC
Schritt 5: Zustandssynchronisation (pfsync)
- Läuft der pfsync-Verkehr über das SYN-Netz?
tcpdump -n -i em2 proto 240
- Anzahl der States auf beiden Knoten vergleichen – die Werte müssen ähnlich sein:
pfctl -s state | wc -l
- Ist die Zahl auf dem Backup dauerhaft 0, überträgt pfsync nicht:
- Synchronize Peer IP zeigt auf die falsche Adresse
- Die Allow-Regel auf dem SYN-Interface fehlt
- SYN-Interface auf einem der Knoten nicht aktiviert
Schritt 6: Konfigurationssynchronisation (XMLRPC)
- Auf der Master-Firewall: System => Log Files => General, im Filter-Feld xmlrpc eingeben
- Gesucht wird ein Eintrag wie: [OK] Configuration synchronized to https://100.64.64.2:443
- Manuell anstoßen: System => High Availability => Status => Synchronize and reconfigure all (Push)
- Gleichzeitig auf dem Backup mitlesen:
tail -f /var/log/configd/latest.log
| Meldung | Ursache |
|---|---|
| Connection refused | Web-GUI des Backups nicht erreichbar oder falscher Port |
| Authentication failed | Falsches Passwort unter "Remote System Password" |
| Certificate verify failed | "Verify peer" ist aktiviert, Zertifikat ist selbstsigniert |
| Endlose Sync-Schleife | Auf dem Backup ist "Synchronize Config" gefüllt – muss leer sein |
Schritt 7: Ausgehende Verbindungen
- Prüfen, mit welcher Quelladresse der Client nach draußen geht:
tcpdump -n -i em1 host 192.168.HS.254
- Es muss die Virtual WAN IP (192.168.HS.X+20) erscheinen, nicht die echte IP des Masters
- Erscheint die echte IP: Outbound-NAT steht noch auf automatisch, oder in der manuellen Regel wurde als Target nicht die VIP gewählt
Nützliche Befehle im Überblick
| Befehl | Zweck |
|---|---|
| ifconfig em0 ; ifconfig em1 | CARP-Rolle und VIPs anzeigen |
| tcpdump -n -i em1 proto 112 | CARP-Advertisements mitlesen |
| tcpdump -n -i em2 proto 240 | pfsync-Verkehr mitlesen |
| wc -l | Anzahl der Firewall-States |
| sysctl net.inet.carp.demotion | Demotion-Zähler |
| sysctl net.inet.carp.log=2 | Ausführliches CARP-Logging |
| tail -f /var/log/configd/latest.log | Konfigurationssync mitlesen |
| configctl filter reload | Regelwerk neu laden |
Netzwerkschnittstellen
Master
em0: flags=1008943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST,LOWER_UP> metric 0 mtu 1500 description: LAN (lan) options=48500b8<VLAN_MTU,VLAN_HWTAGGING,JUMBO_MTU,VLAN_HWCSUM,VLAN_HWFILTER,VLAN_HWTSO,HWSTATS,MEXTPG> ether 08:00:27:c4:a2:6f inet 192.168.1.1 netmask 0xffffff00 broadcast 192.168.1.255 inet 192.168.1.3 netmask 0xffffff00 broadcast 192.168.1.255 vhid 100 carp: MASTER vhid 100 advbase 1 advskew 0 peer 224.0.0.18 peer6 ff02::12 media: Ethernet autoselect (1000baseT <full-duplex>) status: active nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>
em1: flags=1008943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST,LOWER_UP> metric 0 mtu 1500 description: WAN (wan) options=48500b8<VLAN_MTU,VLAN_HWTAGGING,JUMBO_MTU,VLAN_HWCSUM,VLAN_HWFILTER,VLAN_HWTSO,HWSTATS,MEXTPG> ether 08:00:27:61:6f:6d inet 192.168.HS.X+40 netmask 0xffffff00 broadcast 192.168.HS.255 inet 192.168.HS.X+20 netmask 0xffffff00 broadcast 192.168.HS.255 vhid X carp: MASTER vhid X advbase 1 advskew 0 peer 224.0.0.18 peer6 ff02::12 media: Ethernet autoselect (1000baseT <full-duplex>) status: active nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>
Backup
em0: flags=1008943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST,LOWER_UP> metric 0 mtu 1500 description: LAN (lan) options=48500b8<VLAN_MTU,VLAN_HWTAGGING,JUMBO_MTU,VLAN_HWCSUM,VLAN_HWFILTER,VLAN_HWTSO,HWSTATS,MEXTPG> ether 08:00:27:1c:44:8b inet 192.168.1.2 netmask 0xffffff00 broadcast 192.168.1.255 inet 192.168.1.3 netmask 0xffffff00 broadcast 192.168.1.255 vhid 100 carp: BACKUP vhid 100 advbase 1 advskew 100 peer 224.0.0.18 peer6 ff02::12 media: Ethernet autoselect (1000baseT <full-duplex>) status: active nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>
em1: flags=1008943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST,LOWER_UP> metric 0 mtu 1500 description: WAN (wan) options=48500b8<VLAN_MTU,VLAN_HWTAGGING,JUMBO_MTU,VLAN_HWCSUM,VLAN_HWFILTER,VLAN_HWTSO,HWSTATS,MEXTPG> ether 08:00:27:95:9b:4b inet 192.168.HS.X+60 netmask 0xffffff00 broadcast 192.168.HS.255 inet 192.168.HS.X+20 netmask 0xffffff00 broadcast 192.168.HS.255 vhid X carp: BACKUP vhid X advbase 1 advskew 100 peer 224.0.0.18 peer6 ff02::12 media: Ethernet autoselect (1000baseT <full-duplex>) status: active nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>
- Der Master hat advskew 0, das Backup advskew 100. Diesen Wert setzt OPNsense beim Sync automatisch – er wird nicht von Hand vergeben.
Wird Synchronisiert?
- Master
- System
- High Availability
- Status
- Synchronize and reconfigure all (Push)
- Status
- High Availability
- Backup
tail -f /var/log/configd/latest.log
- Hier sollte man die Übertragung sehen.
