Archiv für die Kategorie ‘Allgemeines’

Raketengeschichten

Freitag, 24. Februar 2012

Auf unserer Hosting-Plattform Uberspace.de gibt’s eine neue Rakete im Logo. Gleich vorweg, hier der Vorher-Nachher-Vergleich:

alt neu

Ein bisschen runder, ein Fenster mit Aussicht (wer braucht das nicht!), aber vor allem: Nun wirklich in keinster Weise mehr mit einer Tim-und-Struppi-Rakete verwechselbar.

Wir hatten das auch zuvor nicht wirklich gesehen. Unsere Rakete war ein selbstgezeichneter Entwurf eines Grafikers bei web://contact, der nicht auf der besagten Rakete basierte, sondern vielmehr Anlehnung an die klassische Aggregat 4 (auch bekannt als „V-2“) nahm – ein weithin bekanntes Design von Wernher von Braun von 1939, das offenbar auch Tim-und-Struppi-Zeichner Hergé als Inspiration diente:

… An unmanned subscale prototype of the rocket — the „X-FLR6“, resembling a V-2 rocket — is launched on a circumlunar mission to photograph the far side of the Moon …

Aus unserer Sicht bestand hier auch eine deutliche Abgrenzung – ein anderes Karomuster (2×2 statt 5×2), keine schwarzen Trennlinien zwischen den Karos, ein deutlich anderes Größenverhältnis zwischen Korpus und Flügeln … aber dennoch bekamen wir dann kürzlich Post des Verlags, der die Rechte an den Tim-und-Struppi-Cartoons hält, mit dem Hinweis auf eine unberechtigte Verwendung von Elementen des von Hergé gezeichneten Cartoons und der Aufforderung, jene zu entfernen.

Wir haben dies lange im Team und auch mit der Designagentur besprochen. Einerseits sind wir überzeugt davon, hier keine Rechtsverletzung begangen zu haben – nicht zuletzt, weil das Design, an dessen unsere Rakete Anlehnung fand, schon deutlich älter war ist als der betreffende Cartoon, und weil der Verlag wohl kaum Rechte an jeglicher gezeichneter Comic-Rakete haben kann, selbst dann nicht, wenn sie rot ist. Oder eben auch Karos hat. Andererseits haben wir auch kein Interesse an einem langwierigen und kostenintensiven Rechtsstreit, dessen Ausgang – subjektiv wie solche grafischen Bewertungen nun mal sind – für beide Seiten ungewiss sein dürfte. Last but not least trat der Verlag nicht über eine Anwaltskanzlei mit einer teuren Kostennote und hohen Schadensersatzforderung an uns heran, sondern mit einem eigenen Schreiben mit der schlichten Aufforderung, die beanstandete Rakete zu entfernen und zuzusichern, sie künftig nicht mehr zu verwenden. Heutzutage, wo man ja eigentlich mit allem, was man im Web veröffentlicht, schon mit einem Bein im Knast steht, fanden wir das einen ausgesprochen fairen und seriösen Umgang, den wir mit einem entsprechenden Entgegenkommen auch würdigen wollten.

Rechtsanwalt Jochen Kuschert, der in unserem Auftrag die Situation rechtlich einschätzte, ergänzte seine Stellungnahme mit einer Anmerkung, die weniger der konkreten Rechtsprechung, sondern wohl eher der juristischen Alltagserfahrung geschuldet ist:

Möglich und oft auch üblich ist aber auch, dass von Rechteinhaber vorschnell oder „bewusst“ nicht existierende Ansprüche geltend gemacht werden, um möglichst viel „Freiraum“ zwischen den eigenen Werken und anderen Werken zu schaffen. Denn die rechtlichen Unwägbarkeiten bestehen auf beiden Seiten.

Wir haben daraufhin entschieden, eine Änderung des Designs unserer Rakete zu beauftragen, die seit heute morgen sowohl im Logo als auch im Fuß der Seite entsprechend ausgewechselt wurde. Unser Antwortschreiben an den Verlag dokumentieren wir hier:

Dear […],

thanks a lot for contacting us.

We’re really sorry to hear that you consider the Uberspace.de rocket as a look-alike of the TINTIN rocket from the HERGE comic strip series. Please be assured that we’re trying to solve your concerns in all conscience.

Simply said, the Uberspace.de rocket is not based on the TINTIN rocket, and it differs from it in many aspects:

The TINTIN rocket, for example, …

– has a 5×2 tile set
– uses black lines to separate the tiles from each other
– has enormous wings
– has an antenna at the top

However, the Uberspace.de rocket …

– has a 2×2 tile set
– doesn’t use black lines to separate the tiles
– has much smaller wings
– does not have an antenna at the top

The Uberspace.de rocket resembles the design of the well-known „Aggregat 4“ rocket used in World War II, also known as „V2“, which is a Wernher von Braun design from 1939 and thus nearly 15 years older than the creation of HERGE.

It’s obvious that HERGE has been heavily inspired by the V2, too, but in fact it would be hard to make a comic adaption of the V2 that would *not* have some similarities with the TINTIN rocket.

While we really feel confident that the Uberspace.de rocket isn’t a TINTIN rocket look-alike, we clearly understand that you’d prefer a design which has more „distance“ from the TINTIN rocket. As we’re not interested in a legal dispute – especially because many of our team members are fans of the TINTIN comics, including myself – we have worked together with our designers to create a new rocket for use in both our logo as well as in our footer image.

The new rocket design:

– still differs in all aspects mentioned above
– has an even more different shape with a heavily rounded down top (while the TINTIN rocket has a spiky top)
– stretches the 2×2 tile set throughout the rocket’s body (while the TINTIN rocket encloses its 5×2 tile set by a top and bottom which is completely red)
– adds a black bull’s-eye (which the TINTIN rocket doesn’t have at all)

Summarized, the modified Uberspace.de rocket now has even larger differences from the TINTIN rocket, and we’re pretty sure that nobody would falsely identify the Uberspace.de rocket as a TINTIN rocket.

That being said: While we can provide you with a written undertaking that we won’t, at any time in the future, use any image of a rocket that *might* be mistaken for a TINTIN rocket by an untrained eye, we really can’t acknowledge an injunctive relief against the use of any strip, character, name and symbol, as we have never used (or misused) one of these in the past.

I therefore propose the following phrase:

„I, Jonas Pasche, will not, at any time in the future, use any image of a rocket that might be mistaken for a TINTIN rocket.“

Please let me know if you agree with that phrase or if you still have any concerns.

Best regards,
Jonas Pasche

Wir werden sehen, ob die Angelegenheit damit erfolgreich abgeschlossen werden kann oder nicht.

Lob des Backups

Samstag, 11. Februar 2012

Während ich diesen Artikel schreibe, rauschen im anderen Fenster die Zeilen der rsync-Ausgaben über den Bildschirm. Um 07:12 Uhr bemerkte Icinga die ersten Probleme; um 7:17 Uhr eskalierte es sie auf mein Handy. Seitdem bin ich wach, und weil ich während des nun laufenden Restores ohne nichts weiter unternehmen kann und die nächsten Schritte bereits vorbereitet habe, mache ich das Beste draus: Ich blogge.

Um’s direkt vorwegzunehmen: In technischer Hinsicht bietet dieser Blogpost keinerlei Erkenntnisgewinn. Nur vielleicht eine Moral, die da lautet: Macht Backups. (Und benutzt Sonnencreme, klar.)

Vom Monitoring aus dem Schlaf geholt zu werden, ist erstmal kein großer Aufreger. Nicht selten geht es hier um Probleme vom Kaliber „der Apache braucht mal einen Restart“ oder auch „die Mailqueue ist ungewöhnlich voll“ (was auf einen Spamvorfall hindeutet). Echte Ausfälle sind selten. In diesem Fall war es ein sehr kundenspezifischer Check: Es wird durch ein PHP-Script geprüft, ob eine ganz bestimmte Spalte einer ganz bestimmten Tabelle einer MySQL-Datenbank eine bestimmte Länge hat. Die Gründe für diesen Check führen hier zu weit; es gab vor längerer Zeit einen Vorfall, der dazu Anlass gab, und wir sind eigentlich immer bestrebt, für Vorfälle, die nicht durch unser Monitoring diagnostiziert wurden, anschließend noch bessere Checks zu schreiben. Dieser hier ist ein schönes Beispiel: Der Apache lief. Der MySQL-Server lief. Apache konnte auch mit MySQL kommunizieren. Aber MySQL konnte keine Daten schreiben. Das Gros der Checks des fraglichen Systeme meldete daher fröhlich „alles in Ordnung“ – obwohl überhaupt nichts in Ordnung war. Mir war nur noch nicht bewusst, wie sehr nicht.

Das ausgeführte DESCRIBE kunden führte jedenfalls zu einem Fehler, und zwar konkret zu Can't create/write to file '/tmp/#sql_2fc2_0.MYI' (Errcode: 30). Das muss jetzt erstmal noch keine Katastrophe sein, sondern kann auf simple Probleme wie eine überschrittene Quota oder eine vollgelaufene Partition hindeuten (was wir zwar auch monitoren, aber nur in einer niedrigeren Frequenz, weil sowas typischerweise nicht „von jetzt auf gleich“ passiert). Also habe ich mich dann erstmal per SSH eingeloggt, was auch geklappt hat.

Als df -h dann lediglich Command not found lieferte, und ps ax dann auch, war ich dann sehr schnell richtig wach, und es war klar: Das Problem ist größerer Natur als nur eine vollgelaufene /tmp-Partition. Auch dmesg ließ sich nicht mehr ausführen. Da es sich bei dem System um eine Xen-Instanz handelte, war von daher ein Login auf dem Wirtssystem dran. xm console zeige neben dem Login nur noch einen ext3-Fehler: Journal has aborted. Okay, das kommt mal vor. Selten. Und es ist in der Regel kein Problem, das sich nicht mit einem fsck.ext3 lösen ließe. Also: xm shutdown, xm create -c, … und das sah alles so gar nicht gut aus, denn pygrub mochte keine grub-Konfiguration innerhalb des Blockdevices des Gasts mehr finden.

Nun sieht dieses System so aus, dass eine lokal eingebaute Festplatte im physischen Server lediglich als Boot-Medium fungiert. Die Blockdevices hingegen kommen via iSCSI von unserem Storage-Cluster: Zwei dicke Maschinen mit je einem RAID6 über 16 Platten, die über DRBD repliziert werden, und auf denen dann LVM-Volumes via ietd exportiert werden. Schnell ein Login auf dem derzeit aktiven Filer: Gibt’s Probleme? Ein Blick in die Logfiles, in dmesg, … aber alles sah gut aus. Ich habe mich schnell auf einigen anderen Xen-Gästen eingeloggt, die ebenfalls vom Storage-Cluster aus exportiert werden, aber dort war alles in Ordnung, stabiler Betrieb, auch Schreibzugriffe problemlos möglich.

Im konkreten Fall ist auch der physische Server, auf dem die Xen-Instanz läuft, redundant ausgelegt: Via Heartbeat überwachen sich beide Hosts gegenseitig, und wer aktiv ist, bindet das iSCSI-Target ein und fährt die darauf enthaltene Xen-Instanz hoch. Das Setup läuft an sich prima und hat auch schon einige (geplante) Failover-Vorgänge überstanden. Ich hielt den Zeitpunkt für gegeben, einen Failover durchzuführen. Die Xen-Instanz war schnell gestoppt (tja, wenn das Linux-System erstmal nicht mehr auf sein Blockdevice schreiben kann, geht das verdammt fix …), dann ein iscsiadm --logout – und das hing. Und hing. Und hing. Was von daher merkwürdig war, weil der iSCSI-Server durchaus anpingbar war, also kein Problem mit dem Netzwerkinterface o.ä. vorliegen konnte.

In solchen Fällen ist es wichtig, eine Split-Brain-Situation zu vermeiden, sprich, es sollten nicht zwei Hosts das gleiche Blockdevice bearbeiten. Die einfachste Möglichkeit ist, den ohnehin praktisch toten Host komplett vom Strom zu trennen. Gesagt, getan – ist dank IPMI ja alles kein Problem. Dann die Übernahme des iSCSI-Targets auf den anderen Knoten, die von Heartbeat ohnehin automatisch veranlasst wurde, als der bisher aktive Host nicht mehr anpingbar war. Klappte. Mit einem kleinen Problem: Das Blockdevice beinhaltet eigentlich eine Partitionstabelle, die genau eine Partition umfasst, nämlich die Root-Partition. Wird das iSCSI-Target eingebunden, taucht es insofern als /dev/sdb auf (/dev/sda ist das eingebaute Boot-Plattensystem), und noch dazu ein /dev/sdb1 mit jener Root-Partition.

/dev/sdb war da. /dev/sdb1 fehlte. Ein fdisk -l /dev/sdb behauptete schlicht, es gebe keine Partitionstabelle. Das Blockdevice selbst hatte aber schon mal zumindest die korrekte Größe – der Zugriff via iSCSI an sich schien also zu klappen. Sicherheitshalber habe ich mir dann die Partition direkt auf dem Storage-Cluster angeschaut, um sicherzugehen, dass es wirklich kein iSCSI-Problem ist. Zum Vergleich, so sollte es aussehen (bei einem identisch eingerichteten Xen-Image):

[root@filer01 ~]# fdisk -lu /dev/mapper/vgdrbd1-vserver_working 

Disk /dev/mapper/vgdrbd1-vserver_working: 188.7 GB, 188743680000 bytes
255 heads, 63 sectors/track, 22946 cylinders, total 368640000 sectors
Units = sectors of 1 * 512 = 512 bytes

                              Device Boot      Start         End      Blocks   Id  System
/dev/mapper/vgdrbd1-vserver_working1   *           1   368627489   184313744+  83  Linux

Und so sah es faktisch aus:

[root@filer01 ~]# fdisk -lu /dev/mapper/vgdrbd2-vserver_broken 

Disk /dev/mapper/vgdrbd2-vserver_broken: 188.7 GB, 188743680000 bytes
255 heads, 63 sectors/track, 22946 cylinders, total 368640000 sectors
Units = sectors of 1 * 512 = 512 bytes

Disk /dev/mapper/vgdrbd2-vserver_broken doesn't contain a valid partition table

Okay, iSCSI selbst war als akute Fehlerquelle insofern aus dem Spiel. Es schien an der Zeit, TestDisk ins Spiel zu bringen. Dieses Tool ist Gold wert, wenn es darum geht, defekte Partitionstabellen zu fixen, unter anderem, in dem es die Platte sektorenweise nach Blöcken absucht, die z.B. wie ein ext3-Superblock aussehen – oder nach einem Backup davon, denn ext3 legt immer gleich mehrere Kopien des Superblocks verteilt über das Blockdevice an.

TestDisk fand nichts. Also wirklich überhaupt gar nichts. Insofern hatte ich wenig Hoffnung, dass der darauffolgende Schritt noch etwas bringen würde, nämlich die Partitionstabelle anhand eines identisch aufgesetzten VServers zu rekonstruieren. Aber bevor ich’s nicht versucht habe … erwartungsgemäß scheiterte aber der Versuch, die frisch angelegte Partition zu mounten (die ja eigentlich durchaus noch das Dateisystem hätte enthalten müssen – die Partitionstabelle zu bearbeiten, ändert ja nichts daran, dass auf dem Rest der Platte immer noch unverändert Daten liegen). Aus einem anderen Xen-Image identischer Konfiguration konnte ich noch die Positionen ableiten, die die Backup-Superblocks haben sollten, und habe sie alle durchprobiert – nichts.

An diesem Punkt war dann Schluss. Wir sind kein Datenrettungsunternehmen, und wir können auch nicht mal eben fix ein einzelnes LVM-Volume eines Clusters, auf dem noch ein paar Dutzend weiterer – voll funktionsfähiger – Volumes liegen, an ein solches schicken (wenn, dann müssten wir einen Klon des LVM-Volumes auf eine externe Platte anfertigen), und selbst wenn, würde das das Problem nicht lösen, dass das System im Moment eben nicht läuft. Insofern ist an dieser Stelle der Punkt gekommen, an dem ich mit hängenden Schultern sagen muss: Ich habe absolut keine Ahnung, was hier passiert ist. Der Storage-Cluster meldet weiterhin keinerlei Probleme. Sein RAID6 ist 100% in Ordnung (sagt der Controller). Und vor allem gab es ja keinerlei schreibenden Zugriffe auf Partitionstabelle oder Dateisystem des Images des Xen-Gasts, die irgendwie hätten fehlschlagen und etwas korrumpieren können – und Daten verschwinden eigentlich nicht „einfach so“, nicht ohne dass ein physischer Defekt vorliegen würde, was bei einem RAID-System mit vollkommen intakten Platten nun wirklich ausgesprochen unwahrscheinlich ist. Aber, wie’s aussieht, möglich.

Nun also rasseln die Dateien aus dem letzten Backup via rsync wieder zurück auf ein frisch angelegtes und mit einem Dateisystem versehenen Blockdevice. Der größte Datenblock, nämlich /home, ist schon durch, insofern kann es sich nur noch um Minuten handeln. Deshalb noch kurz ein Punkt im Hinblick auf Backups – darauf legen wir großen Wert: Insbesondere dort machen wir nichts mit proprietärer Software oder irgendwelchem Kompressions- oder diff-Gebastel. Die Gelegenheiten, bei denen uns rdiff-backup (dessen Konzept eigentlich ziemlich cool ist) im Stich gelassen hat, sei es durch absurd lange Restore-Zeiten selbst für einzelne Dateien, oder schlicht durch komplette Abbrüche mitten im Prozess, überwiegt die Zahl der Gelegenheiten, in denen es uns gerettet hat, bei weitem. Deshalb sind wir in Sachen Backup extrem konservativ: Ein extra Server mit eigenen Platten, keine komplizierten LVM-DRBD-Sonstwas-Layer dazwischen, einfach nur eine schnöde Partition angelegt, mit rsync kommen die Daten drauf. Versionierung läuft über Hardlinks. Auf diese Weise liegen zwei Versionen der gleichen Datei zwar nicht so platzsparend wie „erste Version plus Diff“ oder „letzte Version plus Reverse-Diff“ auf der Platte, aber dafür in einem Zustand, bei dem sich jeder Versionsstand mit Bordmitteln wie cp oder eben rsync schnell auf jede beliebige andere Hardware bringen lässt. Der Charme besteht eben nicht in Features, sondern manchmal schlicht im Weglassen selbiger.

So, die letzten Dateien laufen durch. In der Xen-Konfiguration ist bereits die disk-Zeile auf das neue Blockdevice angepasst. rsync fertig, umount, xm create -c. Bootet.

Die Maschine läuft nun also wieder, zwar mit einem Stand von gestern Nacht, aber eine bessere Option gibt es nun mal nicht. Downtime: Von 07:12 Uhr bis 11:01 Uhr – fast vier Stunden, was die Verfügbarkeit im Jahresmittel auf ärgerliche 99,95% herunterkatapultiert (wobei nur etwa eine halbe Stunde auf tatsächliche administrative Arbeiten entfällt – der Rest hingegen auf den Restore der Daten; man sollte also bei einem Backup nicht unterschätzen, dass auch ein Restore eine signifikante Downtime bedeuten kann). Aber der (ziemlich branchenspezifische) Shop wird nachts kaum besucht, am Wochenende noch weniger, von daher hält sich der potentielle Schaden wohl hoffentlich in Grenzen. Dass ein solcher Vorfall nun ausgerechnet bei einem System auftritt, wo sowohl die physischen Maschinen „vorne“ redundant ausgelegt sind als auch der Storage-Cluster „hinten“, ärgert mich um so mehr, aber es ist ein gutes Beispiel dafür, dass Hochverfügbarkeit durch Failover-Cluster auch nur eine Teilmenge möglicher Störungen absichert – und derart tiefgreifende Schäden an Dateisystemen und Partitionstabelle gehören nicht dazu. Die Lehren, die ggf. aus diesem Vorfall zu ziehen sind, werden wir dann am Montag im Teammeeting besprechen.

Also Leute, macht Backups. Gute Backups. Und vor allem vollständige Backups: Seid nicht zu selektiv – kopiert lieber zuviel als zuwenig, auch wenn man im Moment leider immer noch nicht wieder „Plattenplatz kostet doch eh fast nichts“ sagen kann. Und schaut gelegentlich, dass eure Backups auch funktionieren. Ihr wisst nie, wann sie euch mal den Allerwertesten retten.

So, jetzt erstmal Kaffee.

Das Rätsel der öffentlichen Home-Verzeichnisse

Freitag, 30. Dezember 2011

Bei Uberspace.de haben wir in der Vergangenheit immer mal wieder erlebt, dass einzelne Home-Verzeichnisse, die normalerweise nur für den Besitzer les- und schreibbar waren, plötzlich öffentlich lesbar waren:

[root@taurus ~]# ls -l /home
...
drwx------ 5 user1 user1 4096 19. Dez 11:15 user1
drwx------ 5 user2 user2 4096 16. Dez 12:32 user2
drwxr-xr-x 5 user3 user3 4096 20. Dez 15:04 user3
drwx------ 5 user4 user4 4096 20. Dez 11:25 user4
drwx------ 5 user5 user5 4096 15. Dez 13:09 user5
...

Anfangs haben wir, wann immer uns das auffiel, die Rechte entsprechend korrigiert und den User informiert – und in keinem einzigen Fall bekamen wir die Rückmeldung, dass ein User die Rechte tatsächlich selbst so geändert hatte; vielmehr waren alle bass erstaunt, wie so etwas passieren konnte.

Nun ist diese Änderung nicht so schlimm, wie es auf den ersten Blick aussehen mag, denn die meisten sensiblen Daten, beispielsweise Mails, sind auch innerhalb des Home-Verzeichnisses nur für den Eigentümer selbst lesbar, und beispielsweise PHP-Scripts, die MySQL-Zugangsdaten erhalten, liegen im DocumentRoot, der ohnehin außerhalb von /home liegt und von daher nicht betroffen ist. Aber natürlich gibt es potentiell immer noch viele Möglichkeiten, so an Informationen zu gelangen, die niemanden etwas angehen – wenngleich nicht aufgrund einer Sicherheitslücke, denn wir setzen die Rechte ja durchaus korrekt und strikt auf 700, und „von alleine“ ändern sie sich nun mal nicht: Es muss also irgendwas sein, was von den betreffenden Usern selbst ausgelöst wird, und zwar höchstwahrscheinlich versehentlich.

Unsere eigenen Tools haben wir zügig kontrolliert und konnten schon nach kurzer Zeit mit an Sicherheit grenzender Wahrscheinlichkeit ausschließen, dass eins von denen für das Problem verantwortlich zeichnete. Parallel dazu artete die periodische manuelle Kontrolle mit der Zeit in echte Arbeit aus, so dass wir kurzerhand beschlossen, ein Script zu schreiben, das die Rechte alle paar Minuten prüft, automatisiert fixt und den betreffenden User eine Mail sendet, einerseits mit dem Hinweis auf die Korrektur, andererseits aber auch mit der Bitte, uns doch mitzuteilen, was sie in den letzten paar Minuten so getan hatten, um einige Hinweise zu erlangen, wie diese plötzliche Rechteänderung denn nun eigentlich genau passieren kann.

Nach einigen Rückmeldungen verdichteten sich die Hinweise darauf, dass es etwas mit npm zu tun haben könnte, dem Paketmanager von node.js – aber es war schwierig, den Effekt zu reproduzieren: Zeitweise schien es, als würde der Effekt nur bei der Installation ganz bestimmter Module auftreten; aber auch hier trat der Effekt nicht verlässlich auf. Weder die betreffenden User noch wir hatten zunächst in Betracht gezogen, dass es daran liegen könnte, in welchem Verzeichnis man sich befindet, während man npm install ... ausführt.

Ein kleiner Exkurs.

Für praktisch jede Scriptsprache gibt es auch Tools, mit dem zusätzliche Module heruntergeladen und installiert werden können – und das Konzept ist im Grunde immer gleich. Um ein paar Beispiele zu nennen:

  • PHP hat PEAR und installiert Module in das Verzeichnis, das in der ~/.pearrc dafür angegeben wird.
  • Perl hat CPAN und installiert Module in das Verzeichnis, das via PREFIX über die CPAN::MyConfig angegeben wird.
  • Python hat easy_install und pip und installiert Module in das Verzeichnis, das in der distutils.cfg angegeben wird.
  • Ruby hat RubyGems und installiert Module in $HOME/.gem.

Ist hier möglicherweise ein Muster erkennbar? Ich denke doch.

Das Problem ist nun, dass node.js und npm hier ein ganz anderes Konzept verwirklichen. Hier wird ein Verzeichnis node_modules im Verzeichnis der Applikation angelegt, in dem die zu installierenden Module landen. Entwickelt man also unter ein und dem selben Linux-User mehrere node.js-Applikationen, so müssen die gewünschten Module für jede Applikation separat installiert werden. Ja, ich habe da selbst erstmal daran gezweifelt, aber der npm-Autor führt ganz konkret aus:

[…]
Install it in both places. Seriously, are you that short on disk space? It’s fine, really. They’re tiny JavaScript programs.
[…]
The first option is the best in my opinion. Simple, clear, explicit.
[…]

npm hat nun die Eigenschaft, beim Anlegen von Verzeichnissen jene automatisch mit chmod 755 zu behandeln. Im Oktober 2011 kam noch eine Option dazu, um eine umask zu setzen, die dies anpassbar macht, aber 755 ist weiterhin der Default. Das Problem ist nun, dass npm auch bei bereits bestehenden Verzeichnissen sicherstellt, dass jene die vorgegebenen Rechte bekommen.

Und hier liegt letztlich der Hund begraben. npm hat nämlich eine dokumentierte Logik, wohin genau es Module installiert. Man muss sich nämlich nicht zwingend im Applikations-Root-Verzeichnis befinden, sondern kann auch in einem Unterverzeichnis davon sein: npm hangelt sich dann so lange verzeichnisweise nach oben, bis es ein Verzeichnis findet, das ein Unterverzeichnis node_modules beinhaltet, und betrachtet jenes Verzeichnis dann als den Applikations-Root. Dumm ist das nicht:

This behavior is inspired by and similar to git’s .git-folder seeking logic when running git commands in a working dir.

Problematisch wird es dann, wenn es kein Verzeichnis node_modules gibt. In diesem Fall sieht npm nämlich das aktuelle Verzeichnis als Applikations-Root an – und wenn man also während der Installation gerade zufällig in seinem Home-Verzeichnis ist, bekommt jenes ein harsches chmod 755 ab. So sieht das in der Praxis aus:

[dummy@lyra ~]$ ls -ld ~
drwx------ 16 dummy dummy 4096 Dec 30 11:25 /home/dummy

[dummy@lyra ~]$ npm install fibers --verbose
...
npm verb mkdir done: /home/dummy 755
...

[dummy@lyra ~]$ ls -ld ~
drwxr-xr-x 16 dummy dummy 4096 Dec 30 11:25 /home/dummy

Insofern ist dieses Verhalten insbesondere für User gefährlich, die sich mit dem node-Konzept applikationsspezifischer node_modules-Verzeichnisse noch nicht vertraut gemacht haben und es schlicht und einfach von PHP, Perl, Python, Ruby oder irgendeiner anderen Sprache gewohnt sind, dass es keine Rolle spielt, in welchem Verzeichnis man sich gerade befindet, wenn man Module installiert. Auch andere Bugreports zeigen, dass wir und unsere User nicht die einzigen sind, die über dieses Verhalten stolpern.

Als schnellen Workaround haben wir nach unserer Analyse im Uberspace-Wiki dokumentiert, dass es eine gute Idee ist, umask = 077 in seine ~/.npmrc aufzunehmen – auf diese Weise passiert das chmod zwar immer noch, richtet aber keinen Schaden mehr an – und für die Ausführung der node.js-Applikationen ist es zumindest bei Uberspace.de auch nicht vonnöten, dass noch jemand anders als der Eigentümer selbst auf die Applikation zugreifen kann.

Parallel dazu haben wir einen Bugreport verfasst, der dieses Problem thematisiert – und wie es aussieht, wird das in künftigen npm-Versionen dann erfreulicherweise anders laufen. Danke an Nils für seine wichtigen Hinweise; an Peter für seine tatkräftige Unterstützung beim Debugging; und an Isaac für das schnelle Feedback.

Ich will 5 Kinder mit dir!

Dienstag, 13. Dezember 2011

Es wäre viel zu schade, eine solche Mail einfach nur zu beantworten und dann im Ticketsystem als erledigt zu markieren. Für den Fall, dass mal ein Tag oder auch eine ganze Phase so richtig mies läuft und wir Zweifel bekommen, warum wir Uberspace.de überhaupt machen (nicht, dass wir da im Moment tatsächlich irgendwelche Zweifel hätten, aber man weiß ja nie), haben wir uns hier eine Mail eines Users aufbewahrt, die dann genau die Glückspille sein wird, die alles wieder gut werden lässt. Wir bloggen es im Volltext mit freundlicher Genehmigung des Absenders – vielen Dank, dass du uns das teilen lässt.


To: hallo@uberspace.de
Subject: Hallo Ubernauten! Ich glaube ich habe mich gerade verliebt.
Date: Mon, 12 Dec 2011 22:18:49 +0100

Hallo hallo uberspace,

ich bin etwas verlegen und weiß nicht genau wie ich dich ansprechen soll. Ich bin ja eigentlich seit Jahren in ner Beziehung die zwar grundsolide – aber doch nichts wirklich prickelndes mehr ist. Und plötzlich sehe ich dich und glaube weder meinen Augen noch diesem wundervollen Gefühl in der Magengrube. Bitte denk jetzt nichts Falsches von mir, ich bin ein super ehrlicher Typ! Ich schreibe gerade meine Bachelorarbeit und hätte wirklich was Besseres zu tun, als mich nach was anderem umzusehen. Aber wie es halt so ist, im unverhofftesten Moment trifft es einen dann plötzlich. Und nachdem ich dich gefühlte 5 Minuten (3 Stunden) anstarre kann ich nicht anders…

Ich liebe dich und will 5 Kinder mit dir!

Jaaa und jetzt sagst du sicherlich: „Hey das bin ich schon gewohnt, ich habe halt diese Ausstrahlung auf manche Menschen… Aber 1) ich bin nicht monogam und 2) wir wär’s, wenn wir’s erst mal für nen Monat miteinander probieren.“

Das Ding ist: Die 5 erwähnten Kinder kommen aus meiner aktuellen Beziehung und ich kann, wenn ich mich nicht sofort entscheide, das Sorgerecht nicht einfach ohne Komplikationen übertragen…

…ähm Entschuldigung für diese Gefühlsduselei. Hier die Fakten:
– Ich bin gerade über euch gestolpert, finde das Angebot unschlagbar
– $HOSTER Vertrag steht kurz vor der Verlängerung und ich würde gern wechseln
– Ich habe 5 Domains, die ich überwiegend als Mailweiterleitung zu Gmail nutze
– Ich habe keine Ahnung wie ich den Umzug am geschicktesten einleiten und durchführen kann
– Kleines Bedenken: Neben einem positiven Blogeintrag habe ich noch nie was von euch gehört (was nichts heißen muss)

Vielen Dank schon mal im Voraus,
ich hoffe auf eine lange und glückliche Ehe

Stellvertretend für meine Teamkollegen gebe ich gerne zu, dass ich mich nicht einfach nur gefreut habe, sondern tatsächlich auch ein wenig gerührt bin. Eine herzlichere Domainumzugsbestellung gab’s ziemlich sicher noch nie.

VeriSigns Preisschraube

Freitag, 02. Dezember 2011

Seit vielen Jahren betreibt VeriSign unter anderem die .com- und auch die .net-Registry. Der heutige Newsletter unseres Domainregistrars, der schon wieder eine Preiserhöhung für .com- und .net-Domains ankündigte, veranlasste mich, einmal die Preiserhöhungen der letzten Jahre nachzuvollziehen. „Datum PM“ ist dabei mit dem Datum der jeweiligen Pressemitteilung verlinkt, die als Quelle herangezogen wurde; „Datum PE“ ist das Datum, ab dem die jeweilige Preiserhöhung gilt.

Datum PM Datum PE Preis .com Preis .net
Preis Stand 2006 $6.00 $3.50
05.04.2007 15.10.2007 $6.42 +7,0% $3.85 +10,0%
27.03.2008 01.10.2008 $6.86 +6,9% $4.23 +9,9%
17.12.2009 01.07.2010 $7.34 +7,0% $4.65 +9,9%
14.07.2011 15.01.2012 $7.85 +6,9% $5.11 +10,9%

Der Preis für .com-Domains ist also innerhalb von gerade mal fünf Jahren um stolze 30,8% gestiegen, was immer noch ein Witz ist gegen den Preis von .net-Domains, der im gleichen Zeitraum sogar um 46,0% gestiegen ist.

Zu diesen Preisen, die VeriSign erhebt, kommt noch ein TLD-unabhängiger Zuschlag, der von ICANN erhoben wird. Während VeriSign aber immer teurer wird, sank der ursprüngliche ICANN-Zuschlag von ursprünglich $0.25 im Juli 2007 auf $0.20 und liegt derzeit nur noch bei $0.18 pro Domain und Jahr.

Nun hat Christoph Grueneberg vom Domainvermarktermagazin DVmag.de sich bereits ein wenig in den VeriSign-Verträgen mit ICANN umgesehen und ist im Vertrag für den .com-Betrieb in Abschnitt 7.3 auf sehr konkrete Bestimmungen gestoßen, was den Maximalpreis angeht, den VeriSign von seinen Resellern verlangen darf:

(d) Maximum Price. The Maximum Price for Registry Services subject to this Paragraph 7.3 shall be as follows:

(i) from the Effective Date through 31 December 2006, US$6.00;

(ii) for each calendar year beginning with 1 January 2007, the smaller of the preceding year’s Maximum Price or the highest price charged during the preceding year, multiplied by 1.07; provided, however, that such increases shall only be permitted in four years of any six year term of the Agreement. In any year, however, where a price increase does not occur, Registry Operator shall be entitled to increase the Maximum Price by an amount sufficient to cover any additional incremental costs incurred during the term of the Agreement due to the imposition of any new Consensus Policy or documented extraordinary expense resulting from an attack or threat of attack on the Security or Stability of the DNS, not to exceed the smaller of the preceding year’s Maximum Price or the highest price charged during the preceding year, multiplied by 1.07.

Für den Zeitraum vom 01.03.2006 (Abschluss der Vereinbarung bis zum 31.12.2006 liegt der Preis also bei maximal $6.00; beginnend mit dem 01.01.2007 darf VeriSign den Preis für .com-Domains um maximal 7% pro Jahr anheben. Christoph Grueneberg liegt allerdings aus meiner Sicht falsch, wenn er angibt:

Eine solche Gebührenerhöhung ist in der Regel nur alle vier Jahre innerhalb einer sechsjährig laufenden Vereinbarung gültig.

Abgesehen davon, dass „alle vier Jahre innerhalb einer sechsjährig laufenden Vereinbarung“ ja schon rein logisch nur exakt ein einziges Mal vorkommen kann, übersetze ich „such increases shall only be permitted in four years of any six year term of the Agreement“ ganz klar so, dass es in maximal vier von sechs Jahren der Vertragslaufzeit entsprechende Preiserhöhungen geben darf. Insofern hat VeriSign hier keine außerordentliche Belastungen geltend gemacht (wie das Posting auf DVmag.de vermuten ließ), denn in der Zeit vom 01.01.2007 bis heute, also in knapp fünf Jahren, hat VeriSign seine Preise „nur“ die zulässigen vier Mal erhöht, das aber jeweils auch in der maximal möglichen Höhe.

Für den Betrieb von .net wurde währenddessen bereits im April 2001 ein Maximalpreis von $6.00 festgelegt – und zwar ganz ohne eine Erhöhungsklausel. Insofern wundert es nicht, dass VeriSign hier gleich größere Schritte macht – bis zum Maximum von $6.00 ist ja noch ein bisschen Luft.

Zurecht fragt das DVmag, ob bei der steigenden Zahl von Domainregistrierungen nicht eigentlich der Preis eigentlich sinken müsste, zumal bei einem virtuellen Gut wie Domains, die in erster Linie indirekte Kosten in Form von Verwaltungsaufwand und Infrastrukturbetrieb bedeuten. Schauen wir uns also doch mal an, was VeriSign so mitteilt. Hierbei fällt auf, dass bei jeder Preiserhöhung die „queries per day“ verlautbart werden, die natürlich jedes Mal angestiegen sind. Extrahiert aus den Pressemeldungen:

  • 15.04.2007: „nearly 30 billion queries per day today“
  • 27.03.2008: „more than 33 billion DNS queries per day“ (+10%)
  • 17.12.2009: „more than 50 billion queries per day“ (+52%)
  • 14.07.2011: „an average daily query load of 57 billion“ (+14%)

Ich habe mir daher mal angeschaut, wieviele Domains zu den jeweiligen Zeiten so registriert waren, und habe dazu entsprechend stolze Pressemeldungen ausgewählt, die möglichst dicht an den Preiserhöhungs-Pressemeldungen lagen. Da gibt’s zu lesen:

  • 05.03.2007: „The base of .com and .net domain names grew to 65 million domain names by the close of 2006“
  • 05.03.2009: „The .com and .net adjusted base surpassed 80.4 million domain name registrations at the end of 2007.“ (+24%)
  • 21.09.2009: „The overall base of .com and .net domain names grew to 93.5 million domain names during the second quarter of 2009“ (+16%)
  • 31.08.2011: „The .com and .net Top Level Domains (TLDs) experienced aggregate growth, surpassing a combined total of 110 million names in the second quarter of 2011“ (+18%)

Während also von ca. Frühjahr 2007 bis ca. Herbst 2011 die Zahl der täglichen DNS-Anfragen um rund 90% gestiegen sind, ist die Zahl der Domains ebenfalls um immerhin 69% gestiegen – und damit entsprechend auch die mit Domains erwirtschafteten Umsätze.

Wofür sonst braucht VeriSign also kontinuierlich höhere Gebühren?

2007: „To address the increase in both DNS volume and cyber attacks, VeriSign recently announced a major initiative entitled Project Titan to expand the capacity of its global Internet infrastructure by ten times by the year 2010“ – VeriSign will seine Kapazitäten so erweitern, dass sie „over 4 trillion queries a day“ packen.

2008: „The .com and .net infrastructures are continually being fortified and scaled to defend against increasingly sophisticated cyber attacks and to help protect against service disruptions. VeriSign is increasing the capacity of its global Internet infrastructure by ten times by the year 2010.“

2009: „VeriSign will continue investment to build out the .com and .net infrastructures to manage the increasing demands on the infrastructure brought on by the proliferation of Internet-enabled phones and devices and the emergence of DNS-centric technologies and services. VeriSign also continues to scale and fortify the .com and .net infrastructures globally to defend against increasingly sophisticated cyber attacks.“

2011: „Continued strong global Internet usage growth, along with increasingly powerful distributed denial of service (DDoS) attacks leveled against all parts of the Internet’s critical infrastructure, have dramatically increased the demands on Internet infrastructure providers such as Verisign.“

Mit anderen Worten: The same procedure as every year. Immer mehr Leute benutzen das Internet (ach was!), und es gibt „cyber attacks“, und gerade als ich schon dachte, dass alles mit „cyber“ im Namen irgendwie so nach den 90ern klingt, sind’s 2011 prompt „DDoS attacks“ geworden.

Bleibt eben noch die Frage offen, wieso beispielsweise .de-Domains nicht nur schon immer wesentlich günstiger sind als .com- und .net-Domains, sondern auch seit Jahren nicht eine einzige Preiserhöhung erfahren haben – obwohl auch die DENIC kräftig in den Ausbau von Infrastruktur, IPv6-Deployment, Umsetzung von DNSSEC etc. investiert hat. Irgendwas scheint dort also besser zu laufen als bei VeriSign. Vielleicht braucht aber VeriSign das viele Geld auch nicht für Investitionen, sondern für, äh …

Datum Pressemitteilung
06.11.2008 VeriSign Reports 18% Year-Over-Year Revenue Growth in Third Quarter 2008
07.05.2009 VeriSign Reports 13% Year-Over-Year Revenue Growth in First Quarter 2009
06.08.2009 VeriSign Reports 9% Year-Over-Year Revenue Growth in Second Quarter 2009
05.11.2009 VeriSign Reports 6% Year-Over-Year Core Revenue Growth in Third Quarter 2009
02.08.2010 VeriSign Reports 9% Year-Over-Year Revenue Growth in Second Quarter 2010
02.10.2010 VeriSign Reports 10% Year-Over-Year Revenue Growth in Third Quarter 2010
27.01.2011 Verisign Reports 10% Year-Over-Year Revenue Growth in 2010
24.04.2011 Verisign Reports 12% Year-Over-Year Revenue Growth in First Quarter 2011
28.07.2011 Verisign Reports 13% Year-Over-Year Revenue Growth in Second Quarter 2011
27.10.2011 Verisign Reports 14% Year-Over-Year Revenue Growth in Third Quarter 2011

Aber das ist natürlich nur eine schamlose Vermutung. Wenn dann aber Mitte Januar möglicherweise auch bei uns die .com- und .net-Domains teurer werden, dann sollte nun zumindest bekannt sein, warum das so ist.

HTTPS, Websockets, Port-Multiplexing – wenn Apache nicht mehr reicht

Mittwoch, 30. November 2011

Fangen wir mal mit HTTPS an. Wenn man Apache als Webserver einsetzt, heißt die Antwort auf die Frage nach HTTPS in der Regel mod_ssl. Gut integriert, featurereich, stabil. Und so weiter. Vor diesem Hintergrund mag es zunächst nicht ganz offensichtlich sein, warum wir uns nach einer alternativen HTTPS-Implementierung umsehen.

Das ist zugegebenermaßen ein bisschen von hinten durch die Brust ins Auge: Eigentlich geht’s um Websockets, die dem Apache fremd sind. Wem das nichts sagt, dem mag als erste Erklärung reichen, dass es sich hierbei um ein Protokoll handelt, das im Gegensatz zu klassischem statuslosen HTTP (Request, Response, Ende) eine persistente Verbindung zwischen Browser und Webserver offenhält, über die in beiden Richtungen kommuniziert werden kann und die aufgrund ihres binären Protokolls nur wenig Overhead hat, gerade im Vergleich mit HTTP. Das hat große Vorzüge für moderne Webapplikationen, die eben viel stärker mit dem Server interagieren – es dürfte auf der Hand liegen, dass es z.B. für einen Webmailer viel effektiver ist, wenn der Server einfach die Information „1 neue Mail“ an den Clienten senden kann, als wenn der Client alle paar Sekunden beim Server nachfragen muss, ob es etwas Neues gibt (Polling).

Hier fangen aber nun die Unsicherheiten an. Der Verbindungsaufbau einer Websocket-Verbindung läuft über HTTP (oder HTTPS), wobei ein Upgrade-Header mitgeschickt wird, der dem Server mitteilt, dass er auf das Websocket-Protokoll upgraden soll. Damit ist auch schon das primäre Problem beschrieben: Websockets laufen über den HTTP-Port 80 (oder den HTTPS-Port 443), sind aber nun mal kein HTTP, sondern benutzen HTTP nur als eine Art Startrampe. Das bedeutet im Umkehrschluss: Serverseitig wird etwas benötigt, was Websockets kann – oder zumindest ausreichend ignoriert, um sie nicht kaputtzumachen. Webserver können das aber typischerweise nicht, denn die Nutzung von Websockets ist tief in der jeweiligen Applikation verwurzelt, und die Applikationen, die mit Websockets hantieren (die beispielsweise auf node.js und socket.io basieren), sind typischerweise welche, die selbst HTTP und auch Websockets sprechen und dann entsprechend des Upgrade-Headers unterscheiden, was zu tun ist.

Wir stellen insbesondere bei unserer Hostingplattform Uberspace.de fest, dass der Apache im Grunde immer weniger als Webserver genutzt wird, sondern eher als eine Art Applikations-Multiplexer, der eingehende Requests an eigenständig laufende Applikationen weiterleitet. Denn machen wir uns nichts vor: Alle jüngeren Entwicklungen im Web-Umfeld laufen auf solche Setups hinaus und unterstützen dann verschiedenste Deployment-Möglichkeiten – welcher Webserver dann als Frontend eingesetzt wird, tritt zunehmend in den Hintergrund; häufig ist es dann auch kein Apache mehr, sondern ein nginx oder ein lighttpd (was für uns keine Option darstellt, weil wir eben auch noch viele User mit „klassischen“ Webapplikationen haben, bei denen ein Bündel PHP-Dateien irgendwo unterhalb des DocumentRoots abgelegt wird und wo Apaches .htaccess-Features häufig unverzichtbar sind). Ich will diese Entwicklung an dieser Stelle nicht bewerten; sie mag Vor- und auch Nachteile haben. In Bezug auf die Implementierung von HTTP muss ich nur an dieser Stelle einmal loswerden, dass ich schon verwundert bin, warum so viele Tools ihre eigenen HTTP-Stacks implementieren. HTTP ist nämlich durchaus überraschend komplex, und selbst im ziemlich reifen Apache werden bis heute Bugs aufgedeckt (wie kürzlich die allzu sorglose Behandlung fehlerhafter Range-Header, die einen Apache zum Absturz bringen konnte), bei denen ich mich als Applikationsentwickler eher darüber freuen würde, wenn ich mich um die HTTP-Implementierung nicht kümmern müsste und mich stattdessen über ein schmales, strenger definiertes Interface an den Webserver andocken könnte – aber als ausgewiesener Freund von FastCGI bin ich verwunderte Blicke ja bereits lange gewohnt. Vertagen wir jene Diskussion also erstmal, denn darum geht es hier nicht. Fakt ist: Immer mehr Applikationen sprechen selbst HTTP, also brauchen wir einen Weg, jene mittelfristig vernünftiger einzubinden als mit RewriteRule ... [P].

Fest steht erstmal, dass solche Applikationen, die selbst HTTP sprechen, entsprechend nicht auf Port 80 laufen können, weil der ja schon durch den Apache belegt ist. Mit zunehmender Verbreitung von IPv6 könnte man natürlich ins Auge fassen, Usern die freie Wahl zu lassen, auf Port 80 „ihrer“ IPv6-IP (bei Uberspace.de vergeben wir jedem User eine eigene) dann eben ihre eigene Applikation laufen zu lassen, aber machen wir uns nichts vor: IPv4 wird uns noch viele Jahre erhalten bleiben und für vermutlich gar nicht mal so kurze Zeit auch noch unverzichtbar sein. Und da wir bei IPv4 keine separaten IPs vergeben, bleibt hier also nur jenes Multiplexing, bei dem ein HTTP-Frontend die Requests verteilt. Und da wird der Umstand, dass der Apache keine Websockets beherrscht – hier also in dem Sinne, dass er sie nicht via mod_proxy an die Applikation durchreicht und die Verbindung dann auch offenhält – zunehmend zum Problem. Nicht akut, denn nur Chrome 14, Firefox 7 und IE 10 bieten derzeit Unterstützung für den aktuellen Websockets-Standard, und Libraries wie socket.io machen automatisch Fallbacks auf Polling, was ja auch funktioniert. Aber die Frage nach Websockets ist zumindest ausreichend akut, dass wir uns schon heute damit beschäftigen, Lösungen zu finden, die über Hacks und Workarounds hinausgehen.

Den Apache werden wir nicht los, und das wollen wir auch gar nicht, schon aus den oben genannten Gründen, insbesondere wegen seiner .htaccess-Features. Aber er taugt wohl kaum als künftiges Frontend auf Port 80. Dort brauchen wir etwas, was sozusagen Port-Multiplexing macht, und idealerweise auch nur das – da die Applikationen mit ihren ganzen Features erst hintendran laufen, sollte das Frontend bitte besonders schlank und flink sein und durch eine minimale Codebasis auch weitaus einfacher auf mögliche Schwachstellen untersuchbar sein als ein Moloch wie Apache.

Was sich uns zuerst als vielversprechende Alternative in den Weg gestellt hat, war Varnish, und um es gleich vorwegzunehmen: Wir haben Varnish auch nach wie vor im Auge, primär aufgrund seines hervorragenden Rufs als „accelerating cache“. Websockets sind mit Varnish ganz offiziell kein Problem.

Bemerkt Varnish einen Upgrade-Header, öffnet es (geeignete Konfiguration vorausgesetzt) eine Pipe zur Zielapplikation und hält sich bei der weiteren Kommunikation einfach raus – es reicht alles unverändert durch. Das Problem: Varnish beherrscht kein HTTPS, und zwar mit Absicht. Wer mehr darüber lesen will, kann sich diesen launigen Rant des Varnish-Autors zu Gemüte führen, dessen sonstige random thoughts nebenbei auch sonst mehr als lesenswert sind. Seine Empfehlung: Man möge doch bitte einfach einen SSL-Proxy wie nginx oder Pound vor Varnish setzen, was dann HTTPS macht. Gut, das klingt erstmal machbar. nginx kann derzeit noch nicht mit Websockets umgehen (nur via TCP, was dann aber wieder eine eigene IP oder einen eigenen Port braucht und somit nicht auf Basis von Hostnamen oder URLs funktioniert). Zwar gibt es derzeit kein offizielles Statement, inwieweit Pound mit Websockets zurechtkommt, aber zumindest einer gibt an, dass diese Kombination prima funktioniere, so dass wir das für den Moment mal als gegeben hinnehmen, bis wir es selbst getestet haben.

Je länger wir uns mit Pound beschäftigen, desto charmanter erscheint uns diese Lösung – und zwar auch unter dem Aspekt, dass es damit problemlos möglich ist, „das ganze SSL-Zeugs“ unter einer autarken User-ID laufen zu lassen, in einer chroot-Umgebung, und damit gut vom Rest des Systems abgeschottet – im Gegensatz zu mod_ssl, bei dem potentielle Sicherheitslücken immer das Risiko bieten, die Berechtigungen des Apache-Users und damit zumindest Leserechte auf verdammt vielen Sachen zu erlangen.

Dazu bietet Pound eben die Möglichkeit, Verbindungen anhand einer Vielzahl von Kriterien – primär seien hier reguläre Ausdrücke auf die aufgerufene URL sowie auf HTTP-Header genannt – an unterschiedliche Backends weiterzuleiten. Es wäre damit also kein Problem, Aussagen wie „Verbindungen zu www.userdomain.tld an Port 8012 schicken“ oder „Verbindungen zur URL /pad von www.userdomain.tld an Port 8012 schicken“ in einer Pound-Konfiguration auszudrücken, was letztlich Möglichkeiten eröffnet, hier für User ein kleines Interface zu bauen, mit dem sie ihre Hostnamen und ggf. auch die URL-Pfade ihrer Hostnamen selbstständig auf Ports ihrer Wahl routen könnten, was ein hohes Maß an Flexibilität eröffnen und den Support entlasten könnte, der sich in Bezug auf solche Applikationen vor allem auf die wiederkehrende Erläuterung von Proxy-RewriteRules und der Erklärung der Frage „Wieso geht das mit den Websockets nicht“ konzentriert und damit zunehmend Leidensdruck erzeugt.

Und schließlich könnten wir noch eine weitere Fliege mit der gleichen Klappe schlagen: Pound beherrscht ab der Version 2.6 – die zwar noch als experimentell gilt, aber bereits mehrere Releases erfahren hat – auch Server Name Indication (SNI), was Voraussetzung dafür ist, mehrere SSL-Zertifikate auf einer einzelnen IP zu betreiben – bei IPv4 herrscht hier ja nun eben Knappheit, und wir würden durchaus gerne viel lauter „Nutzt doch HTTPS mit euren eigenen Zertifikaten!“ rufen, wenn das nicht derzeit noch regelmäßig damit verbunden wäre, dafür IPv4-IPs zu verbrauchen, was wir gerne vermeiden würden – eine höchst ärgerliche Zwickmühle.

Nun ist der SNI-Support bei Pound noch ein wenig eingeschränkt, weil bei der Auswahl eines passenden Certs ausschließlich nach dem Common Name (CN), nicht aber nach den Subject Alternative Names gematcht wird, die mittlerweile durchaus sehr verbreitet sind – nicht zuletzt bei den SSL-Zertifikaten von StartCom, die wir auf unseren Uberspace-Hosts einsetzen. Hier prüfen wir gerade Möglichkeiten, mit einem entsprechenden Patch diese Funktionalität noch zu erweitern. Wenn’s klappt, könnten wir damit die Möglichkeit bieten, beliebige Stückzahlen von SSL-Zertifikaten einfach in unsere Konfiguration einzubauen – und zwar ohne Apache-Restarts, die ja letztlich immer dazu führen, dass alle aktuellen FastCGI-Prozesse (und damit auch PHP-Interpreter) gestoppt werden und neu gestartet werden müssen, weshalb Apache-Restarts immer möglichst zu vermeiden sind.

Und Varnish? Bleibt interessant, aber vor allem wegen seiner Caching-Fähigkeiten. Als reines Frontend und als Multiplexer tut’s Pound auch alleine – und wenn sich herausstellt, dass es bei Pound mit den Websockets nicht hinhaut, dann müssen wir sowieso nochmal nach einer Alternative suchen, denn Varnish allein tut’s ja auch nicht – wegen fehlendem SSL-Support. Allein wegen seiner vielfältigen Routing-Fähigkeiten und wegen seines SNI-Supports könnte Pound aber schon heute einen wichtigen Beitrag zu leisten, unser Hosting bei Uberspace.de besser zu machen.

Wenn klassische Presse über IT-Themen berichtet

Samstag, 24. September 2011

… dann bleibt Fremdschämen selten aus. Über einen Hinweis von Bert Ungerer kam ich auf den taz-Artikel Panne bei T-Online. Er mag heute einmal repräsentativ für viele Artikel mit IT-Bezug stehen, die man in der Nicht-IT-Presse so findet – dass es hierbei ausgerechnet die taz trifft, ist dabei schlichter Zufall. So wird im Artikel berichtet:

Unangenehme Überraschung für Kunden von T-Online. Wenn sie in den vergangenen Wochen E-Mails versandten, bekamen sie oft unvermutete Fehlermeldungen: Ihre Nachricht konnte nicht zugestellt werden, wurde verzögert oder blieb einfach verschwunden.

Vielleicht bin ich hier einfach nur vorgeschädigt, aber ich kann es langsam nicht mehr hören. E-Mails verschwinden nicht, und es ärgert mich zunehmend, wenn das Medium E-Mail von irgendwelchen Schlaumeiern als unzuverlässig gebrandmarkt wird. Ja, E-Mails landen im Spamordnern. Ja, E-Mails werden verzögert, beispielsweise durch Greylisting. Ja, E-Mails können bouncen, wenn der Empfängerserver sie nicht akzeptiert. Aber sie verschwinden nicht. Das ist nur der vordergründige Eindruck, der bei vielen Usern entsteht, die schlicht keinen Blick in ihren Spamfilter werfen oder Bounces nach dem Motto „Das ist ja Englisch, das versteh ich nicht“ löschen und dann so tun als wüssten sie von nichts. Die Fälle, in denen E-Mails tatsächlich verschwinden, sind extrem selten (triple bounces würden mir hier ad hoc einfallen) und praktisch immer auf klare Fehlkonfigurationen zurückzuführen – die dann aber eben überhaupt nichts mit dem Spamproblem zu tun haben und in der Regel auch nicht auf das Medium E-Mail zurückzuführen sind, sondern auf entsprechende von Usern selbst verfasste Filterregeln.

Schuld war ein Spam-Filter, der die Mail-Server des deutschen Groß-Providers als Versender unerwünschte Werbepost deklarierte.

Man mag den Unterschied für akademisch halten, aber: Spamcop ist eine Blacklist und kein Spamfilter. Betreiber von Mailservern können aus freien Stücken entscheiden, ob sie eine solche Blacklist heranziehen, um ihre SMTP-Verbindungen bereits grob vorzufiltern. Spamcop selbst filtert überhaupt nichts – das wäre technisch auch gar nicht möglich, solange nicht die MX-Records im DNS den Mailtraffic einer Domain explizit über Spamcop routen würden. Stattdessen stellt Spamcop einfach nur die Information bereit: „Von diesem und jenem Host wird viel Spam versendet“, und diese Information ist zunächst einmal schlicht und einfach wahr – das leugnet ja nicht einmal T-Online. Es ist also mitnichten so (und dieser Eindruck entsteht aus dem Artikel), dass einfach mal irgendso eine Antispamfirma den ausgehenden Mailverkehr von T-Online abklemmen könnte. Das ist immer noch die freie Entscheidung der Betreiber der empfangenden Mailserver, ob sie der Empfehlung von Spamcop folgen wollen oder nicht.

Spezialisierte Betreiber wie Spamcop überwachen das Spamvolumen weltweit und registrieren haargenau wie viele Spam-Nachrichten von einer IP-Adresse kommen.

Das ist blanker Unsinn. Wenn Mailserver A eine Mail an Mailserver B sendet, dann bekommt Spamcop davon exakt überhaupt nichts mit. Faktisch hat Spamcop nur zwei Datenquellen: Einerseits manuelle Beschwerden, bei denen Empfänger unerwünschter Mails jene an Spamcop meldeten, und zweitens Spamfallen, also E-Mail-Adressen, die nie für legitimen Mailverkehr verwendet wurden und somit ausschließlich von Spammern angeschrieben werden, sei es, weil sie jene Adressen schlicht geraten haben, oder weil sie Websites oder Verzeichnisse abgescannt haben, wo jene Mailadressen publiziert wurden. Spamcop hat also eben gerade keine Ahnung, wie viele Spam-Nachrichten von einer IP-Adresse kommen. Es zählt nur Beschwerden. Der Unterschied mag im Detail liegen, aber da der Autor explizit konstatiert, Spamcop würde die Anzahl der Spam-Nachrichten „haargenau registrieren“, ist anzunehmen, dass er schlicht nicht begriffen hat, wie Spamcop funktioniert (und wie nicht).

Doch alle Bemühungen von T-Online, von der Spamcop-Liste gestrichen zu werden, scheiterten zunächst, da der Anbieter auf seine Regeln beharrte: Wer von der Liste gestrichen werden will, muss den Spam-Ausstoss seiner Mailserver auf ein Mindestmaß beschränken.

Abgesehen davon, dass der Autor hoffentlich gemeint hat, dass Mailserver den Spam-Ausstoß auf ein Minimum beschränken sollten (denn was wäre denn bitte ein Mindestmaß für Spam-Ausstoß?!), ist das erstmal durchaus richtig. Die Formulierung aber, dass T-Online sich doch irgendwie „bemüht“ habe, und dass das aber „gescheitert“ sei, legt nahe, dass der Autor offenbar der Meinung ist, Spamcop habe das Scheitern gewissermaßen verschuldet. Aber warum genau hätte Spamcop sich auch anders verhalten sollen? Spamcop veröffentlicht, von wessen Servern viel Spam versendet wird. Von den T-Online-Mailservern wurde viel Spam versendet. Die Information, die Spamcop dann entsprechend führt, ist also völlig richtig, und sie ist auch nicht einfach so zurückzunehmen, nur weil T-Online quengelt. Hinter der Formulierung des Autors steht offenbar die Einschätzung, dass T-Online für das Internet sozusagen „too big to fail“ sei und man hier entsprechend Sonderregeln schaffen müsste, weil … ja, warum eigentlich? Es gibt nämlich eben genau keinen Grund, für T-Online irgendwelche Sonderregeln zu schaffen. Die müssen sich an die Spielregeln halten und Spam so weitgehend wie möglich vermeiden, so wie auch wir, und so wie auch jeder andere Provider.

Als es dann schließlich darum geht, dass T-Online nun auch Spamfilterung für ausgehende Mails durchführt, ergänzt der Autor:

Solche Filter sind noch nicht üblich, werden aber von immer mehr Unternehmen eingesetzt. Das Problem: die Trefferquote mag auf Papier weit über 99 Prozent liegen. Bei Millionen Nachrichten Täglich bleiben aber immer wieder einige hängen. Der elektronische Briefträger ist nicht verlässlich.

Schon wieder dieses Märchen von der unzuverlässigen E-Mail..! Es gibt exakt zwei Varianten: Entweder T-Online akzeptiert eine Mail zum Versand – oder aber eben nicht. Und dann erhält der Absender eine Fehlermeldung. Vielleicht versteht er sie nicht; vielleicht ignoriert er sie; vielleicht zeigt sein Mailclient sie ihm nicht auffällig genug an, aber das ändert nichts daran, dass der „elektronische Briefträger“ sehr wohl verlässlich ist.

Versuche ich nun also, mein gesamtes bisheriges Wissen über E-Mail zu vergessen und völlig unbefleckt diesen Artikel zu lesen, so habe ich heute gelernt: E-Mail ist ein unzuverlässiges Frickelsystem, in dem laufend Mails veschwinden; es gibt eine Firma namens Spamcop, die genau weiß, welche IP wieviele Spammails verschickt (wofür rein logisch zwingende Voraussetzung ist, dass sie offenbar den gesamten E-Mail-Verkehr des Universums analysiert); und wenn diese Firma namens Spamcop das so will, dann kann sie alle Mails, die T-Online verschickt, als Spam deklarieren – oder die Mails auch ganz verbannen; der Autor scheint sich da nicht sicher zu sein. Und außerdem hat T-Online sich doch bemüht (man versuche das doch einmal, wie ein „er hat sich stets bemüht“ in einem Arbeitszeugnis zu verstehen), aber obwohl T-Online doch – da sind wir uns natürlich alle einig – ein hochseriöses Unternehmen ist, hat diese patzige Spamcopfirma einen auf dicke Hose gemacht und war so kleinkariert, auf seinen Regeln zu beharren, statt für den womöglich größten Provider Deutschlands doch mal Fünfe gerade sein zu lassen.

Was dem Artikel letzten Endes völlig abgeht, ist der simple Fakt, dass es längst überfällig war, dass T-Online sich seiner hauseigenen Spamproblematik verstärkt annimmt. Ich bezweifele, dass es so zügig dazu gekommen wäre, hätte dieses Blacklisting durch Spamcop nicht stattgefunden. Und ich zolle T-Online meinen Respekt dafür, dass es sich nicht einfach nach Gutsherrenart einen Teufel drum geschert hat, dass irgendeine Firma ihre Mailserver auf einer Blacklist führt, sondern dies zum Anlass genommen hat, tatsächlich aktiv etwas gegen ihr Spamproblem zu tun. Im Endeffekt ist das Internet also ein kleines bisschen besser geworden. Dafür sollten wir nicht nur T-Online für die längst überfällige Spam-Prophylaxe danken, sondern auch Spamcop, die das forciert haben.

Was bleibt, ist meine Enttäuschung über die entsetzliche Qualität des Artikels. Wenn ich nun schon Artikel zu Themen, bei denen ich mich selbst doch recht gut auszukennen glaube, derart haarsträubend finde: Wie könnte ich dann Artikeln aus Themenbereichen, von denen ich weitaus weniger verstehe, noch das Vertrauen schenken, dass mir jene sachlich, korrekt und ausgewogen erklären, was in der Welt gerade so wichtig ist? An einer eigenständigeren, geschärften Medienkompetenz, die einen dazu ermutigt, auch weiterführende und auch alternative Quellen zu berücksichtigen, führt also wieder einmal kein Weg vorbei.

DNS-Resolving mit persistentem Cache

Samstag, 03. September 2011

Wir fahren in Bezug auf DNS-Caching eine einfache, aber sehr effektive Strategie: Alle von uns verwalteten Hosts bekommen einen lokalen DNS-Resolver. Zwar haben wir in unseren Netzen jeweils ebenfalls DNS-Resolver, die direkt mit einem Eintrag in der /etc/resolv.conf netzintern genutzt werden können, aber das brauchen wir eigentlich nur während der Installation von Systemen, die wir typischerweise via PXE-Boot und dann über das Netz machen, wofür ein fertig bereitstehender Resolver vonnöten ist. Sobald eine Maschine läuft, bekommt sie einen eigenen Resolver.

Dieser Ansatz mag erstmal ungewöhnlich wirken, gehört doch das Eintragen von klassischerweise zwei DNS-Resolvern in die eigene Netzwerkkonfiguration zum üblichen Standard. Ein Aspekt ist sicherlich eine gewisse Performancesteigerung – was der gängige Grund sein dürfte, warum oft empfohlen wird, einen lokalen DNS-Cache auf dem heimischen Rechner zu installieren, denn bei gar nicht mal so wenigen Zugangsprovidern sind die von jenen bereitgestellten Resolver oftmals in bedauernswertem Zustand, was die Performance angeht, und gerade die günstigeren Plaste-DSL-Router für Daheim bekleckern sich hier auch oft nicht mit Ruhm – gelegentlich mag auch mein Linksys-Router einfach keine DNS-Anfragen mehr auflösen oder an den Upstream-Resolver durchreichen, obwohl er den Internetzugang an sich noch bereitstellt – nur ein Router-Reboot hilft dann noch. „Stabil“ ist anders.

Im Rechenzentrum zählen diese Argumente weniger. Die dort bereitgestellten DNS-Resolver liegen ja im gleichen Netz wie die anfragenden Hosts, und sie sind zudem mit üppiger Hardware ausgestattet. Was die Performance angeht, liegen wir hier also eher von Optimierungen im Millisekundenbereich, die es wirklich nicht wert wären.

Worum’s uns viel mehr geht, ist, mögliche Störungsquellen zu minimieren. Das DNS an sich ist voller Redundanz und Lastverteilung: Müssen DNS-Anfragen aufgelöst werden, stehen satte 13 IPv4-Adressen von Root-Nameservern bereit; einige sind auch via IPv6 erreichbar. Hinter den meisten stehen nicht etwa einzelne Hosts, sondern gleich etliche, die via Anycast geografisch verteilt sind. Fragt man jene beispielsweise nach der .de-Delegation, erhält man derzeit immerhin gleich fünf mögliche Nameserver, auch wiederum geografisch verteilt und zum Teil über IPv6 erreichbar. Für jede einzelne .de-Domain sind dann auch wieder mindestens zwei Nameserver zuständig, die gemäß der Vorgaben der DENIC in unterschiedlichen Class-C-Netzen stehen müssen (auch wenn das nicht zwingend eine geografische Trennung bedeutet). Es gibt also Redundanzen, wohin das Auge sieht. In den seltenen Fällen, wo tatsächlich einer dieser Hosts gestört ist, hat ein DNS-Resolver also praktisch immer genug Alternativen, um ans Ziel zu kommen – und das auch performant, weil er natürlich auch die Information, dass ein bestimmter Host aktuell nicht ansprechbar ist, eine gewisse Zeit lang cachen kann.

Und diese ganze Redundanz soll man nun aufgeben, in dem man alle Hosts in seinem Netzwerk über einen einzigen DNS-Resolver führt? Natürlich, natürlich: Wenigstens zwei sollten es schon sein; mehr als drei sind aber normalerweise schon gar nicht möglich. Wenn nun einer ausfällt, gönnt sich die C-Library aber defaultmäßig stolze fünf Sekunden, bis sie den nächsten Nameserver versucht – und versucht es ebenso defaultmäßig immer wieder beim ersten. Zwar lässt sich das in gewissen Grenzen anpassen (hint, hint: options timeout:1 rotate), aber so weit, dass dann z.B. ein Resolver, der ein paar Mal nicht antwortet, für eine gewisse Zeit lang gar nicht mehr angesprochen wird, geht die Logik dann eben doch nicht. Es gibt also zwar auch ein bisschen Redundanz, aber wirklich elegant ist sie nicht. Man braucht also wenn dann schon wirklich hoch verfügbare DNS-Resolver im Netz, denn jeder Ausfall zieht empfindliche Probleme nach sich: Logins via SSH brauchen sekundenlang, weil das System versucht, ein Reverse Lookup für die IP durchzuführen und danach auch noch ein Forward Lookup, um zu schauen, ob’s passt, und wehe dem, der kein HostnameLookups Off in seiner Apache-Konfiguration hat.

Ein lokaler DNS-Resolver hingegen fällt eigentlich nur in einer Situation aus: Nämlich dann, wenn die komplette Maschine ausfällt – und dann ist’s ohnehin egal, bzw. dann hat man ohnehin dringendere Probleme. Vor allem aber beträfe ein reiner Resolver-Ausfall dann nur diesen einen Host – und nicht gleich sämtliche Hosts im Netz, die jenen Resolver benutzen. Insofern fahren wir hier gewissermaßen eine Insel-Strategie: Wenn was kaputtgeht, dann jedenfalls nur sehr begrenzt. Und die Realität zeigt nun im Lauf von Jahren auf Dutzenden von Hosts: Es geht nichts kaputt. Das Setup ist rock-solid.

Als lokalen DNS-Cache benutzen wir hierbei dnscache aus dem djbdns-Paket. Das ist, ohne Frage, für jemanden, der sonst nicht mit Software aus dem DJB-Universum, mit einer gewöhnungsbedürften Installation verbunden, die aber schnell ihre Vorzüge ausspielt – und letztlich punktet dnscache genau wie tinydns eben besonders auch mit hoher Stabilität und Sicherheit. Einige Dinge sind aber zu beachten:

Erstens, errno.

Damit djbdns mit einer aktuellen glibc kompiliert werden kann, muss in conf-cc am Ende des Compileraufrufs -include /usr/include/errno.h angehängt werden; ein Umstand, den Dan Bernstein als Bug der glibc ansieht, auch wenn das durchaus diskutiert werden kann (runterscrollen zu „references“).

Zweitens, Root-Nameserver.

Die mit djbdns mitgelieferte Liste von Root-Nameservern ist nicht mehr ganz aktuell. Das lässt sich aber schnell fixen, weil natürlich jeder der (noch verfügbaren) Root-Nameserver eine aktuelle Liste liefern kann. Mit

dnsip `dnsqr ns . | awk '/^answer: \./ { print $5 }'` >
/service/dnscache/root/servers/@

lässt sie sich fix aktualisieren; ein

svc -h /service/dnscache

informiert dnscache zur Laufzeit über die neue Serverliste.

Drittens, Cache-Größe.

Die Standardkonfiguration gibt dnscache eine Cache-Größe von gerade mal 1 MB. Das hat zur Folge, dass insbesondere auf Systemen, die viele DNS-Anfragen machen, der Cache möglicherweise eine stärkere Fluktuation aufweist als nötig wäre, mit der Folge, dass DNS-Anfragen, die er eigentlich aus dem Cache hätte beantworten können, wenn jener nicht zu früh wieder hätte aufgeräumt werden müssen, erneut stellen muss. Angesichts heutiger RAM-Dimensionen stellt es mit Sicherheit kein Problem dar, dem Cache 10 MB zu verpassen:

echo 10000000 > /service/dnscache/env/CACHESIZE

vergrößert den Cache, und

echo 12000000 > /service/dnscache/env/DATALIMIT

teilt dem dnscache begrenzenden softlimit mit, dass dnscache nun mehr RAM belegen darf. Mit

svc -t /service/dnscache

wird die laufende dnscache-Instanz beendet, woraufhin svscan den Dienst automatisch neu startet. Welche Cache-Größe wirklich angemessen ist, führt hier zu weit; entsprechende Strategien sind aber bereits an verschiedenen Stellen dokumentiert.

Viertens sind wir kürzlich auf einen Patch gestoßen, der dnscache etwas äußerst Nützliches beibringen, nämlich die Möglichkeit, seinen aktuellen Cacheinhalt in eine Datei zu dumpen – und bei einem Start seinen Cache auch erstmal direkt aus jener Datei wieder zu befüllen. Auf diese Weise ist der Cache von dnscache auch nach einem Neustart sofort „warm“, ohne dass es erstmal wieder damit beginnen muss, von den Root-Nameservern her die TLD-spezifischen-Nameserver aufzulösen, und so weiter.

Der ursprüngliche Patch stammt von Efgé und bringt die Funktionalität ein, durch Senden eines SIGALRM dnscache dazu zu veranlassen, seinen Cacheinhalt in eine Datei zu dumpen, deren Name durch eine zuvor gesetzte Umgebungsvariable definiert wird, sowie die Funktionalität, jene Datei beim Starten wieder einzulesen. Nikola Vladov hat auf dieser Basis noch einen erweiterten Patch entwickelt, der zum einen dafür sorgt, dass dnscache beim Beenden automatisch einmal seinen Cache speichert, und der selbiges zudem in einem festlegbaren Rhythmus auch von sich aus tut, was insbesondere für Fälle praktisch ist, in denen ein Host crasht und von daher keine Gelegenheit mehr hat, den Cache zu schreiben wie bei einem sauberen Herunterfahren (nicht, dass sowas ständig vorkäme, aber der kluge Mann baut vor).

Lokal ordentlich zu cachen ist in jedem Fall eine gute Praxis, nicht nur der eigenen Performance wegen, sondern auch, um die Last, die man durch DNS-Anfragen auf anderen Hosts erzeugt, gering zu halten – es gewinnen also beide Seiten. Um so wichtiger ist das, wenn man nicht nur „gewöhnliche“ DNS-Daten abfragt, sondern auch Datenbanken, die via DNS publiziert werden, wie beispielsweise das IP to ASN Mapping der Team Cymru Community Services (mit herzlichem Dank an Daniel für den Tipp) – was für uns der Hauptgrund war, uns eingehender mit der Cache-Dumping-Thematik zu befassen. Der konkrete Anlass dafür reicht aber noch problemlos für einen weiteren Blogpost – stay tuned.

syslog-ng und MySQL

Mittwoch, 17. August 2011

Wir haben einen Kunden, der auf einem seiner Systeme die anfallenden Log-Nachrichten nicht nur in die üblichen Logfiles schreiben läßt, sondern auch in seine MySQL-Datenbank. Das sah in der Config dann so aus (Liebe Kinder, bitte nicht nachmachen!):

destination d_mysql {
program(
"/usr/bin/mysql -uSOMEUSER -pSOMEPASSWORD SOMEDATABASE"
template("INSERT INTO logs (host, facility, priority, level, tag, datetime, program, msg) VALUES ( '$HOST', '$FACILITY', '$PRIORITY', '$LEVEL', '$TAG','$YEAR-$MONTH-$DAY $HOUR:$MIN:$SEC','$PROGRAM', '$MSG' ); ")
template-escape(yes)
);
};
filter f_no_debug { not level(debug); };
log { source(s_all); filter(f_no_debug); destination(d_mysql); };

Kurz zur Erläuterung: Hier wird syslog-ng letztlich angewiesen, für jede einzelne Log-Nachricht einen Programmaufruf von mysql zu konstruieren und eine Zeile an eine bestimmte Tabelle (logs) in einer bestimmten Datenbank anzufügen.

Man kann dem Kunden hier auch gar keinen Vorwurf machen, erstens entspricht diese Vorgehensweise einer alten Anleitung, die man an vielen Stellen im Internet findet (offenbar schreiben die Autoren diverser Anleitungen alle voneinander ab), zweitens unterstützte syslog-ng früher kein Logging in Datenbanken. So der so: diese Vorgehensweise ist nicht gerade optimal. Die Probleme im einzelnen sind:
ul>

  • Auf einem gut beschäftigten System sind Log-Nachrichten keine gelegentlich auftretenden Ereignisse, sondern meist eher ein fast kontinuierlicher Strom von Daten. Deswegen neigen die meisten Logging-Lösung (so auch syslog-ng) dazu die anfallenden Nachrichten immer ein kleines bißchen zu puffern und dann erst in die Dateien zu schreiben, weil man sonst dem System viel Arbeit aufbürdet, etwa dann wenn man etwa 200 mal pro Sekunde eine Datei öffnet, eine Zeile hinten anfügt und sie wieder schließt und das am besten mit mehreren Dateien gleichzeitig tut. Das oben gezeigte Beispiel macht mit MySQL ziemlich genau das: es konstruiert wirklich für jede Log-Nachricht einen Befehlsaufruf von mysql.
  • Man muß sich einfach mal vergegenwärtigen was bei einem Programmaufruf so alles passiert: Zunächst muß das Programm von der Festplatte geholt werden (zugegebenermaßen wird Linux dies natürlich bei häufigen Aufrufen cachen, es sei hier trotzdem mal erwähnt), dann müssen die dynamisch zu ladenen Bibliotheken die dieses Programm verwendet geholt werden (auch hier wird später gecachet), dann muß für das Programm Speicher alloziert werden und bei jedem halbwegs modernen Linux-Kernel heißt das auch, daß der Kernel Memory Layout Randomization machen muß, d.h. er muß die einzelnen Programmteile im Speicher zufällig verteilen, damit Angriffe gegen das Programm nicht zuverlässig zu ausnutzbaren Funktionen springen können. Das Erzeugen von Zufall ist für den Kernel auf x86er Hardware nicht einfach (weil die Plattform — wie die allermeisten Computer-Plattformen — nicht vorsieht, daß zwingend eine gute Entropiequelle Teil der Hardware sein muß) und kann Zeit kosten. Wer schonmal mit GnuPG oder OpenSSL große Schlüssel generiert hat, wird das Problem „not enough entropy“ kennen. Wenn dem Kernel die Entropie ausgeht, dann kann das auch andere Programme in Mitleidenschaft ziehen, dann kann es z.B. zu Verzögerungen kommen, wenn der Apache HTTPS-Aufrufe bedienen soll, denn neue SSL-Verbindungen brauchen auch immer eine Prise frische Entropie.
  • Dann muß man sich klar machen, daß auch im Programmablauf einiges passiert, hier sei einmal das wichtigste erwähnt: Unter anderem muß hier jedesmal eine neue Verbindung zur MySQL-Datenbank aufgebaut werden (in diesem Fall verfolgt das glücklicherweise wenigstens über den Socket, die Verbindung muß nicht auch noch zweimal durch den TCP/IP-Stack wandern, wie sie es etwa bei einem Zugriff über das Loopback-Interface müßte). Das heißt aber schonmal mindestens ein SELECT FROM mysql.users pro Verbindung.
  • Ein weiterer, nicht zu vernachlässigender Faktor ist, daß das Kommandozeilenprogramm mysql ein sehr komplexes, mächtiges Programm ist. Es wäre viel sinnvoller, hier ein schmales mysql-insert-Programm zu verwenden, das einen kleineren Memory-Footprint hat und auf diese Aufgabe optimiert ist. (Nein, ich kenne kein solches Programm.)
  • Schließlich und endlich muß man schauen, was in der MySQL-Datenbank dann passiert: An eine nach kurzer Zeit gigantisch große Tabelle muß eine Zeile angefügt werden, diese Operation muß ggf. für eine MySQL-Replikation noch in ein Binlog geschrieben werden (und auf allen Slaves wiederholt werden) und natürlich müssen sämtliche Indizes dieser Tabelle aktualisiert werden. Alle internen Mechanismen zur Optimierung, die MySQL möglicherweise haben könnte um einen Haufen von Inserts in eine große Tabelle effizient abzuarbeiten werden ausgehebelt, weil die Inserts alle einzeln auf das Datenbanksystem einprasseln.
  • Und das alles für jede einzelne Log-Nachricht. Auf einem gut beschäftigten Server also locker viele, viele Male pro Sekunde. Moderne Prozessoren können zwar viele Milliarden Instruktionen pro Sekunde verarbeiten, aber natürlich setzen sich so komplexe Vorgänge wie oben beschrieben aus sehr vielen Instruktionen zusammen und außerdem findet ja längst nicht alles auf dem Prozessor statt, RAM und Festplatten sind aber um einiges langsamer als der Prozessor selbst.

    Als letztes Bonmot sei erwähnt, daß bei dieser Vorgehensweise natürlich ständig in der Prozessliste des Linux-Kernels die Aufrufe von mysql mit den Zugangsdaten zur Datenbank im Klartext auftauchen. Wenn man auf dem System alleine ist (oder zu sein glaubt), mag das vielleicht noch gehen, ist aber trotzdem nicht empfehlenswert, denn die Prozessliste ist auch Usern mit nur wenigen Rechten auf dem System zugänglich. (Immerhin: Bei diesem Kunden war der Datenbankuser nicht ‚root‘ und hatte auch nicht maximale Rechte auf dem Datenbanksystem. Es kommt also nicht immer gleich ganz dicke.)

    Wenn man ein bißchen länger zu dem Thema googelt, findet man noch andere Lösungen, die schon ein bißchen besser sind. Einige Leute schreiben die Log-Nachrichten einfach in eine extra Datei und lassen sie dann von einem anderen Programm alle paar Minuten im Block einlesen und in die MySQL-Datenbank stopfen. Das klingt zwar deutlich holpriger, kann aber bei guter Implementation deutlich besser funktionieren. Wieder andere verwenden eine Named Pipe um die MySQL-Datenbank zu füttern und nicht ständig neue MySQL-Verbindungen aufbauen zu müssen. Auch das ist deutlich besser als das oben gegebene Beispiel. Aber leider schreiben offenbar erschreckend viele Leute ausgerechnet von den Anleitungen ab, die auch das obrige Beispiel hervorgebracht hat.

    Mittlerweile gibt es einen nochmal deutlich eleganteren Weg, denn inzwischen bringt syslog-ng nativen Support für das Logging in Datenbanken mit. Als ich vor ein paar Tagen für unseren Kunden das Logging auf diese neue Methode umstellen wollte, ging es mir nach einem längeren Blick in die Dokumentation von syslog-ng allerdings erstmal wie dem sprichwörtlichen Schwein vorm Uhrwerk. Zwar liefert die Dokumentation ein Beispiel für PostGreSQL mit, aber das läßt leider Fragen offen, die auch von der detailierteren Dokumentation im Anhang nicht zufriedenstellend beantwortet werden (jedenfalls hab ich die Antworten nicht gefunden). So stellte sich mir etwa die Frage, ob ich über ein Socket gehen kann oder durch das Loopback-Interface gehen muß und ob ich ggf. den Pfad zum Socket angeben muß. Auch fragte ich mich, welche Variablen oder Macros ich denn aufrufen könnte und wie gut oder schlecht die Zugangsdaten zur Datenbank denn geschützt werden (wobei diese Frage schon eher unter Kür denn unter Pflicht fällt, solange die Daten nicht in der Prozessliste auftauchen, ist schonmal etwas gewonnen). Ich sage nicht, daß längeres Suchen in der Dokumentation diese Fragen nicht beantwortet, ich will nur mal ein bißchen darüber jammern, daß ich in der Dokumentation von syslog-ng bei jedem Thema immer ein bißchen länger suchen muß um meine Fragen zu beantworten.

    Und auf Anhieb findet man im Internet keine guten Beispiele, wie man diesen nativen SQL-Support in syslog-ng denn nun am besten benutzt. (Weil alle nur von dem selben, alten, dämlichen Beispiel abschreiben oder von den paar wenigen, etwas besseren, alten Beispielen.) Nun gut, Probieren geht über Studieren, ich hab’s dann am Ende ausgetüftelt. Damit zu den alten, dämlichen Beispielen sich mit der Zeit dann vielleicht ein paar geringfügig weniger dämliche gesellen, will ich es hier allgemein zugänglich machen:

    destination d_mysql {
    sql(type(mysql)
    username("SOMEUSER")
    password("SOMEPASSWORD")
    database("SOMEDATABASE")
    table("logs")
    columns("host", "facility", "priority", "level", "tag", "datetime", "program", "msg")
    values("$HOST", "$FACILITY", "$PRIORITY", "$LEVEL", "$TAG","$YEAR-$MONTH-$DAY $HOUR:$MIN:$SEC","$PROGRAM", "$MSG")
    indexes("datetime", "host", "program", "pid", "message"));
    };
    filter f_no_debug { not level(debug); };
    log { source(s_src); filter(f_no_debug); destination(d_mysql); };

    Und siehe da: die Daten strömen!

    Offenbar findet syslog-ng das Socket ohne größere Probleme (ich vermute mal es schaut in die my.cnf). Mit dem Statement host("localhost") könnte man aber auch über das Loopback-Interface (oder das Netzwerk, wenn man einen Hostname angibt) gehen. Bei letzterem bleibt zu beachten, daß MySQL selbst seine Verbindungen nicht verschlüsselt, es sei denn man konfiguriert das explizit. Alternativ bieten sich auch weitere Verschlüsselungsmethoden an, aber das ist ein anderes Thema.

    Um nochmal grob einzuordnen wo die Vorteile liegen:

    • Das Programm syslog-ng und die dynamischen Bibliotheken werden einmalig beim Programmstart geladen und verbleiben im Speicher. Kein ständiges, erneutes Aufrufen von mysql mehr, kein unnötiger entropy drain.
    • Die Verbindung zur MySQL-Datenbank ist persistent. Mag sein, daß syslog-ng sie einmal am Tag schließt und neu initialisiert, aber das fällt nicht weiter ins Gewicht. Meinetwegen kann er sie auch alle fünf Minuten wieder schließen und neu öffnen, solange er dies nicht mehr 200 Mal in der Sekunde tut.
    • Zugangsdaten zur DB liegen in der Config für syslog-ng (die nur root lesen kann) und im Speicherbereich von syslog-ng. Ein Angreifer muß also einen Root-Exploit oder Zugriff auf den Speicher von syslog-ng haben. Das sind höhere Hürden als der Zugriff auf die Prozessliste.
    • Es ist denkbar, daß syslog-ng intern Puffer verwendet und Log-Nachrichten nicht einzeln an die DB schickt, sondern immer einen Block von Nachrichten durchreicht. Es ist hier zumindest denkbar, daß MySQL selbst da auch intern ein bißchen optimiert, um noch etwas mehr Performance herauszukitzeln. Das war vorher schlicht kaum möglich.

    Ein Vergleich der Performance steht noch aus, aber da geht es eher um die Frage wie groß die Verbesserung ausfällt, nicht so sehr ob es eine gibt.

    Die wirklich spannende Frage, ob ich mit Programmen wie logger auf dem Host jetzt MySQl-Injections machen kann, werde ich hier in einem Update auch noch beantworten.

    Facebook und die Bounces

    Dienstag, 07. Juni 2011

    Ich weiß nicht, seit wievielen Jahren genau es als „bad practice“ gilt, Bounces zu versenden. Bounces gehen nun mal an die Adresse, die als Envelope Sender in der SMTP-Session angegeben wird – und das muss eben durchaus nicht die Adresse desjenigen sein, der die Mail tatsächlich verschickt hat. Insbesondere bei Spam ist das regelmäßig nicht der Fall, was letzten Endes heißt: Mailserver, die Mails nicht direkt in der SMTP-Session ablehnen, sondern jene erst akzeptieren und dann später bouncen, lösen damit als Antwort auf eine Welle von Spam eine Welle von Bounces aus, die dann denjenigen trifft, den der Spammer zufällig als Absender gewählt hat. Ich selbst habe im Lauf der Jahre immer mal wieder erlebt, wie plötzlich innerhalb weniger Stunden zehntausende von Bounces auf meiner Mailadresse eintrafen, die jemand als Absender von Spam verwendet hatte.

    Das Ärgerliche dabei ist, dass sich sowas nur schwer filtern lässt, denn die Bounces sind ja so gesehen durchaus „legitime“ im Sinne von „gut gemeinten“ Mails; sie haben keinen strikten Aufbau, und im blödesten Fall zitieren sie auch die Originalmail nicht, so dass der Spamfilter wirklich keine Chance hat, wenn ich ihn nicht darauf trainieren will, allgemein Bounces als Spam anzusehen.

    Folgerichtig haben einige RBL-Betreiber damit begonnen, nicht nur Hosts, die Spam versenden, auf ihre Listen zu setzen, sondern auch Hosts, die Bounces versenden. Dazu gehören nicht nur ganz exotische RBLs, sondern auch Größen wie z.B. SpamCop, die durchaus eine gewisse Reputation haben und das Problem in ihrer FAQ gut erläutern.

    Mittlerweile sind so ziemlich alle Mailserver auf einem Entwicklungsstand, dass sie Mails, die sie dann ohnehin nicht zustellen, auch gar nicht erst annehmen. Selbst das von uns gerne eingesetzte netqmail, dem gerne – und zu Recht – vorgeworfen wird, „delayed bounces“ zu senden, setzen wir nicht ohne eine Empfängerprüfung ein, in der Regel den validrcptto.cdb-Patch.

    Der Einzige, der mir legitimer- und auch erwünschterweise Bounces senden können sollte, dürfte in einer idealen Welt ausschließlich der Mailserver sein, den ich zum Relaying verwendet habe – denn der muss ja nun meine zu verschickenden Mails erstmal annehmen; er ist ja nicht der Empfänger, sondern der Postbote, der dann erstmal schauen muss, wo er eine Mail hintragen muss, und von daher regelmäßig erst später wissen kann, ob ein Empfänger existiert oder nicht.

    An wem aber ist diese an sich doch recht positive Entwicklung, Mails, die man nicht haben will, direkt in der SMTP-Session abzulehnen, weitestgehend spurlos vorübergegangen? Am Über-600-Millionen-User-Netzwerk Facebook, deren Macher es als gute Idee ansehen, dass ihre User nun auch per E-Mail mit Nicht-Facebook-Mitgliedern kommunizieren können (und ich als Nicht-Facebook-Mitglied hatte das immer als Feature angesehen, dass das nicht geht – also, als Feature für mich meine ich).

    Facebook-User haben offenbar die Möglichkeit, dies als Option ein- oder auszuschalten. Leider dringt das nicht bis zum Facebook-MX durch, der Mails an User, die das gar nicht wollen, problemlos annimmt:

    $ dnsmx facebook.com
    10 smtpin.mx.facebook.com
    $ telnet smtpin.mx.facebook.com 25
    Trying 66.220.155.11...
    Connected to smtpin.mx.facebook.com.
    Escape character is '^]'.
    220 smtpin.mx.facebook.com ESMTP
    HELO mainz.jonaspasche.com
    250 smtpin.mx.facebook.com says HELO to 82.207.131.175:46878
    MAIL FROM:<jpasche@jonaspasche.com>
    250 MAIL FROM accepted
    RCPT TO:<SOME_USER@facebook.com>
    250 RCPT TO accepted
    DATA
    354 continue.  finished with "\r\n.\r\n"
    Subject: Test
    
    Test
    .
    250 OK 82/1F-27359-C29DDED4
    QUIT
    221 smtpin.mx.facebook.com closing connection
    Connection closed by foreign host.
    

    Sekunden später trudelt die Bouncemail ein (hier der Übersicht wegen etwas gekürzt; eigentlich ist es ein multipart/report):

    From: Facebook <mailer-daemon@mx.facebook.com>
    To: jpasche@jonaspasche.com
    Subject: Sorry, your message could not be delivered
    Date: Tue, 07 Jun 2011 00:55:25 -0700
    
    This message was created automatically by Facebook.
    
    Based on the email preferences of the person you're trying to email, this message
    could not be delivered.
    
    Reporting-MTA: dns; 10.138.205.200
    Arrival-Date: Tue, 07 Jun 2011 00:55:22 -0700
    
    Status: 5.1.1
    Last-Attempt-Date: Tue, 07 Jun 2011 00:55:55 -0700
    Final-Recipient: rfc822; SOME-USER@facebook.com
    Action: failed
    Diagnostic-Code: smtp; 550 5.1.1 RCP-P2
    http://postmaster.facebook.com/response_codes?ip=82.207.131.175#rcp Refused due to
    recipient preferences
    

    „Refused due to recipient preferences“ – und das wusste man zum Zeitpunkt des RCPT TO noch nicht? Also bitte, das muss doch im Jahr 2011 besser gehen. Vor allem dann, wenn man Mail für so eine große Userbasis macht.


    Impressum