Apache Webserver Teil 2

virtuelle Hosts

Ein einzelner Apache ist auch in der Lage mehrere Websites zu hosten. Dabei wird zwischen IP-basierten virtuellen Webhosting und Namensbasierten virtuellen Webhosting unterschieden. Beim IP-basierten virtuellen Webhosting bekommt jeder virtuelle Host eine eigene IP Adresse. Die Netzwerkkarte des Servers muß so konfiguriert werden, daß alle IP Adressen auf diesem Host verfügbar sind. Da IPv4 Adressen ein knappes Gut sind, wird heutzutage meistens namensbasiertes virtuelles Webhosting eingesetzt. Beim namensbasierten virtuellen Webhosting wird eine einzelne IP Adresse konfiguriert, auf der über den Host-Header einer HTTP-Anfrage analysiert wird, für welchen virtuellen Host eine Anfrage ist. Namensbasiertes virtuelles Webhosting und IP-basiertes virtuelles Webhosting lassen sich auch auf dem gleichen Webserver mischen.

Das Laden eines Moduls ist für virtuelles Webhosting nicht nötig. Diese Funktionalität ist Bestandteil des Apache-Kerns. Für virtuelles Webhosting wird eine eigene Containerdirektive benutzt: <VirtualHost>. Alles was zwischen einem <VirtualHost> und einem </VirtualHost> steht, gilt für den jeweiligen einzelnen virtuellen Host. IP basiertes virtuelles Webhosting läßt sich besonders einfach konfigurieren: Es muß nur die IP Adresse in die öffnende <VirtualHost> Anweisung geschrieben werden:

<VirtualHost 192.168.3.12>
...
</VirtualHost>

Dies wird jede Anfrage an die IP Adresse 192.168.3.12 an diesen virtuellen Host leiten. Innerhalb eines virtuellen Hostcontainers muß mindestens das DocumentRoot angegeben werden, damit der Server weiß welche Dokumente für diesen Host ausgeliefert werden sollen. Es ist auch sinnvoll eigene Zugriffslogs anzugeben. Wird dies nicht gemacht, werden Zugriffe über eine global gesetzte Logdatei mitgeloggt. Die meisten Konfigurationsdirektiven können per virtuellen Host spezifiziert werden. Dies gilt logischerweise nicht für die Direktiven Listen, Port und ServerRoot, die nur in einem globalen Kontext Sinn ergeben. Ein vollständiges Beispiel für die Definition eines IP-basierenden virtuellen Hosts:

<VirtualHost 192.168.5.213>
DocumentRoot /var/www/host1
ServerAdmin webmaster@example.net
TransferLog /var/www/logs/host1.log
</VirtualHost>

Namensbasiertes virtuelles Webhosting läßt sich fast genauso konfigurieren, wie IP-basiertes, nur daß zuerst definiert werden muß, auf welcher IP-Adresse namensbasiertes virtuelles Webhosting durchgeführt werden soll. Hierzu existiert die Direktive NameVirtualHost. Dieser Direktive wird die IP Adresse und die Portnummer übergeben, auf der namensbasiertes virtuelles Hosting durchgeführt werden soll. Der Wert muß natürlich mit dem Wert einer Listen Direktive übereinstimmen. Jeder virtuelle Host der für diese IP Adresse konfiguriert wurde, wird dann als namensbasierter virtueller Host behandelt. Innerhalb des virtuellen Hosts muß dann zwingend auch die Direktive ServerName verwendet werden, welche den vollen Hostnamen für diesen virtuellen Host konfiguriert. Ohne namensbasierten virtuellen Webhosting ist diese Direktive optional. Ein vollständiges Beispiel:

NameVirtualHost 192.168.3.50:80
<VirtualHost 192.168.3.50:80>
DocumentRoot /var/www/host2
ServerName example.net
ServerAdmin webmaster@example.net
TransferLog /var/www/logs/host2.log
</VirtualHost>

Namensbasiertes virtuelles Webhosting setzt zwingend einen funktionierenden DNS-Server vorraus. Da die Hosts nur über ihren Namen unterschieden werden, können diese nicht über eine IP-Adresse aufgerufen werden. Sobald virtuelle Hosts definiert wurden, werden alle Anfragen zu den virtuellen Hosts geleitet, ein DocumentRoot welches global definiert wurde, funktioniert dann nicht mehr. Anfragen an einen Hostnamen der in keinem virtuellen Host definiert wurde, werden in den ersten definierten virtuellen Host geleitet.

Soll ein namensbasierter virtueller Host über mehrere Hostnamen aufrufbar sein, kann zusätzlich zu ServerName noch die Direktive ServerAlias verwendet werden. Diese Direktive akzeptiert mehrere durch Leerschritte getrennte Hostnamen, als zusätzliche Hostnamen für diesen virtuellen Host.

Server Side Includes

Server Side Includes (kurz SSI) sind eine einfache Möglichkeit etwas Dynamik in eine Website zu bekommen. SSI ist keine vollständige Scriptsprache wie etwa PHP oder Perl, kann aber verwendet werden, wenn zum Beispiel nur das aktuelle Datum angezeigt werden soll oder eine Menüdatei in mehrere andere HTML-Dateien eingebunden werden soll. SSI ist als Modul direkt in den Apache Webserver integriert, so das keine zusätzliche Software installiert werden muß. Natürlich muß aber zuerst das Modul geladen werden:

LoadModule include_module /usr/lib/apache2/modules/mod_include.so

Damit SSI funktioniert, muß für Apache 1.3 ein Handler konfiguriert werden und zusätzlich mit der Options Direktive SSI zugelassen werden. Ein Handler ist für Apache ein Programm welches bestimmte Dateitypen erst parst um dann das Ergebnis an den Client auszuliefern. Der Handler für SSI wird so konfiguriert:

AddHandler server-parsed shtml

Dies wird alle Dateien, die auf .shtml enden als SSI parsen und erst das Ergebnis an den Client senden. Die Dateiendung .shtml ist üblich für SSI, aber im Prinzip kann auch jede andere Dateiendung benutzt werden. Man kann auch jede auf .html endende Datei als SSI ausführen, wenn ohnehin die gesamte Website SSI benutzt. Zusätzlich zum Handler muß auch der Mimetype für den Client korrekt definiert sein. Da der Mimetype für den Client text/html ist, kann eine zusätzliche AddType Direktive mit folgenden Inhalt benutzt werden:

AddType text/html .shtml

Seit Apache 2.0 ist das Include Modul als Filter, statt als Handler implementiert. Filter bieten seit Apache 2.0 eine weitreichende Möglichkeit Daten bei der Ein- oder Ausgabe zu verändern und zu manipulieren. Seit Apache 2.0 wird daher SSI über die Direktive AddOutputFilter konfiguriert. Die Konfiguration könnte ab Apache Version 2.0 so aussehen:

AddType text/html .shtml
AddOutputFilter INCLUDES .shtml

Diese Anweisungen konfigurieren die grundsätzliche Verwendbarkeit von SSI, aber SSI muß auch zugelassen werden. Dies erledigt der Wert Includes für die Options Direktive. Dabei kann die Options Direktive auch innerhalb eines <Directory> Containers stehen, wodurch dann SSI nur für dieses Verzeichnis mit seinen Unterverzeichnissen erlaubt wird. Dadurch kann flexibel für bestimmte virtuelle Hosts oder Verzeichnisse SSI erlaubt oder verboten werden. Die vollständige Konfiguration von SSI könnte daher (für Apache 2) zum Beispiel so aussehen:

AddType text/html .shtml
AddOutputFilter INCLUDES .shtml
Options Include

Da über SSI auch CGI-Scripts und Shell-Befehle ausgeführt werden können, lässt sich mit Options IncludesNoExec zwar SSI erlauben, aber ohne die Ausführung von Scripts oder Shellbefehlen zu erlauben. Um Nutzern in einer shared Webhosting Umgebung SSI zu erlauben ohne ihnen jedoch die Möglichkeit zur Codeausführung zu geben, kann daher die Konfiguration auch so aussehen:

AddType text/html .shtml
AddHandler server-parsed .shtml
Options IncludeNoExec

Daneben existiert noch eine komplett andere Methode SSI zu konfigurieren. Mit der Direktive XBitHack kann konfiguriert werden, daß jede Datei mit dem Mimetype text/html, welche das Ausführrecht (x-Bit) für den Besitzer gesetzt hat, als SSI geparst wird. Eine entsprechende Konfiguration könnte so aussehen:

XBitHack on
Options Includes

SSI Anweisungen werden direkt in HTML-Dokumente eingefügt und sehen wie HTML-Kommentare aus. Sie haben die folgende generelle Syntax:

<!--#element attribute=value attribute=value -->

Da die Syntax identisch ist, mit einem HTML-Kommentar wird dieser Text, sofern SSI nicht richtig funktioniert, nicht vom Browser angezeigt, ist dann allerdings in der Quelltextansicht zu sehen. Sofern SSI korrekt arbeitet, wird diese Anweisung durch das entsprechende Ergebnis ersetzt.

Durch SSI kann das aktuelle Datum oder das Veränderungsdatum des Dokumentes angegeben werden:

Heute ist: <!--#echo var="DATE_LOCAL" -->
Dieses Dokument wurde zuletzt am <!--#flastmod file="index.html" --> geändert.

Die entsprechenden Zeitangaben können durch eine vorangehende Konfigurationsanweisung für SSI auch anders formatiert werden. Die Syntax dafür ist:

<!--#config timefmt="%A %B %d, %Y" -->

Die Formatierungszeichen entsprechen dabei denen der auf jedem Unixsystem vorhandenen strftime Funktion (siehe man strftime). Möchte man zum Beispiel das Format 2-stelliger Tag, 2-stelliger Monat, 2-stelliges Jahr, getrennt durch Punkte verwenden, sähe das so aus:

<!--#config timefmt="%d.%m.%y" -->

Häufig werden SSI auch eingesetzt um auf jeder Seite ein Menü welches überall gleich ist einzufügen:

<!--#include virtual="menu.html" -->

Dies wird die Datei menu.html aus dem gleichen Verzeichnis an diese Stelle des HTML-Dokumentes einbinden. Die selbe Technik kann natürlich auch verwendet werden um auf jeder Seite eine stets gleiche Fusszeile anzuzeigen.

Die folgende Tabelle beschreibt alle möglichen SSI-Elemente, mit ihren Attributen:

SSI Elemente und Attribute
ElementAttributBedeutung
configerrmsgDer Wert enthält eine Fehlermeldung die an den Client gesendet wird, wenn es beim parsen zu einem Fehler kommt.
sizefmtBestimmt wie Dateigrößen ausgegeben werden. Möglich sind bytes, welches die Größe immer in Byte angibt oder abbrev wodurch die Größe je nach Bedarf in Megabyte oder Kilobyte angegeben wird.
timefmtDer Wert ist eine Formatierungsanweisung für Zeitangaben. Es werden dabei die selben Formatangaben verwendet wie für die Unixfunktion strftime.
echovarGibt den Wert der angegebenen Variable aus.
execcgiDies führt das angebene Script als CGI-Script aus. Erwartet wird eine Pfadangabe. Das Ziel wird auch dann als CGI verarbeitet wenn es der Server normalerweise nicht tun würde. Lediglich die Ausführung von Scripts für den angebenen Pfad muß erlaubt sein. Diese Anweisung steht nicht zur Verfügung, wenn IncludesNoExec, statt Includes verwendet wird.
cmdDie angegebene Zeichenkette wird mit /bin/sh ausgeführt. Es muß sich also um einen Shellbefehl handeln. Diese Anweisung steht nicht zur Verfügung wenn IncludesNoExec statt Includes benutzt wird.
fsizefileGibt die Größe des angebenen Dokumentes aus. Der Wert ist ein relativer Pfad.
virtualGibt die Größe des angegebenen Dokumentes aus. Der Wert wird zu einer vollständigen Url übersetzt.
flastmodwie bei fsizeGibt das Änderungsdatum des angegebenen Dokumentes aus.
includefileFügt ein anderes Dokument an dieser Stelle eines Dokumentes ein. Der Wert muß ein relativer Pfad sein.
virtualFügt ein anderes Dokument an dieser Stelle eines Dokumentes ein. Der Wert wird zu einer vollständigen Url aufgelöst (auch wenn er als relativer Pfad angegeben wurde). Diesem Attribut sollte gegenüber file der Vorzug gegeben werden, da hierdurch das Schachteln mehrerer include Elemente ermöglicht wird. Auch CGIs können hierdurch ausgeführt werden, allerdings nur wenn der Server das Script auch sonst als CGI ausführen würde. Hierdurch kann im Gegensatz zu exec kein Schaden verursacht werden.
setvarDefiniert eine Variable. Muß zusammen mit value verwendet werden.
valueDefiniert den Wert einer Variablen. Muß immer zusammen mit var verwendet werden.
printenvGibt alle Umgebungsvariablen aus. Dieses Element besitzt keine Attribute.

SSL

SSL sichert die Kommunikation zwischen dem Webserver und einem Client über Verschlüsselung ab. Die Grundlagen der Kryptografie werden in einem eigenen Artikel erklärt. Ebenso existiert auch ein Artikel über die Erstellung einer CA mit OpenSSL, welcher auch einige Befehle von OpenSSL zum Umgang mit Zertifikaten erläutert. Um SSL nutzen zu können muß als erstes ein Schlüsselpaar aus privaten Schlüssel und öffentlichen Zertifikat erstellt werden. Für öffentliche Webserver, muß das öffentliche Zertifikat durch eine anerkannte CA (Certificate Authority) unterschrieben werden. Die Zertifikate der CAs sind in Browsern hinterlegt. Nimmt ein Browser Kontakt mit einem Server auf, dessen Zertifizierungsstelle er nicht kennt, erscheint eine Warnmeldung, die dem Benutzer sagt, das der Website nicht vertraut wird. Daher muß das Zertifikat von einer CA ausgestellt sein, welche in allen großen Browsern als Zertifizierungsstelle anerkannt ist.

Für Testzwecke sowie bei Intranetservern, kann auch ein selbstunterschriebenes Zertifikat oder eine eigene interne CA verwendet werden. Das Zertifikat einer eigenen internen CA, kann genauso wie auch ein selbstunterschriebenes Zertifikat in Clients des eigenen Netzwerkes importiert werden. Beispielsweise in Firefox kann ein Zertifikat über den Zertifikatsmanager importiert werden welcher sich in den Firefox-Einstellungen unter Erweitert, Reiter Verschlüsselung findet. Klickt man dort auf den Button "Zertifikate anzeigen" öffnet sich der Zertifikatsmanager, der in folgenden Bild zu sehen ist:

Zertifikatsmanager des Firefox

Soll ein Zertifikatsrequest erstellt werden, welcher anschließend durch eine CA unterschrieben wird, kann folgender openssl Befehl verwendet werden:

openssl req -new -nodes -days 365 -keyout privkey.pem -out certreq.req

Das Kommando req für openssl erstellt einen Zertifikatsrequest. Die Option -new erzeugt ein neues Schlüsselpaar und die Option -nodes läßt den privaten Schlüssel unverschlüsselt, was meist für ein Serverzertifikat gewünscht ist. Die Option -days gibt eine Gültigkeitsdauer für das Zertifikat an. Es werden einige Fragen gestellt, welche in das Zertifikat aufgenommen werden, wie etwa nach Land, Staat und Organisation. Wichtig ist hier, daß der angegebene Wert für CommonName mit dem Hostnamen des Servers übereinstimmt, für den das Zertifikat ist. Stimmen diese Werte nicht überein, wird im Browser eine Warnmeldung ausgeben, daß das Zertifikat nicht zum aufgerufenen Hostnamen paßt. Anschließend erhält man die beiden Dateien privkey.pem und certreq.req. Die Datei privkey.pem ist der private Schlüssel des Servers. Diese Datei ist unbedingt geheim zu halten und darf den Server nie verlassen. Gerät die Datei in fremde Hände ist, das Zertifikat wertlos. Die Datei sollte daher auch durch restriktive Rechte geschützt werden (chmod 600 und Besitzer root). Die Datei certreq.req ist der Zertifikatrequest, welcher durch eine CA unterschrieben werden muß. Diese Datei muß der CA gegeben werden. Von der CA wird man anschließend das unterschriebene Zertifikat zurück bekommen, welches zusammen mit dem privaten Schlüssel auf dem Server hinterlegt werden muß.

Soll stattdessen (zum Beispiel für Testzwecke) ein selbstunterschriebenes Zertifikat verwendet werden, kann folgender Befehl verwendet werden:

openssl req -x509 -new -nodes -days 365 -keyout privkey.pem -out cert.pem

Die zusätzliche Option -x509 sorgt dafür, daß ein selbstunterschriebenes Zertifikat statt eines Zertifikatsrequests erstellt wird. Die entstehende Datei cert.pem ist das selbstunterschriebene Zertifikat.

In Apache ist das SSL-Modul erst seit Apache 2.0 enthalten. Für Apache 1.3 mußte extern ein Modul nachgerüstet werden. Der In OpenBSD enthaltene Apache welcher auf Apache 1.3 beruht, beinhaltet jedoch bereits das SSL-Modul. Die Konfiguration für den OpenBSD Apache ist daher weitestgehend identisch mit dem hier beschriebenen Vorgehen für Apache 2, wobei nicht mal das SSL Modul geladen werden muß, da dieses im OpenBSD Apache fest einkompiliert ist. Unter Apache 2 wird das SSL Modul so geladen:

LoadModule ssl_module /usr/lib/apache2/modules/mod_ssl.so

Um nun SSL im Apache zu konfigurieren, muß ein zusätzlicher virtueller Host erstellt werden, wenn der Server zusätzlich auch ohne SSL erreichbar bleiben soll. Außerdem muß der Server auch auf Port 443 lauschen, welches der übliche Port für https ist. Daher wird eine zusätzliche Listen Direktive benötigt:

Listen 80
Listen 443

Durch den zusätzlichen Port, kann der virtuelle Host ein Portbasierender virtueller Host sein. Portbasierendes virtuelles Webhosting funktioniert genauso, wie IP basiertes virtuelles Webhosting, nur daß statt der IP Adresse die Portnummer heran gezogen wird um Anfragen für die unterschiedlichen Hosts zu identifizieren. Der entsprechende <VirtualHost> Container könnte daher zum Beispiel so aussehen:

<VirtualHost *:443>

Normalerweise kann Apache SSL nicht für verschiedene namensbasierte Hosts auf dem gleichen Server anbieten. SSL-Hosts benötigen eine eigene IP Adresse. Dies liegt an der Natur des SSL Protokolls, welches bereits beim Verbindungsaufbau ein Zertifikat übermitteln muß, noch bevor der Client mitgeteilt hat, für welchen Hostnamen die Anfrage ist. Durch eine Erweiterung des SSL Protokolls namens Server Name Indication (SNI) ist solch eine Konfiguration jedoch möglich geworden. Apache 2.2 unterstützt SNI, sofern die verwendete OpenSSL Version SNI ebenfalls unterstützt (ab OpenSSL 0.9.8f).

Die eigentlichen Konfigurationsdirektiven für SSL sind SSLEngine, SSLCertificateFile und SSLCertificateKeyFile. Mit SSLEngine wird SSL für einen Host eingeschaltet. Mit SSLCertificateFile wird die Datei mit dem Zertifikat angegeben. Mit SSLCertificateKeyFile wird die private Schlüsseldatei angegeben. Ein virtueller Host mit SSL könnte daher zum Beispiel so definiert sein:

<VirtualHost *:443>
DocumentRoot /var/www/sslhost
ServerAdmin webmaster@example.net
TransferLog /var/www/log/sslhost.log
SSLEngine On
SSLCertificateFile /etc/ssl/certs/cert.pem
SSLCertificateKeyFile /etc/ssl/private/privkey.pem
</VirtualHost>

SSL funktioniert nun mit dieser Konfiguration bereits, jedoch sollte trotzdem noch ein Problem gelöst werden: Die Performance ist so noch nicht optimal. SSL bedeutet einen zusätzlichen erheblichen Rechenaufwand für den Server. Jede einzelne Anfrage benötigt einen vollständigen SSL-Handschlag, inklusive validierung des Zertifikats. Da ein Client meist gleich mehrere Dokumente nacheinander beim Server anfragt (zum Beispiel HTML-Dokument plus mehrerer darin enthaltener Bilder), wäre es effektiver den SSL-Handschlag für mehrere hintereinander folgende Anfragen des selben Clients nur einmal durchführen zu müssen. Apache bietet diese Möglichkeit über einen SSL-Session-Cache. Konfiguriert wird dieser mit der Direktive SSLSessionCache. Als Wert für SSLSessionCache muß der Typ des Caches und ein Pfad angegeben werden. Gängige Typen sind dbm, welches eine Datenbankdatei auf der Festplatte für den Inhalt des Caches erzeugt, oder shm welches RAM-Speicher benutzt um die Session Daten abzulegen und hierdurch sehr schnell ist. Die dbm Variante kann so genutzt werden:

SSLSessionCache dbm:/var/run/sslcache.dbm

Nach dbm: muß der Pfad auf die Datenbankdatei angegeben werden. Die Variante mit shared Memory (shm, im RAM) ist unter hoher Last vorzuziehen, da sie schneller ist. Auch bei dieser Variante muß nach shm: ein Pfad angegeben werden, gefolgt von der Größe in Byte:

SSLSessionCache shm:/var/run/sslcache(512000)

Das SSL-Modul für Apache 1.3 unterstützt die Variante für shared Memory nicht. Diese Direktive muß außerhalb einer <VirtualHost>-Definition in der globalen Serverkonfiguration stehen.

Mit SSLProtocol kann eingegrenzt werden, welche SSL-Versionen vom Server unterstützt werden. Hierbei können folgende Versionen genutzt werden: SSLv2, SSLv3 und TLSv1. Mit dem Schlüsselwort all (welches der Standardwert ist) werden alle Protokollversionen zugelassen. Durch ein vorangestelltes Minus- oder Pluszeichen kann eine Version explizit hinzu gefügt oder weggelassen werden. Da SSL Version 2 inzwischen als unsicher gilt, kann mit folgender Anweisung nur noch SSLv3 und TLSv1 zugelassen werden:

SSLProtocol all -SSLv2

Zu SSL existieren noch einige weitere Konfigurationsanweisungen zum Beispiel zum verifizieren von Clientzertifikaten. Die hier vorgestellten reichen jedoch aus um einen Server mit SSL zu betreiben.

CGI

CGI steht für Common Gateway Interface und ist eine Methode über die Scripts, als Webseiten verwendet werden können. Hierdurch werden interaktive Websites, bzw. Webanwendungen möglich. CGIs sind nicht an eine bestimmte Scriptsprache gebunden. Grundsätzlich kann jede auf dem Webserver verfügbare Scriptsprache verwendet werden. Üblich sind vor allem Perl, Python und Ruby, aber auch zum Beispiel Shellscripts können als CGI-Programm verwendet werden.

CGI ist im Apache als Modul realisiert. Sofern das Modul nicht statisch einkompiliert wurde, muß es daher erst geladen werden:

LoadModule cgi_module /usr/lib/apache2/modules/mod_cgi.so

Nachdem das CGI-Modul geladen wurde, muß der Webserver nur noch wissen welche Dateien er als CGI-Programm ausführen soll. Dafür gibt es 2 Methoden: Die einfachste Methode funktioniert über die Direktive ScriptAlias. ScriptAlias funktioniert ähnlich wie Alias welches ein Verzeichnis außerhalb des DocumentRoot unter einem virtuellen Verzeichnisnamen freigibt. Bei ScriptAlias weiß der Webserver zusätzlich, daß alle Dateien die in diesem Verzeichnis liegen als CGI-Programm ausgeführt werden sollen.

ScriptAlias /cgi-bin/ /var/www/cgi-bin/

In diesem Beispiel liegt /var/www/cgi-bin außerhalb des DocumentRoot. Wenn ein Client die Url http://www.example.net/cgi-bin/script.cgi aufruft, würde die Datei /var/www/cgi-bin/script.cgi aufgerufen werden. Grundsätzlich wird jede Datei (egal welche Endung sie hat) in diesem Verzeichnis als ausführbares CGI-Programm betrachtet. Die CGI-Programme selbst müssen jedoch auch ausführbar gemacht werden, also mit chmod das x-Bit bekommen.

Diese Methode erlaubt gerade in shared-Webhosting Umgebungen eine gute Kontrolle darüber welche Benutzer CGI-Programme ausführen dürfen, da dies über den Zugriff auf das entsprechende Verzeichnis in dem die CGI-Programme abgelegt werden geregelt werden kann. Außerdem ist diese Methode einfach zu konfigurieren. Über die andere Methode der Konfiguration von CGIs lassen sich CGI-Programme in beliebigen Verzeichnissen ausführen. In diesem Fall muß dem Apache Webserver über eine bestimmte Dateiendung signalisiert werden, daß es sich um ein ausführbares CGI-Programm handelt. Hierfür wird ein Handler benutzt:

AddHandler cgi-script .cgi

Dies sorgt dafür, daß der Webserver alle Dateien mit der Endung .cgi als CGI-Programme betrachtet. Es ist auch möglich mehrere Endungen als CGI-Programme zu definieren:

AddHandler cgi-script .cgi .pl

Dies wird sowohl Dateien mit der Endung .cgi als auch Dateien mit der Endung .pl als CGI-Programme definieren.

Zusätzlich müssen CGI-programme in den jeweils gewünschten Verzeichnissen erlaubt werden. Dies erledigt die Options Direktive. Bei der Methode über die Direktive ScriptAlias ist dies nicht nötig.

<Directory /var/www/htdocs>
Options +ExecCGI
</Directory>

Dies wird CGI-Programme im Verzeichnis /var/www/htdocs und allen Unterverzeichnissen erlauben. Beide Methoden funktionieren auf diese Weise sowohl unter Apache 1.3, als auch unter Apache 2.0/2.2.

CGI-Programme unterscheiden sich durch 2 Umstände von anderen Programmen: Zum einen geben sie HTML-Code aus, welcher vom Client interpretiert wird und zum zweiten müssen sie einen HTTP-Header mitsenden. Wichtig am HTTP-Header ist die Angabe des Mimetypes. Ohne diese Angabe kann der Client nicht wissen, das es sich um HTML-Code handelt, der Server sendet dies nicht automatisch mit. Natürlich ist man bei einem CGI-Script nicht darauf festgelegt HTML auszugeben, jedoch ist dies das übliche und meistgenutzteste. Es ist auch möglich zum Beispiel über ein CGI-Programm dynamisch eine PNG-Grafik zu erstellen und an den Client auszugeben. In diesem Fall müßte natürlich auch der Mimetype image/png und nicht text/html sein. Ein einfaches Beispiel in Perl:

#/usr/bin/perl -w
print "Content-type: text/html\n\n";
print "Hello World";

Dieses kleine Perl-CGI wird im Client nur "Hello World" ausgeben. Wichtig ist das senden des Mimetypes als HTTP-Header. Hierzu wird einfach der String Content-type: text/html an den Client gesendet. Der HTTP-Header wird immer durch eine Leerzeile abgeschlossen. Leerzeilen werden in der Ausgabe dieses Scripts über \n erzeugt. Die erste Zeile des Scripts ist die Shebang Zeile die den Scriptinterpreter angibt, in diesem Fall der Perlinterpreter.

PHP

PHP ist eine serverseitige Scriptsprache. PHP gehört nicht zum Umfang des Apache, sondern ist eine externe Erweiterung. PHP kann in den Apache als Modul oder als CGI eingebunden werden. Am performantesten arbeitet es als Modul, weshalb oft diese Möglichkeit vorgezogen wird. Ist PHP installiert muß in der Apachekonfiguration zur Aktivierung des Modules lediglich das Modul geladen werden und der Mimetype für PHP-Dateien gesetzt werden:

LoadModule php5_module /usr/lib/apache2/modules/libphp5.so
AddType application/x-httpd-php .php

Für die Verwendung von PHP im Apache muß Apache mit dem Prefork MPM laufen, da PHP Probleme hat mit Threads innerhalb eines Prozesses umzugehen.

PHP Code wird vermischt mit HTML Code in eine PHP Datei geschrieben. Dabei wird alles was außerhalb von einem PHP-Tag (<?php ?>) steht als HTML interpretiert. PHP Code muß daher immer zwischen dem öffnenden <?php und dem schließenden ?> stehen.

PHP hat eine eigene Konfigurationsdatei namens php.ini in der alle für PHP relevanten Einstellungen stehen. Diese Datei ist je nach System in einem unterschiedlichen Pfad zu finden, wie etwa /etc/php oder /var/www/conf. Im folgenden werden einige besonders wichtige PHP Konfigurationswerte erklärt. Eine vollständige Liste aller möglichen Konfigurationswerte findet sich in der PHP Dokumentation.

Konfigurationswerte in der php.ini
Optionmögliche WerteBedeutung
short_open_tagOn oder OffBestimmt ob statt <?php auch die Kurzform <? als PHP-Tag verwendet werden kann
safe_modeOn oder OffDer Safe Mode auferlegt der Ausführung von PHP Scripts diverse Sicherheitsbeschränkungen und ist für shared Webhostingumgebungen gedacht. Dieser Modus wird nicht mehr empfohlen, ist seit Version 5.3 als deprecated gekennzeichnet und wird in PHP 6 entfernt werden.
disable_functionsAuflistung von PHP Funktionen, getrennt durch KommaDiese Option verbietet die Nutzung der aufgelisteten Funktionen. Dies ist eine Sicherheitsmassnahme mit der man gefährliche Funktionen verbieten kann. Auf einem Produktivserver kann die folgende Funktionsliste verwendet werden: exec, passthru, shell_exec, system, proc_open, popen, curl_exec, curl_multi_exec, parse_ini_file, show_source.
max_execution_timeZeit in SekundenDiese Option definiert wie lange ein einzelnes Script maximal laufen darf. Lang laufende Scripts könnten auf einen Programmierfehler hinweisen (zum Beispiel Endlosschleife).
max_input_timeZeit in SekundenMaximale Zeit die ein Script dafür verwenden darf Eingabedaten zu parsen.
memory_limitBytegrößeMaximale Speichermenge die ein Script verwenden darf. Statt Byte können auch Kilobyte mit dem Suffix K oder Megabyte mit dem Suffix M verwendet werden.
error_reportingE_ERROR, E_WARNING, E_NOTICE, E_ALL und weitere Konstanten (Liste aller verfügbaren Werte)Definiert welche Arten von Fehlern als Fehler angezeigt werden. E_ALL zeigt zum Beispiel alle Fehler, während E_ERROR nur fatale (zum Abbruch führende Fehler) anzeigt, aber keine Laufzeitnotizen oder Warnungen. Für Entwicklungsumgebungen ist E_ALL sinnvoll, während in Produktivumgebungen der Standardwert E_ALL & ~E_NOTICE (schließt nur Laufzeitnotizen, wie etwa nicht gesetzte Variablen aus) sinnvoll ist. Das Tilde-Zeichen dient der Negierung.
display_errorsOn oder OffGibt an ob Fehlermeldungen sichtbar beim Aufruf einer Seite angezeigt werden sollen. Es ist sinnvoll diesen Wert in Produktivumgebungen (aus Sicherheitsgründen) auf Off zu setzen, in Entwicklungsumgebungen jedoch auf On.
log_errorsOn oder OffGibt an ob Fehler auch in einer Logdatei festgehalten werden.
error_logsyslog oder DateinameGibt den Speicherort für eine Logdatei an. Mit syslog werden Logmeldungen über den Logdienst des Systems geloggt, ansonsten muß ein Dateiname mit Pfad angegeben werden. Wenn dieser Wert nicht gesetzt ist und PHP als Apache Modul läuft werden Fehler in den Error-Log des Apache geschrieben.
register_globalsOn oder OffDieser Wert definiert ob Werte die per GET oder POST ins Script kommen direkt als Variablen zur Verfügung stehen oder nicht. Das Einschalten dieser Option kann zu Sicherheitsproblemen führen sofern Scripts nicht sehr umsichtig programmiert sind.
post_max_sizeBytegrößeMaximale Größe die POST-Daten haben dürfen. Wenn das Suffix K verwendet wird in Kilobyte und wenn das Suffix M verwendet wird in Megabyte. Dieser Wert muß größer sein als der Wert für upload_max_filesize.
magic_quotes_gpcOn oder OffMagic Quotes escapen bestimmte Zeichen, die in Datenbanken eine Sonderfunktion haben. Dies soll vor SQL-Injection Angriffen schützen. magic_quotes_gpc werden auf Get, POST und Cookie Daten angewendet. Die Wirkung ist die selbe wie die der PHP Funktion addslashes. Magic Quotes sollten nicht mehr benutzt werden, da es bessere Datenbankspezifische Funktionen gibt, die vor SQL-Injection schützen. magic_quotes_gpc sind seit PHP 5.3 als deprecated gekennzeichnet.
default_mimetypeMime-typesDieser Wert sollte im Regelfall auf text/html stehen. Hiermit wird der ausgelieferte Mime-Type für den Client beschrieben. Da PHP im Regelfall HTML ausgibt, sollte auch der Mime Type auf text/html stehen. Werden Grafiken über PHP ausgegeben (zum Beispiel PNG oder GIF) kann der Mime Type abweichend in der einzelnen Datei selber angegeben werden.
file_uploadsOn oder OffBestimmt ob Dateiupload per PHP möglich sind.
upload_tmp_dirVerzeichnispfadPfad unter dem temporär hochgeladene Dateien gespeichert werden. Muß auf einen gültigen Pfad für temporäre Dateien gesetzt werden, damit Uploads funktionieren.
upload_max_filesizeBytegrößeMaximale Größe von Dateien die hochgeladen werden. Kann auch das Suffix K für Kilobyte oder M für Megabyte haben.
allow_url_fopenOn oder OffBestimmt ob mit der Funktion fopen, mit der normalerweise Dateien geöffnet werden auch Urls geöffnet werden können. Dies sollte aus Sicherheitsgründen ausgeschaltet sein.

Außer den globalen Optionen stehen in der php.ini auch einige Optionen die spezifisch für bestimmte PHP Module (Erweiterungen) sind. Die folgende Tabelle beschreibt einige wichtige Optionen in der php.ini für die PHP Erweiterungen MySQL, MySQLi und Session.

php.ini Konfigurationswerte für einige Module
Optionmögliche WerteBedeutung
MySQL-Erweiterung
mysql.allow_persistentOn oder OffBestimmt ob persistente Verbindungen auf die MySQL DB erlaubt sind.
mysql.max_persistentZahlMaximale Anzahl von persistenten Verbindungen pro Prozess. -1 meint keine Beschränkung.
mysql.max_linksZahlMaximale Anzahl aller (persistenten und nicht-persistenten) Verbindungen pro Prozess. -1 meint keine Beschränkung.
mysql.default_portPortnummerPortnummer, die für Verbindungen zum MySQL Server benutzt werden soll. Wird dieser Wert frei gelassen wird entweder der Wert der Umgebungsvariablen MYSQL_TCP_PORT oder ein einkompilierter Defaultwert verwendet.
mysql.default_socketPfad auf eine SocketdateiLegt die Socketdatei fest, über die sich PHP mit MySQL verbinden soll. Wird der Wert frei gelassen, wird ein in MySQL eingebauter Wert verwendet.
mysql.connect_timeoutZeit in SekundenVerbindungstimeout
MySQLi-Erweiterung
mysqli.max_linksZahlMaximale Anzahl von Verbindungen zum MySQL-Server. -1 meint keine Beschränkung.
mysqli.default_portPortnummerTCP-Portnummer über die sich PHP mit MySQL verbindet. Wird dieser Wert frei gelassen, wird entweder der Wert der Umgebungsvariablen MYSQL_TCP_PORT oder ein einkompilierter Defaultwert verwendet.
mysqli.default_socketPfad auf SocketdateiSocketdatei für die Verbindung zum MySQL Server.
mysqli.reconnectOn oder OffSoll die Verbindung automatisch neu aufgebaut werden, wenn sie abbricht?
Session-Erweiterung
session.save_pathVerzeichnispfadPfad in dem auf dem Server Session-Dateien abgelegt werden. Falls dies ein Verzeichnis mit Leserechten für alle Serverbenutzer ist, können Serverbenutzer Sessions entführen.
session.use_cookies0 oder 1Aktiviert (1) oder deaktiviert (0) die Benutzung von Cookies auf dem Client für Sessions.
session.use_only_cookies0 oder 1Mit dem Wert 1 wird aktiviert, daß Sessions Clientseitig nur noch Cookies verwenden (was die unabsichtliche Übermittlung von Session-Ids verhindern soll).
session.cookie_httponly0 oder 1Mit 1 wird das Cookie so gesetzt, daß es nicht per Javascript, sondern nur durch HTTP-Anfragen übermittelt werden kann. Dies soll Session-Id Diebstahl per XSS Angriffen vorbeugen.
session.gc_maxlifetimeZeit in SekundenMaximale Zeit für welche gespeicherte Session-Daten gültig sind. Danach werden diese vom Garbage Collector aufgeräumt.

Apache als Proxy

Ein Proxy ist ein Mittler, der Verbindungen zwischen 2 Rechnern vermittelt. Proxys werden eingesetzt um den Webzugriff zu beschleunigen oder um Sicherheitspolicies durchzusetzen. Proxys existieren für verschiedene Anwendungsprotokolle wie HTTP, FTP, IMAP und SMTP. Socks-Proxys sind generische TCP Proxys, die beliebige TCP Verbindungen weiterleiten. Apache kann als Proxy für HTTP und FTP verwendet werden. Es existiert auch spezielle Proxyserver-Software, wie etwa Squid, welche für diesen Zweck mehr Funktionen bietet als Apache.

Grundsätzlich existieren 2 Arten von Proxys: Reverse Proxys und Forwarding Proxys. Forwarding Proxys werden in Firmen-LANs eingesetzt um allen Arbeitsplätzen einen kontrollierbaren Internetzugriff zu gewähren. Clients müssen so konfiguriert werden, daß diese den Proxyserver benutzen. HTTP-Anfragen werden dann an den Proxyserver gestellt, der dann den Zielserver kontaktiert und das angefragte Dokument herunterlädt und an den Client weiterreicht. Für den Zielserver erscheint es so, daß die Verbindung direkt vom Proxyserver kommt und nicht vom eigentlichen Client. Ein derartiger Proxyserver muß so abgesichert werden, daß nur die gewünschten Clients Verbindungen aufbauen dürfen, damit der Proxy nicht für das gesamte Internet offen ist. Ein für das gesamte Internet offener Proxy, kann schnell für kriminelle Zwecke mißbraucht werden, um die Herkunft von Verbindungen die mit kriminellen Absichten aufgebaut werden, zu verschleiern. Die Eingrenzung welche Clients den Proxy benutzen dürfen, kann entweder über IP-Adressen erfolgen, oder über eine Benutzerauthentifizierung.

Reverse Proxys sind Proxys die vor einem oder mehreren bestimmten Webservern stehen und den einkommenden Netzwerkverkehr an diesen speziellen Server weiterleiten. Nach außen erscheinen diese Proxys wie normale Webserver. Sie werden oft eingesetzt um Zugriff auf interne Webserver von außen zu ermöglichen oder auch zur Lastverteilung. In diesem Fall muß der Proxy nicht speziell abgesichert werden, da es im allgemeinen gewünscht ist, daß alle Clients zugreifen können und kein Zugriff auf beliebige, sondern nur auf die konfigurierten Zielserver möglich ist.

Desweiteren kann zwischen Cachenden und nicht Cachenden Proxys unterschieden werden. Cachende Proxys halten Inhalte die angefragt wurden in einem lokalen Cache vor um wiederholte Anfragen nach dem gleichen Inhalt schneller bedienen zu können. Apache kann durch ein zusätzliches Modul auch als Cachender Proxy arbeiten. Das hierfür notwendige Modul ist erst seit Apache 2.2 als stabil deklariert.

Um Proxyfunktionalität im Apache zu erhalten muß das Modul mod_proxy geladen werden. Dieses Modul existierte auch schon in der Version 1.3. Seit Version 2.0 ist die Funktionalität dieses Moduls teilweise auf verschiedene weitere Module verteilt worden. Das Modul mod_proxy wird jedoch in jedem Fall für Proxy-Funktionalität benötigt. Zusätzlich müssen noch die für das jeweilige Protokoll benötigten Module geladen werden. Hierfür existieren Module für die Protokolle HTTP, FTP und für die CONNECT Methode von HTTP, welche für Proxys die auch SSL-Verbindungen unterstützen sollen, benötigt wird. Folgendes lädt neben dem eigentlichen Proxy Modul auch noch das meistens verwendete HTTP Modul:

LoadModule proxy_module /usr/lib/apache2/modules/mod_proxy.so
LoadModule proxy_http_module /usr/lib/apache2/modules/mod_proxy_http.so

Apache als Proxyserver wird am häufigsten in der Form eines Reverse Proxys eingesetzt. Ein Reverse Proxy ist einfach zu konfigurieren: Mit der Direktive ProxyPass können virtuelle URLs auf einen Zielserver abgebildet werden:

ProxyPass /webapp/ http://intern.webserver.de/

In diesem Beispiel werden alle Anfragen die in das virtuelle Verzeichnis /webapp/ gehen sollen, auf den Server intern.webserver.de weitergeleitet. Sollte der Proxy zum Beispiel unter proxy.firma.de erreichbar sein, würde die URL http://proxy.firma.de/webapp/foo/ nach http://intern.webserver.de/foo/ weitergeleitet werden.

Im Prinzip ist das bereits alles was für einen Reverse Proxy benötigt wird, aber es bleibt ein Problem: Wenn es zu Umleitungen auf dem Server kommt, werden vom Zielserver Pfade an den Client übermittelt, die für diesen so nicht auflösbar sind, da diese die Umleitung über den Proxy nicht berücksichtigen. Aus diesem Grund gibt es eine Direktive im Apache die Adressen in derartigen Umleitungen übersetzt:

ProxyPassReverse /webapp/ http://intern.webserver.de/

Mit ProxyPassReverse werden Umleitungen des Servers übersetzt. Obiges Beispiel ist die zugehörige Adressumsetzung für die vorher gesetzte ProxyPass-Direktive.

Werden mehrere ProxyPass-Direktiven verwendet, werden diese in der angegebenen Reihenfolge verarbeitet, wobei die erste passende Regel angewendet wird und weitere Regeln nicht mehr beachtet werden. Aus diesem Grund müssen spezifischere ProxyPass Regeln, zum Beispiel für spezielle Unterverzeichnisse, die anders behandelt werden sollen, vor den allgemeineren Regeln stehen.

Apache kann auch als Forwarding Proxy eingesetzt werden. Soll der Apache sowohl als Forwarding Proxy wie auch als normaler Webserver eingesetzt werden, sollte die Proxy-Konfiguration in einen VirtualHost-Container erfolgen. In diesem Fall werden Anfragen an den entsprechenden VirtualHost als Proxy-Anfragen behandelt, während Anfragen an andere VirtualHosts normal behandelt werden. Der virtuelle Host muß dazu entweder ein IP-basierter virtueller Host sein, oder er muß sich über die Portnummer unterscheiden. Sollen zum Beispiel Anfragen an den Port 8080 als Proxyanfragen behandelt werden, könnte folgende Konfiguration benutzt werden:

Listen 8080
<VirtualHost _default_:8080>
ProxyRequests On
<Proxy *>
Order allow,deny
Allow from 192.168.0.0/24 127.0.0.1
</Proxy>
</VirtualHost>

Ein Portbasierter virtueller Host entspricht im wesentlichen einem IP basierten virtuellen Host, nur daß die Unterscheidung der Hosts über die Portnummer, statt der IP Adresse erfolgt. Durch den Eintrag _default_ im VirtualHost-Container gilt dieser Eintrag für alle IP Adressen die nicht durch andere VirtualHost-Container belegt sind. Durch den zusätzlichen Listen Eintrag wird der Apache auch auf Port 8080 lauschen. Alle Anfragen an Port 8080 werden dann durch die Konfiguration dieses virtuellen Hosts geleitet. Die Direktive ProxyRequests On schaltet generell den Forwarding Proxy ein. Wird diese Direktive außerhalb eines virtuellen Hosts gesetzt, arbeitet der Apache ausschließlich nur noch als Forwarding Proxy. Diese Direktive ist nicht notwendig für Reverse Proxys.

Zusätzlich wird noch der Zugriff auf den Proxy auf bestimmte Hosts eingeschränkt. Hierzu wird ein <Proxy>-Container verwendet. Alle Konfigurationen die in einem <Proxy>-Container stehen gelten für alle Proxy-Requests. Dadurch läßt sich ein solcher Container beispielsweise für die Zugriffskontrolle nutzen. Dabei können sowohl Hostbasierte Zugriffsregeln verwendet werden (wie in diesem Beispiel), als auch eine Benutzerauthentifizierung. Ein Forwarding Proxy sollte keinesfalls eingeschaltet werden ohne vorher entsprechende Zugriffsregeln definiert zu haben, da sonst ein offener Proxy konfiguriert wurde, der zum Mißbrauch einlädt.

Die <Proxy>-Container-Direktive erwartet als Argument eine Url. Die Konfiguration innerhalb des Containers wird dann auf Content von der entsprechenden Zielurl angewendet. Dabei können auch die Wildcardzeichen ? und * verwendet werden. Im Beispiel wird die Konfiguration durch das Wildcardzeichen * auf alle Urls angewendet. Dadurch ist es auch möglich bestimmte Zielurls nur für bestimmte Clients zuzulassen oder zu verbieten. Soll zum Beispiel die Url http://www.example.com/ nur von dem Client mit der IP Adresse 192.168.4.11 zugänglich sein, ließe sich folgendes konfigurieren:

<Proxy http://www.example.com/
Order allow,deny
Allow from 192.168.4.11
</Proxy>

Sollen bestimmte Websites oder Begriffe in Urls generell gesperrt sein, läßt sich hierfür die Direktive ProxyBlock einsetzen:

ProxyBlock warez.com badserver adserver.com

Dieses Beispiel blokiert Zugriffe auf die beiden Domains warez.com und adserver.com, sowie alle Domains in denen das Wort badserver vorkommt (wie zum Beispiel badserver.com oder badserver.de). Die Direktive erwartet eine durch Leerschritte getrennte Liste von Domains, Hosts und Wörtern.

Soll der Forwarding Proxy auch als Cachender Proxy arbeiten, muß zusätzlich das Modul mod_cache, sowie mindestens eines der Module mod_mem_cache und/oder mod_disk_cache geladen und konfiguriert werden, was jedoch im Rahmen dieser Unterlagen nicht behandelt wird. Diese Module sind erst seit Apache 2.2 als stabil deklariert.

Webdav

Webdav macht einen HTTP-Server zum Fileserver. Webdav ist eine Erweiterung des HTTP-Protokolls, um Dateien auf einem HTTP-Server hochladen, löschen und verändern zu können. Webdav hat daher viele Auswirkungen auf die Sicherheit. Webdav sollte erst aktiviert werden nachdem der Server abgesichert wurde.

Im Apache 2 wird Webdav durch mehrere Module bereit gestellt. Das Modul mod_dav ist für die generelle Erweiterung des HTTP-Protokolls zuständig. Das Modul mod_dav_fs ist ein Backend Provider für mod_dav, welches den Umgang mit Dateien im Dateisystem ermöglicht. Generell ist es auch möglich andere Backends als mod_dav_fs für mod_dav zu verwenden, jedoch ist dies das meistgenutzteste und im Apache 2 enthalten.

Um Webdav nun nutzen zu können müssen zuerst beide Module geladen werden:

LoadModule dav_module /usr/lib/apache2/modules/mod_dav.so
LoadModule dav_fs_module /usr/lib/apache2/modules/mod_dav_fs.so

Anschließend muß Webdav innerhalb eines <Directory>- oder <Location>-Containers aktiviert werden:

<Location /foo>
Dav On
</Location>

Die Direktive Dav On aktiviert Webdav für den entsprechenden Bereich.

Prinzipiell funktioniert Webdav damit bereits, jedoch müßen noch einige Aspekte berücksichtigt werden: Zum einen ist es sinnvoll ein Locking einzurichten damit sich konkurierende Dateizugriffe nicht gegenseitig überschreiben. Hierzu existiert Die Option DavLockDB, welche durch das Modul mod_dav_fs implementiert wird. Diese Option erwartet einen Dateipfad auf eine Lockingdatenbankdatei, welche erstellt wird. Die Option darf nicht innerhalb einer Containerdirektive stehen, sondern muß in der globalen Serverkonfiguration vorkommen:

DavLockDB /var/db/apache-dav-lock

Außerdem muß irgend eine Form von Zugriffsschutz für das Webdav Verzeichnis existieren, da sonst jeder darauf Dateien anlegen und löschen kann. Hierfür eignet sich der bereits vorher besprochene Verzeichnisschutz. Da bei der Basic Authentifizierung das Passwort unverschlüsselt übertragen wird, sollte entweder die Digest Authentifizierung gewählt werden oder eine Basic Authentifizierung unter zusätzlicher Verwendung von SSL.

Als letztes muß noch beachtet werden, daß das Verzeichnis, welches für Webdav freigegeben wurde für den Apache Webbenutzer schreibbar sein muß. Dateien, die per Webdav geschrieben werden, werden mit den Rechten des Webservers geschrieben. Daher benötigen die Verzeichnisse entsprechende Rechte um das schreiben und löschen zu ermöglichen.

Literatur

Online-Dokumentation des Apache Projektes.

Andrew Ford, Sascha Kersken: Apache - kurz und gut. O'Reilly, 2007.


René Maroufi, dozent (at) maruweb.de
Creative Commons By ND