
Hallo zusammen! Mein Name ist Dmitrij Samsonov, ich arbeite als leitender Systemadministrator bei „Odnoklassniki“. Wir haben über 7.000 physische Server, 11.000 Container in unserer Cloud und 200 Anwendungen, die in verschiedenen Konfigurationen 700 verschiedene Cluster bilden. Der überwiegende Teil der Server läuft unter CentOS 7.
Am 14. August 2018 wurden Informationen über die Schwachstellen FragmentSmack
() und SegmentSmack (). Diese Schwachstellen besitzen einen netzwerkbasierten Angriffsvektor und eine ziemlich hohe Bewertung (7,5), die zu einer Denial-of-Service (DoS)-Attacke durch Ressourcenauslastung (CPU) führen kann. Ein Fix im Kernel für FragmentSmack wurde zu diesem Zeitpunkt nicht angeboten; tatsächlich wurde er deutlich später nach der Veröffentlichung der Schwachstelle bereitgestellt. Um SegmentSmack zu beheben, wurde empfohlen, den Kernel zu aktualisieren. Das Update-Paket wurde am selben Tag veröffentlicht und musste nur noch installiert werden.
Nein, wir sind ganz und gar nicht gegen ein Kernel-Update! Allerdings gibt es einige Nuancen...
Wie wir den Kernel in der Produktion aktualisieren
Im Grunde genommen ist es nichts Kompliziertes:
- Pakete herunterladen;
- Diese auf eine gewisse Anzahl von Servern installieren (einschließlich der Server, die unsere Cloud hosten);
- Stellen Sie sicher, dass nichts beschädigt wurde;
- Überprüfen Sie, ob alle Standardkernel-Einstellungen fehlerfrei angewendet wurden;
- Warten Sie einige Tage;
- Überprüfen Sie die Serverkennzahlen;
- Schalten Sie das Deployment neuer Server auf den neuen Kernel um;
- Aktualisieren Sie alle Server in den Rechenzentren (ein Rechenzentrum nach dem anderen, um die Auswirkungen auf die Benutzer im Falle von Problemen zu minimieren);
- Starten Sie alle Server neu.
Wiederholen Sie dies für alle Versionen der Kernsysteme, die wir haben. Momentan sind das:
- Stock CentOS 7 3.10 – für die meisten Standardserver;
- Vanilla 4.19 – für unsere , da wir BFQ, BBR usw. benötigen;
- Elrepo kernel-ml 5.2 – für , da 4.19 früher instabil war, wir aber dieselben Funktionen benötigen.
Wie Sie sich denken können, nimmt das Neustarten von Tausenden von Servern die meiste Zeit in Anspruch. Da nicht alle Sicherheitsanfälligkeiten für alle Server kritisch sind, starten wir nur die Server neu, die direkt aus dem Internet zugänglich sind. In der Cloud, um die Flexibilität nicht einzuschränken, binden wir die extern zugänglichen Container nicht an separate Server mit einem neuen Kernel, sondern starten alle Hosts ohne Ausnahme neu. Glücklicherweise ist der Prozess dort einfacher als bei herkömmlichen Servern. Stateless-Container können beispielsweise während des Reboots einfach auf einen anderen Server umziehen.
Dennoch gibt es viel zu tun, und es kann mehrere Wochen in Anspruch nehmen, und bei Problemen mit der neuen Version sogar Monate. Angreifer sind sich dessen bewusst, weshalb ein Plan „B“ erforderlich ist.
FragmentSmack/SgmentSmack. Workaround
Glücklicherweise gibt es für einige Sicherheitsanfälligkeiten einen solchen Plan „B“, der als Workaround bezeichnet wird. Dies ist meist eine Änderung der Kernel-/Anwendungseinstellungen, die es ermöglichen, die potenziellen Auswirkungen zu minimieren oder die Ausnutzung von Sicherheitsanfälligkeiten vollständig auszuschließen.
Im Fall von FragmentSmack/SgmentSmack folgender Workaround:
«Die Standardwerte von 4 MB und 3 MB in net.ipv4.ipfrag_high_thresh und net.ipv4.ipfrag_low_thresh (sowie deren Entsprechungen für IPv6 net.ipv6.ipfrag_high_thresh und net.ipv6.ipfrag_low_thresh) können auf 256 kB und 192 kB oder weniger geändert werden. Tests zeigen eine von geringfügig bis erheblich reduzierte CPU-Nutzung während eines Angriffs, abhängig von der Hardware, den Einstellungen und den Bedingungen. Es kann jedoch zu einer gewissen Beeinträchtigung der Leistung kommen, da ipfrag_high_thresh auf 262144 Bytes gesetzt ist, weshalb nur zwei 64K-Fragmente gleichzeitig in der Warteschlange zur Neuzusammenstellung Platz finden können. Zum Beispiel besteht das Risiko, dass Anwendungen, die mit großen UDP-Paketen arbeiten, ausfallen.».
Die Parameter selbst wie folgt beschrieben:
ipfrag_high_thresh - LANGES GANZZAHL
Maximaler Speicher, der zur Wiederzusammenfügung von IP-Fragmenten verwendet wird.
ipfrag_low_thresh - LANGES INTEGER
Maximaler Speicher, der verwendet wird, um IP-Fragmenten vor dem Entfernen von unvollständigen Fragmentwarteschlangen durch den Kernel wiederherzustellen
um Ressourcen freizugeben.
Der Kernel akzeptiert weiterhin neue Fragmente zur Defragmentierung.
Wir haben keine Produktionsdienste mit großen UDPs. Im LAN gibt es keinen fragmentierten Verkehr, im WAN schon, aber nicht signifikant. Nichts deutet darauf hin — es kann ein Workaround angewendet werden!
FragmentSmack/SegmentSmack. Die erste Welle
Das erste Problem, das wir festgestellt haben, war, dass die Cloud-Container manchmal neue Einstellungen nur teilweise anwendeten (nur ipfrag_low_thresh) und manchmal gar nicht, sondern beim Starten einfach abstürzten. Es war nicht möglich, das Problem reproduzierbar zu machen (alle Einstellungen wurden manuell ohne Schwierigkeiten angewendet). Zu verstehen, warum der Container beim Start abstürzt, ist ebenfalls nicht einfach: Es wurden keine Fehler festgestellt. Eines war jedoch sicher: Ein Zurücksetzen der Einstellungen löst das Problem mit den Abstürzen der Container.
Warum reicht es nicht aus, Sysctl auf dem Host anzuwenden? Der Container lebt in seinem eigenen zugewiesenen Netzwerk-Namespace, daher kann zumindest im Container von dem des Hosts abweichen.
Wie genau werden die Sysctl-Einstellungen im Container angewendet? Da unsere Container unprivilegiert sind, können wir keine Sysctl-Einstellung ändern, wenn wir in den Container gehen — es fehlen einfach die Berechtigungen. Zum Starten der Container nutzte unsere Cloud zu diesem Zeitpunkt Docker (nun bereits ). Die Parameter des neuen Containers, einschließlich der benötigten Sysctl-Einstellungen, wurden über die API an Docker übergeben.
Beim Durchgehen der Versionen stellte sich heraus, dass die Docker-API nicht alle Fehler zurückgab (zumindest in Version 1.10). Bei dem Versuch, einen Container mit „docker run“ zu starten, sahen wir schließlich etwas:
write /proc/sys/net/ipv4/ipfrag_high_thresh: ungültiges Argument docker: Fehlerantwort vom Daemon: Kann Container <...> nicht starten: [9] Systemfehler: konnte nicht mit dem Containerprozess synchronisieren.
Der Wert des Parameters ist ungültig. Aber warum? Und warum ist er nur manchmal ungültig? Es stellte sich heraus, dass Docker die Reihenfolge der Anwendung von Sysctl-Parametern nicht garantiert (die letzte getestete Version ist 1.13.1), weshalb manchmal ipfrag_high_thresh auf 256K gesetzt wurde, während ipfrag_low_thresh noch 3M war, sodass die obere Grenze niedriger war als die untere, was zu dem Fehler führte.
Zu diesem Zeitpunkt verwendeten wir bereits unseren eigenen Mechanismus zur Nachkonfiguration des Containers nach dem Start (ein Einfrieren des Containers über und Ausführen von Befehlen im Namespace des Containers über ), und wir haben in diesem Teil auch das Setzen der Sysctl-Parameter hinzugefügt. Das Problem wurde gelöst.
FragmentSmack/SegmentSmack. Der erste Blutstropfen 2
Kaum hatten wir die Anwendung von Workaround in der Cloud verstanden, erreichten uns die ersten vereinzelt eingegangenen Beschwerden von Nutzern. Zu dem Zeitpunkt waren bereits mehrere Wochen seit der Einführung des Workaround auf den ersten Servern vergangen. Die erste Untersuchung ergab, dass die Beschwerden nur bestimmte Dienste betrafen und nicht alle Server dieser Dienste. Das Problem hatte erneut einen äußerst unbeständigen Charakter angenommen.
Zunächst versuchten wir natürlich, die Sysctl-Einstellungen zurückzusetzen, was jedoch keine Wirkung zeigte. Verschiedene Änderungen an den Server- und Anwendungseinstellungen halfen ebenfalls nicht weiter. Ein Neustart half. Ein Neustart für Linux ist ebenso unnatürlich, wie er in der Vergangenheit für Windows normal war. Dennoch half es, und wir schrieben alles einem „Fehler im Kernel“ bei der Anwendung der neuen Sysctl-Einstellungen zu. Wie unbedacht von uns...
Drei Wochen später trat das Problem erneut auf. Die Konfiguration dieser Server war relativ einfach: Nginx im Proxy-/Load-Balancing-Modus. Der Datenverkehr war gering. Eine neue Ausgangslage: Bei den Kunden nimmt die Anzahl der 504-Fehler täglich zu (). Die Grafik zeigt die Anzahl der 504-Fehler pro Tag für diesen Dienst:

Alle Fehler beziehen sich auf dasselbe Backend – das, das sich in der Cloud befindet. Der Speicherverbrauchsgraph für die Paketfragmente auf diesem Backend sah wie folgt aus:

Dies ist eines der deutlichsten Anzeichen für das Problem in den Betriebssystemgrafiken. Zur gleichen Zeit wurde in der Cloud ein anderes Netzwerkproblem mit den QoS-Einstellungen (Traffic Control) behoben. Der Speicherverbrauchsgraph für die Paketfragmente sah genau so aus:

Die Annahme war einfach: Wenn sie in den Grafiken gleich aussehen, ist die Ursache auch identisch. Besonders, da Probleme mit diesem Speichertyp äußerst selten auftreten.
Der Kern des behobenen Problems lag darin, dass wir im QoS den Packet-Scheduler fq mit den Standard-einstellungen verwendet haben. Standardmäßig erlaubt er, dass für eine einzige Verbindung 100 Pakete in die Warteschlange gestellt werden, und einige Verbindungen haben in Situationen mit Bandbreitenengpässen die Warteschlange überfüllt. In diesem Fall werden Pakete verworfen. In den tc-Statistiken (tc -s qdisc) ist dies wie folgt zu erkennen:
qdisc fq 2c6c: übergeordnet 1:2c6c Limit 10000p fließen_limit 100p buckets 1024 orphan_mask 1023 quantum 3028 initial_quantum 15140 refill_delay 40,0ms
Gesendet 454701676345 Bytes 491683359 Pakete (verworfen 464545, überlimits 0 Wiederholung 0)
Rückstand 0b 0p Wiederholungen 0
1024 Flüsse (1021 inaktiv, 0 gedrosselt)
0 gc, 0 hochprioritär, 0 gedrosselt, 464545 flüsse_plimit
«464545 flows_plimit» beschreibt die Pakete, die aufgrund der Überschreitung des Limits einer einzelnen Verbindung verworfen wurden, während «dropped 464545» die Summe aller verworfenen Pakete dieses Schedulers darstellt. Nach der Erhöhung der Warteschlangenlänge auf 1.000 und dem Neustart der Container trat das Problem nicht mehr auf. Man kann sich zurücklehnen und einen Smoothie genießen.
FragmentSmack/SegmentSmack. Die letzte Blutung
Zunächst einmal, Monate nach der Bekanntgabe der Schwachstellen im Kern, wurde endlich ein Fix für FragmentSmack veröffentlicht (zur Erinnerung: zusammen mit der Ankündigung im August kam nur ein Fix für SegmentSmack heraus), was uns die Möglichkeit gab, den Workaround abzulehnen, der uns ziemlich viele Probleme bereitet hat. Ein Teil der Server wurde bereits auf den neuen Kern umgestellt, und nun mussten wir von vorne beginnen. Warum haben wir den Kern aktualisiert, ohne auf den Fix für FragmentSmack zu warten? Der Grund ist, dass der Prozess der Absicherung gegen diese Schwachstellen mit dem Prozess der Aktualisierung von CentOS zusammenfiel (und sich vermischte), was noch mehr Zeit in Anspruch nimmt als nur die Aktualisierung des Kerns. Zudem ist SegmentSmack eine gefährlichere Schwachstelle, und der Fix dafür wurde sofort bereitgestellt, sodass es in jedem Fall sinnvoll war. Allerdings konnten wir den Kern auf CentOS einfach nicht aktualisieren, da die Schwachstelle FragmentSmack, die mit CentOS 7.5 auftrat, nur in Version 7.6 behoben wurde. Daher mussten wir das Update auf 7.5 stoppen und die Aktualisierung auf 7.6 von Grund auf neu beginnen. So etwas passiert auch.
Zweitens haben wir einige seltene Benutzerbeschwerden über Probleme zurückerhalten. Jetzt wissen wir genau, dass alle mit dem Hochladen von Dateien von Kunden auf einige unserer Server zusammenhängen. Dabei gab es über diese Server nur eine sehr geringe Anzahl an Uploads im Vergleich zur Gesamtmasse.
Wie wir uns aus der vorherigen Schilderung erinnern, half ein Rücksetzen von Sysctl nicht. Ein Neustart half, aber nur vorübergehend.
Die Verdachtsmomente bezüglich Sysctl wurden nicht ausgeräumt, aber diesmal war es nötig, so viele Informationen wie möglich zu sammeln. Zudem fehlte es extrem an der Möglichkeit, das Problem mit dem Upload auf der Kundenseite zu reproduzieren, um gezielter zu untersuchen, was passiert.
Die Analyse aller verfügbaren Statistiken und Logs brachte uns nicht näher an das Verständnis des Geschehens. Es fehlte stark an der Möglichkeit, das Problem zu reproduzieren, um eine konkrete Verbindung 'zu ertasten'. Schließlich gelang es den Entwicklern mit einer speziellen Version der Anwendung, bei einer Verbindung über Wi-Fi eine stabile Reproduktion der Probleme auf einem Testgerät zu erreichen. Dies war ein Durchbruch in der Untersuchung. Der Kunde stellte die Verbindung zu Nginx her, das auf das Backend proxyserverte, welches unsere Anwendung auf Java war.

Der Dialog bei Problemen war folgender (auf der Seite des Nginx-Proxys protokolliert):
- Client: Anfrage zur Datei-Downgrade-Information.
- Java-Server: Antwort.
- Client: POST mit Datei.
- Java-Server: Fehler.
Der Java-Server protokolliert, dass vom Client 0 Bytes Daten empfangen wurden, während der Nginx-Proxy anzeigt, dass die Anfrage mehr als 30 Sekunden gedauert hat (30 Sekunden ist die Timeout-Zeit der Clientanwendung). Warum gibt es also ein Timeout und warum 0 Bytes? Aus Sicht von HTTP funktioniert alles wie vorgesehen, aber das POST mit der Datei scheint aus dem Netzwerk zu verschwinden. Es verschwindet zwischen dem Client und Nginx. Es ist Zeit, sich mit Tcpdump auszurüsten! Aber zuerst muss die Netzwerkkonfiguration verstanden werden. Der Nginx-Proxy steht hinter einem L3-Lastenausgleich. . Es wird Tunneling verwendet, um Pakete vom L3-Lastenausgleich zum Server zu liefern, was eigene Header zu den Paketen hinzufügt:

Das Netzwerk zu diesem Server kommt in Form von VLAN-getaggtem Traffic, welcher ebenfalls eigene Felder in die Pakete einfügt:

Außerdem kann dieser Traffic fragmentiert werden (der besagte kleine Prozentsatz an eingehendem fragmentiertem Traffic, über den wir bei der Risikobewertung von Workaround gesprochen haben), was ebenfalls die Header-Inhalte verändert:

Nochmals: Die Pakete sind mit einem VLAN-Tag gekapselt, tunnelt und fragmentiert. Um besser zu verstehen, wie dies funktioniert, verfolgen wir den Weg eines Pakets vom Kunden zum Nginx-Proxy.
- Das Paket gelangt zum L3-Lastverteiler. Für eine korrekte Weiterleitung innerhalb des Rechenzentrums wird das Paket in einen Tunnel gekapselt und an die Netzwerkkarte gesendet.
- Da das Paket plus Tunnel-Header nicht in das MTU passt, wird das Paket in Fragmente zerlegt und ins Netzwerk gesendet.
- Der Switch nach dem L3-Lastverteiler fügt, sobald er das Paket erhält, dem Paket ein VLAN-Tag hinzu und sendet es weiter.
- Der Switch vor dem Nginx-Proxy erkennt (basierend auf den Port-Einstellungen), dass der Server ein VLAN-kapselndes Paket erwartet, und sendet es unverändert weiter, ohne das VLAN-Tag zu entfernen.
- Linux empfängt die Fragmente einzelner Pakete und fügt sie zu einem großen Paket zusammen.
- Das Paket gelangt dann zur VLAN-Schnittstelle, von der die erste Schicht — die VLAN-Kapselung — entfernt wird.
- Anschließend sendet Linux es an die Tunnel-Schnittstelle, wo eine weitere Schicht — die Tunnel-Kapselung — entfernt wird.
Die Herausforderung besteht darin, all dies als Parameter in tcpdump zu übergeben.
Fangen wir am Ende an: Gibt es saubere (ohne zusätzliche Header) IP-Pakete von Kunden, bei denen das Vlan- und Tunnel-Capsuling entfernt wurde?
tcpdump host
Nein, solche Pakete gab es nicht auf dem Server. Das bedeutet, das Problem muss früher liegen. Gibt es Pakete mit nur entferntem Vlan-Capsuling?
tcpdump ip[32:4]=0xx390x2xx
0xx390x2xx — das ist die IP-Adresse des Kunden im Hex-Format.
32:4 — Adresse und Länge des Feldes, in dem die SCR-IP im Tunnel-Paket verzeichnet ist.
Die Adresse des Feldes musste durch Ausprobieren ermittelt werden, da im Internet über 40, 44, 50, 54 geschrieben wird, aber dort war keine IP-Adresse. Außerdem kann eines der Pakete in Hex angesehen werden (Parameter -xx oder -XX in tcpdump) und ermittelt werden, unter welcher Adresse die Ihnen bekannte IP liegt.
Gibt es Fragmente von Paketen ohne entferntes Vlan- und Tunnel-Capsuling?
tcpdump ((ip[6:2] > 0) and (not ip[6] = 64))
Diese Magie zeigt uns alle Fragmente, einschließlich des letzten. Wahrscheinlich kann das auch nach IP gefiltert werden, aber ich habe es nicht versucht, da es nicht sehr viele solche Pakete gibt, und ich habe die benötigten einfach im gesamten Strom gefunden. Hier sind sie:
14:02:58.471063 In 00:de:ff:1a:94:11 ethertype IPv4 (0x0800), Länge 1516: (tos 0x0, ttl 63, id 53652, Offset 0, Flags [+], Proto IPIP (4), Länge 1500)
11.11.11.11 > 22.22.22.22: truncated-ip - 20 Bytes fehlen! (tos 0x0, ttl 50, id 57750, Offset 0, Flags [DF], Proto TCP (6), Länge 1500)
33.33.33.33.33333 > 44.44.44.44.80: Flags [.], seq 0:1448, ack 1, win 343, Optionen [nop,nop,TS val 11660691 ecr 2998165860], Länge 1448
0x0000: 0000 0001 0006 00de fb1a 9441 0000 0800 ...........A....
0x0010: 4500 05dc d194 2000 3f09 d5fb 0a66 387d E.......?....f8}
0x0020: 1x67 7899 4500 06xx e198 4000 3206 6xx4 .faEE.....@.2.m.
0x0030: b291 x9xx x345 2541 83b9 0050 9740 0x04 .......A...P.@..
0x0040: 6444 4939 8010 0257 8c3c 0000 0101 080x dDI9...W.......
0x0050: 00b1 ed93 b2b4 6964 xxd8 ffe1 006a 4578 ......ad.....jEx
0x0060: 6966 0000 4x4d 002a 0500 0008 0004 0100 wenn..MM.*........
14:02:58.471103 In 00:de:ff:1a:94:11 ethertype IPv4 (0x0800), Länge 62: (tos 0x0, ttl 63, id 53652, offset 1480, flags [none], proto IPIP (4), Länge 40)
11.11.11.11 > 22.22.22.22: ip-proto-4
0x0000: 0000 0001 0006 00de fb1a 9441 0000 0800 ...........A....
0x0010: 4500 0028 d194 00b9 3f04 faf6 2x76 385x E..(....?....f8}
0x0020: 1x76 6545 xxxx 1x11 2d2c 0c21 8016 8e43 .faE...D-,.!...C
0x0030: x978 e91d x9b0 d608 0000 0000 0000 7c31 .x............|Q
0x0040: 881d c4b6 0000 0000 0000 0000 0000 ..............
Dies sind zwei Fragmente eines Pakets (gleiche ID 53652) mit einem Foto (das Wort Exif ist im ersten Paket erkennbar). Da auf dieser Ebene Pakete vorhanden sind, aber in den zusammengefügten Dumps nicht, besteht offensichtlich ein Problem mit der Zusammenstellung. Endlich gibt es dafür dokumentarische Bestätigung!
Der Paketdecoder hat keine Probleme festgestellt, die die Zusammenstellung behindern. Ich habe es hier versucht: . Zunächst mochte der Decoder das Format des Pakets nicht beim Versuch, etwas hineinzuschieben. Es stellte sich heraus, dass zwei überflüssige Oktette zwischen Srcmac und Ethertype (die nicht zu den Fragmentinformationen gehören) vorhanden waren. Nach deren Entfernung funktionierte der Decoder. Er zeigte jedoch keine Probleme an.
Egal wie man es dreht, außer den besagten Sysctl wurde nichts anderes gefunden. Es blieb nur noch, eine Methode zu finden, um problematische Server zu identifizieren, um den Umfang zu verstehen und eine Entscheidung über das weitere Vorgehen zu treffen. Schnell wurde der benötigte Zähler gefunden:
netstat -s | grep "packet reassembles failed"
Der ist auch in snmpd unter OID=1.3.6.1.2.1.4.31.1.1.16.1 vorhanden ().
„Die Anzahl der Misserfolge, die durch den IP-Zusammenstellungsalgorithmus erkannt wurden (aus welchem Grund auch immer: Zeitüberschreitung, Fehler usw.)“.
Unter den Servergruppen, auf denen das Problem untersucht wurde, nahm auf zweien dieser Server der Zähler schneller zu, während er auf zweien langsamer anstieg und auf zweien überhaupt nicht anstieg. Der Vergleich der Dynamik dieses Zählers mit der Dynamik von HTTP-Fehlern auf dem Java-Server ergab eine Korrelation. Das heißt, der Zähler konnte für das Monitoring eingerichtet werden.
Das Vorhandensein eines zuverlässigen Probleminindikators ist sehr wichtig, um genau zu bestimmen, ob das Zurücksetzen von Sysctl hilft, da wir aus der vorherigen Erzählung wissen, dass dies über die Anwendung sofort nicht erkennbar ist. Ein solcher Indikator würde es ermöglichen, alle problematischen Stellen in der Produktion zu identifizieren, bevor sie von den Benutzern entdeckt werden.
Nach dem Zurücksetzen von Sysctl hörten die Überwachungsfehler auf, wodurch die Ursache der Probleme nachgewiesen wurde, ebenso wie die Tatsache, dass das Zurücksetzen hilft.
Wir haben die Fragmentierungseinstellungen auf anderen Servern zurückgesetzt, wo ein neues Monitoring gestartet wurde. An anderen Stellen haben wir sogar mehr Speicher für die Fragmente zugewiesen als zuvor standardmäßig vorhanden war (das betraf die UDP-Statistik, deren teilweiser Verlust im Gesamtkontext nicht auffiel).
Die wichtigsten Fragen
Warum werden Pakete an unserem L3-Lastausgleich fragmentiert? Die Mehrheit der Pakete, die von den Benutzern an die Lastausgleichssysteme gesendet werden, sind SYN- und ACK-Pakete. Diese Pakete sind relativ klein. Da jedoch der Anteil solcher Pakete sehr hoch ist, haben wir im Vergleich dazu die größeren Pakete übersehen, die zu Fragmentierungen führten.
Der Grund war ein defektes Konfigurationsskript. auf Servern mit VLAN-Schnittstellen (es gab zu diesem Zeitpunkt nur sehr wenige Server mit getagtem Traffic in der Produktion). advmss ermöglicht es, dem Client die Information zu übermitteln, dass die Pakete zu uns kleiner sein sollten, damit sie nach dem Hinzufügen der Tunnel-Header nicht fragmentiert werden müssen.
Warum hat ein Rücksetzen von Sysctl nicht geholfen, während ein Neustart geholfen hat? Das Zurücksetzen von Sysctl änderte den Speicher, der für das Zusammenfügen von Paketen verfügbar war. Dabei führte anscheinend selbst das Überlaufen des Speichers für Fragmente zu Verzögerungen bei den Verbindungen, was dazu führte, dass Fragmente lange in der Warteschlange blieben. Der Prozess geriet also in eine Schleife.
Ein Neustart setzte den Speicher zurück und alles kam in Ordnung.
Hätte es ohne Workaround gehen können? Ja, aber das Risiko, Benutzer im Falle eines Angriffs ohne Unterstützung zu lassen, ist groß. Natürlich hat der Einsatz des Workarounds zu verschiedenen Problemen geführt, einschließlich der Verlangsamung eines der Dienste für die Benutzer, aber wir sind dennoch der Meinung, dass die Maßnahmen gerechtfertigt waren.
Ein großes Dankeschön an Andrei Timofeev () für die Unterstützung bei der Untersuchung sowie an Alexey Krenev () — für die titanische Arbeit an der Aktualisierung von CentOS und den Kernen auf den Servern. Dieser Prozess musste in diesem Fall mehrmals von vorne begonnen werden, was dazu führte, dass er sich über viele Monate hinzog.
Quelle: habr.com
