texte

Generische Funktionen mit Python

PEAK bietet ja seit geraumer Zeit generische Funktionen ala CLOS für Python an. Ich wollte immer mal damit rumspielen, aber lange Zeit war es ja einfach nur Bestandteil von PyProtocols, und die Installation etwas haarig. Seit September diesen Jahres ist es aber ausgekoppelt und sehr viel einfacher zu installieren. Also hab ich mich mal draufgestürzt.

Und ich muss sagen: wow. Was Phillip J. Eby da geleistet hat ist wirklich fantastisch. Die Integration in Python (funktioniert ab Python 2.3 - er hat einfach eine eigene Implementierung von Dekoratoren für Python 2.3 erfunden) ist super, auch wenn natürlich das eine oder andere etwas gewöhnungsbedürftig ist.

Ein kleines Beispiel:

import dispatch

[dispatch.generic()]
def anton(a,b):
 "handle two objects"

[anton.when('isinstance(a,int) and isinstance(b,int)')]
def anton(a,b):
 return a+b

[anton.when('isinstance(a,str) and isinstance(b,str)')]
def anton(a,b):
 return a+b

[anton.when('isinstance(a,str) and isinstance(b,int)')]
def anton(a,b):
 return a*b

[anton.when('isinstance(a,int) and isinstance(b,str)')]
def anton(a,b):
 return b*a

[anton.before('True')]
def anton(a,b):
 print type(a), type(b)

Dieses kleine Beispiel liefert einfach eine Funktion namens 'anton', welche auf Basis der Parametertypen unterschiedlichen Code ausführt. Das Beispiel ist natürlich völlig sinnfrei, zeigt aber einige wichtige Eigenschaften von generischen Funktionen:

  • generische Funktionen sind - anders als klassische Objekt/Klassenmethoden - nicht an irgendwelche Klassen oder Objekte fest gebunden. Statt dessen werden sie über ihre Parametertypen ausgewählt.
  • Parametertypen müssen demnach definiert werden - im Regelfall passiert das über eine Minisprache, mit der die Bedingungen der Auswahl formuliert werden. Das ist auch der einzige syntaktische Teil der mir nicht so gut gefällt: die Bedingungen werden als String abgelegt. Allerdings ist die Integration sehr gut, man erhält saubere Syntaxfehler schon beim Laden.
  • eine generische Funktion kann mit beliebigen Bedingungen überladen werden - nicht nur der erste Parameter ist entscheidend. Bedingungen können übrigens auch auf Basis von Werten entscheidungen treffen - jeder beliebige Python-Ausdruck kann dort verwendet werden.
  • mit Methodenkombinationen (Methoden sind hier die konkreten Ausprägungen einer generischen Funktion) kann man eine Methode vor oder nach ihrem Aufruf modifizieren, ohne an den Code selber ranzukommen. Das Beispiel benutzt eine before-Methode die immer (deshalb das 'True') herangezogen wird, um Debugging-Output zu erzeugen. Natürlich kann man bei before/after Methoden jederzeit auch Bedingungen benutzen, um sich auf spezifische Ausprägungen des Aufrufs der generischen Funktion zu hängen - womit generische Funktionen ein vollwertiges Event-System sind.

Ein recht guter Artikel über RuleDispatch (das generische Funktionen Paket) gibt es bei Developerworks.

Das Beispiel zeigt übrigens die Python 2.3 Syntax für Dekoratoren. Mit Python 2.4 kann natürlich auch die @-Syntax benutzt werden. Ein Nachteil soll nicht verschwiegen werden: die Definition von generischen Funktionen und ihren Methoden ist nicht interaktiv möglich - jedenfalls nicht mit der Python 2.3 Syntax. Leider muss man da generell mit externen Definitionen in Dateien arbeiten.

RuleDispatch wird definitiv einen Platz in meinem Werkzeugkasten finden - die Syntax ist einfach genug, die Möglichkeiten hingegen sind gigantisch. Als Eventsystem schlägt es an Flexibilität jedes andere System und als generelle Möglichkeit der Strukturierung des Codes kommt es sehr nah an CLOS heran. Schade, das Django sich vermutlich auf PyDispatch ausrichten wird - RuleDispatch würde meiner Meinung nach wesentlich besser passen (da viele Aspekte in Django als Dispatch auf mehrere Parametertypen geschrieben werden könnten).

Blogcounter, Schwanzlängen und andere Lügen

Im Moment wird ja mal wieder wild über Hitzahlen und ähnlichen Unfug diskutiert. Meist interessieren die mich nicht (mein Server hat ein abstrus hohes Freivolumen, das ich eh nie ausnutzen kann, die Serverlast ist auch gering - also was scherts mich, wie viel hier ankommt?), aber bei den diversen Verkündungen von Hitzahlen, Pagesviews und Visits muss ich dann doch immer leicht grinsen.

Mal so als kleine Analyse der ganzen Geschichte. Zuerst der wichtigste Teil: wo kommen die her, diese Zahlen? Grundsätzlich gibts zwei Möglichkeiten. Eine baut darauf auf, das Seiten ein kleines Element enthalten (z.B. ein Image - manchmal unsichtbar - oder ein Stück JavaScript oder ein Iframe - alle gemeinhin unter dem Begriff Webbug (Web-Wanze) zusammengefasst) welches gezählt wird. Die andere Methode geht auf die Logfiles des Webservers und wertet diese aus. Es gibt noch eine dritte, bei der über einen Cookie der einzelne Besucher identifiziert wird - ist aber eher seltener in Benutzung, ausser für einige eher unbeliebte Werbesysteme.

Es gibt grundsätzlich eigentlich nur wenige echte Zahlen die sowas wirklich liefern kann (mit Ausnahme der Individualisierung über Cookies): zum einen Hits, zum anderen Megabytes and Transfer. Ganz entfernt nützlich gibts noch die Anzahl unterschiedlicher Hosts (IP-Adressen) die zugegriffen haben.

Diese Zahlen haben aber ein problem: sie sind rein technisch. Und damit stark von der Technik abhängig. Hits gehen rauf, wenn man viele externe Elemente hat. Bytes gehen rauf wenn man viele lange Seiten hat (oder grosse Bilder oder ...). IP-Adressen gehen runter, wenn viele Besucher hinter Proxies hängen. Und gehen rauf, wenn man viele ISDN-User hat - wegen der dynamischen Einwahladressen. Veränderungen in den Zahlen haben also sowohl mit Veränderungen bei den Besuchern als auch bei den Seiten zur Ursache.

All diese Zahlen sind also so aussagekräftig wie der Kaffeesatz in der morgendlichen Tasse. Deshalb ziehen Leute aus diesen - technisch zumindestens definierten - Zahlen dann andere Zahlen, die was aussagen sollen. Hier sind vor allem die Visits (Besuche auf der Webseite), die Page-Impressions (Zugriffe auf echte Seitenadressen) und die Visitors (unterschiedliche Besucher) zu nennen.

Nehmen wir mal die einfachste Zahl, die wenigstens noch rudimentäre Verankerung in der realen Welt hat: die Page-Impressions. Hierzu kann man auf verschiedenen Wegen kommen. Man kann die oben genannten Webbugs auf die Seiten packen die gezählt werden sollen. Damit ist die Zahl ungefähr so verlässlich wie das Zählsystem. Dummerweise sind die Zählsysteme das absolut nicht, aber dazu gleich mehr. Die Alternative - über die Webserver Logfiles zu gehen - ist etwas besser. Dazu zählt man einfach mit, wie viele Hits mit dem Mime-Type text/html (oder welche auch immer für die eigenen Seiten genutzt werden) ausgeliefert werden. Man kann auch nach .html zählen - aber viele Seiten haben sowas nicht mehr in den Adressen stehen, der Mime-Type ist da verlässlicher.

Aussagekraft? Naja, eher zweifelhaft. Viele Benutzer werden über ihre Provider über Zwangsproxies gezogen - ein Proxy hat aber die Eigenschaft, das er Hits vermeiden hilft. Hat ein Besucher die Seite abgerufen, wird sie möglicherweise (abhängig von der Konfiguration des Proxies) an andere Besucher aus dem Cache ausgeliefert, nicht vom Server geholt. Betrifft zum Beispiel ganz AOL - die Zahlen sind dort deutlich verfälscht. Je A-List-Bloggerisch der Blogger wirklich ist, desto verfälschter (da ja Cache-Hits häufiger sein können als bei wenig besuchten Blogs) sind die Zahlen oft.

Zusätzlich machen Browser auch sowas - Seiten zwischenspeichern. Oder Besucher machen was anderes - Seiten nochmal laden. Proxies wiederholen manchen Ladevorgang automatisch, weil der erste vielleicht wegen Timeout nicht ganz durch ging - das sind alles Verfälschungen der Zahlen. Trotzdem sind die Page-Impressions noch wenigstens halbwegs brauchbar. Ausser man benutzt Webbugs.

Denn Webbugs haben ein generelles Problem: es sind eben nicht Hauptseiten. Sondern eingebettete Objekte. Hier verhalten sich Browser oft noch hartnäckiger - was im Cache ist, wird aus dem Cache angezeigt. Wozu das Bildchen denn nochmal holen? Klar, man kann da vorbeugen mit geeigneten Header - trotzdem gehts oft genug schief. JavaScript-basierte Techniken gehen voll an Benutzern ohne JavaScript vorbei (und glaubt mir, es gibt von denen wesentlich mehr als gerne zugegeben wird). Letzten Endes haben Webbugs die gleichen Probleme wie die eigentlichen Seiten, nur noch ein paar zusätzliche, eigene Probleme. Warum die trotzdem genutzt werden? Weil es die einzige Möglichkeit ist, seine Statistiken auf einem anderen System als dem eigenen zählen zu lassen. Also unerlässlich für globale Schwanzlängenvergleiche.

Nunja, verlassen wir die Page-Impressions und damit den Bereich der Rationalität. Kommen wir zu Visits, und damit eng verwandt den Visitors. Besucher sind geheimnisvolle Wesen im Web - man sieht nur die Zugriffe, aber wer das ist und ob man ihn kennt, das ist nicht sichtbar. Um so wichtiger für Marketingzwecke, denn alles was Humbug ist und nicht nachprüfbar, lässt sich wunderbar für Marketing ausnutzen.

Besucher sind für einen Webbrowser nur über die IP des Zugriffes erkennbar, zuzüglich der Header die der Browser schickt. Das ist leider viel mehr als man wahrhaben will - aber (ausser bei den Cookie-Setzern mit individueller Benutzerverfolgung) nicht genug zur eindeutigen Identifikation. Denn Benutzer teilen sich IPs - jeder Proxy wird als eine IP aufschlagen. Benutzer nutzen vielleicht sowas wie tor - und damit ist die IP häufiger eine andere als beim letzten Mal. Benutzer teilen sich einen Rechner in einem Internet-Cafe - und damit sind es eigentlich nicht Benutzer, sondern Rechner die man zuordnet. Es gibt zwar Header die von Caches gesetzt werden mit denen man Zuordnungen machen kann - aber wenn hinter dem Cache die Benutzer alle nur private IP-Adressen nutzen (die 10.x.x.x oder 172.x.x.x oder 192.168.x.x Adressen die man so aus einschlägiger Literatur kennt), hilft das auch nicht.

Besucher sind aber noch ein bischen zuzuordnen, wenn der Zeitraum kurz ist - aber über Tage? Sorry, aber im Zeitalter von dynamischen IP-Adressen hilft das garnichts. Die Besucher von heute und die von morgen können gleich oder verschieden sein - keine Ahnung. Trotzdem wird stolz verkündet, wie viele Besucher man im Monat hatte. Hat natürlich gar keine Aussagekraft mehr. Selbst Tageszahlen sind schon stark verändert durch dynamische Einwahlen (nicht jeder benutzt ne Flatrate und hat für 24 Stunden die gleiche Adresse).

Aber um dem Wahnsinn noch einen draufzusetzen, es werden ja nicht nur die Besucher gezählt (angeblich), sondern auch noch deren Besuche. Ja, das ist dann richtig spannend. Denn was ist ein Besuch? Ok, einen Besucher über kurze Zeit wiederzuerkennen (mit all den Problemen die durch Proxies und ähnliches dazu kommen natürlich) geht so einigermaßen - und man weiss auch genau wann ein Besuch anfängt. Beim ersten Zugriff nämlich. Aber wann hört er auf? Denn ein Beenden eines Webbesuches (ein Logout) gibts ja nicht. Man geht einfach weg. Kommt nicht so schnell zurück (wenn überhaupt).

Ja, da wirds dann richtig kreativ. Nimmt man einfach die Zeitabstände der Hits? Oder - weil Besucher ja die Inhalte alle immer lesen - berechnet man den Zeitabstand ab wann ein Hit ein neuer Besuch ist aus der Grösse des zuletzt geholten Seitendokumentes? Wie filtert man regelmäßige Refreshs aus? Wie geht man mit den obigen Besucherzählproblemen um?

Garnicht. Man saugt einfach. An den Fingern. Raus kommt dann eine Zahl. Meistens auf der Basis eines Zeitabstandes zwischen Hits - lange Pause, neuer Besuch. Das zählt man einfach. Und haut es in eine Summe. Ungeachtet der Tatsache, das ein Besuch vielleicht durch ein Telefongespräch unterbrochen war - und daher zwei Visits ein Visit waren, nur halt mit Pause. Ungeachtet der Tatsache, das sich Benutzer Rechner oder IP-Adressen teilen - und damit ein Visit in wirklichkeit 10 verzahnte Visits waren.

Oh, ja, ich weiss das manche Software die Referrer-Header des Browsers benutzt um Pfade durch das System zuzuordnen und daraus dann klarerer Visits aufzubauen. Was natürlich nicht mehr glatt geht, wenn der Benutzer mit dem Back-Button zurückgeht oder eine Adresse nochmal eingibt, ohne das ein Referrer produziert wird. Oder eine personal Firewall benutzt, die Referrer teilweise filtert.

Was dann richtig putzig ist, all die Zahlen werden auf den Markt geworfen, ohne das es klare Aussagen gibt. Klar, manchmal wird gesagt über welchen Dienst man die Zahlen ermittelt hat - aber was sagt das aus? Kann man die Zahlen dort fälschen? Zählt der Betreiber korrekt (bei blogcounter.de kann man ja wohl auf banalste Weise die Zahlen verfälschen) und zählt er überhaupt sinnvoll? Ach watt, einfach Zahlen nehmen.

Gerne wird das Argument gebracht, das zwar die Zahlen nicht direkt als absolute Zahl über Zählergrenzen hinweg verglichen werden können, aber man kann ja Zahlen vom gleichen Zähler vergleichen - daraus gründen sich dann Firmen, die Geld damit verdienen, diese Kaffeesatztechnik an andere zu vermieten und damit die tollen übergreifenden Ranglisten zu realisieren. Bis dann jemand merkt, wie man die Zähler banal manipulieren kann ...

Richtig putzig wirds dann, wenn die Zahlen noch mit der Zeitachse in Deckung gebracht werden und daraus Sachen wie durchschnittliche Verweildauer und daraus dann wieder in Kombination mit der Seitengrösse ermittelt wird, wie viele Seiten gelesen und wie viele nur geklickt wurden (basierend auf der üblichen Lesegeschwindigkeit wird sowas tatsächlich von mancher Software "ausgewertet").

Fassen wir also mal zusammen: es gibt einen begrenzten Rahmen an Informationen auf denen man aufsetzen kann. Das sind Hits (also Abrufe vom Server), Hosts (also abrufende IP-Adressen) und übertragene Mengen (summiert die Bytes aus den Abrufen). Dazu noch Hilfsinformationen wie z.B. Referrer und unter Umständen Cookies. Alle Zahlen können manipuliert und verfälscht werden - und viele werden durch gängige Internet-Techniken auch tatsächlich verfälscht (häufigster Fall eben die cachenden Proxies).

Diese also eher unzuverlässigen Zahlen werden durch - teilweise nicht öffentliche - Algorithmen gejagt und daraus dann Mumbojumbo erzeugt, der dazu benutzt wird um zu zeigen was für ein cooler Frood man doch ist und wo das Handtuch hängt.

Und bei so einem Mumpitz soll ich mitmachen?

PS: der Autor dieses Postings hatte laut awstats-Auswertung im letzten Monat 20172 Besucher, 39213 Besuche, 112034 Seitenabrufe in 224402 Zugriffen und schob dafür 3.9 Gigabyte über die Leitung - was, wie oben festgestellt, komplett irrelevant und nichts aussagend ist, ausser das er sich vielleicht sinnvollere Hobbies suchen sollte

Lebende Daten

Komischer Titel, oder? Naja, mir ist einfach nur was aufgefallen bei der Beschäftigung mit Webframeworks und anderen Anwendungen, speziell im Ruby und Pyhton Umfeld. Und zwar die Art und Weise wie Minidaten gespeichert werden und wie zum Beispiel Konfigurationsdaten gehalten werden.

Im Java-Umfeld gibts da eine Inflation von XML-Mini-Languages - Berge von toten Daten. Tot deshalb, weil diese Daten eben nur im XML-Format leben und nur über XML-Werkzeuge bearbeitet und verändert werden können - habe ich zum Beispiel sich ständig wiederholende oder algorithmisch beschreibbare Konfigurationsblöcke (z.B. einen Berg von sich ziemlich ähnlich sehenden URL-Mustern für ein Webframework), kann ich diese nur über XML-Werkzeuge generieren - z.B. mittels XSLT aus einfacheren Formaten generieren. Oder ich schreib mir kleine Tools dafür.

In Ruby sieht die Situation ähnlich aus - nur das da statt XML dann eben YAML genommen wird. Letztendlich ist das aber auch nicht besser - die Konfiguration ist immer noch ein totes File.

Aber sowohl im Python-Umfeld als auch bei diversen anderen dynamischen Sprachen gibt es eine gute Alternative dazu: nimm einfach ein Modul in deiner Programmiersprache. Denn zum Beispiel Pythonmodule leben - ist die Struktur komplexer, aber teilweise repetitiv - einfach eine kleine Python-Funktion schreiben die bei der dynamischen Erstellung der Config hilft. Soll die Config teilweise aus Datenbankinhalten kommen - einfach eine Python-Funktion schreiben die diese Daten zur Laufzeit aus der DB liest und in die Config einmischt. Lebende Konfigurationdaten eben.

Natürlich kommen so Sicherheitsprobleme mit ins Spiel - wir wollen ja nicht den PHP-Fehler mit dem ewigen eval wiederholen. Was dazu also dringend notwendig wäre, wäre eine saubere Sandbox für solche Module. Leider ist genau da in Python ein massives Loch in der Implementierung. Es gab früher mal die Bytecodehacks, die auch wiederbelebt wurden - aber das sind eben nur Hacks. Auch die Methode mittels eingeschränkter Imports und Proxy-Objekten eine Scheinsandbox aufzubauen wie es Zope macht ist nicht der Weisheit letzter Schluss.

Perl bietet hier - wie bei allen Sicherheitsfeatures in Perl üblich wird das natürlich von fast keinem Projekt verwendet - eine sehr saubere Methode über die safe execution. Man kann bis ins kleinste hinein reglementieren was der Code in einer solchen Sandbox darf - und damit ist eine Konfiguration über Perl-Modul definitiv besser abzusichern als in Sprachen ohne so ein Konzept.

Java selber hat natürlich ein ziemlich ausgefeiltes Sicherheitsmanagement - zwangsweise, es soll ja auch in Browsern mit sehr stark beschränkten Rechten laufen. Dieses Security-Modell ist auch für Anwendungen nutzbar und könnte zum Beispiel für Servlets oder eben auch Java-Configs zum Einsatz kommen - vor allem da man mit Java ja auch problemlos Files zur Laufzeit übersetzen und dynamisch laden kann. Erklär mir jetzt mal einer warum die Java-Leute so fixiert auf XML sind, wo sie doch die besten Grundlagen für sichere lebende Daten haben ...

Das Safe-Modell von PHP ignorieren wir hier mal geflissentlich, denn das ist ein Sekt-oder-Selters-Modell - entweder läuft jeder Code unter safemode, oder garkeiner. Was wir bräuchten wäre aber eine selektive Aktivierung unterschiedlicher Sicherheitsklassen für einen einzelnen Codeblock oder Modulimport (ok, Modulimporte hat PHP auch nicht, nur Includes - ich sag ja, wir ignorieren es einfach mal).

Bisher kann man also bei Python nur dann mit lebenden Konfigurationen arbeiten, wenn man sich sicher ist das die Konfigurationen nur von Usern ohne böse Absichten bearbeitet werden. Django zum Beispiel benutzt nur lebende Konfigurationen - es wäre daher eine ziemlich dumme Idee, würde man z.B. die Konfigurationdateien bei zentral gehosteten Anwendungen über das Web editierbar machen.

Wir brauchen dringend eine saubere Sandbox für Python. Ich glaube sogar das wäre ein wichtigeres Teilprojekt als die diversen syntaktischen Erweiterungen die immer wieder angegangen werden.

Softwarepatente - Kommentar bei der NY Times

Die NY Times fragt warum Bill Gates 3,000 neue Patente will und findet eine massive Belagerung des Patentbüros mit Bergen von Softwarepatenten, die oft einfach nur Trivialpatente sind (wie das zitierte Patent zum Zufügen/Entfernen von Leerzeichen in Dokumenten). Der Kommentator stellt in dem Kommentar eine Forderung auf (nachdem er überlegt ob man Microsoft nicht einfach alle Patente entziehen sollte die sie schon haben):

Perhaps that is going too far. Certainly, we should go through the lot and reinstate the occasional invention embodied in hardware. But patent protection for software? No. Not for Microsoft, nor for anyone else.

Und das aus dem Land das die Softwarepatente schon lange hat und das immer wieder von den Softwarepatentproponenten in der EU als Grund für eine notwendige weltweite Harmonisierung gebracht wird.

Nein, Softwarepatente sind auch dort nicht gern gesehen und nicht wirklich sinnvoll. Auch Dan Bricklin - dem einen oder anderen noch als Visicalc-Vater bekannt - findet das:

Mr. Bricklin, who has started several software companies and defensively acquired a few software patents along the way, says he, too, would cheer the abolition of software patents, which he sees as the bane of small software companies. "The number of patents you can run into with a small product is immense," he said. As for Microsoft's aggressive accumulation in recent years, he asked, "Isn't Microsoft the poster child of success without software patents?"

Und warum macht Microsoft das jetzt? Der dafür zuständige Manager gibt einen Grund, wie er nur einem BWLer einfallen kann, so blöd ist der:

"We realized we were underpatenting," Mr. Smith explained. The company had seen studies showing that other information technology companies filed about two patents for every $1 million spent on research and development. If Microsoft was spending $6 billion to $7.5 billion annually on its R&D, it would need to file at least 3,000 applications to keep up with the Joneses.

Ok, alleine schon die Idee der Patentanmeldung alleine von Zahlen aus der Branche zu orientieren ist hirnrissig, aber wie blöd muss man sein um einen Bezug zwischen der Patentanzahl und dem Umsatz im Bereich Forschung und Entwicklung zu ziehen?

Die NY Times zieht da auch die Parallele zur Pharmaindustrie, die - zumindestens laut eigenen Aussagen - bei einem Forschungseinsatz von 20 Millionen froh ist dann ein Patent auf ein Medikament zu bekommen (wobei das schon kritisch genug ist, wie man gerade bei der AIDS-Bekämpfung in Afrika sehen konnte).

Und der Fallout wird bei der NY Times auch gut zusammengefasst:

Last year at a public briefing, Kevin R. Johnson, Microsoft's group vice president for worldwide sales, spoke pointedly of "intellectual property risk" that corporate customers should take into account when comparing software vendors. On the one side, Microsoft has an overflowing war chest and bulging patent portfolio, ready to fight - or cross-license with - any plaintiff who accuses it of patent infringement. On the other are the open-source developers, without war chest, without patents of their own to use as bargaining chips and without the financial means to indemnify their customers.

Die Frage, was Jefferson (der Gründer des US Patentsystems) dazu sagen würde, was heute alles Patente bekommen soll, ist da durchaus berechtigt. In seinem Sinne - der ja eigentlich eher auf den Schutz der wirklichen erfinderischen Genialität vor der Ausnutzung durch Konzerne galt - ist das ganze definitiv nicht.

Einfacher Dateisystem-Browser mit Django schreiben

Dieser Artikel ist mal wieder in Englisch, da er auch für die Leute auf #django interessant sein könnte. Dieser Beitrag zeigt, wie man einen sehr einfachen Dateisystem-Browser mit Django erstellt. Dieser Dateisystem-Browser verhält sich größtenteils wie ein statischer Webserver, der die Verzeichnisnavigation ermöglicht. Die einzige Besonderheit ist, dass Sie das Django-Admin verwenden können, um Dateisysteme zu definieren, die in den Namensraum des Django-Servers eingebunden werden. Dies dient nur zur Demonstration, wie eine Django-Anwendung verschiedene Datenquellen neben der Datenbank nutzen kann. Es ist nicht wirklich dazu gedacht, statischen Inhalt zu servieren (obwohl es mit hinzugefügter Authentifizierung quite nützlich für eingeschränkten statischen Inhalt sein könnte!).

Auch wenn die Anwendung sehr einfache Sicherheitsprüfungen an den übergebenen Dateinamen durchführt, sollten Sie dies nicht auf einem öffentlichen Server ausführen - ich habe keine Sicherheitstests durchgeführt und es könnte buttloads von schlechten Dingen geben, die Ihre privaten Daten der Welt preisgeben könnten. Sie wurden gewarnt. Wir beginnen wie üblich mit der Erstellung der Dateisystem-Anwendung mit dem Befehl django-admin.py startapp filesystems. Machen Sie es einfach so, wie Sie es mit Ihrer Umfrageanwendung im ersten Tutorial gemacht haben. Nur zur Orientierung, so sieht die myproject-Verzeichnis auf meiner Entwicklungsmaschine aus:


.
|-- apps
| |-- filesystems
| | |-- models
| | |-- urls
| | `-- views
| `-- polls
| |-- models
| |-- urls
| `-- views
|-- public_html
| `-- admin_media
| |-- css
| |-- img
| | `-- admin
| `-- js
| `-- admin
|-- settings
| `-- urls
`-- templates
 `-- filesystems

Nach der Erstellung der Infrastruktur beginnen wir mit dem Aufbau des Modells. Das Modell für die Dateisysteme ist sehr einfach - nur ein Name für das Dateisystem und ein Pfad, an dem die Dateien tatsächlich gespeichert sind. Hier ist es also, das Modell:


 from django.core import meta

class Filesystem(meta.Model):

fields = ( meta.CharField('name', 'Name', maxlength=64), meta.CharField('path', 'Path', maxlength=200), )

def repr(self): return self.name

def get_absolute_url(self): return '/files/%s/' % self.name

def isdir(self, path): import os p = os.path.realpath(os.path.join(self.path, path)) if not p.startswith(self.path): raise ValueError(path) return os.path.isdir(p)

def files(self, path=''): import os import mimetypes p = os.path.realpath(os.path.join(self.path, path)) if not p.startswith(self.path): raise ValueError(path) l = os.listdir(p) if path: l.insert(0, '..') return [(f, os.path.isdir(os.path.join(p, f)), mimetypes.guess_type(f)[0] or 'application/octetstream') for f in l]

def file(self, path): import os import mimetypes p = os.path.realpath(os.path.join(self.path, path)) if p.startswith(self.path): (t, e) = mimetypes.guess_type(p) return (p, t or 'application/octetstream') else: raise ValueError(path)

admin = meta.Admin( fields = ( (None, {'fields': ('name', 'path')}), ), list_display = ('name', 'path'), search_fields = ('name', 'path'), ordering = ['name'], )


Wie Sie sehen können, ist das Modell und das Admin eher langweilig. Was interessant ist, sind jedoch die zusätzlichen Methoden `isdir`, `files` und `file`. `isdir` überprüft, ob ein gegebener Pfad unter dem Dateisystem ein Verzeichnis ist oder nicht. `files` gibt die Dateien des angegebenen Pfades unter dem Basispfad des Dateisystems zurück und `file` gibt den echten Dateipfad und den MIME-Typ einer gegebenen Datei unter dem Basispfad des Dateisystems zurück. Alle drei Methoden überprüfen die Gültigkeit des übergebenen Pfades - wenn der resultierende Pfad nicht unter dem Basispfad des Dateisystems liegt, wird eine ValueError ausgelöst. Dies soll sicherstellen, dass niemand `..` im Pfadnamen verwendet, um aus dem definierten Dateisystem-Bereich auszubrechen. Das Modell enthält also spezielle Methoden, die Sie verwenden können, um auf den Inhalt des Dateisystems selbst zuzugreifen, ohne sich Gedanken darüber zu machen, wie dies in Ihren Ansichten zu tun ist. Es ist die Aufgabe des Modells, solche Dinge zu kennen.

Der nächste Teil Ihres kleinen Dateisystem-Browsers wird die URL-Konfiguration sein. Sie ist eher einfach, sie besteht aus der Zeile in `settings/urls/main.py` und dem Modul `myproject.apps.filesystems.urls.filesystems`. Zuerst die Zeile im Haupt-URLs-Modul:

from django.conf.urls.defaults import *

urlpatterns = patterns('', (r'^files/', include('myproject.apps.filesystems.urls.filesystems')), )


Als nächstes das eigene URLs-Modul der Dateisysteme:

from django.conf.urls.defaults import *

urlpatterns = patterns('myproject.apps.filesystems.views.filesystems', (r'^$', 'index'), (r'^(?P.?)/(?P.)$', 'directory'), )


Sie können die Anwendung nun der Haupt-Einstellungsdatei hinzufügen, damit Sie es später nicht vergessen. Suchen Sie einfach nach der Einstellung INSTALLED_APPS und fügen Sie den Dateibrowser hinzu:

INSTALLED_APPS = ( 'myproject.apps.polls', 'myproject.apps.filesystems' )


Ein Teil fehlt noch: die Ansichten. Dieses Modul definiert die extern erreichbaren Methoden, die wir im URL-Mapper definiert haben. Wir benötigen also zwei Methoden, `index` und `directory`. Die zweite funktioniert tatsächlich nicht nur mit Verzeichnissen - wenn sie eine Datei übergeben bekommt, präsentiert sie einfach den Inhalt dieser Datei mit dem richtigen MIME-Typ. Die Ansicht macht Gebrauch von den in dem Modell definierten Methoden, um auf den tatsächlichen Dateisysteminhalt zuzugreifen. Hier ist der Quellcode für das Ansichtsmodul:

from django.core import template_loader from django.core.extensions import DjangoContext as Context from django.core.exceptions import Http404 from django.models.filesystems import filesystems from django.utils.httpwrappers import HttpResponse

def index(request): fslist = filesystems.getlist(orderby=['name']) t = templateloader.gettemplate('filesystems/index') c = Context(request, { 'fslist': fslist, }) return HttpResponse(t.render(c))

def directory(request, filesystem_name, path): import os try: fs = filesystems.getobject(name exact=filesystemname) if fs.isdir(path): files = fs.files(path) tpl = templateloader.gettemplate('filesystems/directory') c = Context(request, { 'dlist': [f for (f, d, t) in files if d], 'flist': [{'name':f, 'type':t} for (f, d, t) in files if not d], 'path': path, 'fs': fs, }) return HttpResponse(tpl.render(c)) else: (f, mimetype) = fs.file(path) return HttpResponse(open(f).read(), mimetype=mimetype) except ValueError: raise Http404 except filesystems.FilesystemDoesNotExist: raise Http404 except IOError: raise Http404


Sehen Sie, wie die Elemente des Verzeichnismusters als Parameter an die Directory-Methode übergeben werden - der Dateisystemname wird verwendet, um das richtige Dateisystem zu finden, und der Pfad wird verwendet, um den Inhalt unter dem Basispfad dieses Dateisystems zuzugreifen. MIME-Typen werden mit dem mimetypes-Modul aus der Python-Distribution ermittelt, übrigens.

Der letzte Teil unseres kleinen Tutorials sind die Vorlagen. Wir benötigen zwei Vorlagen - eine für den Index der definierten Dateisysteme und eine für den Inhalt eines Pfades unter einem Dateisystem. Wir benötigen keine Vorlage für den Dateiinhalt - der Dateiinhalt wird roh geliefert. Zuerst also die Hauptindexvorlage:

{% if fslist %}

definierte Dateisysteme

{% else %}

Entschuldigung, es wurden keine Dateisysteme definiert.

{% endif %}

Die andere Vorlage ist die Verzeichnisvorlage, die den Inhalt eines Pfades unter dem Basispfad des Dateisystems anzeigt:

{% if dlist or flist %}

Dateien in //{{ fs.name }}/{{ path }}

    {% for d in dlist %}
  • {{ d }}
  • {% endfor %} {% for f in flist %}
  • {{ f.name }} ({{ f.type }})
  • {% endfor %}
{% endif %}


Beide Vorlagen müssen irgendwo in Ihrem TEMPLATE-PFAD gespeichert werden. Ich habe einen Pfad im TEMPLATE-PFAD mit dem Namen der Anwendung eingerichtet: `filesystems`. Dort habe ich die Dateien als `index.html` und `directory.html` gespeichert. Natürlich würden Sie normalerweise eine Basisvorlage für die Website erstellen und diese in Ihren normalen Vorlagen erweitern. Und Sie würden eine `404.html` hinzufügen, um 404-Fehler zu behandeln. Aber das bleibt als Übung für den Leser. Nachdem Sie Ihren Entwicklungsserver für Ihr Admin gestartet haben (vergessen Sie nicht, DJANGO SETTINGS MODULE entsprechend einzustellen!), können Sie ein Dateisystem zu Ihrer Datenbank hinzufügen (haben Sie irgendwann zwischenzeitlich `django-admin.py install filesystems` gemacht? Nein? Machen Sie es jetzt, bevor Sie Ihren Server starten). Jetzt stoppen Sie den Admin-Server, ändern Sie Ihr DJANGO SETTINGS MODULE und starten Sie den Haupt-Einstellungsserver. Jetzt können Sie zu [http://localhost:8000/files/](http://localhost:8000/files/) surfen (zumindest wenn Sie Ihre URLs und Ihren Server so eingerichtet haben wie ich) und die Dateien in Ihrem Dateisystem durchsuchen. Das ist alles. War nicht sehr kompliziert, oder? Django ist wirklich einfach zu verwenden.

Django, lighttpd und FCGI, zweiter Versuch

In meinem ersten Versuch mit diesem Zeug habe ich ein Beispiel gegeben, wie man Django-Projekte hinter lighttpd mit einfachen FCGI-Skripten, die in den Server integriert sind, ausführen kann. Ich werde ein wenig mehr über dieses Zeug erzählen, mit einer Möglichkeit, lighttpd und Django zu kombinieren, die viel mehr Flexibilität bei der Verteilung von Django-Anwendungen über Maschinen bietet. Dies ist besonders wichtig, wenn Sie mit hohen Lasten auf Ihren Servern rechnen. Natürlich sollten Sie den Django-Caching-Middleware verwenden, aber es gibt Zeiten, in denen selbst das nicht ausreicht und die einzige Lösung darin besteht, mehr Hardware an das Problem zu werfen.

Aktualisierung: Ich pflege meine Beschreibungen jetzt in meinem Trac-System. Siehe die lighty+FCGI-Beschreibung für Django.

Hinweis: Da Django sehr neue Software ist, habe ich keine Produktionserfahrungen damit. Daher handelt es sich hier eher um einen theoretischen Standpunkt, in den Wissen einfließt, das ich durch den Betrieb von Produktionssystemen für mehrere größere Portale gewonnen habe. Am Ende kommt es nicht so sehr darauf an, welche Software Sie verwenden - es kommt nur darauf an, wie Sie sie über Ihren Server-Farm verteilen können.

Um dieser Dokumentation zu folgen, benötigen Sie die folgenden Pakete und Dateien, die auf Ihrem System installiert sind:

  • [Django][2] selbst - derzeit aus dem SVN geholt. Folgen Sie den Setup-Anweisungen oder verwenden Sie python setup.py install.
  • [Flup][3] - ein Paket mit verschiedenen Möglichkeiten, WSGI-Anwendungen auszuführen. In dieser Dokumentation verwende ich den threaded WSGIServer.
  • [lighttpd][4] selbstverständlich. Sie benötigen mindestens die FastCGI-, die Rewrite- und die Accesslog-Module, diese werden in der Regel mit dem System kompiliert.
  • [Eunuchs][5] - nur erforderlich, wenn Sie Python 2.3 verwenden, da Flup socketpair in den vorkompilierten Servern verwendet und dies erst ab Python 2.4 verfügbar ist.
  • [django-fcgi.py][6] - mein FCGI-Server-Skript, das eines Tages Teil der Django-Distribution sein könnte, aber für den Moment einfach hier herunterladen. Legen Sie dieses Skript irgendwo in Ihren $PATH, z.B. /usr/local/bin und machen Sie es ausführbar.
  • Wenn das oben genannte aus irgendeinem Grund nicht funktioniert (vielleicht unterstützt Ihr System socketpair nicht und kann daher den vorkompilierten Server nicht verwenden), können Sie [django-fcgi-threaded.py][7] - eine Alternative, die den Threading-Server mit all seinen Problemen verwendet. Ich verwende es zum Beispiel auf Mac OS X für die Entwicklung.

Bevor wir beginnen, lassen Sie uns ein wenig über Serverarchitektur, Python und hohe Last sprechen. Die noch bevorzugte Installation von Django erfolgt hinter Apache2 mit mod python2. mod python2 ist eine recht leistungsfähige Erweiterung für Apache, die einen vollständigen Python-Interpreter (oder sogar viele Interpreter mit unterschiedlichen Namensräumen) in den Apache-Prozess integriert. Dies ermöglicht es Python, viele Aspekte des Servers zu steuern. Aber es hat einen Nachteil: Wenn der einzige Zweck darin besteht, Anfragen von Benutzern an die Anwendung weiterzuleiten, ist es eine ziemliche Übertreibung: Jeder Apache-Prozess oder Thread wird einen vollständigen Python-Interpreter mit Stack, Heap und allen geladenen Modulen enthalten. Apache-Prozesse werden auf diese Weise etwas fett.

Ein weiterer Nachteil: Apache ist einer der flexibelsten Server da draußen, aber im Vergleich zu kleinen Servern wie lighttpd ein Ressourcenfresser. Und - aufgrund der Architektur der Apache-Module - wird mod_python die vollständige Anwendung im Sicherheitskontext des Webservers ausführen. Zwei Dinge, die Sie in Produktionsumgebungen nicht oft mögen.

Ein natürlicher Ansatz ist also die Verwendung leichterer HTTP-Server und das Hinterlegen Ihrer Anwendung dahinter - unter Verwendung des HTTP-Servers selbst nur zum Servieren von Medien und unter Verwendung von FastCGI, um Anfragen vom Benutzer an Ihre Anwendung weiterzuleiten. Manchmal stellen Sie diesen kleinen HTTP-Server hinter einen Apache-Front, der nur mod_proxy (entweder direkt oder über mod_rewrite) verwendet, um Anfragen an den Webserver Ihrer Anwendung weiterzuleiten - und glauben Sie es oder nicht, dies ist tatsächlich viel schneller als das Servieren der Anwendung direkt mit Apache!

Die zweite Falle ist Python selbst. Python hat eine recht schöne Threading-Bibliothek. Es wäre also ideal, Ihre Anwendung als Thread-Server zu erstellen - weil Threads viel weniger Ressourcen als Prozesse verbrauchen. Aber das wird Sie beißen, wegen einer speziellen Funktion von Python: der GIL. Der gefürchtete globale Interpreter-Lock. Dies ist kein Problem, wenn Ihre Anwendung zu 100% Python ist - der GIL greift nur, wenn interne Funktionen verwendet werden oder wenn C-Erweiterungen verwendet werden. Schade, dass fast alle DBAPI-Bibliotheken mindestens einige Datenbank-Client-Code verwenden, der eine C-Erweiterung verwendet - Sie starten einen SQL-Befehl und das Threading wird deaktiviert, bis der Aufruf zurückkehrt. Keine mehreren Abfragen laufen ...

Die bessere Option ist also die Verwendung eines Fork-Servers, weil der GIL dann nicht greift. Dies ermöglicht einem Fork-Server, mehrere Prozessoren in Ihrer Maschine effizient zu nutzen - und so auf lange Sicht viel schneller zu sein, trotz des Overheads von Prozessen gegenüber Threads.

Für diese Dokumentation nehme ich einen dreischichtigen Ansatz zur Verteilung der Software: Die Front wird Ihr vertrauenswürdiger Apache sein, der einfach alles an Ihren projektspezifischen lighttpd weiterleitet. Der lighttpd hat Zugriff auf das Dokumentenstammverzeichnis Ihres Projekts und leitet spezielle Anfragen an Ihren FCGI-Server weiter. Der FCGI-Server selbst kann auf einem anderen Rechner laufen, wenn dies für die Lastverteilung erforderlich ist. Er wird einen vorkompilierten Server verwenden, weil es in Python ein Threading-Problem gibt, und er kann Multiprozessor-Maschinen nutzen.

Ich werde nicht viel über die erste Ebene sprechen, weil Sie dies leicht selbst einrichten können. Leiten Sie einfach Dinge an die Maschine weiter, auf der Ihr lighttpd läuft (in meinem Fall läuft der Apache normalerweise auf anderen Maschinen als die Anwendungen). Schauen Sie in der mod_proxy-Dokumentation nach, normalerweise ist es nur ProxyPass und ProxyPassReverse.

Die zweite Ebene ist interessanter. lighttpd ist ein bisschen seltsam in der Konfiguration von FCGI-Dingen - Sie benötigen FCGI-Skripte im Dateisystem und müssen diese mit Ihrem FCGI-Serverprozess verbinden. Die FCGI-Skripte müssen tatsächlich keinen Inhalt enthalten - sie müssen nur im Dateisystem vorhanden sein.

Wir beginnen also mit Ihrem Django-Projektverzeichnis. Legen Sie einfach ein Verzeichnis public_html dort hinein. Das ist der Ort, an dem Sie Ihre Mediendateien ablegen, z.B. das Admin-Media-Verzeichnis. Dieses Verzeichnis wird das Dokumentenstammverzeichnis für Ihren Projekt-Server sein. Stellen Sie sicher, dass Sie nur Dateien dort ablegen, die keine privaten Daten enthalten - private Daten wie Konfigurationen und Module sollten besser an Orten bleiben, die nicht vom Webserver zugänglich sind. Als Nächstes richten Sie eine lighttpd-Konfigurationsdatei ein. Sie werden nur die Rewrite- und die FastCGI-Module verwenden. Keine Notwendigkeit, ein Zugriffsprotokoll zu führen, dies wird von Ihrer ersten Ebene, Ihrem Apache-Server, geschrieben. In meinem Fall befindet sich das Projekt in /home/gb/work/myproject - Sie müssen dies an Ihre eigene Situation anpassen. Speichern Sie den folgenden Inhalt als /home/gb/work/myproject/lighttpd.conf


 server.modules = ( "mod_rewrite", "mod_fastcgi" )
 server.document-root = "/home/gb/work/myproject/public_html"
 server.indexfiles = ( "index.html", "index.htm" )
 server.port = 8000
 server.bind = "127.0.0.1"
 server.errorlog = "/home/gb/work/myproject/error.log"

fastcgi.server = (
"/main.fcgi" => (
"main" => (
"socket" => "/home/gb/work/myproject/main.socket"
 )
 ),
"/admin.fcgi" => (
"admin" => (
"socket" => "/home/gb/work/myproject/admin.socket"
 )
 )
 )

url.rewrite = (
"^(/admin/.*)$" => "/admin.fcgi$1",
"^(/polls/.*)$" => "/main.fcgi$1"
 )

mimetype.assign = (
".pdf" => "application/pdf",
".sig" => "application/pgp-signature",
".spl" => "application/futuresplash",
".class" => "application/octet-stream",
".ps" => "application/postscript",
".torrent" => "application/x-bittorrent",
".dvi" => "application/x-dvi",
".gz" => "application/x-gzip",
".pac" => "application/x-ns-proxy-autoconfig",
".swf" => "application/x-shockwave-flash",
".tar.gz" => "application/x-tgz",
".tgz" => "application/x-tgz",
".tar" => "application/x-tar",
".zip" => "application/zip",
".mp3" => "audio/mpeg",
".m3u" => "audio/x-mpegurl",
".wma" => "audio/x-ms-wma",
".wax" => "audio/x-ms-wax",
".ogg" => "audio/x-wav",
".wav" => "audio/x-wav",
".gif" => "image/gif",
".jpg" => "image/jpeg",
".jpeg" => "image/jpeg",
".png" => "image/png",
".xbm" => "image/x-xbitmap",
".xpm" => "image/x-xpixmap",
".xwd" => "image/x-xwindowdump",
".css" => "text/css",
".html" => "text/html",
".htm" => "text/html",
".js" => "text/javascript",
".asc" => "text/plain",
".c" => "text/plain",
".conf" => "text/plain",
".text" => "text/plain",
".txt" => "text/plain",
".dtd" => "text/xml",
".xml" => "text/xml",
".mpeg" => "video/mpeg",
".mpg" => "video/mpeg",
".mov" => "video/quicktime",
".qt" => "video/quicktime",
".avi" => "video/x-msvideo",
".asf" => "video/x-ms-asf",
".asx" => "video/x-ms-asf",
".wmv" => "video/x-ms-wmv"
 )

Ich binde den lighttpd nur an die localhost-Schnittstelle, weil in meiner Testumgebung der lighttpd auf demselben Host wie der Apache-Server läuft. In Multi-Server-Einstellungen werden Sie den lighttpd-Servern natürlich an die öffentliche Schnittstelle binden. Die FCGI-Skripte kommunizieren in dieser Einstellung über Sockets, weil in dieser Testumgebung ich nur einen Server für alles verwende. Wenn Ihre Maschinen verteilt wären, würden Sie die "host" und "port" Einstellungen anstelle der "socket" Einstellung verwenden, um mit FCGI-Servern auf verschiedenen Maschinen zu verbinden. Und Sie würden mehrere Einträge für die "main" Sache hinzufügen, um die Last der Anwendung auf mehrere Maschinen zu verteilen. Schauen Sie in der lighttpd-Dokumentation nach, welche Optionen Sie haben werden.

Ich richte zwei FCGI-Server für dies ein - einen für die Admin-Einstellungen und einen für die Haupt-Einstellungen. Alle Anwendungen werden durch die Haupt-Einstellungen FCGI weitergeleitet und alle Admin-Anfragen werden an den Admin-Server geleitet. Dies geschieht mit den beiden Rewrite-Regeln - Sie müssen eine Rewrite-Regel für jede Anwendung hinzufügen, die Sie verwenden.

Da lighttpd die FCGI-Skripte benötigt, um sie zu existieren, um den PATH_INFO an das FastCGI weiterzugeben, müssen Sie die folgenden Dateien berühren: /home/gb/work/myprojectg/public_html/admin.fcgi ``/home/gb/work/myprojectg/public_html/main.fcgi

Sie müssen keinen Code enthalten, sie müssen nur im Verzeichnis aufgeführt sein. Ab lighttpd 1.3.16 (zum Zeitpunkt der Abfassung dieser Zeilen nur in svn) können Sie ohne die Stubs für die .fcgi laufen - Sie fügen einfach "check-local" => "disable" zu den beiden FCGI-Einstellungen hinzu. Dann sind die lokalen Dateien nicht mehr erforderlich. Wenn Sie also diese Konfigurationsdatei erweitern möchten, müssen Sie nur einige sehr grundlegende Regeln beachten:

  • jede Einstellungsdatei benötigt ihren eigenen .fcgi-Handler
  • jeder .fcgi muss im Dateisystem berührt werden - dies könnte in einer zukünftigen Version von lighttpd verschwinden, aber für den Moment ist es erforderlich
  • die Lastverteilung erfolgt auf Ebene der .fcgi - fügen Sie mehrere Server oder Sockets hinzu, um die Last auf mehrere FCGI-Server zu verteilen
  • jede Anwendung benötigt eine Rewrite-Regel, die die Anwendung mit dem .fcgi-Handler verbindet

Jetzt müssen wir die FCGI-Server starten. Das ist eigentlich ganz einfach, verwenden Sie einfach das bereitgestellte django-fcgi.py-Skript wie folgt:


 django-fcgi.py --settings=myproject.work.main
 --socket=/home/gb/work/myproject/main.socket
 --minspare=5 --maxspare=10 --maxchildren=100
 --daemon

django-fcgi.py --settings=myproject.work.admin
 --socket=/home/gb/work/myproject/admin.socket
 --maxspare=2 --daemon

Diese beiden Befehle starten zwei FCGI-Serverprozesse, die die angegebenen Sockets zur Kommunikation verwenden. Der Admin-Server wird nur zwei Prozesse verwenden - dies liegt daran, dass der Admin-Server oft nicht der Server mit den vielen Hits ist, das ist der Hauptserver. Daher erhält der Hauptserver eine höhere als Standard-Einstellung für Reserveprozesse und maximale Kindprozesse. Natürlich ist dies nur ein Beispiel - passen Sie es an Ihre Bedürfnisse an.

Der letzte Schritt ist das Starten Ihres lighttpd mit Ihrer Konfigurationsdatei: lighttpd -f /home/gb/work/myproject/lighttpd.conf

Das war's. Wenn Sie jetzt entweder den lighttpd direkt unter http://localhost:8000/polls/ oder durch Ihren Front-Apache zugreifen, sollten Sie die Ausgabe Ihrer Anwendung sehen. Zumindest, wenn alles richtig gelaufen ist und ich nicht zu viele Fehler gemacht habe.

Django mit FCGI und lighttpd ausführen

Diese Dokumentation ist für einen grösseren Kreis als nur .de gedacht, daher das ganze in Neuwestfälisch Englisch. Sorry. Update: I maintain the actually descriptions now in my trac system. See the FCGI+lighty description for Django. There are different ways to run Django on your machine. One way is only for development: use the django-admin.py runserver command as documented in the tutorial. The builtin server isn't good for production use, though. The other option is running it with mod_python. This is currently the preferred method to run Django. This posting is here to document a third way: running Django behind lighttpd with FCGI.

First you need to install the needed packages. Fetch them from their respective download address and install them or use preinstalled packages if your system provides those. You will need the following stuff:

  • [Django][2] selbst - derzeit aus dem SVN. Folgen Sie den Setup-Anweisungen oder verwenden Sie python setup.py install.
  • [Flup][3] - ein Paket mit verschiedenen Möglichkeiten, WSGI-Anwendungen auszuführen. Ich verwende den threaded WSGIServer in dieser Dokumentation.
  • [lighttpd][4] selbstverständlich. Sie benötigen mindestens die FastCGI-, die Rewrite- und die Accesslog-Module, diese sind in der Regel mit dem System kompiliert.

Nach der Installation von ligthttpd müssen Sie eine lighttpd-Konfigurationsdatei erstellen. Die hier angegebene Konfigurationsdatei ist an meine eigenen Pfade angepasst - Sie müssen diese an Ihre eigene Situation anpassen. Diese Konfigurationsdatei aktiviert einen Server auf Port 8000 auf localhost - genau wie der runserver-Befehl dies tun würde. Aber dieser Server ist ein Server für die Produktion mit mehreren FCGI-Prozessen und einer sehr schnellen Medienauslieferung.


 # lighttpd-Konfigurationsdatei
 #
 ############ Optionen, die Sie wirklich beachten müssen ####################

server.modules = ( "mod_rewrite", "mod_fastcgi", "mod_accesslog" )

server.document-root = "/home/gb/public_html/"
 server.indexfiles = ( "index.html", "index.htm", "default.htm" )

 diese Einstellungen binden den Server an dieselbe IP und denselben Port wie runserver

server.errorlog = "/home/gb/log/lighttpd-error.log"
 accesslog.filename = "/home/gb/log/lighttpd-access.log"

fastcgi.server = (
"/myproject-admin.fcgi" => (
"admin" => (
"socket" => "/tmp/myproject-admin.socket",
"bin-path" => "/home/gb/public_html/myproject-admin.fcgi",
"min-procs" => 1,
"max-procs" => 1
 )
 ),
"/myproject.fcgi" => (
"polls" => (
"socket" => "/tmp/myproject.socket",
"bin-path" => "/home/gb/public_html/myproject.fcgi"
 )
 )
 )

url.rewrite = (
"^(/admin/.*)$" => "/myproject-admin.fcgi$1",
"^(/polls/.*)$" => "/myproject.fcgi$1"
 )

Diese Konfigurationsdatei startet nur einen FCGI-Handler für Ihre Admin-Angelegenheiten und die Standardanzahl von Handlern (jeder davon mehrthreadig!) für Ihre eigene Website. Sie können diese Einstellungen mit den üblichen ligthttpd FCGI-Einstellungen feinabstimmen, sogar externe FCGI-Erzeugung und Auslagerung von FCGI-Prozessen auf einen verteilten FCGI-Cluster nutzen! Admin-Medien-Dateien müssen in Ihr lighttpd-Dokumentenverzeichnis.

Die Konfiguration funktioniert, indem alle Standard-URLs so übersetzt werden, dass sie vom FCGI-Skript für jede Einstellungsdatei behandelt werden - um weitere Anwendungen zum System hinzuzufügen, würden Sie nur die Umschreiberegel für die /polls/-Zeile duplizieren und diese in choices oder was auch immer Ihr Modul heißt ändern. Der nächste Schritt wäre die Erstellung der .fcgi-Skripte. Hier sind die beiden, die ich verwende:


 #!/bin/sh
 # dies ist myproject.fcgi - legen Sie es in Ihr Docroot

export DJANGOSETTINGSMODULE=myprojects.settings.main

/home/gb/bin/django-fcgi.py

 #!/bin/sh
 # dies ist myproject-admin.fcgi - legen Sie es in Ihr Docroot

export DJANGOSETTINGSMODULE=myprojects.settings.admin

/home/gb/bin/django-fcgi.py

Diese beiden Dateien nutzen nur ein django-fcgi.py-Skript. Dies ist nicht Teil der Django-Distribution (noch nicht - vielleicht werden sie es einbeziehen) und der Quellcode ist hier gegeben:


 #!/usr/bin/python2.3

def main():
 from flup.server.fcgi import WSGIServer
 from django.core.handlers.wsgi import WSGIHandler
 WSGIServer(WSGIHandler()).run()

if name == 'main':
 main()

Wie Sie sehen, ist es eher einfach. Es verwendet den threaded WSGIServer aus dem fcgi-Modul, aber Sie könnten genauso leicht den geforkten Server verwenden - aber da lighttpd bereits Preforking durchführt, denke ich, dass es nicht viel Sinn hat, auf der FCGI-Ebene zu forken. Dieses Skript sollte sich irgendwo in Ihrem Pfad befinden oder einfach mit vollständig qualifiziertem Pfad wie ich es tue referenziert werden. Jetzt haben Sie alle Teile zusammen. Ich habe meine lighttpd-Konfiguration in /home/gb/etc/lighttpd.conf, die .fcgi-Skripte in /home/gb/public_html und das django-fcgi.py in /home/gb/bin. Dann kann ich das ganze mit /usr/local/sbin/lighttpd -f etc/lighttpd.conf starten. Dies startet den Server, preforkt alle FCGI-Handler und trennt sich vom tty, um ein richtiger Daemon zu werden. Das Schöne daran: Dies wird nicht unter einem speziellen Systemkonto, sondern unter Ihrem normalen Benutzerkonto ausgeführt, sodass Ihre eigenen Dateibeschränkungen gelten. lighttpd+FCGI ist recht leistungsfähig und sollte Ihnen eine sehr schöne und sehr schnelle Option zum Ausführen von Django-Anwendungen bieten. Probleme:

  • Unter hoher Last stürzen einige FCGI-Prozesse ab. Ich habe zunächst die fcgi-Bibliothek verdächtigt, aber nach etwas Herumspielen (Core-Debugging) stellte sich heraus, dass es tatsächlich der psycopg auf meinem System ist, der abstürzt. Sie könnten also mehr Glück haben (es sei denn, Sie laufen auch Debian Sarge)

  • Die Leistung hinter einem Front-Apache ist nicht das, was ich erwartet hätte. Ein lighttpd mit Front-Apache und 5 Backend-FCGI-Prozessen erreicht nur 36 Anfragen pro Sekunde auf meinem Rechner, während django-admin.py runserver 45 Anfragen pro Sekunde erreicht! (immer noch schneller als mod_python über apache2: nur 27 Anfragen pro Sekunde) Updates:

  • Die Trennung der beiden FCGI-Skripte funktionierte nicht richtig. Jetzt passe ich nicht nur auf die .fcgi-Erweiterung, sondern auf den Skriptnamen an, so dass /admin/ wirklich das myproject-admin.fcgi verwendet und /polls/ wirklich das myproject.fcgi verwendet.

  • Ich habe [ein anderes Dokument online][6], das mehr Details zur Lastverteilung enthält

Pass-Chips und deren möglicher Missbrauch

Owl Content

Schon etwas älter, aber trotzdem interessant: Biometrie/BSI-Vortragsprogramm auf der CeBIT 2005. Speziell interessant die Aussagen zur Authorisierung der Lesegeräte am Pass-Chip:

Der ICAO-Standard schlägt gegen nicht authorisiertes Auslesen optional einen passiven Authentifikationsmechanismus vor (Basic Access Control). Kügler schätzte dessen Effektivität als nur gering ein. Für das Gesichtsbild sei Basic Access Control insofern jedoch geeignet, da es sich hier um nur schwach sensitive Daten handeln würde.

Das ist der Teil, um den es derzeit geht beim Pass - das Authentifizieren des Lesegerätes beim Pass über die Daten der maschinenlesbaren Zone. Dieser Weg ist gegen Kopieren des Schlüssels nicht geschützt - wenn der einmal ermittelt ist, kann er benutzt werden um einen Pass zu erkennen. Auch aus grösserer Entfernung.

Der kontaktlose Chip im Paß nach ISO 14443 wird (natürlich) maschinenlesbar und digital signiert sein sowie die biometrischen Daten enthalten. Als Auslesedistanz gab Kügler einige Zentimenter an, wies jedoch darauf hin, daß mit heutiger Technik ein Auslesen auf mehrere Meter Entfernung möglich sei. Um einen Kopierschutz zu gewährleisten, soll sich der RFID-Chip mittels eines individuellen Schlüsselpaars, das ebenfalls signiert wird, aktiv authentisieren.

Wichtig hier: der Kopierschutz wird durch eine aktive Zweiseitige Authentifizierung erledigt. Ein Pass könnte also auch mit gespeichertem Schlüssel nur dann ausgelesen werden, wenn er aktiv beteiligt ist. Die dann übermittelten Schlüssel sind sozusagen auf die jeweilige Kommunikation gebunden - denn sowohl Pass als auch Lesegerät hätten ein jeweils eigenes Schlüsselpaar. Dadurch sind auch Angriffe über Sniffing der Authentifizierung wesentlich komplizierter, da man zwei Schlüsselpaare knacken muss um mit den Daten etwas anfangen zu können. Leider ist aber derzeit eben nur das Basic Verfahren angedacht, also nur die Schlüssel pro Lesegerät. Und es kommt noch schlimmer:

Den Fingerabdruck hingegen schätzte Kügler als stark sensitives Merkmal ein. Daher müsse der Zugriffsschutz durch einen aktiven Authentifikationsmechanismus (Extended Access Control) gewährleistet werden. Im ICAO-Standard wurde dieser nicht definiert, ist damit also nur für nationale Zwecke oder auf bilateraler Ebene verwendbar.

Otto Orwell träumt also davon Fingerabdrücke zu speichern - das Verfahren, wie diese aber gesichert werden müssten, ist noch garnicht definiert und standardisiert. Eine solche Speicherung wäre also auch nicht übergreifend Nutzbar. Ebenfalls wichtig ist die Sicherstellung, das nur authorisierte Geräte lesen dürfen. Dazu bekämen alle Lesegeräte ein Schlüsselpaar, welches von einer Zentralstelle signiert sein muss. Jeder der schom mal mit einer Zertifizierungsstelle zu tun hatte weiss, das es zwingend eine Sperrliste geben muss - einen Weg, wie man Zertifikate zurückziehen kann. Gerade auch bei Pass-Lesegeräten wäre das wichtig, wenn diese z.B. entwendet würden (nicht lachen, auch an Grenzeinrichtungen verschwinden Geräte - hey, es sind schon ganze Röntgenschleusen aus Flughäfen geklaut worden). Dummerweise sehen das die Experten anders: >n der nachfolgenden kurzen Diskussion wurde die Frage gestellt, ob ein Mechanismus vorgesehen sei, die Schlüssel der Lesegeräte zurückzuziehen (Revokation). Kügler gab an, daß dies bisher nicht der Fall sei. Es sei jedoch momentan in der Diskussion, die Gültigkeit der Schlüssel zeitlich zu begrenzen, dies wäre aber noch nicht entschieden.

Hallo? Also es gibt keine Möglichkeit einen Schlüssel eines Gerätes zurückzuziehen. Und es gibt - derzeit - keinen Ablauf eines Schlüssels. Wenn jemand also an ein Lesegerät herankommt, hat er den Schlüssel des Gerätes und dessen Technik zur Verfügung um jeden Pass in der Nähe auszulesen. Ohne Möglichkeit ein missbräuchlich eingesetztes Gerät wieder los zu werden. Das ist wie ein Computersystem, bei dem man keine Möglichkeit der Passwortänderung hat und keine Möglichkeit einen User - selbst bei nachweislichem Fehlverhalten - zu löschen.

Und nochmal, die erweiterte Prüfung (und nur für die ist wohl diese Schlüsseltechnik plus Zertifikat im Lesegerät gedacht) ist nur ein Vorschlag (der möglicherweise wegen des mangelnden Interesses der Amerikaner an der ganzen Sache garnicht umgesetzt wird):

Den Vorschlag des BSI bezüglich der Extended Access Control beschrieb Kügler im Anschluß. Demnach wird für jedes Lesegerät ein asymmetrisches Schlüsselpaar mit einem korrespondierenden, verifizierbaren Zertifikat erzeugt (Berechtigung nur pro Lesegerät). Daher muß der Chip für die Extended Access Control Rechenleistungen erbringen können. [...] Innerhalb der EU ist der Zugriffsschutz durch die Extended Access Control derzeit nur als Vorschlag zu sehen, sagte Kügler. Ein weiterer (namentlich unbekannter) BSI-Kollege pflichtete ihm bei und fügte hinzu, daß von seiten der US-Amerikaner ohnehin kein Fingerabdruck als biometrisches Merkmal auf dem Chip gefordert werde, ihnen würde vielmehr das digitale Gesichtsbild genügen. Lediglich inneramerikanisch sei eine digitale Aufnahme des Fingerabdrucks geplant. Aus diesem Grund sei also die technische Umsetzung der Extended Access Control nicht dringlich.

Nur in diesem Vorschlag ist es überhaupt vorgesehen das die Geräte eindeutige Schlüsselpaare und darauf beruhende Zertifikate erhalten. Warum das ganze jetzt so kritisch ist? Nunja, die Diskussion konzentriert sich ständig nur auf die Daten und das Auslesen der Daten - aber die sind garnicht mal so kritisch. Denn selbst die gespeicherten Fingerabdrücke sind ja nicht die kompletten Fingerabdrücke zwecks Rekonstruktion, sondern nur die relevanten Kenndaten zwecks Wiedererkennung (wobei auch da noch die Diskussion aussteht ob diese gespeicherten Kenndaten wirklich eindeutig sind - gerade in dem globalen Rahmen von dem wir reden - oder ob da nicht deutlich mehr Daten gespeichert werden müssen als bei einem rein nationalen Ansatz).

Was aber immer möglich ist, wenn wir von solchen Pässen reden: die Authentifizierung und Identifikation einer Person. Eine Zweiwegeauthentifizierung kann alleine als Authentifizierung schon sagen wer in meiner Nähe ist. Habe ich z.B. für das vereinfachte Verfahren einen Key eines Passes gespeichert, kann ich mit diesem Key dann jederzeit berührungslos feststellen, ob dieser Pass in der Nähe ist - natürlich nur im Rahmen der Sicherheit der kryptografischen Algorithmen, aber das wäre schon eine ziemlich sichere Bestätigung, denn das zwei Pässe auf den gleichen Key eine Authentifizierung erlauben wäre schon eine ziemliche Pleite des ganzen Verfahrens und ist hoffentlich von den Entwicklern ausgeschlossen worden.

Ich kann also mir die Keys von Personen besorgen - für das vereinfachte Verfahren reicht dazu das was in der maschinenlesbaren Zeile des Passes steht - zum Beispiel einfach durch simple mechanische Wege wie Einbruch, Taschendiebstahl, Social Engineering, etc. - und die speichern. Dann kann ich ein Lesegerät damit füttern das z.B. an einem definierten Bereich einfach mehrere Passdaten die mich interessieren bei Durchschreiten einer Schleuse - z.B. eine Drehtür mit vorgegebener Drehzahl ist da sehr praktisch - prüfen. Nur der Pass mit den entsprechenden Daten in der maschinenlesbaren Zone wird seine Daten darauf rausrücken, bzw. eine Bestätigung der Authentifizierung geben.

Ich könnte also zum Beispiel ermitteln wann eine Person ein Gebäude betritt und verlässt - ohne das Wissen dieser Person und vollautomatisch. Bei einer Authentifizierungszeit von 5 Sekunden kann man da schon mehrere Keys durchprüfen wärend jemand durch die Drehtür geht.

Natürlich ist das weiterhin keine Identifikation der Person - sondern nur des Passes. Aber gerade wenn die so überwachte Person nichts von der Überwachung weiss wird der Pass eben an der Person getragen. Es gibt ja keinen Grund den Pass nicht dabei zu haben. Und im Ausland ist es oft eine schlechte Idee seinen Pass nicht dabei zu haben - also ist er in den Fällen zwangsweise in der Nähe der Person.

Nunja, aber das ist ja laut Otto Orwell alles nur Angstmacherei und sowieso nicht wahr und völlig falsch. Basiert nur dummerweise auf Aussagen von Mitarbeitern des BSI - die im Prinzip seine Leute sind.

Zum Abschuss freigegeben

In der Zeit : Zum Abschuss freigegeben, ein Dossier über die Opfer der Aufmerksamsgeilheit ala Raab und Bild ...

Das grosse Problem das ich dabei sehe sind nicht mal nur die Bildzeitung und Raab und ähnlicher Medienmüll - das grosse Problem ist die Akzeptanz mit der dieser Mist konsumiert wird. Nach Monaten weiss man nicht mehr wo man etwas gelesen oder gehört hat - und trägt so als Vektor zur Verbreitung des Schwachsinns mit bei.

Wenn ich mir dann vorstelle das der Springerverlag sich die Pro7/Sat.1-Gruppe unter den Nagel reissen will und damit dann wohl dem nächst Bildzeitung und Raab gemeinsam an einem Strick ziehen, wird mir übel ...

Eine demokratische Gesellschaft lebt unter anderem von der Meinungsvielfalt die sich auch in Medienvielfalt niederschlagen muss. Wird die Medienlandschaft aber medienübergreifend von einem Konzern mit klarer politischer Agenda (wer das bezweifelt kann sich ja mal die Berichterstattung der Bildzeitung zur Zeit der letzten Bürgerschaftswahl in Hamburg angucken - bitte Speibeutel bereithalten sonst triffts die Tastatur) dominiert wird, geht ein wichtiger Faktor Demokratie verloren.

Und so bildet sich ein übler Schulterschluss aus Wirtschaftsverbänden und einer Medienkultur bei der man das Wort Kultur nicht mehr in den Mund nehmen mag - und ufert in Hetze gegen Kranke, Arbeitslose, Ausländer und linke Politiker aus, die schon arg an Zeiten erinnert die man eigentlich als vorbei wähnte ...

Auf in den Polizeistaat

Owl Content

Bundeskabinett segnet Gesetzentwurf zur Ausweitung der DNA-Analyse ab:

... sollen DNA-Analysen von Personen künftig auch dann gespeichert werden dürfen, wenn diese lediglich kleinere Delikte wie Sachbeschädigungen oder Hausfriedensbrüche begangen haben oder zu erwarten ist, dass sie künftig solche Delikte begehen. Zudem erhalten Ermittler das Recht, DNA-Analysen im Eilverfahren anzuordnen, ohne dass ein Richter diese genehmigen muss.

Du machst an einer Demo mit, die jemandem nicht passt? Kein Problem, deine Daten werden aufgenommen und in die Kartei gepackt. Hausfriedensbruch ist bei ner Demo schnell passiert, Sachbeschädigung wird dir schnell unterstellt und wenn man keinen Richter fragen braucht, kommt man auch gleich viel schneller voran. Und so wird sich fix eine kleine und feine DNA-Datenbank von all den unliebsamen Subjekten sammeln die ein Staat nun garnicht gebrauchen kann - nämlich Leute die sich öffentlich engagieren und das Maul auf machen.

Wie, die Bürgerrechte bleiben auf der Strecke dabei? Scheiss drauf, interessiert weder Otto Orwell noch die geballte Inkompetenz im Justizministerium.

Oh, und wer glaubt das ich nur paranoid bin, hier das vom Justizministerium zitierte Fallbeispiel:

A ist verurteilt worden, weil er wiederholt den Lack von Kraftfahrzeugen mit einem Schraubenzieher zerkratzt hat. Die Prognose ergibt, dass auch künftig entsprechende Straftaten von ihm zu erwarten sind.

Ja, du bist Rollstuhlfahrer und regst dich über die idiotisch parkenden Autofahrer auf und hast mal einem den Lack zerkratzt? Hey, du bist auch weiterhin im Rollstuhl und wir unterstellen dir einfach mal das du auch weiterhin dich über die idiotischen Autofahrer aufregst - also ab in die DNA-Kartei zu den Mördern, Terroristen und Sexualstraftätern. Schliesslich bist du ja mindestens so Gesellschaftsbedrohend wie die.

In was für eine Scheisse reitet uns dieses rot/grüne Puppentheater in Berlin noch rein. Es ist absolut unfassbar

zorniges Gesicht

Und wenn ihr glaubt das würde mit der Union besser:

... zum anderen reicht die vorgeschlagene Neuregelung der DNA-Analyse der CDU längst nicht aus. "Der Gesetzentwurf ist ein Schritt in die richtige Richtung. Er ist aber zu kurz," sagte der stellvertretende Unions-Fraktionsvorsitzende Wolfgang Bosbach. Die Union werde die bestehende Rechtslage bei einem Wahlsieg weiter verschärfen, erklärte der Innen- und Rechtspolitiker. Es gebe kein Recht von Tätern, unerkannt zu bleiben.

Wer da spontan daran denkt jeden streikenden Arbeiter dort zu erfassen ist wohl auf dem richtigen Weg nach deren Vorstellung ...

Und all das von Leuten die sich unter dem Deckmäntelchen des Neo-Liberalismus eine Reduzierung des Staates auf seine Kernfunktionen auf die Fahne geschrieben haben - und als Kernfunktionen nur Überwachung, Ausbeutung und Gängelung des Bürgers sehen.

Wir bewegen uns schnurstracks auf etwas zu das man mit demokratischer Gesellschaft und Rechtsstaat nicht mehr in Verbindung bringen kann.

Wie funktioniert FileVault

Als Nachtrag zum vorigen Eintrag über die Probleme mit dem Backup von FileVaults aus einem aktiven FileVault-Account heraus hab ich mir mal genauer angeguckt was Apple da eigentlich für FileVault treibt. So richtig Begeisterung kommt bei mir ob der Vorgehensweise nicht auf.

Zu allererst mal ist ein FileVault nichts weiter als ein sogenanntes Sparse Image - ein Plattenimage bei dem nur die tatsächlich belegten Blöcke gespeichert werden. Wenn es also leer ist ist es wurscht wie gross es dimensioniert war - es belegt nur wenig Plattenplatz. Mit den gespeicherten Daten wächst dieses Image und man kann es aufräumen lassen - dabei werden die Datenblöcke die freigeworden sind (z.B. durch Löschungen) wieder auch im Sparse Image freigegeben, das Image schrumpft dann also. Zusätzlich ist für die FileVault-Images die Verschlüsselung aktiviert. Das Schrumpfen passiert halbautomatisch beim Logout: das System fragt den Benutzer ob es darf. Wenn er zustimmt, wird bereinigt. Das ist aber nur der Mechanismus wie die Files gespeichert werden - eben als HFS+ Volume in einem speziellen File. Wie aber wird es beim Login automatisch geöffnet und wie wird sichergestellt, das Programme die Daten an den richtigen Stellen finden wo sie sie suchen? Dazu muss das FileVault-Image ja gemounted werden. Im Prinzip ist der Vorgang der gleiche wie beim Doppelklick auf ein Image-File - das File wird als Laufwerk gemounted und steht in der Liste der Laufwerke im Finder und auf dem Desktop zur Verfügung. Bei FileVault-Images wird allerdings das Desktop-Icon unterdrückt. Statt des Desktop-Icons und dem Mounten nach /Volumens/ wie es normalerweise der Fall ist, ist das Mounten eines FileVault-Images allerdings etwas modifziert. Und zwar liegt ein FileVault-Image normalerweise im User-Verzeichnis eines Benutzers als einzige Datei. Also bei abgemeldetem Benutzer hugo liegt in /Users/hugo/ ein hugo.sparseimage. Sobald sich der Benutzer hugo anmeldet, geschehen eine ganze Reihe von Dingen. Zuerst mal wird das Sparse-Image von /Users/hugo/ nach /Users/.hugo/ verschoben. Und heisst dort nicht mehr hugo.sparseimage sondern .hugo.sparseimage. Dann wird es direkt nach /Users/hugo/ (das ja jetzt leer ist) gemounted (deshalb muss es auch aus dem Benutzer-Verzeichnis rausgeschoben werden, da es ja sonst nicht erreichbar wäre, wenn ein anderes Filesystem drübergemounted würde).

Jetzt ist also das Volume erreichbar als Homeverzeichnis des Benutzers. Zusätzlich sehen alle Programme die Daten ganz am gewohnten Ort, da es ja direkt nach /Users/hugo gemounted wurde und damit z.B. /Users/hugo/Preferences/ ein gültiges Verzeichnis im Image ist. Beim Logout geht das ganze umgekehrt: unmounten des Images und dann rückverschieben und Entfernen des /Users/.hugo/ Verzeichnisses. Zusätzlich - optional - das komprimieren des Images.

Jetzt wird auch klar welches Problem Backup-Programme haben: wenn der Backup läuft, ist das Homeverzeichnis leer und das Image in das Punktverzeichnis verschoben. Ein Boot in das so erstellte Backup würde das Homeverzeichnis des Benutzers nicht finden und dem Benutzer ein leeres Home präsentieren - es erscheint als hätte er alle Files verloren. Das ist auch eines der grossen Probleme von FileVault: wenn der Rechner crashed wärend man angemeldet ist, sind die Verzeichnisse und Dateien verschoben und umbenannt. Wer also FileVault benutzt und nach einem Crash nicht an seine Files kommt: eventuell hilft es sich mit einem anderen FileVault-freien Benutzer (den man zwecks Backups auch haben sollten!) anzumelden und das Homeverzeichnis zu reparieren . Keine Ahnung ob das Plattenreparaturprogramm von Apple das auch machen würde - bisher ist noch keine meiner FileVault-Installationen gecrashed. Für den Notfall sollte man sich aber das hier vielleicht merken. Insgesamt macht das ganze einen recht gehackten Eindruck auf mich - Lieber wäre es mir wenn das ganze System ohne Umbenennungen und Verschiebereien auskäme. Zum Beispiel könnte das FileVault einfach als /Users/.hugo.sparseimage friedlich neben /Users/hugo liegen und nur gemounted werden - dann hätten Backups keine Probleme, da die Struktur zwischen angemeldet und abgemeldet identisch wäre. Keine Ahnung warum Apple diese eher komplizierte Form genommen hat, warscheinlich wegen der Rechte an dem Sparse-Image und dem daraus folgenden Ablageort im Homeverzeichnis des Benutzers.

Experten plädieren für Mehrwertsteuer-Erhöhung

Experten plädieren für Mehrwertsteuer-Erhöhung - guckt man sich dann diese angeblichen Experten an, findet man den IW-Direktor Hüther und den Chefvolkswirt der Deutschen Bank. Völlig neutrale Experten , klar. Wieso machen die angeblich so professionellen Journalisten solchen Mist? Jeder Vollspacke aus irgendeinem Arbeiggeberverband oder arbeitgebernahem Institut oder Grossbank wird als Experte tituliert - wenn aber was aus dem Arbeitnehmerlager kommt, sind es Kritiker aus den Gewerkschaften . So wird schön die neoliberale Scheisse hochgehalten und schön dem Bürger erzählt wo er seine Experten zu suchen hat - ganz egal ob diese Experten alles andere als Experten sind (ich denke noch mit grausen an den mathematisch komplett unbegabten und auch sonst ziemlich unfähigen Finanzexperten Mertz) oder eine eigen politische Agenda verfolgen. Das in diesem speziellen Fall einiges faul sein muss mit den Experten müsste auch dem dümmsten Journalisten auffallen: zwar soll die Merhwertsteuer gehoben werden, aber natürlich nur mit begleitenden Massnahmen. Guckt euch diese Massnahmen an. Der eine schreit nach Absenkung der Lohnnebenkosten als begleitende Massnahme und der Abschaffung des Solidaritätszuschlages - nur letzteres ist aber für den Konsumenten relevant. Und jetzt guckt euch an was jemand mit Sozialhilfe oder ALG II an Solidaritätszuschlag zahlt - nix. Aber die Mehrwertsteuererhöhung macht derjenige trotzdem voll mit.

Der Andere redet davon, das das Risiko der Konsumminderung in Kauf genommen werden müsse, da die Vorteile aus der Minderung der Arbeitskosten überwiegen - denn auch er will diverse Zahlungen senken. Immerhin für beide Seiten - zumindestens hat er nicht explizit nur von der Arbeitgeberseite geredet, aber vermutlich hat er einfach nur vergessen das es auch eine Arbeitnehmerseite gibt. Und auch hier: die Sozialhilfeempfänger und ALG II Empfänger werden davon nicht entlastet und kriegen voll die Mehrwertsteuererhöhung ab.

Keiner der sogenannten Experten hat davon gesprochen das eine Mehrwertsteuererhöhung mit einer Erhöhung der Sozialhilfe und des ALG II einhergehen muss. Beide nehmen in Kauf das Menschen die heute schon verarmt sind dann noch beschissener dran sind und das mehr Menschen unter die Armutsgrenze fallen. Sie tun so als wären sie Experten - sie sind aber letzten Endes nur Schergen der Ausbeuter und Abzocker und wollen nur genau das gleiche was die Arbeitgeberseite schon die ganze Zeit fordert: die Arbeitnehmer noch mehr auszuquetschen.

Die Mehrwertsteuer ist die unsozialste Steuer die wir haben. Zum Einen ist sie sowieso nur für Konsumenten relevant, und zwar für Inlandskonsumenten. Zum Anderen richtet sie sich nach dem Verbrauch - und der kann nunmal nicht unter ein gewisses Mass drunter gehen, denn leben muss jeder und dafür muss man bezahlen - und damit trifft diese Steuer die am härtesten die am wenigsten haben. Denn deren Konsum ist kaum noch zu reduzieren.

Kassenbeiträge werden wieder nicht sinken

Umfrage: Finanzlage der Krankenkassen wieder schlechter - wir werden alle fleissig verarscht. Von Politikern die versprechen die Beitragssätze zu senken und natürlich nicht können. Von Kassen die eigentlich unsere Interessen vertreten aber natürlich nicht tun. Von Ärzten die Kooperation bei der Kostensenkung versprechen, aber natürlich nicht auf ihre Einnahmen verzichten wollen (*). Von Apothekern, die als Vertrauensstelle für den Patienten dienen sollen, aber das Vertrauen schon längst verspielt haben.

Natürlich, die Beitragssenkung für die Arbeitgeber - dafür ist dann immer Geld da. Nur die Patienten, die dürfen das alles wieder bezahlen. Kassen, Ärzte und Apotheker hingegen sitzen auf ihren Besitzständen und weigern sich weiter auch nur minimal zu einer Senkung beizutragen die auch ihre Einnahmen betreffen würden.

Kassen machen dann tolle Sachen wie Hausarztmodell und Hausapothekenmodell - bringt nur nix, wenn die Ärzte sich dann einfach weigern dabei mitzumachen (passiert hier in Münster durchaus). Korrekte Abrechnung der Praxisgebühr erlebt man auch nur selten - wenn einfach nur ein Rezept abgeholt wird, ohne das der Arzt auch nur ein Fitzelchen an Leistung erbracht hat (ausser seiner Unterschrift), wenn das Medikament schon seit Jahren genommen wird - egal, die Praxisgebühr nimmt man noch mal schnell mit.

Qualitätskontrolle der Ärzte? Fehlanzeige - da weigert man sich, das wäre ja zu viel Einfluss für den Patienten. So verstecken sie sich weiter hinter der angeblich freien Arztwahl - die aber schon lange durch Abwanderung von Fachärzten aus den Kassenärztlichen Vereinigungen nur noch lächerlich ist. Bei mancher Fachrichtung hat man als Kassenpatient nur noch im Krankenhaus eine Chance einen wirklich qualifizierten Arzt zu treffen - ausserhalb findet man nur Quacksalber ...

Gleichzeitig reden aber immer mehr Politiker und Funktionäre der diversen Verbände davon, das Patienten mehr Eigenverantwortung übernehmen sollen und mehr von den Kosten tragen müssen. Natürlich, bei der Beratung sollen wir den Ärzten vertrauen. Bei der Wahl des Medikamentenherstellers sollen wir den Apothekern vertrauen. Bei der Abrechnung sollen wir den Kassen vertrauen. Wie bitte sollen wir in einer solchen Situation die auf Vertrauen ohne Kontrolle aufbaut mehr Eigenverantwortung übernehmen? Was soll Eigenverantwortung in dem Kontext überhaupt heissen - es geht doch garnicht um Verantwortung, es geht doch allein um Kostenabwälzung. Und Risikoabwälzung: Was, Ihre Beschwerden haben sich verschlimmert, weil Sie wegen der Kosten zu früh die Behandlung abgebrochen haben? Selber Schuld, warum machen Sie auch sowas. Wenn verlangt wird, das der Patient mehr Eigenverantwortung übernimmt, dann müssen ihm auch Mittel an die Hand gegeben werden mit denen er diese Eigenverantwortung in Form von Einflussmöglichkeiten und Kontrollen auch umsetzen kann. Ansonsten sind das nur hohle Phrasen.

Ärzte bekommen von der Pharmaindustrie Vorzugsbehandlung und verschreiben dann brav deren Ergebnisse - ist ja so schön praktisch und komfortabel und man hat was davon. Die Kassen sitzen da und beschäftigen sich mehr mit ihrem Wasserkopf und ihrer eigenen Absicherung als damit den Ärzten auf die Finger zu gucken und dafür zu sorgen das eben genau diese Verbandelung zur Pharmaindustrie nicht so überhand nimmt. Die Apotheker kämpfen um den Erhalt ihrer Privilegien und gehen gegen jede alternative Form der Medikamentenversorgung an und argumentieren mit ihrer Beratungsleistung - die aber defakto oft schon garnicht mehr existiert, wenn in einer Apotheke nur ein oder zwei ausgebildete Apotheker arbeiten, der Rest bestenfalls bessere Drogeristen sind ... (und der Hauptumsatz in Apotheken mit Pflegemitteln, Gummibärchen und irgendwelchem obskurem Quatsch gemacht wird - hey, wieso sollte man Leuten vertrauen, die homöopathischen Murks anbieten und "beraten"?)

Und die Pharmaindustrie? Die sind der Lachende Fünfte im Hintergrund. Ordentliche Gewinnmargen, trotzdem natürlich Arbeitsplätze abbauen, denn die Margen müssen ja noch steigen. Im Prinzip Monopolstellungen durch absurde Patentpolitik (ich erinnere an das Stickstoff-Patent von Linde - das zum Glück gekippt ist) und eine mitlerweile völlig undurchsichtige Zulassungsbürokratie. Klar, Medikamente müssen geprüft werden vor Zulassung - aber was die derzeitigen Prüfungen wirklich bringen, hat man ja in diversen Fällen in letzter Zeit (Lipobay, Vioxx und ander COX-2 Hemmer - nur um zwei Fälle zu nennen) gesehen.

Was gebraucht würde wäre eine wesentlich radikalere Umstrukturierung des Gesundheitssystems, eine Umstrukturierung die darauf ausgelegt ist das der Patient tatsächlich auch Eigenverantwortung übernehmen kann, weil ihm die Informationen gegeben werden die er dafür benötigt und weil ihm Beratungseinrichtungen gegeben werden, die ihn dabei unterstützen.

Trennung der Abrechnungssystematik und der Kontrollfunktion bei den Kassen - die Kontrollfunktion wird von denen eh nicht ausreichend ausgeübt, die gehört in eigene unabhängige Institutionen die über Pflichtabgaben der am Gesundheitssystem beteiligten (Ärzte, Apotheker, Pharmaindustrie und Anteilig Krankenkassenbeiträge) finanziert werden.

Die Abrechnungsvorgänge sollten über unabhängige Buchstellen für Patienten und Ärzte erledigt werden, die sich nur über ihre Abrechnungsdienstleistungen finanzieren sollten - sowas ist in der Wirtschaft schon gängig, dort werden Abrechnungsleistungen ausgegliedert in eigene Unternehmungen die dann durch Anteile an der Kostenersparnis der Beteiligten finanziert werden.

Mehr Transparenz bei der Pharmaindustrie - Forschungsergebnisse müssen zwangsweise freigegeben werden, wenn ein Unternehmen Medikamente zugelassen bekommen will. Viele Forschungseinrichtungen sind sowieso teilweise vom Staat mitfinanziert oder durch Universitätsnähe vom Bundesland. Eine transparente Prüfungsrichtlinie für Medikamente muss her - eine die Wissenschaftler und Mediziner durchschauen können und in der diese Leute eingebunden sind, so das Probleme frühzeitiger erkannt werden können - und nicht vom Unternehmen verheimlicht werden können (wie bei Vioxx der Fall war).

Gleichzeitig muss eine effektive Kostenkontrolle für Medikamente her - die Begründungen mit den Forschungskosten reicht da nicht, das ganze muss nachvollziehbar sein. Addiert man mal die angeblichen Forschungskosten der Pharmaindustrie aus verschiedenen Medikamenten zusammen, kommt man irgendwann an den Punkt wo das Bruttosozialprodukt alleine in den Forschungseinrichtungen der Pharmaindustrie erwirtschaftet wird. Hier muss es eine wesentlich grössere Transparenz her, damit Preiswucher bei Medikamenten wirksam verhindert werden kann.

Und die Apotheker? Sorry, aber die müssen sich einfach überlegen wie und welchen Platz sie überhaupt noch haben. Dazu würde gehören das sie ihre Beratungsleistung wieder ernst nehmen und sich darauf konzentrieren was Ihre Aufgabe wäre: die Anwendungsberatung von Medikamenten und die Beratung des Einsatzes von Rezeptfreien Medikamenten. Das kann aber eine Fachverkäuferin mit einer Drogeristenausbildung nicht leisten. Die eigene Existenz mit einem Verkaufsmonopol für Medikamente zu begründen ist jedenfalls nicht genug. Und den Beipackzettel vorlesen reicht auch nicht.

(*) Hier sind natürlich die Ärzte in Krankenhäusern ausgenommen - deren Job ist dafür dann so ziemlich das letzte in der Gesundheitsbranche und von menschenwürdigen Arbeitszeiten kann bei denen nicht geredet werden.

Gentechnik - es geht nicht nur um die Wurst

Bundesrat lehnt Gentechnikgesetz ab - die Union will halt das wir Genfood fressen und was das für Folgen hat und ob z.B. ökologischer Anbau in der Nähe von Gen-Feldern garnicht mehr möglich wird (weil die Landwirte nicht die strengen Auflagen erfüllen können, da nunmal gen-veränderte Pflanzen sich eben doch ausbreiten), ist denen völlig egal. Das selbst die meisten Landwirte keinen gesteigerten Wert auf Genshit legen ist auch egal. Das letzten Endes an der ganzen Gen-Technik nur die grossen Konzerne gewinnen und Interesse haben - weil sie damit Landwirte knebeln können und noch weiter auspressen können - ist vermutlich nicht egal. Denn irgendwoher kommen ja sicherlich die Spendenmillionen ...

Gentechnisch veränderte Lebensmittel dienen der Kombination (Zwangskombination!) von Saatgut und Düngemittel oder Pflanzenschutzmittel und der patentrechtlichen Absicherung der Nutzung des Saatgutes. Sie attackiert direkt die klassische hergebrachte Arbeitsweise von Landwirten - z.B. ist in der Regel die Verwendung von Frucht für die nächste Saat nicht möglich (weil unfruchtbar) oder verboten (per Vertrag). Einen biologischen Grund gibt es in Deutschland nicht - weder haben wir extreme Klimabedingungen auszuhalten noch besonders katastrophale Schädlingsattacken. Es geht einzig und allein um die Maximierung der Unternehmen, die die gentechnisch veränderte Saat produzieren.

Wenn man sich dann mal anschaut, wer dahinter steckt, fällt noch was auf: ein weiterer Punkt ist nämlich die Ausschaltung der klassischen Produktionsstellen für das Saatgut - viele der Gentechnik-Unternehmen sind eher der Pharma- oder Chemieindustrie zuzuordnen als der klassischen Landwirtschaft (es gibt allerdings auch bei den Saatproduzenten schwarze Schafe - diese sind aber selber auch eher der Industrie angehörig). Hier dringt einfach Industrie in einen Bereich vor den sie bisher nicht bedienen konnten und in dem sie - mit Zwangsmitteln letztendlich - einbrechen wollen.

Mit gentechnisch veränderter Saat werden also nicht nur Lebensmittel produziert deren Konsum von der Mehrheit der Verbraucher abgelehnt wird - damit wird auch ein ganzer Wirtschaftszweig geknebelt oder unter Umständen auch vernichtet. Mindestens aber stark geschädigt.

Die Landwirtschaft hat durch ihre Strukturen mit Genossenschaften, Verbänden, Interessenvertretungen und politischer Lobby eine recht grosse Macht und Einfluss auf ihr Schicksal - bisher. Jetzt wollen aber die bösen Jungs mitspielen, deren Ziel genau die Übernahme dieser - bisher selbstverwalteten - Macht ist.

Klar das die Union - die sich ja immer wieder als Industriehörig offenbahrt - sich vor den Karren spannt. Und klar, das unser Industriekanzler diesen Eiertanz aufführt und Ministerin Künast ein Gesetz vorlegen muss, das schon verwässert ohne Ende ist - und selbst das noch abgelehnt wird im Rat (der eben Unionsmehrheit hat).

PostgreSQL 8.0.2 released with patent fix

Gerade gefunden: PostgreSQL 8.0.2 released with patent fix. PostgreSQL hat also eine neue Minor-Version bekommen in der ein patentierter Caching-Algorithmus (arc) gegen einen nicht patentierten (2Q) ausgetauscht wurde. Das interessante: das ist eines der Patente die IBM für Open Source freigegeben hat. Und warum haben die trotzdem gewechselt? Weil IBM diese Patente für Open Source Nutzung zwar freistellt, aber nicht für kommerzielle Nutzung - PostgreSQL ist aber unter BSD Lizenz, die explizit auch kommerzielle Nutzung völlig frei stellt.

Für PostgreSQL selber hätte das kein Problem bedeutet: solange es BSD bleibt, hätte weiter die Nutzung des IBM-Patents keine Probleme gemacht. Nur ein späterer Lizenzwechsel - wie er z.B. entsteht wenn jemand die BSD-Software für ein kommerzielles Produkt als Basis wählt - wäre ausgeschlossen.

Ein schönes Beispiel wie selbst liberal gehandhabte Softwarepatente Probleme machen. Denn gerade mittelständische Firmen die kommerzielle Produkte auf Open Source aufbauen hätten also eine bisher verfügbare Basis verloren - nur aufgrund des patentierten Cachingalgorithmus (effiziente Speicherung von und effizienter Zugriff auf Daten - also nach Clements' Vorstellung patentierbar).

Im Falle von PostgreSQL ist es unkritisch abgelaufen: der patentierte Algorithmus ist nicht schneller oder besser als sein nicht patentierter. Und für die Software selber ist nichts wirklich weltbewegendes verändert worden. Aber das muss (und wird) nicht immer so glimpflich ablaufen. Im Bereich Audioprocessing und Videoprocessing sind die patentierten Minenfelder viel weiter ausgebaut und damit viel kritischer für freie Projekte.

Ok, man mag jetzt noch argumentieren das mit GPL Lizenz dem PostgreSQL das nicht passiert wäre. Aber mit GPL Lizenz sind eben manche Formen der Verwendung wie sie bei PostgreSQL heute schon existieren (z.B. das Firmen Spezialdatenbanken auf PostgreSQL aufbauen ohne diese Spezialdatenbanken zu Open Source zu machen) nicht möglich. Man mag dazu stehen wie man will - Ideologie hin oder her - das PostgreSQL Projekt hat nunmal die BSD Lizenz als Basis gewählt.

Selbst wohlwollende Patenthandhabung im Kontext von Open Source Software wäre also problematisch. Genau sowas ist der Grund warum ich generell gegen Softwarepatente bin.

Polizei fürchtet Anonymität und Kryptographie im Netz

Die Polizei fürchtet Anonymität und Kryptographie im Netz - und wettert daher zum Beispiel gegen staatlich geförderte Anonymisierungsdienste. Dabei ist es eben einfach nur der ganz normale Konflikt der Technik: die Anwendung kann in zwei Weisen geschehen. Von den Gründen warum Anonymisierungsdienste und Verschlüsselungssysteme durchaus berechtigt benutzt werden redet niemand, einzig die kriminelle Verwendung ist Thema. Verbieten wir Hammer und Sichel, schliesslich kann man mit beidem Menschen umbringen.

Bedenklich an dieser Entwicklung ist, das über kurz oder lang vermutlich der Einsatz von Kryptographie eingeschränkt - oder wie es neudeutsch heisst: reguliert - wird. Und irgendwann wird die Situation kommen wo verschlüsselte Mails an sich schon als verdächtig gelten. Verdacht braucht es ja sowieso keinen mehr um jemanden auszuspionieren. Und was liegt näher als jemandem der seine Mails verschlüsselt Illegalität zu unterstellen?

Jede Gesellschaft muss mit Missbrauch des Systems und Missbrauch der Gesellschaft klar kommen - und mit denen die völlig aus der gesellschaftlichen Norm fallen. Das ist ärgerlich und in vielen Fällen sogar tragisch - aber nicht zu ändern. Davon, das die gesamte Gesellschaft unter Generalverdacht gestellt wird, löst sich das Problem aber nicht. Was letztendlich über bleibt ist irgendwann eine Gesellschaft die nicht mehr lebenswert und erhaltenswert ist, weil alles auf Überwachung und Denunziantentum aufbaut. Davon das die Rechte des normalen Bürgers eingeschränkt werden gibt es keinen einzigen Verbrecher weniger - eher mehr, weil sich immer mehr Bürger gegen die Auflagen wehren werden (und nach Definition von Menschen wie Otto Orwell dann eben einfach Verbrecher sind).

Was hierbei völlig ignoriert wird, ist meines Erachtens aber der Punkt das Verbrechen eben nicht nur aus dem vielleicht technisch schwer zugänglichen verschlüsselten Kanal bestehen - es muss immer auch Auswirkungen ausserhalb geben. Kinderpornografie wird nicht nur im Internet getauscht - sie wird irgendwann auch produziert. Organisierte Kriminalität organisiert nicht nur den PGP-Schlüssel-Ausstausch im Internet - sie organisiert eben Menschenschmuggel, illegales Glücksspiel, Drogenhandel und was weiss ich noch alles. Jedes Verbrechen hat also immer auch Facetten die ganz offen und erkennbar in der Gesellschaft stattfinden. Die Ermittlungen finden bisher auch primär in diesem Bereich statt - die Lauschangriffe haben bisher keine reproduzierbar besseren Ergebnisse gebracht als durch normale Ermittlungen schon erzielt wurden. Im Gegenteil: die Lauschangriffe, Rasterfahndungen und ähnlichen Ansätze sind bisher alle gefloppt, ganz besonders wenn man die immensen Personaleinsätze (und damit Kosten) dieser Aktionen betrachtet. Und nein, auch beim Moshammermord war die Genprobe nicht ausschlaggebend.

Auch wird eine Reglementierung von Netztechniken nicht die Verwendung für kriminelle Zwecke verhindern - sie wird nur den legalen Einsatz erschweren oder brandmarken. Wer Menschen schmuggelt hat sicherlich weitaus weniger Skrupel gegen Kryptografiegesetze zu verstossen als jemand der Kryptografie nur deshalb benutzt weil ihm nicht passt das der Staat alles mitlesen will.

grsecurity installieren

Ich hab früher schon mal mit grsecurity gespielt, aber die Installation war etwas hakelig - vor allem wusste man nicht was man wie konfigurieren sollte als Start und wie eine vernünftige rule-based Security anfangen sollte - das ganze war damals eher ein trial-and-error-Gehopse als eine verständliche Installation. Für eine Security-Lösung für ein Betriebssystem ist es aber eher negativ wenn man nicht das Gefühl bekommt zu verstehen was dort passiert.

Mit den aktuellen Versionen von grsecurity hat sich das allerdings weitestgehend geändert. Zum Einen laufen die Patches völlig problemlos in den Kernel rein, zum Anderen gibt es zwei wesentliche Features die den Einstieg leichter machen: einen Quick Guide und RBACK Full System Learning.

Der Quick Guide liefert eine kurze und knappe Installationsanleitung für grsecurity mit einer Startkonfiguration für die ganzen Optionen die schon eine recht gute Basis bietet und problematische Optionen (die manchen Systemdienst ausgrenzen könnten) ausschliesst. Dadurch kriegt man eine grsecurity-Installation hin die eine Menge Schutz bietet, aber normalerweise nicht mit üblichen Systemdiensten in Konflikt gerät. Das ist besonders wichtig für Leute mit Root-Servern - eine falsche Grundkonfiguration könnte sie selber aus dem System aussperren und damit das System unbenutzbar und zum Servicefall machen.

Richtig nett ist aber das Full System Learning: hier wird die RBAC-Engine in ein Logging-System umgewandelt und mitprotokolliert welche Benutzer was ausführen und was für Rechte dafür von Nöten sind. Gesteuert wird das ganze noch durch entsprechende Basisconfigs die verschiedene Systembereiche unterschiedlich einstufen (z.B. sicherstellen das der Benutzer auf alles in seinem Home zugreifen kann, aber nicht zwingend auf alles in diversen Systemverzeichnissen). Man lässt das System einfach ein paar Tage laufen (um auch Cron-Jobs mitzubekommen) und lässt daraus dann eine Startkonfiguration für RBAC erzeugen. Die kann man dann natürlich noch finetunen (sollte man auch später machen - aber als Start ist das schon ganz brauchbar).

RBAC ist im Prinzip eine zweite Sicherheits/Rechte-Schicht oberhalb der klassischen user/group Mechanismen von Linux. Der Root-Benutzer hat also nicht automatisch alle Rechte und Zugriffe auf alle Bereiche. Statt dessen muss ein Benutzer sich parallel zu seiner normalen Anmeldung (die bei Systemdiensten ja implizit durch den Systemstart geschieht!) noch an das RBAC-Subsystem anmelden. Dort werden Regeln hinterlegt die beschreiben wie verschiedene Rollen im System verschiedene Zugriffserlaubnis haben.

Der Vorteil: auch automatisch gestartete Systemdiensten dürfen eben nur auf das zugreifen was in der RBAC-Konfiguration vorgesehen ist - selbst wenn sie unter root-Rechten laufen. Sie haben nur eingeschränkte Fähigkeiten im System bis sie sich am RBAC Subsystem anmelden - dazu ist aber bei den höheren Rollen in der Regel eine manuelle Passworteingabe nötig. Angreifer von aussen können also zwar die von RBAC eingeschränkten Benutzerrechte erlangen, in der Regel aber nicht auf die höheren Rollen hochkommen und daher nicht so weit in das System eingreifen wie es ohne RBAC möglich wäre.

Der Nachteil (sollte man nicht verschweigen): RBAC ist komplex. Und kompliziert. Macht man was falsch, ist das System gesperrt - bei Rootservern die irgendwo draussen im Netz stehen ziemlich lästig. Man sollte also immer Fallback-Strategien haben, damit man ein blockiertes System noch erreichen kann. Zum Beispiel nach Änderungen der RBACs die automatische Aktivierung beim Systemstart auskommentieren, so das bei Problemen ein Reboot das System in einen offeneren Zustand versetzt. Oder einen Notzugang haben, über den man auch ein blockiertes System noch einigermaßen administrieren kann. Generell gilt wie bei allen komplexen Systemen: Pfoten weg wenn man nicht weiss was man tut.

Zusätzlich zu dem sehr mächtigen RBAC bietet grsecurity noch eine ganze Reihe weiterer Mechanismen. Der zweite grosse Block ist pax(wichtig: hier muss eine aktuelle Version benutzt werden, in allen älteren ist ein böses Sicherheitsloch) - ein Subsystem das Bufferoverflow-Attacken einschränkt in dem es Speicherblöcken die Ausführbarkeit und/oder die Schreibbarkeit entzieht. Vor allem wichtig für den Stack, da gerade dort die meissten Buffer-Overflows ansetzen. Pax sorgt dafür, das beschreibbare Bereiche nicht gleichzeitig ausführbar sind.

Ein dritter grösserer Block ist die bessere Absicherung von chroot-Jails. Die klassischen Möglichkeiten für Prozesse aus einem chroot-Jail auszubrechen sind nicht mehr gegeben, da viele dafür nötige Funktionen in einem chroot Jail einfach deaktiviert sind. Gerade für Admins die ihre Dienste in chroot-Jails laufen lassen bietet grsecurity wichtige Hilfsmittel, da eben diese chroot-Jails nur sehr umständlich wirklich ausbruchsicher hinzubekommen waren.

Der Rest von grsecurity befasst sich mit einer ganzen Sammlung von kleineren Patches und Änderungen im System, viele beschäftigen sich mit besserer Randomization von Ports/Sockets/Pids und anderen System-IDs. Dadurch werden Angriffe schwieriger, weil das Verhalten des Systems weniger vorhersagbar ist - besonders wichtig bei diversen local exploits, bei denen zum Beispiel die Kenntniss der PID eines Prozesses dazu genutzt wird um Zugriffe auf Bereiche zu erlangen, die über die PID identifiziert werden (Speicherbereiche, temporäre Files etc.). Auch die Sichtbarkeit von Systemprozessen wird eingeschränkt - normale Benutzer bekommen einfach keinen Zugriff auf die gesamte Prozessliste und werden auch im /proc Filesystem eingeschränkt - und können deshalb nicht so leicht laufende Systemprozesse angreifen.

Eine komplette Liste der Features von grsecurity ist online.

Alles in allem bietet grsecurity eine sehr sinnvolle Zusammenstellung von Sicherheitspatches die jedem Betreiber eines Servers ans Herz gelegt werden sollte - die Möglichkeit von Remote Exploits wird drastisch eingeschränkt und auch die lokale Systemsicherheit durch die RBAC deutlich aufgewertet. Es gibt bei der doch recht einfachen Implementierung des grsecurity-Patches in ein bestehendes System (einfach den Kernel patchen und neu installieren, booten, lernen, aktivieren - fertig) keinen Grund den Patch nicht zum Beispiel auf Rootservern standardmäßig zu nutzen. Eigentlich sollte ein Security-Patch genauso zur Systemeinrichtung gehören wie eine Backupstrategie.

Jetzt wärs natürlich noch schöner wenn die eigentliche Dokumentation des Systems etwas grösser als die man-Pages und ein paar Whitepapers wäre - und vor allem auf aktuellem Stand wäre. Das ist noch immer ein echtes Manko, weil eben das richtige Gefühl das System verstanden zu haben sich ohne qualifizierte Dokumentation nicht so recht einstellen will ...

mod_fastcgi und mod_rewrite

Tja, da hab ich das doch glatt mal ausprobiert mit dem PHP als FastCGI - unter anderem weil ich dann auch gleich ein neueres PHP benutzen könnte. Und was ist? Nix ist. Und zwar gab es ein massives Problem mit mod rewrite Regeln. In der .htaccess vom WordPress wird nämlich alles auf die index.php umgeschrieben. Dazu wird der eigentliche Pfad der angesprochen wurde als PATH INFO an die index.php angehängt. Tja, und das PHP flöht dann diese Informationen da wieder raus und macht das richtige.

Aber als ich FastCGI aktiviert hatte, klappte das nicht - das PHP behauptete immer, das kein Input-File übergeben wurde. Also als hätte ich das PHP ohne Parameter aufgerufen. Die WordPress Administration - die mit normalen PHP Files arbeitet - funktionierte wunderbar. Und auch die Rechtegeschichte klappte gut, alles lief unter meinem eigenen User.

Nur die Rewrite-Rules halt nicht - und damit die ganze Site nicht. Ziemlich blöd, das ganze. Vor allem weil ich das nicht vernünftig testen kann ohne meine Hauptsite runterzureissen. Blöd ist auch, das scheinbar suexec die eigentlichen FCGI-Starter in der Document-Root des primären virtuellen Servers sucht - nicht in denen der eigentlichen Virtuellen Server. Macht die ganze Situation etwas unübersichtlich, da die Programme (die Starter sind ja kleine Shellscripte) nicht da liegen, wo die Dateien liegen. Ausser man hat seine virtuellen Server unterhalb des primären virtuellen Servers angelegt - aber das halte ich persönlich für hochgradig schwachsinnig, da man dann unter Umständen durch direkte Pfadangaben über den Default-Server an im virtuellen Server geladenen Perl-Modulen vorbeikommen kann.

Ergo: ein Reinfall. Leider. Ärgerlich. Jetzt muss ich mir irgendwie erstmal eine Testkiste zusammenstellen mit der ich dieses Problem analysieren kann ...

Update: ein bischen Suchen und Wühlen im Netz und ein kurzer Test und ich bin schlauer: PATH_INFO bei PHP als FCGI-Version unter Apache ist kaputt. Scheinbar kriegt PHP den falschen PATH INFO Eintrag geliefert und den falschen SCRIPT NAME. Dadurch findet der Interpreter bei gesetztem PATH INFO einfach schlicht sein Script nicht und nix geht mehr. Jetzt muss ich also weiter suchen, ob es eine Lösung gibt. cgi.fix pathinfo = 1 (was allgemein als Hilfe dafür geboten wird) funktioniert jedenfalls nicht. Aber wenn ich das richtig sehe gibts keine brauchbare Lösung dafür - jedenfalls keine für mich offensichtliche. Mist.

Update 2: Ich hab eine Lösung gefunden. Diese basiert darauf einfach nicht den Apache zu benutzen, sondern den lighttpd - und den Apache nur als transparenten Proxy vorne vor zu stellen. Das geht ganz gut, vor allem wenn ich den Apache stark entkerne und das PHP dort rauswerfe wird er auch deutlich schlanker. Und lighttpd kann unter verschiedenen User-Accounts laufen, dadurch spare ich mir auch das wilde gehacke mit suexec. Allerdings läuft dann pro User ein lighttpd Prozess (lighttpd braucht nur einen Prozess pro Server, da es mit asynchroner Kommunikation arbeitet) und die PHPs toben sich als FastCGI Prozesse aus, nicht als Apache-integrierte Module. Apache selber ist dann nur für rein statische Präsenzen oder Sites mit Perlmodulen zuständig - davon habe ich nämlich noch eine ganze Reihe. Im Moment habe ich da nur eine Spiel-Site laufen, aber vielleicht wird das in den nächsten Tagen umgestellt. Die Methode wie cruft-free URIs produziert werden ist übrigens recht witzig: bei WordPress kann man einfach das index.php als Error-Document eintragen: ErrorDocument 404 /index.php?error=404 wäre der Eintrag in der .htaccess, bei lighttpd gibt es eine äquivalente Eintragung. Dadurch werden automatisch nicht existierende Files (und die cruft-free URIs existieren ja nicht als physikalische Files) auf WordPress umgeleitet. Dort wird dann geguckt ob wirklich keine Daten da sind für die URI und wenn doch was da ist (weil es eine WordPress URI ist), wird einfach der Status zurückgesetzt. Für letzteres musste ich einen kleinen Patch in WordPress einbauen. Dadurch spart man sich die ganzen RewriteRules und kommt mit fast jedem Server zurecht. Und weil jetzt 1:41 ist, geh ich jetzt mal pennen ...

Von Firefox wieder auf Camino ...

... und zurück. Odyssee der Webbrowser.

Nachdem ich jetzt einige Tage mit Firefox gearbeitet habe, war ich wieder zurück zu Camino gewechselt. Warum? Nunja, unter OS X ist FireFox dann doch suboptimal. Zum Einen habe ich den Eindruck das Fonts irgendwie grundsätzlich kleiner dargestellt werden als in Camino oder anderen echten Mac Programmen. Mag Einbildung sein. Keine Einbildung hingegen ist, das FireFox unter OS X die Services nicht unterstützt. Und das ist nervig - was bringts, wenn haufenweise Programme sich in das Services-Menü einklinken und nützliche Dienste zur Verfügung stellen die auf markierten Texten in anderen Programmen aufbauen, wenn die Hauptapplikation in der ich meine Zeit am Rechner vertüddel genau nicht unterstützt wird?

Genauso nervig war, das ausgerechnet Tab-X unter OS X nicht unterstützt wird. Diese Erweiterung packt ein Close-Icon an jeden Tab dran. Keine Ahnung was den UI-Designer vom Firefox geritten hat, aber ich betrachte weder das zwingende Aktivieren eines Tabs und anschliessend das Klicken auf ein winziges X am rechten Rand der Toolbar als ergonomisch noch das Schliessen eines Tabs über das Context-Menü. Ok, daran kann man sich zur Not gewöhnen.

Desweiteren hat mich ständig gestört, das FireFox seinen eigenen Passwortverwalter hat und nicht die KeyChain benutzt. Ich finds einfach praktisch das alle möglichen Programme sich an einer zentralen Stelle eintragen und ich genau dort meine Passwörter löschen kann, wenn ich das mal brauche. Ausserdem hilft das dabei nicht ständig die Passwörter neu eingeben zu müssen, nur weil man eine Seite mal mit einem anderen Browser besucht.

Leider verliere ich damit alles so nette Sachen wie es über die Erweiterungen von FireFox verfügbar ist - zum Beispiel die Web Developer Toolbar. Nur das die sowieso auf meinem Mac nicht funktioniert, weiss der Geier warum - von daher hab ich die sowieso immer nur unter Linux gehabt, und da benutze ich ja weiter FireFox. Das Plugin für den Google PageRank Status und das Plugin für mozcc werde ich hingegen schon vermissen - beides war durchaus praktisch. Irgendwie blöd, das ich nicht beides haben kann - einen FireFox mit vernünftiger Integration in OS X, das wärs schon ...

Aufgrund der ziemlich broken 0.8.2 von Camino habe ich mir aber die 0.8.1 wieder runtergeladen und installiert. Die hat wenigstens funktionierende Tabs und crashed nicht dauernd. Keine Ahnung was die mit der 0.8.2 gemacht haben, aber es war definitiv nicht zum Vorteil von Camino.

Und natürlich war gleich nach dem ich das hier geschrieben habe der Camino angefangen rumzuzicken. Ich fasse es nicht. Die 0.8.1 hat vorher einwandfrei funktioniert. Trotzdem gabs jetzt die gleichen Problem wie mit der 0.8.2 - also vermutlich ausgelöst durch irgendwelche Seiten mit denen ich jetzt im Gegensatz zu früher häufiger arbeite? Ich hab keinen blassen Schimmer - spezielle Tools habe ich nicht extra installiert unter OS X, im Gegenteil, ich hab eins deinstalliert.

Also andere Browser wieder mal ausprobieren. Safari 1.0 unter OS X 10.2.8 ist deutlich zurück in den Features - zur Not würde der mir aber noch als Alternative bleiben, er crashed aber immer mal wieder auf Seiten. OmniWeb ist im Prinzip ein aufgemotzter Safari, crashed aber noch häufiger. Und Opera kommt mit dem CSS vom WordPress-Admin überhaupt nicht klar - das ist wild durcheinandergewürfelt. Ausserdem fragt der immer mehrfach nach Passwörtern und Keychain-Zugriff wenn ich auf manche geschützten Seiten zugreife. Und diese Macke hat er schon seit Monaten - nicht sehr vertrauenserweckend.

Der IE für den Mac ist nicht mal eine Verzweifelungsoption. Netscape? Nee, sorry, aber das muss nicht. Mozilla auch nicht - dann schon lieber FireFox, denn der Mozilla bindet sich nicht nur nicht gut ins System ein, er sieht auch noch völlig anders aus als OS X Anwendungen ...

Der einzige wirklich brauchbare alternative Browser unter OS X 10.2 ist - trotz seiner Probleme - der OmniWeb. Notfalls der Safari, aber der OmniWeb ist bei manchen Seiten fortgeschrittener im Rendering. Unterstützt aber trotzdem nicht so Sachen wie z.B. Klicken auf das Label einer Checkbox zum Toggeln derselben - wird im WordPress Admin gerne benutzt und vermeidet alberne Zielübungen. Ausser bei OmniWeb oder Safari. Ok, das die QuickTag-Bar im OmniWeb und Safari fehlt ist Absicht bei WordPress - das JavaScript ist wohl nicht ganz kompatibel.

Also wieder retour das ganze und doch wieder den FireFox benutzen und sich über die fehlenden Services ärgern (die übrigens auch bei Carbon-Applikationen funktionieren können - wenn der Programmierer das berücksichtigt hat in seinem Programm)? Oder doch erstmal mit OmniWeb spielen und gucken ob man über die Probleme nicht wegkommt?

Und was lernen wir daraus? All Browsers suck. Even the good ones.

Neues Spiel, neues Glück: b2evolution

Heute hab ich mir mal b2evolution angeguckt (wie üblich nur ein kurzer oberflächlicher Testflug). Das ist ja mit WordPress verwandt und alleine deshalb schon interessant - mal gucken was andere aus dem gleichen Basiscode gemacht haben. Also das Zeug geholt, das Kubrick Skin geholt (hey, ich mag Kubrick mitlerweile ) und losgelegt.

Was mir sofort auffällt: b2evolution legt wesentlich mehr Wert auf multi-allesmögliche. Multi-Blog (es werden gleich 4 Blogs vorinstalliert mitgeliefert, wovon eines ein "alle Blogs" Blog ist und eines ein Linkblog), Multi-User (mit Berechtigungen für Blogs etc. - also als Bloggerplattform für kleinere Usergruppen geeignet) und Multi-Language (nett: man kann an jedem Posting die Sprache festlegen, Sprachen pro Blog festlegen). Das gefällt schon mal. Das Backend ist leidlich gut zu bedienen und man findet das meiste recht fix wieder.

Aber dann die Dokumentation. Ok, ja, das wichtigste ist dokumentiert und auffindbar. Aber sobald man in die Tiefe geht, ist nahezu nichts selbsterklärend oder dokumentiert. Ok, ich gebs zu, ich hätte mir nicht gleich auf die Fahnen schreiben sollen die URIs auf die komplizierteste Form zu bringen - nämlich über sogenannte Stub-Dateien. Das sind alternative PHP-Files über die alles gezogen wird um darüber spezielle Einstellungen vorzubelegen. Angeblich soll man damit auch eine URI-Struktur wie bei Wordpress hinkriegen - der b2evolution-Standard ist nämlich so, das in der URI immer das index.php vorkommt und die zusätzlichen Elemente hinten drangehängt sind. Das ist hässlich. Das will ich nicht. Das zu ändern geht scheinbar nur mit Apache-Mitteln in Handarbeit (nein, nicht wie bei WordPress die nette und freundliche Unterstützung der automatisch generierten .htaccess Datei) und dann entsprechenden Einstellungen in b2evolution. Ok, kann man machen - ich kenne Apache gut genug. Aber wieso so umständlich, wenns auch einfacher geht?

Nunja, aber der echte Pferdefuss für mich kommt noch: b2evolution kann nur Blogs. Jedenfalls in der Standardausstattung. Genau - nur Listen von Postings die zeitlich geordnet sind. Langweilig. Nicht mal einfache statische Pages - sorry, aber wo pack ich das Impressum hin? Von Hand erstellte Files, die man daneben packt? Möglich, klar. Aber nicht gerade anwenderfreundlich.

Antispam-Mittel gibts auch einige, zum Beispiel eine zentral gepflegte Sperrwortliste (naja, Sperrwortlisten halte ich persönlich nicht für so geeignet) und Benutzerregistrierung. Nicht viel, aber erstmal ausreichend. Mehr kann man sicherlich über Plugins machen. Beim Stichwort Plugins ist eine sehr nette Eigenschaft zu nennen: man kann am Posting unterschiedliche Filter aktiviert haben. Je nach Posting immer wieder neu. Sehr nett - WordPress hat da ein echtes Defizit, die aktivierten Filter gelten für alles über alles - eine Änderung und alte Postings werden plötzlich falsch formatiert (wenn es ein Output-Filter ist).

Ebenfalls nett: die hierarchischen Kategorien verhalten sich wirklich hierarchisch - bei WordPress sind die ja nur hierarchisch gruppiert, aber z.B. wird mit der Hierarchie nicht viel gemacht. Bei b2evolution wandern Postings einer Kategorie automatisch an die übergeordnete, wenn eine Kategorie gelöscht wird. Ausserdem kann man durch die Multiblog-Eigenschaft an einem Posting Kategorien verschiedener Blogs aktivieren und damit sozusagen crossposten - wenn es denn in den Settings erlaubt ist.

Layoutanpassungen gehen über Templates und Skins. Templates sind vergleichbar zum WordPress 1.2 Modus und Skins eher zum WordPress 1.5 Modus. Also bei Templates wird alles durch ein PHP-File gezogen und bei Skins werden mehrere Vorlagen zusammengefasst und dann daraus das Blog gebaut. Spezialanpassungen kann man dann über eigene Stubdateien machen (die gleichen die auch für die hübscheren URIs genommen werden sollen) und darüber z.B. feste Layouts aufbauen mit denen man dann zum Beispiel statische Seiten simulieren könnte.

Alles in allem das Ergebnis des Kurzfluges: nettes System (trotz der etwas barocken Ecken in der URI-Erstellung und der recht spartanischen Dokumentation) für Hacker und Leute die sich in den Code reinwühlen mögen. So zum direkt losstarten finde ich es weniger geeignet - da ist WordPress wesentlich einfacher zu verstehen und zu starten. Und um mit Drupal zu konkurrieren ist b2evolution in den Features zu mager - einfach zu stark auf Blogs ausgerichtet. Man kann es zwar in die passende Richtung verbiegen - aber warum sollte man das machen wollen, wenn man auch was fertiges nehmen kann, das all das schon kann?

Hmm. Klingt relativ ähnlich zu dem was ich vor fast einem Jahr über b2evolution geschrieben habe. Viel Entwicklung hat es dort irgendwie nicht gegeben in der Zwischenzeit.

Und nochmal Logfiles

Da ich ja nun ein interessantes Studienobjekt hatte, wollte ich mal gucken inwieweit ich mit ein bischen Clusteranalyse in meinen Logfiles irgendwas interessantes zutagefördern würde. Ich habe also eine Matrix angelegt aus Referrern und zugreifenden IP-Adressen und mir damit mal einen Überblick über typische Userszenarien gemacht - also wie sehen normale User aus im Log, und wie sehen Referrer-Spammer aus und wie sieht unser Freund aus.

Alle drei Varianten lassen sich gut unterscheiden, auch wenn ich im Moment da noch eher davor zurückschrecken würde das algorithmisch zu fassen - das lässt sich nämlich alles recht gut simulieren. Trotzdem sind ein paar Auffälligkeiten zu sehen. Zuerst mal ein ganz normaler Benutzer:


aa.bb.cc.dd: 7 Zugriffe, 2005-02-05 03:01:45.00 - 2005-02-04 16:18:09.00
 0065*-
 0001*http://www.tagesschau.de/aktuell/meldungen/0,1185,OID4031994 ...
 0001*http://www.tagesschau.de/aktuell/meldungen/0,1185,OID4031612 ...
 0001*http://mudbomb.com/archives/2005/02/02/wysiwyg-plugin-for-wo ...
 0001*http://www.heise.de/newsticker/meldung/55992
 0001*http://log.netbib.de/archives/2005/02/04/nzz-online-archiv-n ...
 0001*http://www.heise.de/newsticker/meldung/56000
 0001*http://a.wholelottanothing.org/2005/02/no_one_can_have.html

Man sieht schön wie dieser User von meinem Weblog weggeklickt hat und wieder zurückgekommen ist - die Referrer sind nämlich mitnichten alles Links auf mich, sondern falsche Referrer die die Browser schicken, wenn der Benutzer von einer Site auf eine andere wechselt. Eigentlich sollen Referrer ja nur dann geschickt werden, wenn auch wirklich ein Link geklickt wird - kaum ein Browser macht das aber richtig. Der Besuch war an einem definierten Tag und er ist direkt eingestiegen durch Eingabe des Domainnamens (die "-" Referrer stehen oben und oben steht der früheste Referrer der vorkommt).

Oder hier mal ein Zugriff von mir:


aa.bb.cc.dd: 6 Zugriffe, 2005-02-04 01:11:56.00 - 2005-02-03 08:27:09.00
 0045*-
 0001*http://www.aylwardfamily.com/content/tbping.asp
 0001*http://temboz.rfc1437.de/view
 0001*http://web.morons.org/article.jsp?sectionid=1&id=5947
 0001*http://www.tagesschau.de/aktuell/meldungen/0,1185,OID4029220 ...
 0001*http://sport.ard.de/sp/fussball/news200502/03/bvb_verpfaende ...
 0001*http://www.cadenhead.org/workbench/entry/2005/02/03.html

Ich erkenne mich daran, das Referrer mit temboz.rfc1437.de vorkommen - das ist mein Online-Aggregator. Sieht ähnlich aus - ne Menge falsch geschickter Referrer. Noch ein anderer User:


aa.bb.cc.dd: 19 Zugriffe, 2005-02-12 14:45:35.00 - 2005-01-31 14:17:07.00
 0015*http://www.muensterland.org/system/weblogUpdates.py
 0002*-
 0001*http://www.google.com/search?q=cocoa+openmcl&ie=UTF-8&oe=UTF ...
 0001*http://blog.schockwellenreiter.de/8136
 0001*http://www.google.com/search?q=%22Rainer+Joswig%22&ie=UTF-8& ...
 0001*http://www.google.com/search?q=IDEKit&hl=de&lr=&c2coff=1&sta ...

Dieser kam öfter (also mehrere Tage) über meine Update-Seite auf muensterland.org und zusätzlich hat er noch nach Lisp-Themen gesucht. Und vom Herrn der Schockwelle ist er auch mal gekommen. Absolut typisches Verhalten.

Jetzt mal im Vergleich ein typischer Referrer-Spammer:


aa.bb.cc.dd 6 Zugriffe, 2005-02-12 17:27:27.00 - 2005-02-02 09:25:22.00
 0002*http://tramadol.freakycheats.com/
 0001*http://diet-pills.ronnieazza.com/
 0001*http://phentermine.psxtreme.com/
 0001*http://free-online-poker.yelucie.com/
 0001*http://poker-games.psxtreme.com/

Alle Referrer sind direkte Domain-Referrer. Keine "-" Referrer - also keine Zugriffe ohne Referrer. Keine sonstigen Zugriffe - würde ich es genauer analysieren nach Seitentyp, würde auffallen das keine Bilder etc. zugegriffen werden. Leicht zu erkennen - sieht einfach mager aus. Typisch ist auch das jede URL nur einmal oder zweimal angegeben ist.

Jetzt unser neuer Freund:


aa.bb.cc.dd: 100 Zugriffe, 2005-02-13 15:06:16.00 - 2005-02-11 07:07:55.00
 0039*-
 0030*http://irish.typepad.com
 0015*http://www208.pair.com
 0015*http://blogs.salon.com
 0015*http://hfilesreviewer.f2o.org
 0015*http://betas.intercom.net
 0005*http://vowe.net
 0005*http://spleenville.com

Was auffällt sind die Referrer ohne abschliessenden / - untypisch für Referrer-Spam. Ausserdem halt ganz normale Sites. Was auch auffällt, es werden Seiten zugegriffen ohne Referrer - dahinter verstecken sich die RSS-Feeds. Auch dieser ist also leicht von Usern zu unterscheiden. Vor allem da ein gewisser Rhythmus drin ist - scheinbar immer 15 Zugriffe mit einem Referrer, dann den Referrer wechseln. Entweder ist die Referrer-Liste recht klein, oder ich hatte Glück das er zweimal den gleichen bei mir probiert hat - einer ist nämlich 30x da.

Normale Bots braucht man nicht gross zu vergleichen - die wenigsten schicken Referrer mit und sind deshalb völlig uninteressant. Ich hatte einen, der mir aufgefallen war:


aa.bb.cc.dd: 5 Zugriffe, 2005-02-13 15:21:26.00 - 2005-01-31 01:01:07.00
 2612*-
 0003*http://www.everyfeed.com/admin/new_site_validation.php?site= ...
 0002*http://www.everyfeed.com/admin/new_site_validation.php?site= ...

Eine neue Suchmaschine für Feeds die ich noch nicht kannte. Scheinbar hat der Admin gerade vorher irgendwo meine Adresse eingetragen und dann hat der Bot losgelegt die Seiten zu sammeln. Danach hat er dann im Administrationsinterface meine von ihm neu gefundenen Feeds freigeschaltet. Scheint ein kleines System zu sein - der Bot läuft von der gleichen IP wie das Administrationsinterface. Die meisten anderen Bots kommen von ganzen Botfarmen, Webspidern ist halt eine aufwändige Sache ...

Zusammenfassend lässt sich also feststellen, das die derzeitige Generation von Referrer-Spammer-Bots und anderen Mal-Bots noch recht primitiv aufgebaut ist. Sie benutzen keine Botnetze um viele unterschiedliche Adressen zu verwenden und sich dadurch zu verstecken, sie benutzen reine Server-URLs statt Seiten-URLs und haben auch sonst recht viele typische Kennzeichen wie z.B. bestimmte Rhythmen. Ausserdem kommen sie fast immer mehrfach.

Leider sind das keine guten Merkmale um sie algorithmisch zu fassen - ausser man lässt seine Referrer in eine SQL-Datenbank laufen und prüft jeden Referrer mit entsprechenden Selects auf die typischen Kriterien. Darüber könnte man dann durchaus die üblichen Verdächtigen erwischen und gleich auf dem Server blocken. Denn normale User-Zugriffe sehen deutlich anders aus.

Allerdings sind auch schon neue Generationen in der Mache - wie mein kleiner Freund, der mit dem fehlenden /, zeigt. Und dank der dämlichen Browser mit ihren falsch erzeugten Referrern (die viel mehr über die History des Browsers aussagen als über tatsächliche Link-Verfolgung) kann man nicht einfach die referenzierten Seiten gegenchecken, da viele Referrer reine Blindreferrer sind.

Weg mit Trackback

Isotopp grübelt anlässlich des Spamtags über Trackback Spam und stellt mehrere Ansätze vor. Einer davon arbeitet mit einer Gegenprüfung der Trackback-URL gegen die IP des einsendenden Rechners - wenn der Rechner eine andere IP hat als der im Trackback beworbene Server, dann wäre das warscheinlich Spam. Ich hab mal meine eigenen Kommentare dazu zusammengeschrieben - und begründet, warum ich Trackback lieber heute als morgen los wäre. Komplett. Und ja, das ist eine komlette 180-Grad Wendung meinerseits zum Thema Trackback.

Der IP-Test-Ansatz kommt mal wieder aus der Sicht der reinen servererstellten Blogs. Es gibt aber dummerweise einen grossen Haufen Trackback-fähiger Softwareinstallationen die nicht auf dem Server laufen müssen (oft auch nicht laufen) auf dem die Blogseiten liegen - alle Tools die statischen Output produzieren zum Beispiel. Grosse Installationen sind Radio Userland Blogs. Kleinere PyDS Blogs. Oder auch Blosxom-Varianten im offline-Modus (sofern es da mitlerweile trackbackfähige Versionen gibt - aber das es typische Hackertools sind, gibts das mit Sicherheit).

Dann gibts noch die diversen Tools die nicht Trackback-fähig sind, wo die User dann einen externen Trackback-Agent benutzen um die Trackbacks abzusetzen.

Und last but not least kommen auch noch die diversen Blogger/MetaWeblogAPI-Clients hinzu, die selber den Trackback absetzen weil z.B. nur MoveableType im MetaWeblogAPI das Triggern von Trackbacks erlaubt, aber andere APIs nicht.

Von daher ist der Ansatz mit der IP entweder nur als ein Filter zu sehen der einen Teil der Trackbacks durchwinkt, oder aber eine Verhinderung von Trackbacks von den oben genannten Usern. Und letzteres wäre ausgesprochen unschön.

Eigentlich ist das Problem ganz einfach: Trackback ist ein krankes Protokoll das mit der heissen Nadel gestrickt wurde, ohne das sich der Entwickler auch nur einen Hauch von Gedanken zu dem ganzen Thema gemacht hat. Und gehört daher IMO auf den Müllhaufen der API-Geschichte. Das ich es hier unterstütze liegt einfach nur daran, das WordPress es standardmäßig implementiert hat. Sobald der manuelle Moderationsaufwand zu hoch wird, fliegt Trackback hier ganz raus.

Sorry, aber in dem Punkt Trackback haben die MoveableType-Macher wirklich Nähe zu Microsoft-Verhalten gezeigt: einen völlig unzureichenden Pseudo-Standard durch Marktdominanz durchgedrückt - ohne sich überhaupt mal über die Sicherheitsimplikationen Gedanken zu machen. Warum wohl bei RFCs immer ein entsprechender Absatz über Sicherheitsprobleme zwingend ist? Leider haben die ganzen Blogentwickler alle fleissig mitgezogen (ja, ich auch - bei Python Desktop Server) und wir haben dieses alberne Protokoll am Hals. Und seine - völlig erwartbaren - Probleme.

Besser jetzt eine bessere Alternative entwickeln und forcieren - z.B. PingBack. Bei PingBack ist definiert, das die Seite die einen PingBack auf eine andere Seite ausführen will auch wirklich diesen Link dort exakt so enthalten muss - im API werden immer zwei URLs übertragen, die eigene und die fremde URL. Die eigene URL muss im Source auf die fremde URL zeigen, nur dann wird der fremde Server den PingBack annehmen.

Für Spammer ist das ziemlich absurd zu handhaben - sie müssten vor jedem Spam die Seite umschiessen oder über entsprechende Servermechanismen dafür sorgen, das die gespammten Weblogs dann beim Test entsprechend eine Seite vorgegaukelt bekommen, in der dieser Link drin ist. Natürlich ist das durchaus machbar - aber der Aufwand ist deutlich höher und durch die nötige Servertechnik ist das nicht mehr mit fremden offenen Proxies und/oder Dialup-Zugang machbar.

Von daher wäre der richtige Weg einfach der Wechsel des Linkprotokolls. Weg mit Trackback. Das Trackback-Loch kann man nicht stopfen. PS: wer sich mal meinen Trackback in Isotopps Posting anguckt sieht gleich das zweite Problem von Trackback: abgesehen vom riesigen Sicherheitsproblem ist nämlich die Zeichensatzunterstützung von Trackbacks schlichtweg ein totales Debakel. Auch hier hat der ursprüngliche Autor des Pseudo-Standards keine Minute über mögliche Probleme nachgedacht. Und dann wundern sich noch manche Leute wenn TypeKey von den Moveable-Type-Leuten nicht so richtig akzeptiert wird - sorry, aber Leute die so bescheidene Standards machen werde ich auch noch gerade die Loginverwaltung übertragen ...

Zope-Hosting und Leistung - Englische Version

Jemand bat mich um eine englische Übersetzung meines Artikels zu Zope Hosting und Performance. Hier ist sie - okay, es ist nicht so sehr eine direkte Übersetzung, sondern eine Überarbeitung der Geschichte auf Englisch. Viel Spaß damit.

Kürzlich hatte der Schockwellenreiter Probleme mit seinem Blog-Server. Er verwendet Zope mit Plone und CoreBlog. Da ich seit einigen Jahren professionelles Zope-Hosting betreibe, Systeme, die im Bereich von 2000-3000 Hits pro Minute liegen, dachte ich, ich stelle einige der Dinge zusammen, die ich gelernt habe (manchmal auf die harte Tour) über Zope und Performance.

  • Der wichtigste Schritt, den ich unternehmen würde: schlankere Anwendung. Werfen Sie alles aus Ihrer Zope-Datenbank, was nicht dort bleiben muss. Wenn es kein Content-Management benötigt, speichern Sie es in Ordnern, die von Apache bereitgestellt werden. Verwenden Sie mod_rewrite, um es nahtlos in Ihre Website zu integrieren, so dass die Leute von außen keinen Unterschied bemerken. Dies kann am besten für Layout-Bilder, Stylesheets usw. erfolgen - Apache ist viel schneller beim Liefern dieser Dateien.
  • Verwenden Sie Zope-Caching, wenn möglich. Der Hauptparameter, den Sie überprüfen müssen: Haben Sie genug RAM. Zope wird beim Verwenden von Caching wachsen (insbesondere der RAMCacheManager). Die automatische Bereinigung wird Sie nicht retten - Zope wird trotzdem wachsen. Richten Sie eine Prozessüberwachung ein, die automatisch Zope-Prozesse tötet und neu startet, die über eine obere Grenze hinauswachsen, um Paging aufgrund eines zu hohen Speicherverbrauchs zu verhindern. Dies ist sogar eine gute Idee, wenn Sie überhaupt kein Caching verwenden.
  • Es gibt zwei bemerkenswerte Cache-Manager: einer verwendet RAM und der andere einen HTTP-Beschleuniger. Der RAMCacheManager zwischenspeichert Ergebnisse von Objekten im Speicher und kann verwendet werden, um kleine Objekte zu zwischenspeichern, die viel Zeit oder Ressourcen für die Konstruktion benötigen. Der HTTPCacheManager dient zur Verwendung eines HTTP-Beschleunigers - die meisten Leute werden Squid verwenden, aber Sie können auch einen entsprechend konfigurierten Apache verwenden. Der Cache-Manager stellt die richtigen Expires- und Cache-Control-Header bereit, so dass der meiste Verkehr aus den HTTP-Beschleunigern und nicht aus Zope geliefert werden kann.
  • Große Zope-Objekte töten die Performance von Zope. Bei der Verwendung von Caching zerstören sie die Effizienz des Cachings, indem sie den Cache mit großen Blobs von Dingen verunreinigen, die nicht oft benötigt werden, und Zope selbst wird durch sie ebenfalls in der Performance beeinträchtigt. Der Grund dafür ist, dass die Zope-Ausgabe im Speicher konstruiert wird. Die Konstruktion großer Objekte im Speicher verbraucht viele Ressourcen aufgrund der Sicherheits- und Architekturschichten in Zope. Besser ist es, sie mit Cronjobs oder anderen Mitteln außerhalb des Zope-Servers zu erstellen und sie direkt mit Apache zu liefern. Apache ist viel schneller. Eine typische Situation ist, wenn Benutzer PDF-Dokumente in Zope erstellen, anstatt sie außerhalb zu erstellen. Schlechte Idee.
  • Verwenden Sie ZEO. ZEO ist großartig. Wirklich. Im Wesentlichen handelt es sich dabei nur um die ZODB mit einer kleinen Kommunikationsschicht darüber. Diese Schicht wird in Zope-Instanzen verwendet, anstatt die ZODB direkt zu verwenden. Auf diese Weise können Sie mehrere Prozessgruppen auf Ihrer Maschine ausführen, die alle mit derselben Datenbank verbunden sind. Dies hilft bei dem oben erwähnten Prozessneustart: Wenn einer ausgefallen ist, erledigt der andere die Arbeit. Verwenden Sie mod_backhand in Apache, um die Last zwischen den Prozessgruppen zu verteilen, oder verwenden Sie andere Lastausgleichswerkzeuge. ZEO macht auch regelmäßige Datenbankpacks einfacher: Sie laufen auf dem Server und nicht in den Zope-Instanzen - sie bemerken den laufenden Pack eigentlich kaum.
  • Wenn Sie es haben, verwenden Sie eine SMP-Maschine. Oder kaufen Sie eine. Wirklich - das hilft. Sie müssen ZEO und mehrere Zope-Instanzen ausführen - andernfalls wird Sie der globale Interpreter-Lock von Python treffen und Zope wird nur einen der beiden Prozessoren verwenden. Das ist einer der Gründe, warum Sie mehrere Prozessgruppen von Anfang an wollen - Verteilung der Last auf der Maschine selbst, Nutzung mehrerer Prozessoren.
  • Sie können die Leistung verbessern, indem Sie die architektonischen Schichten reduzieren, durch die Ihr Code geht. Python-Skripte sind schneller als DTML. Zope-Produkte sind schneller als Python-Skripte. Entfernen Sie komplexen Code von Ihrem Server und verschieben Sie ihn in Produkte oder andere externe Orte. Dies erfordert das Umschreiben von Anwendungscode, daher ist es nicht immer eine Option - aber wenn Sie es tun, wird es sich auszahlen.
  • Lassen Sie Ihre ZODB-Datei nicht zu groß werden. Die ZODB fügt nur beim Schreiben hinzu - die Datei wächst. Sie wächst ziemlich groß, wenn Sie nicht regelmäßig packen. Wenn Sie nicht packen und Sie Multi-GB-ZODB-Dateien haben, beschweren Sie sich nicht über langsame Serverstarts ...
  • Wenn Sie komplexen Code in Ihrer Zope-Anwendung haben, kann es sich lohnen, ihn in einen externen Server zu stellen und mit Zope über RPC-Mittel zu verbinden, um die Ausführung auszulösen. Ich verwende mein |TooFPy| für solche Dinge - ziehen Sie einfach den Code heraus, bauen Sie ein Tool und hängen Sie es über XMLRPC in die Zope-Anwendung ein. Ja, XMLRPC kann ziemlich schnell sein - zum Beispiel ist pyXMLRPC eine C-geschriebene Version, die sehr schnell ist. Das Verschieben von Code außerhalb von Zope hilft, weil dieser Code keinen der statisch zugewiesenen Listener blockieren kann, um Dinge zu berechnen. Das Erhöhen der Anzahl der Listener-Threads zahlt sich nicht so aus, wie Sie erwarten würden: aufgrund des globalen Interpreter-Locks wird trotzdem nur ein Thread zur gleichen Zeit laufen und wenn Ihr Code C-Erweiterungen verwendet, kann er sogar alle anderen Threads blockieren, während er verwendet wird.
  • Wenn Sie PostgreSQL verwenden, verwenden Sie PsycoPG als Datenbank-Treiber. PsycoPG verwendet Sitzungs-Pooling und ist sehr schnell, wenn Ihr System viele Hits erhält. Andere Treiber blockieren Zope oft aufgrund von Einschränkungen wie nur einer Abfrage zur gleichen Zeit und anderem solchen Unsinn. Viele Administratoren mussten auf die harte Tour lernen, dass 16 Listener-Threads nicht wirklich 16 verfügbare Slots sind, wenn SQL-Treiber ins Spiel kommen ...

Es gibt noch mehr Möglichkeiten, die Leistung zu verbessern, aber die oben genannten sind mit relativ wenig Aufwand umsetzbar und hängen größtenteils davon ab, ob Sie genug Speicher haben und vielleicht eine SMP-Maschine. Speicher ist wichtig - je mehr, desto besser. Wenn Sie Speicher in Ihren Computer stecken können, tun Sie es. Es gibt so etwas wie zu viel Speicher nicht (solange Ihr Betriebssystem die Menge an Speicher unterstützt, natürlich).

Was tun, wenn selbst diese Tipps oben nicht funktionieren? Ja, ich war in dieser Situation. Wenn Sie in eine solche Situation geraten, gibt es nur eine - eher brutale - Lösung: aktives Caching. Damit meine ich das Herausziehen von Inhalten vom Zope-Server mit Cronjobs oder anderen Mitteln und das Speichern in Apache-Ordner und das Verwenden von mod_rewrite, um nur statische Inhalte an Benutzer zu liefern. mod_rewrite ist Ihr Freund. Im Wesentlichen nehmen Sie einfach die Seiten, die Sie derzeit umbringen, und machen sie pseudo-statisch - sie werden nur gelegentlich aktualisiert, aber die Hits erreichen Zope überhaupt nicht.

Ein weiterer Schritt ist natürlich mehr Hardware. Wenn Sie ZEO verwenden, ist es kein Problem, eine Farm von Zope-Servern vor Ihren ZEO-Maschine zu stellen (wir haben derzeit 5 Dual-Prozessor-Maschinen, die die Zope-Instanzen ausführen, und zwei eher große, fette, hässliche Server im Hintergrund für Datenbanken, Frontend mit zwei Apache-Servern, die im Vergleich zu den Backend-Dingen fast wie Zwerge aussehen).

Zope ist eine fantastische Software - verstehen Sie mich nicht falsch. Ich mag es. Besonders die Tatsache, dass es eine integrierte Entwicklungsumgebung für Webanwendungen und Content-Management ist, ist sehr schön. Und die einfache Integration externer Datenquellen ist auch schön. Aber Zope ist ein Ressourcenfresser - das ist nicht diskutierbar. Es gibt kein solches Ding wie ein kostenloses Mittagessen.

Zope Hosting und Performance

Der Schockwellenreiter hat Probleme mit seinem Zope-Server. Da ich nun schon seit einigen Jahren professionell (in der Firma) Zope-Hosting mache und dabei auch ein paar ziemlich heftige Portale laufen habe (zwischen 2000 und 3000 Hits pro Minute sind da nicht selten - allerdings verteilt auf viele Systeme), hier mal ein paar Tipps von mir zur Skalierung von Zope.

  • Der wichtigste Schritt, den ich jedem empfehlen würde, ist entschlacken. Werft aus dem Zope all das raus was nicht rein muss - was statisch erstellt werden kann, was sich nur selten ändert, wo kein Content-Management nötig ist: raus damit. Packt es in normale Apache-Verzeichnisse. Nehmt Apaches mod_rewrite und sorgt dafür das die alten Adressen weiter funktionieren, aber aus dem Apache kommen. Das betrifft vor allem all die kleinen Scheisserlein wie Layoutgrafiken und so - die brauchen nicht aus dem Zope zu kommen, die werden besser aus dem Apache geliefert.
  • Benutzt das Zope-Caching, wenn irgend möglich. Irgend möglich heisst: genug Speicher auf dem Server, damit auch sich fettfressende Prozesse mal etwas Luft haben. Generell führt das Zope-eigene Caching dazu, das Prozesse immer fetter werden - das Aufräumen im eigenen Cache ist recht unbrauchbar. Also setzt eine Prozessüberwachung ein, die einen Zope-Prozess abschiesst und neu startet wenn er zu viel Speicher braucht. Ja, das ist wirklich sinnvoll und notwendig.
  • Gute Cachingmöglichkeiten gibt es im Zope zwei: den RAMCacheManager und den HTTPCacheManager. Ersterer speichert Ergebnisse von Zope-Objekten im Hauptspeicher und kann daher einzelne Seitenkomponenten cachen - packt das da rein, was komplex zu ermitteln ist.Der zweite (HTTPCache) arbeitet mit einem Squid zusammen. Packt einen Squid als HTTP Accelerator vor euren Zope und konfiguriert den HTTP Cache Manager entsprechend, so das Zope die passenden Expire-Header erzeugt. Dann wird ein Grossteil eures Traffics aus dem Squid betrieben. Der ist schneller als euer Zope. Alternativ zum Squid kann man auch einen Apache als HTTP Accelerator mit lokalem Cache konfigurieren - ideal für die, die keinen Squid installieren können oder wollen, aber durchaus Möglichkeiten zur weitergehenden Konfiguration eines Apache haben.
  • Grosse Zope-Objekte (und das meine ich wirklich im Sinne von KB) machen Zope tot. Beim Caching zerfetzen sie einem die schönste Cache-Strategie und Zope selber wird arschlahm wenn die Objekte zu gross werden. Der Grund liegt in der Zope-Architektur: alle Objekte werden erst mühsam in mehreren Schichten durch mehrere Softwarelayer zusammengedengelt. Im Speicher - und belegen daher im Speicher auch entsprechend Platz. Raus mit den komplexen Objekten mit riesigen KB-Zahlen. Macht sie kleiner. Erstellt sie statisch per Cronjob. Liefert sie aus dem Apache aus - es gibt nix blöderes als seine grossen PDFs alle im Zope in der ZODB abzulegen, oder gar dynamisch dort zu generieren.
  • Installiert ZEO. Das Teil rockt. Im Prinzip ist es nur die ZODB mit einem primitiven Serverprotokoll. Wichtig daran: euer Zope kann auf mehrere Prozessgruppen aufgeteilt werden. Das wollt ihr wenn ihr per Prozessüberwachung einen amoklaufenden Zopeprozess abschiessen wollt, aber das Portal von aussen möglichst unbeschädigt aussehen soll - in dem Fall packt ihr auf den Apache einfach mod_backhand drauf, oder eine andere Balancer-Technik zwischen Apache und Zope. Zusätzlich macht der ZEO auch das Packen der ZODB (sollte täglich laufen) einfacher, da der Pack im Hintergrund auf dem ZEO läuft und die Zope-Server selber nicht gross beeinträchtigt werden.
  • Wenn ihr habt, nehmt einen SMP-Server. Oder kauft euch einen. Wirklich - das bringt ne Menge. Voraussetzung ist allerdings die oben angesprochene Technik mit den mehreren Prozessgruppen - Python hat ein globales Interpreterlock, welches dazu führt das auch auf einer Mehrprozessor-Maschine eh nie mehr als ein Python-Thread gleichzeitig läuft. Von daher wollt ihr mehrere Prozessgruppen.
  • Performance gewinnt man auch durch Ausschaltung von Schichten. Leider ist das oft nur mit Softwareänderungen realisierbar, daher eher für Selberstricker interessant. Packt komplexe Abläufe raus aus dem Zope-Server und packt sie in Zope Products. Zope Products laufen native ohne Beschränkungen im Python Interpreter. Zope Python Scripte und DTML Dokumente werden hingegen durch viele Layer geschleppt die dafür sorgen das ihr euch an die Zugriffsrechte von Zope haltet, nichts böses tut und auch sonst ganz lieb seid. Und sorgen dafür das ihr langsamer werdet. Products lohnen sich - kosten aber Arbeit und sind entgegen der anderen mehr technischen Tipps nicht immer umsetzbar.
  • Zusätzlich hat es sich bewährt nicht zu viel Daten in die ZODB zu packen, vor allem nichts was diese erweitert - die ZODB wird nämlich nur grösser, kleiner wird sie nur beim Packen. Nach einiger Zeit hat man dann locker eine ZODB im GB-Bereich und braucht sich über langsame Serverstarts nicht wundern ...
  • Wenn komplexere Abläufe im System vorkommen kann es Sinn machen die ganz auszulagern. Bei mir kommt dann immer das |TooFPy| zum Einsatz. Einfach die ganzen komplexeren Sachen in ein Tool umwandeln und dort reinhängen - der Code läuft ungebremst schnell ab. Vom Zope dann einfach mit einem SOAP Client oder XMLRPC Client auf den Toolserver zugreifen und die Funktionen dort ausführen. Ja, die mehrfache XML Wandlung ist tatsächlich weniger kritisch als der Ablauf von komplexerem Code im Zope - vor allem wenn dieser Code einiges an Laufzeit beansprucht. Zope blockiert dann nämlich einen seiner Listener - die Anzahl ist statisch. Und einfach hochdrücken bringt nix - dank des globalen Interpreterlocks würden dann nur mehr Prozesse aufeinander warten, das dieses Lock freigegeben ist (z.B. bei jeder C-Erweiterung die zum Einsatz kommt). Für die XMLRPC Kommunikation gibt es eine gute und schnelle C-Implementierung die in Python integriert werden kann, damit ist das XML-Overhead-Problem irrelevant.
  • Wenn ihr PostgreSQL als Datenbank benutzt: nehmt PsycoPG als Datenbanktreiber. Das Session-Pooling bringt Zope erst richtig auf Touren. Generell solltet ihr schauen ob der entsprechende Datenbanktreiber irgendeine Form von Session Pooling unterstützt - notfalls über einen externen SQL Proxy. Denn sonst hängt in Zope bei SQL-Queries unter Umständen das ganze System, weil eine fette Query auf das Ergebnis wartet. Da ist schon mancher drauf reingefallen und hat lernen müssen das 16 Zope-Threads nicht gleichbedeutend mit 16 parallel bearbeiteten Zope-Zugriffen bedeuten muss, wenn SQL-Datenbanken im Spiel sind.

Es gibt natürlich noch eine ganze Menge mehr Sachen die man machen kann, aber die obigen sind weitestgehend aus dem Stand zu bewältigen und hauptsächlich davon abhängig das ihr genug Speicher im Server habt (und evtl. eine Mehrprozessormaschine - geht aber auch ohne). Wichtig ist der Speicher - je mehr desto besser. Wenn geht, steckt halt noch mehr Speicher rein. Man kann nicht zu viel Speicher haben ...

Was machen wenn das alles auch noch nicht reicht (ja, ich hatte das schon - manchmal hilft nur die ganz grobe Axt). Nun, in dem Fall gibt es Abwandungen der obigen Techniken. Meine Lieblingstechnik in dem Bereich ist aktives Caching. Damit meine ich, das im Zope an einer Stelle hinterlegt wird welche Dokumente aktiv gecached werden sollen. Dazu gehört dann auf der Kiste ein Script, das die Seiten vom Zope abruft und in ein Verzeichnis packt. Per Apache Rewrite Rules wird dann dafür gesorgt das von aussen die statischen Inhalte ausgeliefert wird. Im Prinzip sorgt man also dafür das die Seiten die am häufigsten besucht werden und die für diese Technik geeignet sind (also z.B. keine Personalisierungsdaten enthalten) einfach eine statische Seite rausgeht, egal was sonst passiert - die normalen Cachetechniken gehen da einfach nicht brutal genug vor, da geht noch zu viel Traffic durch auf den Server.

Ein weiterer Schritt ist natürlich der Einsatz weiterer Maschinen - einfach weitere Kisten daneben stellen und mit der ZEO-Technik miteinander verbinden.

Zope ist eine fantastische Software - gerade die hohe Integration von Entwicklungsumgebung, CMS und Server ist oft ungemein praktisch und die leichte Integrierbarkeit in externe Datenquellen ist ebenfalls sehr nett. Aber Zope ist ein Ressourcenschwein, das muss man so einfach mal sagen.

Caching für PHP Systeme

Es gibt ja grundsätzlich zwei Wege wie man in einem PHP-basierten System caching betreiben kann. Ok, es gibt viel mehr, aber zwei Hauptwege sind deutlich erkennbar. Ich hab mal zusammengeschrieben was in dem Zusammenhang interessant ist - gerade weil im Moment ein paar Kollegen unter hoher Serverlast zu leiden haben. Das ganze ist allgemein gehalten, betrachtet aber aus begreiflichen Gründen auch die speziellen Folgen für WordPress.

  • Caching von vorkompilierten PHP Seiten
  • Caching von Seitenoutput

Zu beiden Hauptwegen gibt es eine ganze Reihe von Varianten. Die PHP Seiten selber liegen bei Webservern ja als Source vor - unverarbeitet und nicht irgendwie optimiert für den Ladeprozess. Hat man komplexe PHP Systeme laufen, fällt also bei jedem PHP-File das parsen und kompilieren in den internen Code an. Das kann bei Systemen mit vielen Includs und vielen Klassenbibliotheken durchaus beachtlich sein. An diesem Punkt setzt die erste Hauptrichtung des Cachings an: der erzeugte Zwischencode wird einfach weggespeichert. Entweder in shared Memory (Speicherblöcke die vielen Prozessen eines Systems gemeinsam zur Verfügung stehen) oder auf die Festplatte. Hier gibt es eine Reihe von Lösungen - ich selber setze turck-mmcache ein. Der Grund ist hauptsächlich das es nicht im shared Memory cached, sondern auf der Platte (was aber meines Wissens die anderen ähnlichen Lösungen auch tun) und das es für turck-mmcache ein Debian-Paket gibt. Und das ich bisher relativ wenig negative Erfahrungen damit gemacht habe (jedenfalls bei Debian stable - bei Debian testing sieht das anders aus, da fliegt einem die PHP Anwendung um die Ohren). Da WordPress auf einer grösseren Menge von Bibliotheksmodulen mit recht grossem Sourceinhalt basiert, bringt ein solcher Cache eine ganze Menge um die Grundlast von WordPress zu mindern. Da diese Caches in der Regel vollkommen transparent - ohne sichtbare Auswirkungen ausser der Beschleunigung - sind, kann man einen solchen Cache auch generell aktivieren.

Die Zweite Hauptrichtung für das Caching ist das Zwischenspeichern von Seiteninhalten. Hier kommt als Spezialität hinzu, das Seiten ja oft dynamisch abhängig von Parametern erstellt werden - und daher eine Seite nicht immer den gleichen Output erzeugt. Man denke nur an so banale Sachen wie die Anzeige des Benutzernamens wenn ein Benutzer angemeldet ist (und einen Cookie dafür gespeichert ist). Auch aufgrund von HTTP Basic Authentication (die Anmeldetechnik bei der das Popup-Fensterchen für Benutzer und Passwort kommt) können Seiteninhalte unterschiedlich sein. Und POST-Requests (Formulare die ihre Inhalte nicht über die URL mitschicken) produzieren auch wieder Output der von diesen Daten abhängig ist.

Grundsätzlich muss also ein Output-Cache diese ganzen Eingangsparameter berücksichtigen. Eine gute Strategie ist oft die POST-Ergebnisse garnicht zu cachen - denn dort würden auch Fehlermeldungen etc. auftauchen, die abhängig von externen Quellen (Datenbanken) sind und daher sogar bei identischen Eingabewerten unterschiedliche Ausgaben produzieren könnten. Man kann also eigentlich nur GET-Anfragen (URLS mit Parametern direkt in der URL) sinnvoll cachen. Hierbei muss man aber sowohl die gesendeten Cookies als auch die gesendeten Parameter in der URL berücksichtigen. Sofern das eigene System mit basic-Authentication arbeitet, muss auch das mit einfliessen in den Cache-Begriff.

Ein zweites Problem ist, das Seiten selten rein statisch sind - auch statische Seiten enthalten durchaus Elemente die man eigentlich lieber dynamisch haben möchte. Hier muss man also eine wesentliche Entscheidung treffen: reicht rein statischer Output, oder muss ein Mix kommen? Desweiteren muss man noch die Entscheidung treffen, wie sich Seitenaktualisierungen auswirken sollen - wie merkt der Cache, das etwas geändert wurde?

Ein Ansatz den man verfolgen kann ist ein sogenannter reverse-Proxy. Man packt einfach einen normalen Web-Proxy so vor den Webserver, das jeglicher Zugriff auf den Webserver selber technisch durch diesen Webproxy gezogen wird. Der Proxy steht direkt vor dem Webserver und ist somit für alle Benutzer verbindlich. Da Webproxies das Problem der Benutzerauthentifizierung, der Parameter und der POST/GET-Unterscheidung schon gut beherrschen sollten (in der normalen Anwendungssituation für Proxies sind die Probleme ja die gleichen), ist das eine sehr pragmatische Lösung. Auch die Aktualisierung wird von solchen Proxies meist recht gut gelöst - und im Notfall kann der Benutzer durch einen erzwungenen Reload den Proxy ja überreden die Inhalte neu zu ziehen. Leider geht diese Lösung nur dann, wenn man den Server selber unter Kontrolle hat - und der Proxy zieht noch weitere Resourcen, weshalb unter Umständen dafür kein Platz auf dem Server ist. Auch ist es stark von der Anwendung abhängig wie gut sie mit Proxies klar kommt - wobei Probleme zwischen Proxy und Anwendung auch bei normalen Benutzern aufstossen würden und deshalb sowieso gelöst werden müssen.

Der zweite Ansatz ist die Software selber - letzten Endes kann die Software ja genau wissen wann Inhalte neu erstellt werden und was für das Caching berücksichtig werden muss. Hier gibt es wieder zwei Richtungen der Implementierung. MoveableType, PyDS, Radio Userland, Frontier - diese erzeugen alle statische HTML Seiten und haben deshalb das Problem mit der Serverlast bei Seitenzugriffen nicht. Der Nachteil liegt auf der Hand: Datenänderungen erzwingen ein Neuerstellen der Seiten, was bei grossen Sites nervig werden kann (und dazu geführt hat das ich von PyDS auf WordPress gewechselt habe).

Die zweite Richtung ist das Caching aus der dynamischen Anwendung selber: beim ersten Zugriff wird der Output unter einem Cache-Key gespeichert. Beim nächsten Zugriff auf den Cache-Key wird einfach geguckt ob der Output schon vorliegt, wenn ja wird er ausgeliefert. Der Cache-Key setzt sich aus den GET-Parametern und den Cookies zusammen. Wenn Datenbankinhalte geändert werden, werden die entsprechenden Einträge im Cache gelöscht und damit die Seiten beim nächsten Zugriff neu erstellt.

WordPress selber hat mit Staticize ein sehr praktisches Plugin für diesen Einsatzzweck. In der aktuellen Beta ist es schon im Standardumfang mit drin. Dieses Plugin erzeugt wie oben beschrieben für Seiten einen Cache-Eintrag. Und berücksichtigt dabei die Parameter und Cookies - basic Authentication wird bei WordPress ja nicht benutzt. Der Clou ist aber, das Staticize die Seiten als PHP speichert. Die Cache-Seiten sind also selber wieder dynamisch. Diese Dynamik kann jetzt dazu benutzt werden um Teile der Seite mit speziellen Kommentaren zu markieren - wodurch für diese Teile der Seite dann wieder dynamische Funktionsaufrufe eingesetzt werden. Der Vorteil liegt auf der Hand: wärend die grossen Aufwände für die Seitenerstellung wie das Zuladen der diversen Bibliotheksmodule und das Lesen der Datenbank komplett erledigt sind können einzelne Teilbereiche der Site weiterhin dynamisch bleiben. Natürlich müssen die Funktionen dafür dann so aufgebaut sein das sie nicht das ganze Bibliothekswerk von WordPress brauchen - aber zum Beispiel dynamische Counter oder Anzeigen der gerade aktiven Benutzer oder ähnliche Spielereien lassen sich auch in den Cacheseiten somit weiter dynamisch halten. Matt Mullenweg benutzt es zum Beispiel um auch auf Seiten aus dem Cache ein Zufallsbild aus seiner Bibliothek anzuzeigen. Staticize löscht bei Erstellung oder Änderung eines Beitrags einfach den ganzen Cache - sehr primitiv und bei vielen Files im Cache kann das dann schon mal etwas länger dauern, aber dafür sehr wirksam und pragmatisch.

Welche Caches setzt man sinvollerweise jetzt wie ein? Ich würde bei komplexeren Systemen immer gucken ob ich nicht einen PHP Codecache einsetzen kann - also turck mMCache oder Zend Optimizer oder phpAccelerator oder was es sonst noch so gibt.

Den Anwendungscache selber würde ich persönlich nur dann aktivieren wenn er auch wirklich nötig wird durch die Last - bei WordPress kann man ja ein Plugin vorhalten und nur bei Bedarf aktivieren. Denn Caches mit statischer Seitenerstellung haben nunmal so ihre Probleme - Layoutänderungen sind erst nach Löschung des Cache aktiv etc.

Wenn man einen Reverse-Proxy einsetzen kann und die Resourcen auf der Maschine dafür auch ausreichen ist er sicherlich immer zu empfehlen. Alleine schon weil man so selber die Probleme mitbekommt, die in der eigenen Anwendung eventuell bezüglich Proxies drin sind - und die jedem Benutzer hinter einem Webproxy auch Ärger machen würden. Gerade wenn man zum Beispiel Zope benutzt gibt es sehr gute Möglichkeiten in Zope die Kommunikation mit dem Reverse Proxy zu verbessern - dazu ist ein Cache Manager in Zope verfügbar. Auch andere Systeme bieten dafür gute Grundlagen - aber im Endeffekt sollte jedes System das saubere ETag und Last-modified Header produziert und korrekt conditional GET (bedingte Zugriffe die mitschicken welche Version lokal schon vorliegt und dann nur aktualisierte Inhalte sehen wollen) behandeln geeignet sein.

Verzögerte Ausführung mit Python

Der ursprüngliche Text ist auf das PyDS Weblog umgezogen. Der Grund ist das ich mit der neuen Software den Text nicht vernünftig verwalten kann, weil die nötigen Tools hier nicht verfügbar sind (speziell das Sourcecodeformatieren klappt hier nicht, ausserdem ist der Text zu riesig - jedenfalls wenn er als XHTML gespeichert ist).

Visual Studio Magazine - Guest Opinion - Save the Hobbyist Programmer

Ein schon älterer, aber interessanter Artikel der auf ein wichtiges Problem hinweist: Hobby-Programmierer werden immer mehr ausgeschlossen aus der Erstellung von kleinen Hacks und einfachen Lösungen durch die immer komplexeren Systeminterfaces und ständigen Wechsel von APIs und Programmierwerkzeugen in der Windows Welt. Und das ist nicht nur die Windows Welt, die davon betroffen ist. Linux und OS X leiden teilweise genauso darunter.

Klar, es gibt immer noch kleine Helfer mit einfacher Programmiermöglichkeit. Oder Scriptsprachen, die leicht zu lernen und zu benutzen sind - z.B. Python. Aber das ist nicht wirklich eine Lösung für diese Bastler. Was für Hobby-Bastler früher das omnipräsente Basic war, oder zum Beispiel die - wenn auch kranke - Sprache in DBase, das fehlt heute. Kaum noch eine Programmierumgebung die nicht ohne Objektorientierten Ansatz daher kommt. Kaum noch Lösungsansätze die nicht versuchen gleich eine allgemeine Entwicklungsumgebung für komplette Programme zu sein.

Nette Ausnahmen gibts noch - FileMaker auf dem Mac versucht immer noch den Hobby-Hacker anzusprechen. Aber es stimmt trotzdem: die einfachen Einstiege werden weniger.

Selbst AppleScript ist auf dem Mac mitlerweile dermaßen komplex und überfüllt worden, das es kaum noch einem Seiteneinsteiger möglich ist damit gleich mal loszulegen. Einige Ecken von AppleScript sind selbst für alte Programmierhasen wie mich obskur und kompliziert. Dazu kommt natürlich, das es für die ganzen Scriptsprachen zwar viele tolle Möglichkeiten der Integration von Anwendungen gibt - aber gerade diese Teile ausgesprochen mies dokumentiert sind.

Um beim Beispiel AppleScript zu bleiben: zwar gibt es die Anwendungsdictionaries, in denen die AppleScript-Fähigkeit eines Programms dokumentiert wird, aber nahezu alle dort abgelegten Beschreibungen die ich bisher gelesen habe gingen davon aus, das der Nutzer schon komplettes und weitgehendes Wissen über AppleScript und die AppleScript-Strukturen hat (was sind Objekte in AppleScript, wie arbeitet man mit Containern etc.). Obwohl also diese Dictionaries genau dem Hobby-Programmierer als Startpunkt dienen könnten, werden sie von den Erstellern (und das sind die Profi-Programmierer in den Softwarehäusern) so gestaltet, das oftmals sogar nur sie selber damit was anfangen können.

Ähnlich in der Linux-Welt. TCL war mal die Standardscriptsprache für den einfachen Einstieg mit simpler Struktur, geradezu primitivster Erweiterungsschnittstelle und der Möglichkeit selbst für Nicht-Programmierer schnell zu Lösungen zu kommen. Heute besteht TCL in der Standardauslieferung (die dann netterweise oft Batteries-Included heisst - nur dummerweise fehlt die verständliche Anleitung) schon aus Bergen von Paketen, von denen eine ganze Reihe sich mit Metasprachenaspekten beschäftigen (z.B. incrTCL und den darauf und auf TK aufbauenden Widget-Libraries - herrjeh, allein in diesem kurzen Hinweis auf den Inhalt sind mehr für einen Einsteiger unverständliche Wörter drin als Füllwörter), die ein Einsteiger nie kapieren wird.

Und auf die desolate Situation unter Windows mit dem Scripting-Host und den OLE Automation Schnittstellen (oder wie auch immer die Dinger heute heissen werden) brauche ich garnicht eingehen - wer da einmal einen Versionswechsel einer Anwendung mitgemacht hat und seine komplette Lösung komplett neu schreiben durfte dank des totalen Wechsels des Scripting-Modells von zum Beispiel Access, der weiss wovon ich rede.

Letzten Endes nehmen wir (wir == professionelle Programmierer) damit den Endanwendern ein Stück Freiheit weg - die Freiheit rumzuspielen und ja, auch die Freiheit sich selbst in den Fuss zu schiessen. Und ich denke, das gerade in der Welt der freien Software die Programmierer anfangen sollten hieran wieder mal ein paar Gedanken zu verschwenden. Es ist ja nett, das nahezu jedes grössere Programm irgendeine Scripting-Sprache einbettet. Aber weniger nett ist, das nahezu garkeine dieser Einbettungen eine vernünftige Dokumentation ihrer Möglichkeiten hat und nur primitivste Beispiel sowie Komplettlösungen sehr komplexer Anwendungen als Startpunkt fürs Lernen da sind. Gerade Hobby-Programmierer lernen nun mal am einfachsten durch das Lesen vorhandener Tools. Und ja, auch ich bin da nicht unbedingt ein gutes Beispiel, denn der Python Desktop Server hat zwar eine Reihe von Erweiterbarkeiten, die auch gerade für Endanwender gedacht sind - aber auch ich hab da viel zu wenig Dokumentation geschrieben. Irgendwie schade, denn so werden viele Projekte zu inzestuösen Veranstaltungen, weil die eigentlichen Endanwender aussen vor bleiben. Nein, eine echte Lösung hab ich keine - denn gerade bei freien Projekten ist nunmal die Dokumentationserstellung ein oftmals nerviger und unbeliebter Anteil des Projektes und wird deshalb stiefmütterlich behandelt. Ausserdem sind die meisten Programmierer sowieso nicht in der Lage allgemeinverständliche Dokumentationen zu erstellen. Vielleicht ist das aber auch eine Chance für Projekte, die versuchen die Aktivität in Grossprojekten der Open Source Gemeinschaft auf bisher geringer beteiligt sind. Mir fällt da spontan debian-women ein (da Jutta sich damit derzeit beschäftigt). Denn einer stärkeren Beteiligung von Frauen wäre sicherlich auch Dokumentation und Information die nicht unbedingt einen vollausgebildeten Master-Hacker voraussetzt hilfreich. Schliesslich hat nicht jeder Mensch Lust sein ganzes Leben auf das Lernen von immer neuen APIs und Tools zu verwenden ... Hier gibts den Originalartikel.

Kaktusmilbe

112-640-640.jpeg

Zum Grössenvergleich: der Abstand zwischen den Mitten der beiden dunkleren Balken beträgt einen Millimeter! Das Bild ist nicht optimal scharf, da ich das ganze relativ primitiv aufnehmen musste - zum Beispiel hatte ich zum Zeitpunkt der Aufnahme keinen Makro-Einstellschlitten für die Fokussierung, sondern musste das manuell machen. Trotzdem schon beeindruckend was mit relativ geringem Aufwand an Bild herumkommen kann.

Python Community Server

Muensterland.org arbeitet mit dem Python Community Server, daher hier mal ein bischen was von mir dazu. Der Python Community Server ist eine Open Source Implementation des xmlStorageSystem. Hierbei handelt es sich um ein Protokoll auf der Basis von XML-RPC zur Speicherung von statischen Inhalten. Im Prinzip ist der Python Community Server also nichts weiter als ein Webserver mit einem etwas eigenwilligen Upload-Protokoll und ein paar wenigen vorgefertigten CGI's - es gibt Kommentare auf Artikel, es gibt ein Mailformular und ein paar einfache Möglichkeiten eine Website als RDF-Channel zu abonnieren.

Was soll das ganze also? Wieso der Hype über dieses Tool? Wirklich interessant wird es erst durch den Einsatz von Radio UserLand als Client. Denn xmlStorageSystem ist das Protokoll das Radio Userland als Hintergrundsystem benutzt. Darüber werden also die Radio Communities gesteuert.

Radio Userland ist eine Kombination eines News-Aggregators, eines Website-Designers und eines Weblogger-Tools. Der News-Aggregator sammelt News im Internet ein und bietet sie lokal an. Der Benutzer kann dann einzelne Artikel rüberposten in sein eigenes Weblog. Ausserdem kann er mit recht mächtigen Funktionen seine Websitegestaltung vereinfachen. Das besondere an Radio Userland ist, das es im Prinzip eine lokale Website auf dem Rechner des Benutzers ist. Und von dieser Website kann eine Replikation auf andere Server stattfinden. Das kann über Standardprotokolle wie das Blogger-API geschehen (hier werden nur die Weblog-Inhalte transportiert, das Layout bleibt beim Serverbetreiber), über FTP (hier werden statische HTML-Exporte erstellt, im Prinzip ist Radio dann nur ein überdrehtes Mirror-Script, interaktive Features sind demnach stark eingeschränkt) und eben über xmlStorageSystem. Und hier schliesst sich wieder der Kreis zum Python Community Server, denn dieser ist nichts weiter als die Implementation des letzteren.

Es gibt übrigens auch für Linux ein Tool, aber dieses ist eher an den klassischen Weblogger Tools orientiert und bietet keine weitergehenden Layout-Tools an, wie es Radio Userland macht. Und natürlich gibt es jetzt ja auch den Python Desktop Server, der im Prinzip wie Radio funktioniert. Er ist für fast jede Posix-Plattform auf der Python läuft verfügbar.

Ansonsten: einfach mal ein Weblog hier registrieren und einsetzen. Und das ganze mal ausprobieren. Muensterland.org ist bis auf weiteres frei, jeder kann dort ein Weblog aufsetzen. Es ist - an der Domain leicht erkennbar - natürlich primär für die Region Münsterland gedacht, aber auch andere können teilnehmen. Gibt ja auch Exil-Münsteraner

Vergleich von Rollei 6008 und Hasselblad System

Da ich gerade gesehen habe das jemand mit der Suche nach "Vergleich Rollei und Hasselblad" hier vorbeigekommen ist, habe ich mir überlegt, warum ich eigentlich eine Rollei 6008 habe und keine Hasselblad. Zur M6 würde die Hasselblad wesentlich besser passen - beide mechanisch. Die Rollei dagegen ist ein Hightech-Monster. Ok, ein Grund war das die Rollei da stand im Fenster und der Preis gut war, klar. Aber ich hätte sie ja auch stehen lassen können und auf eine Hasselblad warten. Warum also Rollei?

Für mich ist die Rollei in vielen Dingen die Krone der Entwicklung von Kameras mit manuellem Fokus. Ich wüsste nicht mehr viel was man da noch reinstecken könnte. Die Rollei hat eine ganze Reihe von Besonderheiten gegenüber vielen anderen MF-Kameras. An vorderster Stelle steht da die Belichtungsmessung auch bei Lichtschacht. Allerdings ist es das nicht alleine, sondern auch die Art wie die Belichtung gemessen und gesteuert wird. Genau so habe ich mir das immer vorgestellt: eine freie Wahl der Belichtungsmessart, beliebig kombinierbar mit Blendenautomatik, Zeitautomatik oder manueller Nachführmessung. Ok, eine Programmautomatik für den hektischen Einsatz hat sie auch noch. Einfach die Einstellungen auf Automatik stellen, die automatisch sein sollen - steht Blende und Zeit auf Automatik, ist es eine Programmautomatik. Kein alberner Moduswahlschalter.

Dazu kommen natürlich noch die weiteren Features der Rollei, die mich überzeugt haben: eingebauter Motor (der ist nicht schnell, aber er ist eingebaut und dadurch kompakt). Das Rollo an den Magazinen ist ebenfalls eine klasse Sache, damit gibt es keine verlorenen Schieber mehr. Der lange Filmweg bei den Magazinen hilft gegen das lästige Filmplanlageproblem der klassischen Hasselblad und Zenzamagazine. Die elektronische Übermittlung der Empfindlichkeit vom Magazin an die Kamera macht den Magazinwechsel mit verschiedenen Empfindlichkeiten praktikabel und schnell.

Und dann hat die Rollei natürlich noch die "Kür-Features": die 1000stel Sekunde bei den PQS-Objektiven zum Beispiel. Die rein elektronische Übertragung der Signale, was selbst mit den neuen AF-Objektiven keine Änderung im Bajonett erforderte. Die absolut klasse Zeiss-Rechnungen, die wirklich feine Objektive ergeben - auch wenn ich nur ein einziges Objektiv habe (das 2.8/80 PQS). Und ein robustes Gehäuse hat das ganze auch noch.

Mein Fazit: natürlich hätte eines der grossen Hasselblad-Modelle mit der integrierten Belichtungsmessung und einem zusätzlichen Winder viele der Features der Rollei, aber definitiv nicht alle. Und nicht in dieser sehr angenehm zu bedienenden Form. Und schon garnicht zu dem Gebrauchtpreis den ich bezahlt habe.

Hmm. Ich muss dringend mal wieder mit der Rollei losziehen