Bericht zum Firewall-Ausfall am 8.8.

Daniel Baumann daniel.baumann at bfh.ch
Wed Aug 9 07:12:57 CEST 2023


Hallo zusammen

Hier ist der Bericht zum Ausfall der Firewall fuer Cage-A vom 8.8 von
~15:37 bis 16:45 Uhr.

Das folgende Post-Mortem dient einerseits der internen Dokumentation des
Vorfalls an sich und andererseits als Erlaeuterung des Setups im Sinne
des internen Knowhow-Transfers. Wir teilen dies wie immer auf dieser
Liste mit allen technisch interessierten Personen.


1. Was wir machen wollten
=========================

Bevor wir im Rollout der Netzwerk-Gesamterneuerung nach Abschluss der
Solothurnstrasse mit dem naechsten Standort beginnen koennen, stehen
ein paar blockierende Aufraeum- und Korrektur-Arbeiten an.

Eine dieser Arbeiten ist der Wechsel der Netzwerkkarten in allen
Firewalls des neuen Netzes zur notwendigen
Bandbreiten-Kapazitaetserhoehung fuers neue Netz von bisher
8x 10G = 80gbit/s auf 8x 25G = 200gbit/s pro Server an.

Wir haben insgesamt vier HA-Firewalls bestehend jeweils aus zwei Servern:

  * Firewall 1+2: Cage-A (= Basis-Dienste und alle LNI Systeme)

  * Firewall 3+4: Cage-B (= alle MSC und IDS Systeme)

  * Firewall 5+6: Fabric 1 bis 5 (= alle Standorte im neuen Netz)

  * Legacy 1+2: Cisco Netz (= alle Standorte im alten Netz, hier ist
    kein Upgrade notwendig/sinnvoll weil die alten Switches und APs
    nicht mehr Traffic von den Standorten liefern koennen)

Nachdem wir im April bereits alle Basis-Dienste und eines von vier Racks
im Cage-A mit LNI Systemen auf 25G migriert haben, stand gestern das
Upgrade von Firewall 1+2 fuer Cage-A an. Da unsere Linux
Container-Platform bei allen Systemen identisch ist (stimmt mit einziger
Aussnahme der Firewalls nicht gans, siehe spaeter), ist dies eine
einfach durchzufuehrende Aenderung die wir schon viele Male gemacht haben.


2. Wie mans richtig gemacht haette
==================================

Der normale und korrekte Ablauf der Migration eines HA-Systems
funktioniert vollstaendig unterbruchsfrei und laeuft folgendermassen,
hier am Beispiel mit Firewall 1+2, ab:

  1. Failover der aktiven Container von Firewall 1 auf Firewall 2

  2. Firewall 1: alle Container herunterfahren
  3. Firewall 1: neueste Updates auf dem Hostsystem installieren
                 (wir haben am Vormittag noch ein wichtiges Kernel-
                 Security Update ins Repository hochgeladen)
  4. Firewall 1: Hostsystem runterfahren und Netzwerkkarten/SFPs
                 tauschen
  5. Firewall 1: SFPs am Switch tauschen
  6. Firewall 1: Hostsystem wieder starten
  7. Firewall 1: Netzwerk-Interfacenamen auf Aenderung pruefen und ggf.
                 in system-networkd Bonding-Konfiguration (LACP) des
                 Hostsystems anpassen und Hostsystem nochmal rebooten.
  8. Firewall 1: alle Container starten

  9. Failover von Firewall 2 auf Firewall 1

 10. Firewall 2 analog Firewall 1 auf 25G migrieren
 11. Profit :)


3. Welche Fehler wir gemacht haben
==================================

3.1 Ausgangslage: LACP fuer alle LNI Hostsysteme, ausser Firewalls
------------------------------------------------------------------

Alle unsere Linux-Server haben einen 1G Kupfer Netzwerk-Port fuers
Sysadmin/Management sowie mehrere 10G oder neu 25G Glasfaser Netzwerk-Ports.

Die Glasfaser-Ports sind auf allen Systemen zu einem einzigen logischen
Interfaces zusammengefasst (Bonding mit LACP active-active) fuer mehr
Bandbreite und Redundanz.

Auf dem Bonding-Interface sind dann die VLAN-Interfaces, dadrauf die
Bridges pro VLAN und wiederum darauf sind dann die veth-Interfaces der
Container "eingesteckt".

Dass alle physikalischen Interfaces in einem einzigen Bond sind statt
jeweils einem Bond pro VLAN, ist nicht nur oekonomisch sinnvoll (braucht
weniger Netzwerk-Ports und Switch-Ports), sondern hat auch fuer die
darauf laufenden Container mehrere Vorteile:

  * insgesamt bessere Ausnutzung der verfuegbaren Bandbreite fuer
    einzelne Systeme (Peaks moeglich statt unnoetiger Drosselung)

  * insgesamt hoehere Redundanz der verfuegbaren Redundanz

  * simplere und dadurch robustere Konfiguration der Netzwerk-Interfaces
    auf dem Hostsystem

  * einfachere Verkabelung mit den Switches (die Reihenfolge spielt
    keine Rolle - Ports koennen innerhalb des einen grossen Bonds
    beliebig verkabelt werden und muessen nicht pro einzelnem
    Port-zu-Port individuell zugeordnet/verwaltet werden).

Insbesondere der letzte Punkt spart enorm Zeit und veringert die
Komplexitaet massiv - in der ersten Iteration der Science DMZ im
2015/2016 mussten wir dies noch mit riesigen Papier-Listen manuell
tracken und in einem Fehlerfall in tagelanger(!) Arbeit mehrere hunderte
Kabel-zu-Ports manuell nachkontrollieren um einen einzigen Fehler finden
zu koennen. Durch die reihenfolgenunabhaengige, "blockweise"
Konfiguration der Ports enfaellt dies und mit LLDP auf den Hostsystemen
sehen wir trotzdem (automatisch) die individuellen
Interface-Informationen (d.h. welcher Serverport ist auf welchem
Switchport eingesteckt).


3.2 Ausgangslage: LACP fuer alle Firewall Hostsysteme
-----------------------------------------------------

Firewall Hostsysteme sind grundsaetzlich analog aller anderen LNI
Hostsysteme konfiguriert, mit einer entscheidenden Abweichung:

Auf den Firewalls wird Traffic gefiltert, d.h. es kommen
Netzwerk-Verbindungen auf der einen Seite rein, passieren die Filter,
und verlassen die Firewall auf der anderen Seite wieder.

Aus historischen Gruenden sind die Firewalls des neuen Netzes
netzwerk-technisch aehnlich Konfiguriert wie die Firewalls des alten
Netzes (mit welcher wir im 2020 die Checkpoint Firewall-Appliances
abgeloest haben). Dort hatten wir (im Wesentlichen) drei Interfaces:
Intern, Extern und DMZ.

Weil es dort also mehr als nur Intern+Extern gab, haben wir dort jeweils
einen Bond pro "Funktion" gemacht mit der Absicht, ein Hardware-QoS als
physikalische DoS-Protection als Last Resort noch auf Interface-Ebene zu
haben.

Konkret: wuerde z.B. (aus welchen Gruenden auch immer) die DMZ mit
Traffic ueberlastet, haette dies keine Auswirkung auf den Rest des
Netzes und umgekehrt.

Physikalisch bedeutet das (um Ausfaelle von einer von zwei
Netzwerk-Karten abfangen zu koennen), dass die Bonds kartenuebergreifend
konfiguriert sind, d.h. die linken 2 Ports der ersten Karte mit den
linken 2 Ports der zweiten Karte sind ein Bond, und die rechten 2 Ports
der ersten Karte mit den rechten 2 Ports der zweiten Karte sind ein Bond.

Der Nachteil dieser Konfiguration ist, dass mit mehreren Bonds
natuerlich die Zuordnung der Switch-Ports auf die Netzwerk-Ports
innerhalb der Ports fuer das jeweiligen Hostsystems stimmen muss.

Im neuen Netz haben wir keine traditionellen Perimeter-Firewalls mehr
sondern den moderneren Ansatz einer Pod-Firewalls, d.h. die Firewalls
stehen einer Fabric als "Durchlauf-Erhitzer" vor und pruefen den
gesamten (auch internen) Traffic.

Damit gibt es aus Firewall-Sicht nur noch Intern+Extern und ein fixes
Zuordnen der Interfaces auf mehrere Bonds bringt nichts mehr: wenn z.B.
der externe Bond mit Traffic ueberlastet ist, bringt es nichts dass der
interne Bond davon nicht betroffen ist, weil ja aufgrund der
Ueberlastung des externen Bonds der Traffic-Durchfluss durch die
Firewall sowieso unterbrochen ist.

[ Security-technisch machts uebrigens auch keinen Unterschied: auf der
Firewall fliesst sowieso funktionsbedingt intern und extern zwingend
zusammen, als Durchlauf-Erhitzer (Edge-Switch -> Firewall ->
Edge-Switch) gilt gleiches auch fuer den Switch, womit die Segregierung
auf unterschiedliche physikalische Kabel daher nichts bringt. ]

Im Gegenteil ist eine solche Konfiguration mit mehreren Bonds sogar
kontraproduktiv:

  * wenn es nur einen grossen Bond gibt, kann dem typischerweise
    (und auch bei uns) auftretende asymmetrische Trafficaufkommen in
    modernen Umgebungen besser und schneller bewaeltigt werden weil
    ingesamt mehr ungebundene Ressourcen zur Verfuegung stehen

  * die Konfiguration der Netzwerk-Interfaces wie zuvor beschrieben
    vereinfacht sich auf dem Hostsystem enorm, ist dadurch robuster
    und das Tracking der Serverport zu Switchport entfaellt ebenfalls.

Diese und andere nicht kritische aber wichtige Aufraeumarbeiten schieben
wir seit laengerem vor uns hin, weil wir aufgrund von anderweitigen
Verzoegerungen Abstriche machen mussten um Termine trotzdem halten zu
koennen und waehren fuer nach dem Abschluss des Rollouts der
Netzwerk-Gesamterneuerung vorgesehen gewesen.


3.3 Ausgangslage: systemd-nspawn und systemd-Updates
----------------------------------------------------

Wir verwenden als Container-Engine systemd-nspawn.

In seltenen Faellen von gleichzeitigem systemd und Kernel-Update gibt
es eine Situation, bei denen die Highlevel-Containertools die laufenden
Container nicht mehr anzeigen koennen (Ausgabe einer Fehlermeldung
"could not enter /proc/ns/$pid namespace" statt des Auflistens der
Container).

Mit Lowlevel-Containertools sieht man die laufenden Container trotzdem
(nur halt ohne Komfort-Funktionen) und kann diese auch manipulieren
(starten, stoppen, etc.).

Ein Reload der notwendigen systemd-Komponente behebt dies (es ist kein
Reboot des Hostsystems notwendig) und sollte eigentlich automatisch
passieren. Das ist ein Bug im ensprechenden Debian Paket. Da dies sehr
selten auftritt und mit einem einzigen Kommando behebbar ist, haben wir
aus Zeitgruenden die Patches dafuer nie fertig gemacht und auf die lange
Bank geschoben.


3.4 Fehler #1: nach Wechsel auf 25G waren Ports falsch verkabelt
----------------------------------------------------------------

Bei Schritt 5 von "Wie mans richtig gemacht haette" wurde beim Wechsel
der SFPs beim Switch die Kabel nicht wieder auf den gleichen Switch-Port
eingesteckt. Korrekt waere sequentiell "ein einzelnes Kabel raus, SFP
Wechseln, Kabel am gleichen Port wieder einstecken" gewesen statt "alle
Kabel einer Karte raus, SFPs Wechseln, alle Kabel einer Karte (in nicht
mehr identischen Ports) wieder einstecken

Wie oben erlaeutert spielt das beim normalen/richtigen Setup mit nur
einem Bond keine Rolle, da die Firewalls aber noch zwei Bonds hatten
spielts leider eine Rolle.

Der Effekt war, dass der interne Bond mind. eine Vertauschung hatte,
so dass zwei von vier Ports falsch/disfunktional waren.


3.5 Fehler #2: nach Wechsel auf 25G Interface-Namen nicht geprueft
------------------------------------------------------------------

Bei Schritt 7 von "Wie mans richtig gemacht haette" wurde nach dem
Wechsel der Netzwerkkarten nicht ueberprueft, ob sich die
Interface-Namen geaendert haben.. was "natuerlich" der Fall war (Murphy
laesst gruessen :)

Da die Bonds aus jeweils vier Interfaces auf zwei Karten verteilt
bestehen, waehre aufgrund von Fehler #1 immer noch 2 Interfaces richtig
gewesen. Zusammen mit Fehler #2 war eine Karte (deren Name gewechselt
hat) komplett inaktiv, somit waren alle 4 Interfaces des internen Bonds
offline mit dem Effekt, dass Firewall1 Traffic von ausserhalb Cage-A
angenommen haette, aber nichts in Cage-A gekommen waere und von Cage-A
nichts nach aussen gekommen waere.


3.6 Fehler #3: System-Upgrade auf Firewall 2 vor Failover auf Firewall 1
------------------------------------------------------------------------

Anstatt strickt sequenziellem Vorgehen, bei welchem zuerst Firewall 1
komplett fertig gemacht wird mit einem Anschliessenden Failover von
Firewall 2 auf Firewall 1, wurde waehrend dem Firewall 1 noch nicht
fertig war, parallel dazu auf Firewall 2 schon System-Updates eingespielt.

Dabei ist der in 3.3 beschrieben Fall eingetreten, was die weitere
Administration der Container aufgrund des "Wegfalls" der Highlevel-Tools
erschwert hat.


3.7 Fehler #4: Failover auf Firewall 1 ohne Check von Firewall 2
----------------------------------------------------------------

Der HA-Check der Firewalls geht ueber den internen Bond, d.h. wenn beide
Firewall-Container sich gegenseitig sehen, ist alles gut und nur ein
System ist aktiv.

Sehen sie sich nicht mehr, funktionerts der HA-Abgleich nicht mehr und
jeder Firewall-Container haelt den anderen fuer tot und setzt sich
selbst als aktiv (d.h. legt die HA-IP Adressen auf die Interfaces).

Aufgrund von Fehler #1 und #2 war der interne Bond unterbrochen, so dass
beim Starten der Container auf Firewall 1 sich diese als aktiv gesetzt
hat. Die Container auf Firewall1 haben aber nicht korrekt funktioniert,
weil ja der interne Bond unterbrochen war und somit kein Traffic
fliessen konnte.

[ Fun-Fact:

  * Bei IPv4 gibt es keine Protokoll-inherente Massnahmen um doppelte
    IPs im Netz zu erkennen, so dass wenn zweimal die gleiche IP
    verwendet wird, "Random"-Resultate auftreten.

  * Bei IPv6 wird mittels DAD vor Benutzung einer Adresse diese auf
    allfaellige doppelte Verwendung geprueft und im positiven Fall
    nicht verwendet (d.h. Interface ist oben, aber hat keine IPv6 IP).

  * In obiger Konstellation sehen sich zwar durch den Unterbruch
    des internen Bonds die Firewalls nicht mehr, fuer IPv6 funktioniert
    DAD aber trotzdem - d.h. die Container auf Firewall 1 hatten keine
    IPv6 Adresse weil bereits auf Container von Firewall 2 in Benutzung.

    Somit floss der IPv6 Traffic weiterhin ueber die zu diesem Zeitpunkt
    noch funktionierenden Container auf Firewall 2 und wir, an anderen
    Dingen arbeitend, haben von allem bisherigen nichts gemerkt da wir
    nur mit unseren Systemen gearbeitet haben, welche vollstaendig
    Dualstack haben und weiterhin erreichbar waren. ]

Aufgrund von Fehler #3 konnten/wurden die Container auf Firewall 2 nicht
richtig gestoppt. Zur Behebung des "Split-Brains" mussten alle Container
auf Firewall 2 komplett runtergefahren werden. Dadurch verloren wir den
Zugang auf das Host-System von Firewall 1.


3.8 Fehler #5: OOB Zugang auf Firewalls funktioniert nicht
----------------------------------------------------------

Durch einen noch zu flickenden Bug funktioniert der Zugang auf die
Firewalls via das Out-of-Band Netz mit separatem Internet-Uplink nicht
(alle anderen Systeme sind via Out-of-Band erreichbar, nur die Firewalls
nicht), auch dies ein Item auf unserer TODO-Liste das auf spaeter
wegpriorisiert werden musste.

Dadurch war es nicht moeglich, ohne physikalischen Zugang auf eines der
beiden Firewall Hostsysteme zu gelangen.


3.9 Fehler #6: Physikalischer Firewall-Bypass nicht fertig gemacht
------------------------------------------------------------------

Fuer Faelle wie diese haben wir noch einen Joker in der Hand:

  * beim Aufbau von Cage-A haben wir auf den Edge-Switches die
    entsprechenden Kabel gesteckt, die die Firewall komplett
    ueberbruecken.

  * fallen aus welchen Gruenden auch immer die Firewalls von Cage-A aus,
    so ist de-fakto das gesamte BFH Netz "tot" weil (noch) alle Basis-
    Dienste ausschliesslich in Cage-A sind.

    [ Dies ist Design-technisch nicht so vorgesehen: einerseits waere
    dies gelindert mit dem Fabric-Split fuer die Basis-Dienste, welcher
    aufgrund von Prioritaeten von 2022 auf 2023 und so wies im Moment
    ausschaut wohl weiter auf Anfang 2024 verschoben werden musste/muss,
    andererseits sind pro Fabric individuelle Instanzen aller Basis-
    Dienste vorgesehen (Vorbedingung ist Fabric Split, diese kommen also
    erst danach). ]

  * um in einem solchen Fall so schnell wie moeglich alle Basis-Dienste
    wieder erreichbar zu haben, koennen wir auf den Edge-Switches eine
    einzige Route aendern, so dass der Traffic statt ueber die Firewalls
    ungefiltert ueber ein "Direkt-Kabel" in den Cage-A kommt.

  * da alle unsere Systeme in Cage-A so gebaut sind, dass sie Security-
    technisch auch direkt im Internet stehen koennten, ist eine solche
    zeitlich beschraenkte Notfallmassnahme bis die Firewalls wieder
    laufen wuerden, voellig unbedenklich.

Leider wurde das "Direkt-Kabel" auf den Edge-Switches aus
Priotiaetsgruenden noch nicht vollstaendig konfiguriert, so dass wir
diesen Joker nicht nutzen konnten, ansonsten waere der Ausfall nur kurz
gewesen.


4. Was wir nach dem Ausfall gemacht haben
=========================================

4.1 Firewalls laufen wieder
---------------------------

Nachdem wir keine Verbindung mehr hatten, sind wir um 16 Uhr ins
Rechenzentrum gegangen und haben beide Firewall Hostsysteme runtergefahren.

Danach haben wir Firewall 1 hochgefahren, Interface-Namen korrigiert und
die falsch eingesteckten Ports entdeckt.

Zur schnellen Service-Wiederherstellung haben wir Firewall 2 wieder
hochgefahren (da waren ja noch alle Interfaces und Ports unveraendert)
so dass um 16:45 Uhr alle Verbindungen Dienste wieder wie gewohnt
verfuegbar waren.


4.2 Bonding vereinheitlicht
---------------------------

Danach haben wir uns kurz ueber "wie sollte es richtig sein" bezueglich
der Bonds auf den Firewalls unterhalten und, sequentiell, alle
Konfigurationen unterbruchsfrei angepasst so dass wir um 18:15 fertig
waren und auf beiden Firewalls nur einen Bond haben (vergl. Punkte 3.1
und 3.2).

Wir werden dies auf den Firewalls fuer Cage-B und fuer die
Standort-Fabrics in den naechsten Tagen im Zuge der 25G Migration
ebenfalls so, aber ohne Ausfall, umsetzen.


5. Meta
=======

Auch wenn (unser) Management das nicht gerne hoert - der Raubbau durch
Sub-MVP Umsetzung von teilweise kritischen Infrastruktur-Teilen zur
Kompensation von anderweitigen Verzoegerungen um Termine halten zu
koennen hat halt Konsequenzen was Ausfaelle angeht (Risikoakzeptanz
schuetzt davor nicht).

Es ist uns sehr wichtig und ein grosses Anliegen, dass wir den
Sollzustand bis Ende 2024 erreichen koennen. Es fehlt nicht mehr viel,
aber die Arbeit muss trotzdem gemacht werden und gemacht werden koennen.

Wenn wir die aktuell ausgeschriebenen Stellen besetzen koennen (siehe
dazu separate Mail spaeter) sind wir zuversichtlich, dass wir dies tun
koennen.


Wir entschuldigen uns fuer den Ausfall und die daraus entstandenen
Unannehmlichkeiten, und hoffen auf euer Verstaendnis.

Gruesse,
Daniel

-- 
Berner Fachhochschule / Bern University of Applied Sciences
Services / IT-Services
Daniel Baumann
Teamleiter Linux & Infrastructure Services
___________________________________________________________
Dammweg 3, CH-3013 Bern
Telefon direkt +41 31 848 48 22
Telefon Servicedesk +41 31 848 48 48
daniel.baumann at bfh.ch
https://bfh.ch
https://bfh.science


More information about the bfh-linux-announce mailing list