Der Blog

Die Entdeckung des Poison Packet von Apache ZooKeeper

von Evan Gilman 7. Mai 2015 | 15 Minuten Lesezeit

ZooKeeper ist für diejenigen, die es nicht wissen: Es handelt sich um ein bekanntes Open-Source-Projekt, das eine äußerst zuverlässige verteilte Koordination ermöglicht. Viele Menschen auf der ganzen Welt vertrauen darauf, darunter auch PagerDuty. Es bietet hohe Verfügbarkeit und Linearisierbarkeit durch das Konzept eines Leaders, der dynamisch wiedergewählt werden kann, und gewährleistet Konsistenz durch ein Mehrheitsquorum.

Die Mechanismen zur Leader-Auswahl und Fehlererkennung sind ziemlich ausgereift und funktionieren normalerweise einfach … bis sie es nicht mehr tun. Wie kann das sein? Nun, nach einer langwierigen Untersuchung gelang es uns, vier verschiedene Fehler aufzudecken, die sich zusammenschlossen, um uns zu benachteiligen, was zu zufälligen Cluster-weiten Blockaden führte. Zwei dieser Fehler lagen in ZooKeeper, und die anderen beiden lauerten im Linux-Kernel. Das ist unsere Geschichte.

Hintergrund: Der Einsatz von ZooKeeper bei PagerDuty

Hier bei PagerDuty haben wir mehrere unterschiedliche Dienste, die unsere Alarmierungspipeline antreiben. Wenn Ereignisse empfangen werden, durchlaufen sie diese Dienste als eine Reihe von Aufgaben, die aus verschiedenen Arbeitswarteschlangen ausgewählt werden. Jeder dieser Dienste nutzt einen dedizierten ZooKeeper-Cluster, um zu koordinieren, welcher Anwendungshost welche Aufgabe verarbeitet. Daher können Sie sich vorstellen, dass ZooKeeper-Operationen für die Zuverlässigkeit von PagerDuty insgesamt absolut entscheidend sind.

Teil I: Die ZooKeeper-Bugs

Zu viele Clientsitzungen

Letztes Jahr bemerkte ein Techniker eines Tages, dass einer der ZooKeeper-Cluster in unserer Belastungstestumgebung defekt war. Dies äußerte sich in Sperrtimeouts in der abhängigen Anwendung. Wir bestätigten, dass der Cluster erreichbar war und zuhörte, aber irgendetwas stimmte nicht – jeder Client hatte Dutzende aktive Sitzungen zu seinen jeweiligen ZooKeeper-Clustermitgliedern. Normalerweise haben sie nur zwei. Infolgedessen erreichten wir ein Limit für die Anzahl der pro Knoten zulässigen aktiven Sitzungen und eine entsprechende Ausnahme wurde protokolliert.

ZooKeeper sessions climbing during one of the events

Wie konnte der Kunde nur so dumm sein? Vielleicht gab es einen Fehler im ZooKeeper-Bibliothek wir zu der Zeit verwendeten. Ein Neustart des gesamten ZooKeeper-Clusters behob das Problem und wir hatten keine Möglichkeit, es zu reproduzieren. Nachdem wir ein wenig im Bibliothekscode herumgestöbert hatten, konnten wir keinen Zustand finden, der zu einer Sitzungsüberlastung führen könnte. Wir waren ratlos und das Schlimmste war, dass wir keine Ahnung hatten, ob es in der Produktion auftreten könnte oder nicht.

Fehler Nr. 1

Weniger als eine Woche später trat der Zustand in unserer Belastungstestumgebung erneut auf. Diesmal betraf es einen anderen ZooKeeper-Cluster und es trat auf, während wir eine erhebliche Belastung erzeugten. Wir stellten fest, dass wir das Problem reproduzieren konnten, indem wir eine synthetische Belastung herbeiführten und einfach ein oder zwei Stunden warteten.

Wir bemerkten, dass die Anzahl der Sitzungen über alle ZooKeeper-Knoten hinweg linear anstieg. Daraus schlossen wir, dass selbst wenn es sich um ein Problem mit dem Client handelt, es wahrscheinlich einen Zustand in ZooKeeper gibt, der das Verhalten auslöst. Wir begannen, uns tiefer in die ZooKeeper-Protokolle einzuarbeiten. Nach einer Weile fanden wir im Protokoll des Leiters etwas vielversprechendes:

 java.lang.OutOfMemoryError: Java-Heap-Speicherplatz bei org.apache.jute.BinaryInputArchive.readString(BinaryInputArchive.java:81) bei org.apache.zookeeper.data.Id.deserialize(Id.java:54) bei org.apache.jute.BinaryInputArchive.readRecord(BinaryInputArchive.java:108) bei org.apache.zookeeper.data.ACL.deserialize(ACL.java:56) bei org.apache.jute.BinaryInputArchive.readRecord(BinaryInputArchive.java:108) bei org.apache.zookeeper.proto.CreateRequest.deserialize(CreateRequest.java:91) … 

Beim Durcharbeiten des Stacktraces haben wir Folgendes gefunden: Schema=a_.readString('Schema'); . Hmpf. Nun, das ZooKeeper-Protokoll hat einen vier Byte Schemalänge Feld … vielleicht berechnet der Client den Wert falsch. Unabhängig davon schreibt das Protokoll eine maximale Größe für das Schema vor. Leider gibt es für dieses Feld keine Grenzwertprüfung.

Nach vielen, vielen Paketerfassungen konnten wir ein einziges Problempaket finden. Es enthielt ein Schemalänge von OS-Version: … oder etwa 1,7 GB. Das Fehlen einer Grenzwertprüfung führte dazu, dass ZooKeeper versuchte, Speicher für die falsche Länge zu reservieren, was zu einer OutOfMemory-Ausnahme führte und den Thread beendete. Cool. Naja, nicht so cool, aber jetzt kommen wir langsam voran. Es gibt noch so viele Fragen, aber das wichtigste Problem ist klar: Wenn der Leader tot ist, warum wird er dann nicht wiedergewählt?

Fehler Nr. 2

Es stellte sich heraus, dass ZooKeeper nicht behandelte Ausnahmen von seinen kritischen Threads nicht abfing, was bedeutet, dass, wenn einer starb, der ZooKeeper-Prozess weiterlaufen ohne . Leider bedeutet das, dass die Herzschlagmechanismen auch weiterhin laufen und die Anhänger glauben machen, dass der Anführer gesund ist. Da Schemalänge vom Anforderungs-Präprozessor-Thread gehandhabt wird (durch den alle Anforderungen laufen müssen), wird der gesamte Cluster effektiv katatonisch – scheinbar lebendig, aber weitgehend nicht reagierend.

Wir haben eine Prozessüberwachung mit zk-spezifischen Integritätsprüfungen, aber aufgrund der Art des Fehlers wurden diese Integritätsprüfungen (ärgerlicherweise) weiterhin bestanden. Der Thread würde also beim Leader abstürzen und alle nachfolgenden Vorgänge blockieren, während er gleichzeitig mehrere Fehlererkennungsmechanismen umgeht. Das Endergebnis ist, dass wir herausgefunden haben, dass wir einen ganzen ZooKeeper-Cluster mit einem einzigen Poison-Paket zum Einsturz bringen können. Was für ein Wahnsinn.

Teil II: Die Kernel-Bugs

Beschädigung der TCP-Nutzlast

Jetzt verstehen wir die ZooKeeper-Fehler, aber eine viel größere Frage bleibt: Wie in aller Welt sehen wir diese Art von Werten für Schemalänge ?? Beim Dekodieren des Pakets konnten wir sehen, dass es nicht nur Schemalänge das war betroffen, aber ein ganzer 16-Byte-Block war anscheinend beschädigt. Hier ist ein Ausschnitt des ersten fehlerhaften Pakets, das wir gefunden haben:

 0170 00 00 00 03 00 00 00 b6 00 03 2a 67 00 00 00 01 ..........*g.... 0180 00 00 00 7e 2f 47 65 6d 69 6e 69 2f 63 6f 6d 2e ...~/Gemini/com. 0190 70 61 67 65 72 64 75 74 79 2e 77 6f 72 6b 71 75 pagerduty.workqu 01a0 65 75 65 2e 53 69 6d 70 6c 65 51 75 65 75 65 61 eue.SimpleQueuea 01b0 62 6c 65 2f 52 45 44 41 43 54 45 44 5f 52 45 44 ble/REDACTED_RED 01c0 41 43 54 45 44 5f 52 45 44 41 43 54 45 44 5f 52 ACTED_REDACTED_R -aeb 1-46b5-b223- 01f0 38 65 34 65 31 37 38 34 32 31 34 39 2d 6c 6f 63 8e4e17842149-loc 0200 6b 2d 00 00 00 09 31 32 37 2e 30 2e 30 2e 31 00 k-....127.0.0.1. 0210 7c 0e 5b 86 df f3 fc 6e dd 0b 51 dd eb cb a1 a6 |.[....n..Q..... 0220 00 00 00 06 61 6e 79 6f 6e 65 00 00 00 03 ....jemand.... 

Die Beschädigung steht nicht mit den Feldern im Einklang, die man im ZooKeeper-Protokoll erwarten würde, sondern mit 16-Byte-Grenzen innerhalb des Pakets selbst (beginnend bei Offset 0210). Angesichts dieser Informationen erscheint es nun ziemlich unwahrscheinlich, dass ZooKeeper etwas mit dem Fehler zu tun hat. Schemalänge Wert überhaupt.

Diese Aufnahme wurde gemacht, als das Paket an einem der ZooKeeper-Knoten aus der Leitung kam. Mit anderen Worten, es war bereits beschädigt, als es ZooKeeper erreichte. Das bedeutet, dass entweder der Client das beschädigte Paket gesendet oder ein Netzwerkgerät es beschädigt haben muss. Wenn ein zwischengeschaltetes Gerät für die Beschädigung verantwortlich ist, wird normalerweise eine Reihe von Prüfsummen ungültig gemacht und das empfangende System verwirft sie. Die TCP-Nutzlast erreichte in diesem Fall eindeutig ZooKeeper, daher müssen die Prüfsummen alle gut sein … aber zu unserer großen Überraschung waren sie es nicht!

IPSec

Bevor wir fortfahren, ist es wichtig zu wissen, dass PagerDuty verwendet IPSec In Transportmodus um unseren gesamten Inter-Host-Verkehr zu sichern. Ohne zu ausführlich zu sein, ist es im Grunde dasselbe wie Ihr gewöhnliches IPSec-basiertes VPN, abzüglich des VPN-Teils. Die IP-Nutzlast wird verschlüsselt, wobei die IP-Header zurückbleiben, sodass das Paket wie gewohnt durch das Netzwerk geleitet werden kann. Das Ergebnis ist eine VPN-ähnliche Verschlüsselung ohne die Notwendigkeit, einen separaten Adressraum zu pflegen. Darüber hinaus wird der Overhead verteilt, da jeder Host seinen eigenen Verkehr verschlüsselt und entschlüsselt.

Es ist wichtig zu verstehen, wie wir diese Technologie einsetzen, da alle bisher untersuchten Aufnahmen zur Analyse entschlüsselt wurden. Da die TCP-Header und die Nutzlast innerhalb von IPSec verschlüsselt sind, die IP-Header jedoch nicht, bedeutet dies, dass wir außerhalb von IPSec eine Prüfsumme haben und innerhalb. Dies bedeutet auch, dass eine erfolgreiche Entschlüsselung des Pakets beweist, dass es nach der Verschlüsselung nicht beschädigt wurde.

Unsere Prüfung der Prüfsummen ergab, dass die IP-Prüfsumme gültig war, die TCP-Prüfsumme jedoch nicht. Dies könnte nur möglich sein, wenn die Beschädigung aufgetreten ist nach Der TCP-Frame wurde gebildet, aber Vor die IP-Header wurden generiert. Unabhängig davon, wie/warum/wo diese Beschädigung der TCP-Nutzlast auftreten könnte, sollte sie vom Empfänger unbedingt verworfen werden, da die TCP-Prüfsumme ungültig ist … was ist los?

Fehler Nr. 3 – Unklares Verhalten

Das einzig Sinnvolle an dieser Stelle ist, sich aus erster Hand zu informieren. Ein bisschen Recherche im Linux-Quellcode bringt die folgenden Zeilen zutage, die im Linux-Masterzweig zum Zeitpunkt des Schreibens:

 /* * 2) UDP/TCP-Prüfsummen im Fall * von NAT-T im Transportmodus ignorieren oder * andere Nachbearbeitungskorrekturen durchführen * gemäß draft-ietf-ipsec-udp-encaps-06, * Abschnitt 3.1.2 */ if (x->props.mode == XFRM_MODE_TRANSPORT) skb->ip_summed = CHECKSUM_UNNECESSARY; 

WUT!!! Es fühlt sich so falsch an – wie kann das richtig sein? RFC 3948 erzählt die Geschichte. Es besagt, dass der Client bei Verwendung von IPSec im NAT-T-Transportmodus auf die Validierung der TCP/UDP-Prüfsumme verzichten KANN, da er davon ausgeht, dass die Paketintegrität bereits geschützt ist durch ESP . Wer hätte je gedacht, dass es einen Fall geben könnte, in dem man TCP-Prüfsummen nicht validiert?? Die Annahme der Autoren ist falsch, da es eindeutig reichlich Gelegenheit für eine Beschädigung vor der ESP/IP-Bildung gibt. Während Prüfsummen eine großartige Möglichkeit sind, Beschädigungen während der Übertragung zu erkennen, können sie auch als Werkzeug verwendet werden, um Beschädigungen während der Paketbildung zu erkennen. Es ist der letzte Punkt, der übersehen wurde, und diese Optimierung hat sich nun als Nachteil erwiesen. Die fehlende Validierung hat hier unsere mysteriöse Beschädigung durchgelassen – und ZooKeeper fehlerhafte Daten geliefert, von denen es vernünftigerweise annahm, dass sie durch TCP geschützt seien. Wir behaupten, dass dies ein Fehler ist – ob beabsichtigt oder nicht.

Der Test

Im Laufe unserer Untersuchung stellten wir fest, dass es seltsam schwierig war, das Problem zu reproduzieren, obwohl es uns gelegentlich gelang. Wir brauchten eine einfache Möglichkeit, diese Beschädigung zu erkennen und zu analysieren, ohne die Dinge durch den Einsatz von ZooKeeper, Wire Capture-Entschlüsselung usw. zu verkomplizieren. Nach ein paar Versuchen entschieden wir uns für einen extrem einfachen Ansatz: Verwenden Sie Netzkatze um Nullen wegzuleiten von /dev/null über das Kabel und senden Sie sie an xxd (ein Hex-Befehlszeilentool). Jeder Wert ungleich Null, der gelesen wird von xxd ist eine offensichtliche Beschädigung. So sehen einige unserer beschädigten TCP-Nutzdaten bei diesem Ansatz aus:

 --evan@hostB:~ $ nc -l 8080 | xxd -a 0000000: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 189edea0:0000 1e30 e75c a3ef ab8b 8723 781c a4eb ...0......#x... 189edeb0:6527 1e30 e75c a3ef ab8b 8723 781c a4eb e'.0......#x... 189edec0:6527 1e30 e75c a3ef ab8b 8723 781c a4eb e'.0......#x... 189eded0:6527 1e30 e75c a3ef ab8b 8723 781c a4eb e'.0......#x... 189edee0:6527 9d05 f655 6228 1366 5365 a932 2841 e'...Ub(.fSe.2(A 189edef0:2663 0000 0000 0000 0000 0000 0000 0000 &c.............. 189edf00:0000 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 4927d4e0:5762 b190 5b5d db75 cb39 accd 5b73 982b Wb..[].u.9..[s.+ 4927d4f0:5762 b190 5b5d db75 cb39 accd 5b73 982b Wb..[].u.9..[s.+ 4927d500:5762 b190 5b5d db75 cb39 accd 5b73 982b Wb..[].u.9..[s.+ 4927d510:5762 b190 b5d db75 cb39 accd 5b73 982b Wb..[].u.9..[s.+ 4927d520:01db 332d cf4b 3804 6f9c a5ad b9c8 0932 ..3-.K8.o......2 4927d530:0000 0000 0000 0 0000 0000 0000 0000 ................ * 4bb51110:0000 54f8 a1cb 8f0d e916 80a2 0768 3bd3 ..T..........h;. 4bb51120:3794 54f8 a1cb 8f0d e916 80a2 0768 3bd3 7.T..........h;. 4bb51130:3794 54f8 a1cb 8f0d e916 80a2 0768 3bd3 7.T..........h;. 4bb51140:3794 54f8 a1cb 8f0d e916 80a2 0768 3bd3 7.T..........h;. 4bb51150:3794 20a0 1e44 ae70 25b7 7768 7d1d 38b1 7. ..Dp%.wh}.8. 4bb51160:8191 0000 0000 0000 0000 0000 0000 0000 ................ 4bb51170:0000 0000 0000 0000 0000 0000 0000 0000 ................ * 4de3d390:0000 0000 0000 ...... -- evan@hostB:~ $ 

Das sieht nach furchtbarer Hardware aus, nicht wahr? Tritt typischerweise bei 16-Byte-Grenzen mit Wiederholung auf. Wir vermuteten, dass wir vielleicht einige Hosts mit fehlerhafter Hardware haben, also begannen wir, diesen Test auf verschiedenen Hostpaaren in unserer Infrastruktur auszuführen, um sie herauszufinden. Was wir herausfanden, war interessant.

Die betroffenen

Das Erste, was uns auffiel, war die Kernelversion. So sehr wir uns auch bemühten, wir konnten den Zustand unter Linux 2.6 nicht reproduzieren. Eine erfolgreiche Replikation war von einem Linux 3.0+-Kernel abhängig. Obwohl wir das Problem unter Linux 3.0+ sehen konnten, war es inkonsistent. Eine Gruppe betroffener Hosts, auf denen eine bestimmte Version von 3.0+ ausgeführt wurde, war betroffen, während eine andere Gruppe von Hosts mit derselben Version nicht betroffen war. Also begannen wir, nach anderen Faktoren zu suchen.

Nach ein paar Wochen erfolgloser Recherchen und Tests machten wir schließlich eine beunruhigende Entdeckung: Die Reproduktion war abhängig von der Xen-Version, auf der unsere Hosts liefen. Xen 4.4-Gäste waren nicht betroffen, aber Xen 4.1 und Xen 3.4 waren beide betroffen. Dieses Detail erklärt, warum unsere Testergebnisse bei Verwendung von festen Kernel-Versionen inkonsistent waren. Wir haben also festgestellt, dass auf jedem Host, auf dem ein Linux 3.0+-Kernel auf Xen 4.1 oder 3.4 läuft, sporadische Beschädigungen während der IPSec-Verschlüsselung auftreten. Zum ersten Mal konnten wir das Problem zuverlässig reproduzieren und vorhersagen, welche Hosts betroffen sein würden. Jetzt müssen wir nur noch herausfinden, wie wir es beheben können!

Hilfe!

An diesem Punkt verfügen wir über genügend Informationen, um unsere Suche über die organisatorischen Grenzen von PagerDuty hinaus auszuweiten. Wir haben eine mail auf LKML, um zu sehen, ob jemand anderes schon einmal auf dieses Problem gestoßen ist. Innerhalb weniger Tage hatten wir eine Antwort. Herbert Xu, einer der Betreuer des Krypto-Subsystems, antwortete, dass er schon einmal etwas Ähnliches gesehen habe. Er berichtete von Spekulationen, dass der HVM-Modus von Xen möglicherweise nicht betroffen sei, und zeigte mit dem Finger auf eine bestimmte Intel-Anweisung: aes-ni .

Fehler Nr. 4 – aesni-intel

Der Intel x86 Befehlssatz beinhaltet einen AES-Anweisung wird verwendet, um AES-Berechnungen in Hardware durchzuführen. Ein Kernelmodul, aesni-intel ist für die Nutzung dieser Anweisung in den AES-Verschlüsselungsfunktionen des Linux-Kernels verantwortlich. Da unsere IPSec-Verschlüsselung AES verwendet, würde dieses Modul wahrscheinlich zur Verkehrsverschlüsselung in Gegenwart von verwendet werden aes-ni auf Intel-Hardware. Auf den Tipp von LKML hin zeigt eine schnelle Überprüfung, dass wir tatsächlich die aesni-intel Kernelmodul auf Hosts in unserer gesamten Flotte geladen. Wenn das Modul zum Entladen gezwungen wird, verschwindet die Beschädigung! Halleluja!!

Weitere Tests zeigten, dass Herbert Recht hatte, dass HVM nicht betroffen war. Schließlich scheint das Problem in einer Wechselwirkung zwischen aesni-intel und Xen-Paravirtualmodus.

Mit einem so ernsten Virus im aesni-intel Modul, fragen Sie sich vielleicht – wie konnte das bisher niemandem aufgefallen sein? Schließlich wird AES gelegentlich auch für SSL-Verkehr verwendet. Die Antwort darauf liegt in Fehler Nr. 3 – nur im IPSec NAT-T-Transportmodus validiert der Kernel keine TCP-Prüfsummen. Das bedeutet, dass unter allen anderen Bedingungen die Prüfsummenvalidierung fehlschlägt und das Paket gelöscht wird, wodurch die Anwendung vor beschädigten Daten geschützt wird. Dies sowie die Einschränkungen bei der Xen-Version und dem Virtualisierungstyp machen dieses Problem äußerst selten … ein exotisches AES-Einhorn, das nur von denen gesehen werden kann, die wissen, wo es liegt. Wir haben Glück :).

Teil III: Der Workaround

Rückblick

Nach über einem Monat unermüdlicher Forschung und Tests sind wir unserem ZooKeeper-Rätsel endlich auf den Grund gegangen. Eine Beschädigung während der AES-Verschlüsselung in paravirtuellen Xen-Gästen v4.1 oder v3.4, die einen Linux 3.0+-Kernel ausführen, in Kombination mit der fehlenden TCP-Prüfsummenüberprüfung im IPSec-Transportmodus führt dazu, dass beschädigte TCP-Daten auf einem ZooKeeper-Knoten zugelassen werden, was zu einer unbehandelten Ausnahme führt, von der ZooKeeper sich nicht erholen kann. Meine Güte. Das ist wie eine Nadel im Heuhaufen … Selbst nach all dem sind wir uns immer noch nicht sicher, wo genau der Fehler liegt. Trotzdem sind wir mit dem Ergebnis der Untersuchung ziemlich zufrieden. Jetzt müssen wir nur noch eine Lösung finden.

Was wir gemacht haben

Das Entladen des Moduls hat sich als Mittel zur Vermeidung der von uns festgestellten Fehler erwiesen, allerdings geht dies mit Leistungseinbußen einher. Wir haben die Auswirkungen gemessen und es hat sich herausgestellt, dass sie nur bei sehr hohen Durchsätzen ein Problem darstellen, für die wir andere Möglichkeiten zur Abschwächung haben. Wir wissen auch, dass Xen HVM-Gäste nicht betroffen sind und Linux 2.6 auch nicht. Mit diesem Wissen können wir beginnen, einen Angriffsplan zu erstellen.

Vorhandene paravirtuelle Xen-Gäste mit Linux 3.0+ wurden auf 2.6 zurückgestuft, aber nur, wenn sie eine betroffene Version von Xen verwendeten. Wir haben ein Chef-Rezept zur Xen-Erkennung geschrieben und die aesni-intel Modul, wenn die Problembedingungen bestehen. Wir haben auch begonnen, HVM-Hosts anstelle von paravirtuell zu standardisieren, damit wir unter anderem weiterhin die AES-Hardwarebeschleunigung nutzen können.

Abschließend

Leider fehlt uns noch immer eine richtige Lösung. Da spätere Versionen von Xen nicht betroffen sind und die Akzeptanz von HVM zunimmt, besteht seitens der Community wenig Interesse daran, die erforderliche Zeit zu investieren, um den Code zu isolieren, der das Problem verursacht. Wir haben ein ähnliches Problem mit ZooKeeper – Version 3.5 verfügt über eine Lösung, die verhindert, dass ein kritischer Thread unbemerkt stirbt, obwohl 3.5 noch in der Alpha-Phase ist und dies auch noch einige Zeit bleiben wird. Es wird darüber gesprochen, die Lösung zurück zu portieren, obwohl die offizielle Haltung ist, dass dies der Fall ist. wird nicht als Blocker betrachtet für weitere 3.4.x-Versionen.

Wir möchten den Mitgliedern unserer verschiedenen Engineering-Teams für ihre Hilfe und Beiträge zur Isolierung dieser Probleme danken. Wir bei PagerDuty nehmen Zuverlässigkeit sehr ernst und sind sehr stolz und haben Freude daran, selbst die Ursachen für die exotischsten Probleme herauszufinden. Wenn Sie das auch so sehen, würden wir uns freuen, wenn Sie begleiten Sie uns .

Monitoring_Ebook_728_90