Archiv der Kategorie 'Entwicklung'

LiveConfig für eco Internet Award 2013 nominiert

Montag, den 27. Mai 2013

eco Internet AwardWir freuen uns riesig, dass unsere Controlpanel-Software LiveConfig für den begehrten eco Internet Award nominiert wurde! Mit diesem Preis zeichnet der eco - Verband der deutschen Internetwirtschaft e.V. jährlich die innovativsten Produkte und Dienstleistungen aus. In der Kategorie Hosting/Housing/Datacenter zählt LiveConfig zu den drei Besten.

Nun heißt es: abwarten und Daumen drücken - die Verleihung des Preises findet am 27. Juni im Rahmen einer feierlichen Gala in Köln statt.

Um sich bis dahin die Zeit etwas zu verkürzen empfehle ich, einen Blick auf die neue Version 1.6.2 von LiveConfig zu werfen: mit Zwei-Faktor-Authentifizierung (z.B. via Google Authenticator), einem Live-Log-Viewer, der Verwaltung globaler php.ini-Einstellung, Login für E-Mail-Endkunden und vielem mehr.

An den nächsten Features wird bereits gearbeitet. :)

Never change a running system

Mittwoch, den 3. Oktober 2012

In der Datenbank-Bibliothek, die unserem LiveConfig zugrunde liegt, gabe es eine Reihe Änderungen, die nun u.a. auch ein Profiling der Datenbankabfragen ermöglichen. Bei der Gelegenheit habe ich SQLite von v3.7.13 auf 3.7.14 aktualisiert - was zu einem kompletten Crash geführt hat.

Offenbar gibt es in SQLite 3.7.14 einen Bug im Zusammenhang mit dem optimierten Query Planner, der aufgrund einer Nullpointer-Dereference zu einem SEGFAULT führt. Ein einfacher Aufruf von “ANALYZE” in der Datenbank beseitigt das Problem (zumindest vorerst).

Meldung an die Mailingliste ist schon raus. :)

[Update] Nun gibt’s ein offizielles Trouble Ticket dazu…

Klartext-Passwörter

Freitag, den 13. Juli 2012

Es war nun einige Wochen lang recht still hier im Blog, ich habe mir vorgenommen das wieder zu ändern. :-)

Los geht’s aus aktuellem Anlass mit Klartext-Passwörtern. Das Controlpanel “Plesk” steht derzeit wieder im Kreuzfeuer, da laut dem Sicherheitsexperten Brian Krebs ein sogenannter “0day exploit” die Runde macht und gleichzeitig die Sicherheitslücke vom Februar 2012 für bittere Nachwirkungen sorgt. Damals konnte durch eine SQL-Injection in der Remote-API von Plesk völlig anonym auf betroffene Systeme zugegriffen werden, wobei im großen Stile die Benutzerpasswörter ausgelesen wurden. Diese speichert Plesk in der Tabelle psa.accounts unverschlüsselt und nicht gehashed.

Parallels hat natürlich reagiert und neben dem Bugfix auch ein Tool zur Massen-Änderung von Passwörtern bereitgestellt. Blöd ist nur, dass die kompromittierten Passwörter nicht auf eine Blacklist gesetzt wurden - so haben viele Endkunden die neuen (kryptischen) Passwörter einfach wieder zurück geändert. Wie will man sonst auch seine Kunden dazu “zwingen”, wirklich ein neues Passwort zu verwenden?

Nun, die Tatsache, dass in einer Remote-API eine SQL-Injection möglich war, ist eigentlich schon schockierend genug - es gibt viele Methoden um so etwas zu vermeiden (von der statischen Code-Analyse bis zur Verwendung von Prepared Statements). Aber das Passwörter jeglicher Art im Klartext gespeichert werden ist kein Programmierfehler, sondern meiner Meinung nach grobe Fahrlässigkeit. Auf Unix/Linux-System werden seit zig Jahren alle Account-Passwörter gehashed (anfangs nur mit crypt(), inzwischen schon mit komplexen SHA256-Hashes). MySQL ist nun auch nicht gerade für maximale Zugriffssicherheit bekannt (zumindest möchte ich mal behaupten, dass es für einen normalen Systembenutzer einfacher ist, in MySQL einzusteigen als /etc/shadow auszulesen).

Es gibt durchaus das Problem, dass einem Controlpanel ein reiner Hash des Benutzerpassworts nicht reicht - das ist beispielsweise bei E-Mail-Passwörtern häufig der Fall. Wir lösen das so, indem E-Mail-Clients sich nur über das CRAM-MD5-Verfahren am Mailserver anmelden dürfen (PLAIN macht keinen Sinn, da Passwörter völlig unverschlüsselt über den Äther gesendet würden). Auf dem Mailserver kann statt dem Klartext-Passwort nun das “vorgehashte” Passwort abgelegt werden - eine Speicherung im Klartext ist überflüssig (bei Dovecot ist es das Passwort-Schema CRAM-MD5).
Warum Plesk aber selbst das Admin-Passwort mit einem einfachen Shell-Befehl ausgeben kann, entzieht sich meiner Vorstellungskraft. Und wie mit diesem Panel betriebene Webserver eine PCI-Zertifizierung erhalten, obwohl wissentlich kritische Passwörter unverschlüsselt bleiben, ist ebenfalls fragwürdig.

Programmierfehler kann jeder machen - keine Frage. Ich kann auch nicht dafür garantieren, dass es bei LiveConfig keine Sicherheitslücken gibt (das wird vermutlich niemand können). Wir können aber unser Bestes tun, um so etwas zu vermeiden und im “worst case” den potenziellen Schaden zu minimieren: Passwörter werden grundsätzlich nur als gesalzene Hashes gespeichert, kritische Daten (etwa Private Keys für SSL-Zertifikate) werden stark verschlüsselt, SQL-Injections durch Prepared Statements unmöglich gemacht, und so weiter. Ein System ist eben nur so sicher wie seine schwächste Stelle.

Und zum Thema “automatische Passwörter” folgt in Kürze der nächste Beitrag.

Ach ja, bis heute werden in Plesk 10 (v10.4.4#36) die Passwörter nicht verschlüsselt!

CentOS 6.2 & VirtualBox: Kernel Panic

Freitag, den 13. April 2012

Nach einem Update auf einer CentOS 6.2 Instanz unter VirtualBox bootete diese komischerweise immer direkt in eine Kernel Panic.

Das Problem ließ sich lösen, indem man in den Systemeinstellungen der VM den Chipsatz von “PIIX3″ auf “ICH9″ ändert. :-)

(nur falls noch jemand dieses Problem hat - erspart viel Debugging…)

MySQL-Fehler für Fortgeschrittene

Freitag, den 27. Januar 2012

Manchmal ist es schon echt zum verzweifeln, wie hart einen Bugs in fremden Produkten treffen können.

Unser Hosting Control Panel unterstützt ja auch MySQL als Backend zur Speicherung der “eigenen” Daten. Der Zugriff auf MySQL erfolgt aus Performancegründen ausschließlich mittels Prepared Statements. Nun hat uns kürzlich ein LiveConfig-Kunde auf ein merkwürdiges Problem aufmerksam gemacht: regelmäßig erschien nach der Durchführung eines MySQL-Backups (via mysqldump) in LiveConfig die Fehlermeldung “Prepared statement needs to be re-prepared”.

Die Suche hiernach führte zum MySQL-Bug #41119. Als Workaround haben wir daher nun eine explizite Fehlerbehandlung nach mysql_stmt_execute() eingeführt, welche mit mysql_stmt_errno() die jeweilige Fehlernummer ausliest und in unserem Fall (Fehlercode 1615) das betroffene Statement explizit neu erzeugt.

Nur leider funktionierte dieser Workaround nicht. Also überhaupt nicht.

Ein ausführliches Debugging und Tracing aller Datenbankzugriffe hat dann ergeben, dass unsere Fehlerbehandlung gar nicht aufgerufen wird. Zusätzliche Debug-Ausgaben bestätigten dann tatsächlich, dass mysql_stmt_errno() immer “0″ zurückliefert! WTF!?!?

Diesmal hat MySQL-Bug #53311 zugeschlagen. Beseitigt ist dieser Fehler seit knapp einem Jahr ab MySQL Connector/C 6.0.3. Dummerweise ist diese Version aber nach wie vor nicht freigegeben (”aktuell” verfügbar ist nur Version 6.0.2).

Letztendlich haben wir hier nun den über 650 MB großen bazaar-Branch von libmysql ausgeckeckt, und müssen uns nun zwangsweise die 6.0.3-Release selbst erzeugen

:roll:

Nachtrag:

Finger weg vom MySQL-Connector/C 6.0.3! Ich glaube zu verstehen, warum diese Version noch nicht freigegeben wurde - da funktioniert die Hälfte nicht mehr… vor allem bei Prepared Statements scheint diese Version besonders merkwürdige Fehler zu haben - so funktioniert selbst ein einfaches UPDATE mit drei bestimmten Parametern (String/Int/String) reproduzierbar nicht mehr; ändert man die Reihenfolge der Parameter (zB. String/String/Int oder Int/String/String) so klappt’s plötzlich wieder. Und der Bug liegt definitiv nicht bei uns - selbst ein eigenes Testprogramm (basierend auf dem Beispiel von MySQL) führt bei einer bestimmten Variablenfolge zu einem “Incorrect arguments to mysqld_stmt_execute”. Ob das mit MySQL-Bug #61225 zu tun hat weiß ich nicht, aber auch dessen Fehlerbeschreibung lässt einen fast erschaudern.

Der Trick zu einem stabilen und aktuellen MySQL-Client führt über die GA-Pakete des Community-Servers. Einfach dort die entsprechenden Bibliotheken und Developer-Pakete zusammensuchen, oder aus dem “Generic”-Paket (.tar.gz) die Verzeichnisse “/lib” und “/include” nehmen. Der Client aus MySQL 5.5.20 spricht ebenfalls Protokollversion 10 (wie auch Client 6.0.x), dürfte daher soweit kompatibel sein. Man muss seinen eigenen Code allerdings neu gegen diese Bibliotheken linken, da es ein paar kleinere ABI-Unterschiede gibt.

HTTP auf HTTPS-Port

Donnerstag, den 15. Dezember 2011

Für uns “Tekkies” ist das kein Thema, aber viele “normale” ;) Benutzer verwechseln gerne mal HTTP und HTTPS bei der Eingabe einer URL. Zum Glück kann OpenSSL erkennen, ob statt eines erwarteten SSL-Handshakes etwas eintrifft was eher nach einem “HTTP GET” ausschaut. Ist das der Fall, gibt OpenSSL die Fehlermeldung SSL_R_HTTP_REQUEST zurück.

So fängt LiveConfig in der nächsten Version (1.3) auch solche falschen Zugriffe ab und bietet dem Benutzer die “richtige” URL an. Ein kleines, aber praktisches Feature.

error400.png

Eventuell werden wir bei Gelegenheit auch einen Test einbauen, ob bei HTTP-Verbindungen ein SSL-Handshake eintrifft, um dann ebenfalls eine entsprechende Fehlermeldung zu erzeugen.

Datenbank-unabhängiges SQL

Freitag, den 5. August 2011

Beim Durchstöbern meiner RSS-Feeds bin ich eben auf die Meldung von Icinga gestoßen, wonach diese künftig zur Abstraktion der Datenbankabfragen Doctrine einsetzen, statt für jede unstützte Datenbank eigene SQL-Befehle zu pflegen.

Der Hintergrund ist folgender: möchte man seine Daten beispielsweise sowohl in einer MySQL- als auch in einer Oracle-Datenbank speichern können, müssen beide Datenbanksysteme häufig mit unterschiedlichen SQL-Statements angesprochen werden. Das liegt darin begründet, dass jeder Datenbankhersteller sein eigenes Süppchen kocht - verschiedene Standards (SQL-92, SQL-99) werden zwar relativ breit unterstützt, aber manchmal hört es mit der Kompatibilität schon auf, wenn man nur einen Datum-Wert in einem Statement verwenden möchte. Sowas wird übrigens gerne auch als Vendor Lock-In bezeichnet.

Ein Ansatz ist eben, die eigentlichen Zugriffe durch die Verwendung eines Objektpersistenz-Frameworks zu abstrahieren. Der Vorteil ist ganz klar eine ordentliche Trennung der verschiedenen Funktionsbereiche und eine saubere Klassenstruktur. Ein Nachteil dabei kann aber sein, dass man auf die Performance durch Optimierung der Abfragen vielleicht keinen Einfluss hat (kommt sehr auf den jeweiligen Einzelfall an).

Ein anderer Weg wäre, für alle zu unterstützenden SQL-Dialekte eigene SQL-Statements zu pflegen. Davon rate ich aber entschieden ab: zum Testen muss die komplette Anwendung somit auf allen unterstützten Datenbanksystemen vollständig geprüft werden, der Pflegeaufwand ist sehr hoch und entsprechend auch sehr fehleranfällig.

Bei LiveConfig standen wir vor einer ganzen Weile vor genau dem selben Problem. Es sollen verschiedene Datenbanksysteme unterstützt werden (vorerst SQLite und MySQL, später PostgreSQL und vielleicht auch Oracle), während aber nicht für jedes DBMS eigene SQLs gepflegt werden sollen. Doctrine kam nicht in Frage (schließlich entwickeln wir in C/C++), und besonders viele leichtgewichtige C/C++-Persistenz-Frameworks auf SQL-Basis scheint’s nicht zu geben.

Wir haben daher einen völlig anderen Ansatz verfolgt. Das war zwar mit einem recht hohen Initial-Aufwand verbunden, zahlt sich aber (unserer Ansicht nach) auf Dauer aus: die Entwicklung eines eigenen SQL-Übersetzers. Man muss sich das so vorstellen: die Anwendung “spricht” einen relativ generischen SQL-Dialekt (im Groben und Ganzen ein SQL99), und bevor ein Statement zur Datenbank gesendet wird, wird es in den entsprechenden Ziel-Dialekt übersetzt.

Beispiel: es sollen die Datensätze 11 bis 15 einer Tabelle abgerufen werden. In MySQL macht man das mit

SELECT a, b, c FROM table WHERE b=1 ORDER BY a LIMIT 5 OFFSET 11

während man mit Oracle (als Extrembeispiel) etwas mehr tricksen muss:

SELECT a, b, c FROM (SELECT a, b, c, rownum AS limit_rownum FROM (SELECT a, b, c FROM table WHERE b=1 ORDER BY a)) WHERE limit_rownum BETWEEN 11 AND 15

Unsere Lösung wird in Form einer eigenen C-Bibliothek in den Code eingebunden und stellt eine datenbankunabhängige API zur Verfügung. Die Bibliothek kümmert sich dabei selber um das Laden der notwendigen Datenbank-Treiber (libmysqlclient, OCI, …); somit weiß man auf Anwendungs-Ebene im Grunde auch gar nicht, auf welchem System die Anfragen nun tatsächlich ausgeführt werden.

In unserem Fall würde die Anwendung für o.g. SQL-Anfrage also folgendes Statement verwenden:

SELECT a, b, c FROM table WHERE b=:1 ORDER BY a LIMIT :2 OFFSET :3

Intern wird dieses dann automatisch in den gewünschten Ziel-Dialekt (s.o.) übersetzt. Jede Anfrage wird dabei als Prepared Statement abgearbeitet, welches durch unsere Bibliothek intern auch noch zur Wiederverwendung gecached wird. Die Vorteile liegen auf der Hand:

  • die Übersetzung in den Ziel-Dialekt muss während der gesamten Laufzeit nur ein einziges Mal durchgeführt werden
  • auch die Datenbank muss das Statement nur ein einziges mal parsen
  • da alle Parameter über Variablen gebunden sind, sind SQL Injections praktisch unmöglich

Das Ergebnis: eine unschlagbare Performance und gleichzeitig voller Einfluss auf den Aufbau der SQL-Statements. Die Bibliothek lässt sich isoliert wunderbar testen (für alle SQL-Anfragetypen haben wir entsprechende checklib Unit Tests geschrieben), und auch für andere Projekte prima recyclen.

Der Hauptaufwand bestand in der Entwicklung des entsprechenden SQL-Parsers (hier auf Basis von lex und yacc) sowie der möglichst vollständigen Unit-Tests. Natürlich wird nur ein Bruchteil des kompletten SQL-Sprachumfangs abgedeckt, aber eben alles, was unsere Anwendungen benötigen. Als letztes “Leckerli” können wir natürlich auch zentral alle SQL-Anfragen loggen, und diese Logs analysieren (z.B. die Häufigkeit der Statements auswerten, oder fertig übersetzte Statements zur Optimierung der Tabellen-Indizes verwenden).

Für Perl bin ich vor Jahren mal über SQLFairy gestoßen, was scheinbar einen ähnlichen Ansatz verfolgt. Eine andere (etwas schwergewichtigere) Alternative wäre übrigens ODBC.

Das Orakel befragen…

Donnerstag, den 9. Juni 2011

Schon bei der Übernahme von MySQL durch Oracle hatte ich irgendwie kein gutes Gefühl im Bauch. Die letzten Monate über hat sich das immer weiter bestärkt. Am 30.12.2010 kam ein Brief aus den USA (per DHL-Express - ich will gar nicht wissen was das gekostet hat), mit dem alle bisherigen Partnerverträge von Sun fristlos aufgekündigt wurden. Danach hat es einige Monate gedauert, bis Oracle das hauseigene “OPN” (Oracle Partner Network) um eine Hand voll MySQL-Produkte erweitert hat (in der Zwischenzeit ging bekanntlich “gar nichts”). Nach einem mehrstufigen Registrierungsprozess, spontanen Anrufen von Oracle-Callcentern und etlichen Webcasts kann darf man dann (theoretisch) MySQL-Produkte verkaufen - aber nur, wenn man mindestens den “Gold-Level” (für schlappe $2.995,- pro Jahr) hat.
Das eigentliche Problem besteht aber woanders: stellt man nun eine (kommerzielle) Software her, die nicht unter GPL-Lizenz steht und auch nicht die Kriterien für die FOSS-Ausnahmen erfüllt, dann kann man derzeit MySQL-Datenbanken nicht unterstützen. Die Verwendung der Client-Bibliotheken ist alternativ nämlich nur mit einer OEM-Lizenz möglich, und die gibt es bei Oracle derzeit irgendwie nicht. Ich stehe nun schon seit Wochen in Kontakt mit dem “Orakel” :) (u.a. mit dem viel zitierten “OEM Sales Team”), aber habe schlicht und ergreifend noch gar keine Antwort erhalten. Telefonisch wies man mich mit Floskeln auf die eben erwähnte Gold-Level-Mitgliedschaft im OPN hin, wenngleich diese auch keine OEM-Lizenzen oder vergleichbare Berechtigungen beinhaltet. Und selbst wenn - ich sehe es nicht ein, Geld für die Anbindung eines Open-Source-Datenbanksystems zu zahlen.

Wie geht es weiter? Nun, die Idee, die eigene Software einfach mit den MySQL-Client-Bibliotheken einer Distribution zu verlinken, klappt auch nicht, da immer noch die unter GPL stehenden MySQL-Header genutzt werden (müssen). Ich sehe derzeit folgende alternative Ansätze:

  • eine Ausnahme in der (alten) MySQL-Lizenzvereinbarung, die es erlaubt, bisher veröffentlichte Produkte mit MySQL-Client-Bibliotheken in der selben Version auch weiterhin veröffentlichen zu dürfen -> ist halt blöd wenn man Updates machen will
  • eigene Header-Files, welche ABI-kompatibel zum derzeitigen MySQL-Client sind, und unter “großzügigeren” Lizenzen stehen. Ist aber auch blöd, weil dann natürlich alle Funktionsnamen, Strukturen usw. an die “eigenen” Header angepasst werden müssen. Außerdem ist die Lizenzfrage in so einem Fall immer noch recht strittig: der MySQL-Client steht nämlich unter GPL und nicht LGPL
  • Verwendung des Drizzle-Clients, der (angeblich) zu MySQL kompatibel ist. Problem: nach einer kürzlichen Code-Konsolidierung des Clients in den Haupt-Codebaum kaum separat bzw. “von Hand” compilierbar…
  • Entkoppelung der Datenbankanbindung durch eine eigene Bibliothek, die unter einer der FOSS-Lizenzen steht. Nachteil: diese Zwischen-Bibliothek müsste dann aber im Sourcecode veröffentlicht werden.

Vielleicht liest zufällig ja jemand bei Oracle diesen Artikel, und möchte dann Kontakt mit mir aufnehmen. Ich hatte auch auf der CeBIT einige Gespräche in dieser Richtung, und viele Entwickler sind derzeit äußerst zurückhaltend was die MySQL-Unterstützung betrifft, da Oracle es nicht schafft mal eine klare Aussage zu treffen.

Mir wäre es ja am liebsten, wenn Oracle das bisherige “MySQL Enterprise Driver Software License Agreement” wieder aktivieren und fortführen könnte… *wink_mit_dem_Zaunpfahl*