Schlagwort-Archive: Apache

phpmyadmin sicher(er) zur Verfügung stellen

Möchte man phpmyadmin mehreren Domains zur Verfügung stellen, so bietet sich dafür das Debian-Paket „phpmyadmin“ an:

1
apt-get install phpmyadmin

Danach kann man unter /etc/phpmyadmin/apache.conf eine config finden, die man per

1
ln -s /etc/phpmyadmin/apache.conf /etc/apache2/conf.d/phpmyadmin.conf

in das Apache-Verzeichnis einbindet.

Jetzt muss an hier aber noch ein wenig gefeilt werden, denn so stellt man phpmyadmin grundsätzlich auch über HTTP – also unverschlüsselt – zur Verfügung. Administriert jetzt jemand über ein öffentliches Netzwerk seine Datenbank (immer an den DAU denken, auch wenn es sich erst mal vollkommen an den Haaren herbeigezogen anhört), hat man ruck-zuck ein Problem.

Deshalb leite ich grundsätzlich alle HTTP-Anfragen an phpmyadmin einfach nach HTTPS weiter. Dazu öffnet man die /etc/phpmyadmin/apache.conf und fügt ganz oben folgenden Text ein:

1
2
3
4
5
6
7
8
9
<IfModule mod_rewrite.c>
        <IfModule mod_ssl.c>
                <Location ~ "/phpmyadmin(/|$)">
                        RewriteEngine on
                        RewriteCond %{HTTPS} off
                        RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI}
                </Location>
        </IfModule>
</IfModule>

Jetzt wird jeder Besucher von http://domain.de/phpmyadmin zu https://domain.de/phpmyadmin umgeleitet, was phpmyadmin ein ganzes Stück sicherer macht. Trotzdem ist zu überlegen, ob man die Software nicht lieber zusätzlich mit einem .htaccess-Schutz versieht. Wie das geht, erfährt man unter anderem bei SELFHTML.

Nodejs-Server via Apache-Reverse-Proxy und Monit

Um einen Nodejs-Server aufzusetzen braucht es nicht viel. Eigentlich nur die Binaries und eine JS-Datei mit dem eigentlichen Server. Doch wenn man den Server dauerhaft und bequem zugreifbar betreiben möchte, sind ein paar mehr Schritte notwendig.

Zunächst wird Nodejs und der Paketmanager npm installiert:

1
apt-get install nodejs npm

Danach richten wir den Proxy ein, sonst wäre ein Aufruf nur über einen Nicht-Standard-Port (80 ist ja schon vom Apachen belegt) möglich. In der Regel verwendet Nodejs hier Port 3000, aber das ist je nach Script unterschiedlich. Wir aktiviren also zunächst den Passthrough-Modus:

1
2
a2enmod proxy
a2enmod proxy_http

Für die eigentliche Einrichtung kann jetzt ein eigener VHost (z.B. eine Subdomain) angelegt werden, oder – wie in meinem Beispiel – ein Unterverzeichnis hergenommen werden.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<VirtualHost ip:80>
	ServerName   myhost.com:80
	ServerAlias  www.myhost.com
	UseCanonicalName Off
	DocumentRoot /var/www/vhosts/myhost/httpdocs
	ErrorLog  /var/www/vhosts/myhost/statistics/logs/error_log
 
	...
 
	<Directory /var/www/vhosts/myhost/httpdocs>
		Options -Includes +ExecCGI
	</Directory>
 
	<Proxy *>
  		Order deny,allow
  		Allow from all
	</Proxy>
 
	<Location /mynodejspath>
  		ProxyPass http://localhost:3000
  		ProxyPassReverse http://localhost:3000
	</Location>
</VirtualHost>

Würde man nun nodejs manuell starten, könnte man schon per http://myhost.com/mynodejspath darauf zugreifen. Da Nodejs aber noch ein wenig fehleranfällig ist und dann gern mal sang- und klanglos abtritt, benutze ich monit zur Überwachung und ggf. zum Neustart des Servers (der Nodejs-App).

1
apt-get install monit

Als Startscript verwende ich ein Standard-init.d-Script, sodass ich auch beim Systemstart problemlos den Nodejs-Server mitstarten lassen kann. Dabei ist zu beachten, dass ich auf Benutzer „root“ prüfe und den Nodejs-Server ggf. über einen extra User starte. Andere Nutzer starten die Nodejs-App mit diesem Script als sie selbst.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#!/bin/bash
DIR=/var/www/vhosts/myhost/httpdocs/mynodejspath
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
NODE_PATH=/usr/lib/node_modules
NODE=/usr/bin/node
 
test -x $NODE || exit 0
 
function start_app {
  if [ $(whoami) = "root" ]
  then
    NODE_ENV=production nohup sudo -u nodejsusername "$NODE" "$DIR/app.js" 1>>"$DIR/logs/node.log" 2>&1 &
    echo $! > "$DIR/pids/node.pid"
  else
    NODE_ENV=production nohup "$NODE" "$DIR/app.js" 1>>"$DIR/logs/node.log" 2>&1 &
    echo $! > "$DIR/pids/node.pid"
  fi
}
 
function stop_app {
  kill `cat $DIR/pids/node.pid`
}
 
case $1 in
   start)
      start_app ;;
    stop)
      stop_app ;;
    restart)
      stop_app
      start_app
      ;;
    *)
      echo "usage: myapp {start|stop|restart}" ;;
esac
exit 0

Die Verzeichnisse „pids“ und „logs“ müssen unter dem App-Pfad noch angelegt werden. Danach kommt das Monit-Script nach /etc/monit/noderc:

1
2
3
4
5
6
7
8
9
10
#!monit
set logfile /var/log/monit.log
 
check process nodejs with pidfile "/var/www/vhosts/myhost/httpdocs/mynodejspath/pids/node.pid"
    start program = "/etc/init.d/myapp start"
    stop program  = "/etc/init.d/myapp stop"
    if failed port 3000 protocol HTTP
        request /
        with timeout 10 seconds
        then restart

… und ein Starter-init.d Script nach /etc/init.d/monit:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/bin/bash
 
case $1 in
   start)
      monit -d 60 -c /etc/monit/noderc
      ;;
    stop)
      killall monit
      ;;
    restart)
      stop_app
      start_app
      ;;
    *)
      echo "usage: monit {start|stop|restart}" ;;
esac
exit 0

Ein Symlink nach rc2.d komplettiert die Automatisierung:

1
2
cd /etc/rc2.d/
ln -s ../init.d/monit S99monit

 

Somit haben wir viele Probleme gelöst:

  • Nodejs startet mit dem Server
  • Es wird überwacht und falls notwendig neu gestartet
  • Der Jeweilige Besitzer (und root) können es manuell neu starten
  • Es läuft mit eingeschränkten Userrechten
  • Es ist über Port 80 zugreifbar
  • und wir haben wieder etwas gelernt 😉

Lange Zend DB-Queries bringen PHP/Apache zum Absturz

Beim Programmieren einer Anwendung mit Zend und IBM DB2 bin ich auf ein Phänomen gestoßen, das zunächst unerklärlich schien. Führte ich einen langen Query aus (in diesem Fall sollte ein längerer XML-String in ein XML-Datenfeld der IBM DB2 Express-C geschrieben werden), so stürzte Apache ohne Fehlermeldung ab. In den Logs tauchte nur „restarting“ auf, sonst nichts.

Nach etwas Recherche stellte sich heraus, dass wohl eine fehlerhafte Implementierung der PCRE-Erweiterung Schuld an dem Desaster ist. Ist Backtracking auf einen zu hohen Wert eingestellt, so konsumiert PCRE (Zend Framework benutzt preg_match(), um SQL-Queries auf Korrektheit zu prüfen) den gesamten Stack, wodurch der Prozess sang- und klanglos abgeschossen wird.

Eine Erklärung zu PCRE-Backtracking gibt es hier: http://www.regular-expressions.info/catastrophic.html.

Für PHP ist die Lösung relativ simpel, der voreingestellte Wert von 100.000 für pcre.backtrack_limit und pcre.recursion_limit muss (entgegen einiger Aussagen in diversen Bugreports und Tutorials) heruntergesetzt werden. In meiner PHP.ini sieht das zurzeit folgendermaßen aus:

1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
[Pcre]
;PCRE library backtracking limit.
; http://php.net/pcre.backtrack-limit
pcre.backtrack_limit=1000
 
;PCRE library recursion limit.
;Please note that if you set this value to a high number you may consume all
;the available process stack and eventually crash PHP (due to reaching the
;stack size limit imposed by the Operating System).
; http://php.net/pcre.recursion-limit
pcre.recursion_limit=1000

Damit laufen meine Queries (~1800 Zeichen pro Query) anstandslos durch. Bei Bedarf kann dieser Wert auch weiter abgesenkt werden, auf 500 zum Beispiel. Da die PCRE-Funktion einen Aufwand von O(2^n) hat, bringt auch diese relativ kleine Änderung wieder recht viel.

Gitlab aufsetzen unter Debian/Ubuntu mit Apache

github.com ist ein großartiges Tool für Open Source Projekte auf der ganzen Welt. Es stellt einfachen Zugriff auf das git-Versionierungssystem zur Verfügung, ohne dass man sich um die Befehle für das Erstellen und Warten eines solchen Repos Gedanken machen müsste.

Das ist toll, allerdings hat github einen Nachteil: für private Repositories – also solche, bei denen der Code nicht der ganzen Welt zur Verfügung steht – werden (relativ hohe) Gebühren fällig. Somit ist dieses Tool für kommerzielle Zwecke nur bedingt geeignet, falls man nicht die „Plans“ (github: Plans & Pricing) in Anspruch nehmen möchte.

Lange Zeit gab es keine Softwarelösung, die man sich auf dem eigenen Server installieren konnte, und die ähnlichen Komfort wie github zu bieten hatte. Doch seit einiger Zeit etabliert sich eine solche Lösung: Gitlab. Das Tool steht github kaum noch in etwas nach und kann kostenfrei auf (fast) jedem Rootserver betrieben werden:

  • Ubuntu Linux
  • Debian/GNU Linux
  • Fedora
  • CentOs
  • RedHat.

Zur Installation reichen je nach Komplexität der Installation einige wenige Schritte aus, mehr Komfort gibt es – wie immer – mit mehr Schritten. Doch zunächst das Basis-Setup:

Wer ein paar Basis-Programme nicht installiert hat, muss dies zuerst nachholen:

1
sudo apt-get install wget git git-core build-essential libyaml-dev

Als Nächstes laden wir das Installscript von gitlab herunter und installieren es, was uns viel Arbeit erspart. Es ist dabei von Vorteil, noch kein RubyOnRails installiert zu haben (falls Ruby schon installiert ist, muss es zwingend Ruby 1.9.2 oder besser sein). Ansonsten muss das Script angepasst werden, die Befehle sind relativ selbsterklärend. Hier nun die Variante mit Script (Download-Adresse aktualisiert 19.10.2012):

1
2
3
wget https://raw.github.com/gitlabhq/gitlab-recipes/master/install/debian_ubuntu.sh
chmod +x debian_ubuntu.sh
./debian_ubuntu.sh

Alternativ sind Schritte 1-4 dieser Anleitung auszuführen.

Nun setzen wir die Komponenten für den Ruby-Server auf:

1
2
3
4
5
6
7
sudo gem install charlock_holmes
sudo pip install pygments
sudo gem install bundler
cd /home/gitlab
sudo -H -u gitlab git clone git://github.com/gitlabhq/gitlabhq.git gitlab
cd gitlab
sudo -u gitlab cp config/gitlab.yml.example config/gitlab.yml
1
2
3
sudo -u gitlab cp config/database.yml.sqlite config/database.yml
ODER
sudo -u gitlab cp config/database.yml.example config/database.yml

Nachdem die Datenbank-Konfiguration kopiert wurde, müssen bei MySQL die Verbindungsparameter angepasst werden. SQLite läuft sofort „out of the box“.

Im folgenden Schritt initialisieren und konfigurieren wir Gitlab für den Ruby-Server, anschließend testen wir die Umgebung:

1
2
3
sudo -u gitlab -H bundle install --without development test --deployment
sudo -u gitlab bundle exec rake gitlab:app:setup RAILS_ENV=production
sudo -u gitlab bundle exec rake gitlab:app:status RAILS_ENV=production
1
2
3
4
5
6
7
8
9
10
11
12
Starting diagnostic
config/database.yml............exists
config/gitlab.yml............exists
/home/git/repositories/............exists
/home/git/repositories/ is writable?............YES
remote: Counting objects: 603, done.
remote: Compressing objects: 100% (466/466), done.
remote: Total 603 (delta 174), reused 0 (delta 0)
Receiving objects: 100% (603/603), 53.29 KiB, done.
Resolving deltas: 100% (174/174), done.
Can clone gitolite-admin?............YES
UMASK for .gitolite.rc is 0007? ............YES

Der Output des Testbefehls sollte keine „failed“- oder „no“- Einträge haben. Falls alle Zeichen auf „grün“ stehen können wir den Server starten.

1
2
sudo -u gitlab bundle exec rails s -e production -d
./resque.sh

Der Server läuft nun und ist über Port 3000 bereits zugreifbar (nicht wundern, der erste Seitenaufruf dauert ziemlich lange). Wem das reicht, der ist bereits fertig.

Ich selbst mag meine Web-Anwendungen alle über HTTP(S) abrufen, so auch gitlab. Dazu nutze ich nicht die vom Tutorial vorgeschlagene Variante nginx + Unicorn sondern Passenger für Apache (weil Apache bei mir für alle Webanwendungen genutzt wird und Unicorn viel zu umständlich für kleine bis mittlere Installationen ist):

1
2
gem install passenger
passenger-install-apache2-module

Bei der Installation einfach den Anweisungen folgen, es sind ja nicht viele. Danach kann ein VirtualHost für gitlab angelegt werden, wichtig dabei: DocumentRoot muss auf das „public“-Verzeichnis von gitlab zeigen! Schon kann man z.B. über http(s)://gitlab.yourdomain.com auf gitlab zugreifen und loslegen.

Wichtig: Passenger ersetzt quasi den Deploy-Prozess von gits bundle exec, d.h. der Server auf dem Port 3000 muss dann nicht mehr laufen. Wenn man die Wartezeit für den ersten Besucher von gitlab nach dem Serverstart verkürzen will kann man den Apache wie folgt konfigurieren:

1
2
3
4
5
6
7
8
9
PassengerPreStart https://git.yourdomain.com/
 
<VirtualHost ip:443 >
        ServerName git.yourdomain.com
        UseCanonicalName Off
        DocumentRoot /home/gitlab/gitlab/public
 
        PassengerMinInstances 1
        ...

Als optionalen Schritt kann man nun noch ein Shell-Script für init.d schreiben, damit man resque nach einem Neustart nicht immer von Hand aufrufen muss:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#! /bin/bash
### BEGIN INIT INFO
# Provides:          gitlab
# Required-Start:    $local_fs $remote_fs $network $syslog redis-server
# Required-Stop:     $local_fs $remote_fs $network $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: resque service for gitlab
# Description:       resque service for gitlab
### END INIT INFO
 
NAME=resque
DESC="resque service"
RESQUE_PID=/home/gitlab/gitlab/tmp/pids/resque_worker.pid
 
case "$1" in
  start)
        CD_TO_APP_DIR="cd /home/gitlab/gitlab"
        START_RESQUE_PROCESS="./resque.sh"
 
        echo -n "Starting $DESC: "
        if [ `whoami` = root ]; then
          sudo -u gitlab sh -l -c "$CD_TO_APP_DIR > /dev/null 2>&1 && $START_RESQUE_PROCESS"
        else
          $CD_TO_APP_DIR > /dev/null 2>&1 && $START_RESQUE_PROCESS
        fi
        echo "$NAME."
        ;;
  stop)
        echo -n "Stopping $DESC: "
        kill -QUIT `cat $RESQUE_PID`
        echo "$NAME."
        ;;
  restart)
        echo -n "Restarting $DESC: "
        kill -USR2 `cat $RESQUE_PID`
        echo "$NAME."
        ;;
  reload)
        echo -n "Reloading $DESC configuration: "
        kill -HUP `cat $RESQUE_PID`
        echo "$NAME."
        ;;
  *)
        echo "Usage: $NAME {start|stop|restart|reload}" >&2
        exit 1
        ;;
esac
 
exit 0

Diese Datei speichern wir unter /etc/init.d/resque und machen es mit

1
sudo chmod +x /etc/init.d/resque

ausführbar. Danach noch einen symbolischen Link nach /etc/rc2.d/ anlegen

1
2
cd /etc/rc2.d
ln -s /etc/init.d/resque S99resque

Fertig.

Achtung: Das von Gitlab angelegte Admin-Passwort ist immer gleich, d.h. es ist zu empfehlen, sich einen eigenen Account mit Administrationsrechten anzulegen und den Admin-Account dann zu blocken, oder das Passwort des Admin-Accounts zu ändern.

Hosting absichern: Apache2 mit ITK-MPM

Für Multiuser-Environments wie eine Hosting-Umgebung stellt sich spätestens ab einer gewissen Userzahl die Frage nach der Absicherung bzw. Abschottung der einzelnen User voneinander. Dafür gibt es verschiedene Varianten, die allesamt unterschiedliche Vor- und Nachteile haben.

Stuart Herbert hat sich dankenswerter Weise schon vorher die Mühe gemacht, die 5 gängigsten Varianten (suphp, mpm-peruser, mpm-itk, PHP + FastCGI und suexec + PHP + FastCGI) einem Vergleich zu unterziehen. Zwar sind die durchgeführten Performance-Tests mit Vorsicht zu genießen (sie spiegeln in keiner Weise ein realitätsnahes Szenario wieder und übersteigern die Performanceverluste erheblich), aber die Artikel vermitteln dennoch ein sehr gutes Bild der jeweiligen Verfahren und ihrer Stärken und Schwächen.

Ich habe mich für meinen Server schlussendlich für mpm-itk entschieden, da es sehr flexibel ist, alle Vorteile von mod_php weiterhin genutzt werden können und der zusätzliche Speicher- und CPU-Bedarf nicht ganz so extrem ist wie bei mpm-peruser. Zudem setzt die Sicherung im Gegensatz zu den fcgi-Varianten hier bereits im Apachen – also an der richtigen Stelle – an. Die Performance-Einbußen sind, anders als der Test es vermuten lassen würde, im Praxisbetrieb sehr moderat.

Um Apache2 mit ITK-MPM einzurichten bedarf es – vorausgesetzt Apache2 ist bereits installiert – unter Ubuntu/Debian nur eines kurzen Befehls:

apt-get install apache2-mpm-itk

Ein vermutlich vorhandenes anderes MPM (Multi-Processing Module) (standardmäßig dürfte das apache2-mpm-prefork sein) wird bei der Installation automatisch entfernt. Es kann also immer nur ein MPM gleichzeitig tätig sein, was ja auch logisch ist.

Nach dem automatisch durchgeführten Apache-Neustart ist das Modul sofort einsatzbereit. Um Gebrauch davon zu machen fügt man in den VirtualHost-Eintrag folgendes ein:

<VirtualHost *:80>
  ServerName www.example.com
  ...
 
  <IfModule mpm_itk_module>
    AssignUserId username groupname
    MaxClientsVHost 50
    NiceValue 10
  </IfModule>
</VirtualHost>

Alle Angaben sind optional. Wird kein Username zugewiesen läuft die jeweilige Domain unter dem Standard-User und der Standard-Gruppe (normalerweise www-data). Der MaxClients-Eintrag ermöglicht es (wie der Name schon sagt), die maximale Anzahl Clients für diesen spezifischen VHost festzulegen. Mit dem nice-Parameter kann man schlussendlich die Prozesspriorität pro VHost regeln (negative Werte = Priorität niedriger als normal).

Update: Es ist wichtig, jedem vHost auch wirklich eine User-Id zuzuordnen. Anderenfalls wird die Id von Apache genommen, was idR dann root ist („Note that if you do not assign a user ID, the default one from Apache will be used.“ lautet der lapidare Hinweis auf der Projektwebsite).

Trafficbeschränkung mit mod_cband unter Debian/Ubuntu

Jeder, der auf seiner Website Downloads anbieten möchte kommt irgendwann zu der Frage „Wie beschränke ich den Traffic“. In Zeiten der Flatrates denk man auf Userseite nur selten an Traffic-Limits. Doch viele Server und vServer haben noch begrenzten Traffic. Und selbst wenn nicht möchte man vielleicht die gleichzeitigen Downloads beschränken oder verhindern, dass Download-Traffic andere (interaktive) Inhalte ausbremst.

Hier kann das Apache-Modul mod_cband (http://codee.pl/cband.html) zum Einsatz kommen. Es ist schlank, sehr performant und bietet eigentlich alles, was man braucht (u.a. VHost-abhängige Limitierung, Limit pro User, Limitierung maximal zugelassener Connections, Ausbremsen oder Umleiten nach Erreichen des Limits etc.).

Um mod_cband zu installieren geht man wie folgt vor:

Als Erstes wird das Paket apache2-prefork-dev (und ggf. noch weitere benötigte Pakete) installiert:

1
apt-get install apache2-prefork-dev gcc build-essential

Danach holt man sich von der Entwicklerwebsite die neueste Version (zzT. 0.9.7.5) von mod_cband, entpackt das heruntergeladene Archiv in ein beliebiges Verzeichnis und führt configure aus:

1
2
3
4
wget http://cband.linux.pl/download/mod-cband-0.9.7.5.tgz
tar xf mod-cband-0.9.7.5.tgz
cd mod-cband-0.9.7.5
./configure

Jetzt muss man eine Änderung am Makefile vornehmen, wenn man ein 64-Bit-System hat. Die Zeile
APXS_OPTS=-Wc,-Wall -Wc,-DDST_CLASS=3
muss um den Parameter lm erweitert werden:
APXS_OPTS=-lm -Wc,-Wall -Wc,-DDST_CLASS=3

Speichern und das Modul kompilieren und installieren:
make && make install

Wenn alles glatt gegangen ist hat man jetzt in /usr/lib/apache2/modules/ eine funktionsfähige mod_cband.so liegen. Diese muss nun noch geladen werden. Dazu nimmt man sich /etc/apache2/httpd.conf oder die /etc/apache2/apache2.conf vor und fügt folgende Zeilen ein:

1
2
3
LoadModule cband_module /usr/lib/apache2/modules/mod_cband.so
CBandScoreFlushPeriod 1
CBandRandomPulse On

Die Config-Parameter sind übrigens für eine gute Performace wichtig. Wer sich gut auskennt kann natürlich auch die Verzeichnisse mods-available und mods-enabled nutzen 😉

 

Als Letztes kommen wir nun zu den Beschränkungseinstellungen selbst. Diese kann man sowohl serverübergreifend (in httpd.conf oder apache2.conf) als auch VirtualHost-basiert vornehmen. Ich habe mich für Variante 2 entschieden, weil ich für meine Downloads die extra Subdomain downloads eingerichtet habe.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<VirtualHost *:80>
  ServerName downloads.yourdomain.com
  ServerAdmin webmaster@yourdomain.com
  DocumentRoot /var/www/downloads
  CBandSpeed 50Mbps 10 30
  CBandLimit 100G
  CBandExceededSpeed 5Mbps 5 15
  CBandScoreboard /var/run/yourdomain.scoreboard
  CBandPeriod 4W
  <Location /cband-status>
    SetHandler cband-status
  </Location>
  <Location /cband-status-me>
    SetHandler cband-status-me
  </Location>
</VirtualHost>

Mit diesen Einstellungen beschränkt man das Download-Volumen auf 100GByte in 4 Wochen. Der Speed liegt für alle Downloads zusammen bei 50 MBit/s, es sind 10 Requests pro Sekunde und 30 offene Verbindungen erlaubt. Falls das Volumen überschritten wird drosselt mod_cband den Speed auf 5 MBit/s, die maximal offenen Verbindungen auf 15 und die Requests pro Sekunde auf 5. Die „History“-Daten werden unter /var/run/yourdomain.scoreboard (diese Datei muss mit touch angelegt werden!) gespeichert. Unter /cband-status und /cband-status-me können Server- bzw. Userstatisikdaten abgerufen werden.

Alle verfügbaren Optionen listet http://codee.pl/cband_documentation.html auf, zusammen bilden sie einen Pool, der für fast jede Serversitaution die richtigen Konfiguration ermöglichen sollte.

Apache mit SNI aufsetzen (Debian)

HTTPS ist schön und gut – solange man richtig kräftig dafür zahlt. Denn kostenlose Zertifikate (z.b. bei StartSSL) gibt es nur für eine Domain, Wildcard- oder Multi-Domain-Zertifikate kosten hingegen richtig viel Geld. Nun ist es aber so, dass man eventuell mehrere Domains mit unterschiedlichen Zertifikaten (soll ja kostenlos bleiben) auf einer IP nutzen möchte. Bis vor ein paar Jahren wäre das ein hoffnungsloses Unterfangen gewesen, da bei SSL normalerweise alles außer der IP verschlüsselt übertragen wird, d.h. auch der „Host“-Header, den der Client mit überträgt. Ohne diesen Header ist es dem Server nicht möglich, ein Request einer bestimmten Domain zuzuordnen. Er muss also erst das zur IP gehörende Zertifikat nutzen, um an den verchlüsselten Host-Eintrag zu gelangen. Ergo nur 1 Zertifikat pro IP.

Die Lösung dieses Problems heißt SNI (Server Name Indication) und wird seit ein paar Jahren von allen gängigen Browsern unterstützt:

  • Mozilla Firefox 2.0+
  • Opera 8.0+ (TLS 1.1 muss aktiviert sein)
  • Internet Explorer 7+ (Windows Vista oder neuer)
  • Google Chrome (Windows Vista oder neuer)
  • Safari Safari 3.2.1+ auf Mac OS X 10.5.6+ und Windows (Windows Vista oder neuer)

Durch SNI wird der gewünschte Host als „server_name“-Parameter bereits beim Verbinungsaufbau übergeben – keine Probleme bei der Zuordnung mehr. Das klingt an sich sehr gut, also habe ich mich auf die Suche nach einer Anleitung gemacht und musste feststellen, dass mein Debian Etch nicht die nötige OpenSSL-Version (mindestens 0.9.8f) installiert hat. Aber da gibt es ja noch mod_gnutls, damit soll es ohne Apache oder OpenSSL neu zu kompilieren gehen. Nachdem ich alles eingerichtet hatte dann die Enttäuschung: mod_gnutls funktioniert zwar aber nicht mit den Zertifikaten von StartCom, weil die nämlich „intermediate“ CA-Zertifikate benötigen um verifiziert werden zu können (und mod_gnutls das nicht unterstützt). Somit zeigte er mir immer eine Warnung nach dem Muster „unknown certificate issuer“ an. Toll, das hätte ich auch mit einem selbst signierten Zertifikat hinbekommen.
Nach Überwindung meiner Enttäuschung dachte ich mir dann: Na ja was solls, dist-upgrade wollte ich eh schon lange mal machen also ab zu lenny. Halt… zunächst natürlich noch ein Backup erstellt für den Fall der Fälle.

Alle folgenden Befehle werden als root ausgeführt. Also entweder als root einloggen oder (besser) per su bzw. sudo –s zum root werden.

Danach die /etc/apt/sources.list.d/debian.list bearbeiten:

1
2
3
4
5
6
7
# new lenny packages.
deb http://ftp2.de.debian.org/debian/ lenny main contrib non-free
deb http://security.debian.org/ lenny/updates main contrib non-free
 
# source packages.
deb-src http://ftp2.de.debian.org/debian/ lenny main contrib non-free
deb-src http://security.debian.org/ lenny/updates main contrib non-free

Speichern und

1
2
apt-get update
apt-get dist-upgrade

ausführen. Die Installation hat bei meinem vServer etwa 10 Minuten gedauert. Je nach Anzahl und Umfang der installierten Pakete kann es aber durchaus länger dauern. Die jeweiligen Default-Werte (N) für das beibehalten der Konfiguration sind eigentlich optimal. Ich konnte auch kein Programm feststellen, das ohne neue Config nicht mehr arbeitet. Das wa’s eigentlich schon, jetzt ist aus Etch Lenny geworden und OpenSSL liegt in Version 0.9.8g (Stand: 27.12.09) vor. Damit unterstützt es jetzt von Haus aus SNI. Leider unterstützt Apache in der von Debian Lenny standardmäßig installierten (und wirklich alten!) Version es aber noch nicht. Daher muss hier nochmal Hand angelegt werden. Die Anleitung dazu habe ich bei blog.mellenthin.de gefunden und nur leicht modifiziert, da bereits Apache 2.2.14-4 vorliegt. Die gerade aktuelle Version findet sich immer unter http://packages.debian.org/source/squeeze/apache2 und kann dort auch herunter geladen werden. Zunächst brauchen wir aber noch ein paar Tools (falls sie noch nicht installiert sind):

1
2
apt-get install dpkg-dev build-essential fakeroot libcap2-dev autoconf
apt-get build-dep apache2

Dann den Sourcecode laden:

1
2
3
wget http://ftp.de.debian.org/debian/pool/main/a/apache2/apache2_2.2.14-4.dsc
wget http://ftp.de.debian.org/debian/pool/main/a/apache2/apache2_2.2.14.orig.tar.gz
wget http://ftp.de.debian.org/debian/pool/main/a/apache2/apache2_2.2.14-4.diff.gz

mit dem Befehl

1
dpkg-source -x apache2_2.2.14-4.dsc

wird der Quellcode entpackt und mit

1
2
cd apache2-2.2.14/
dpkg-buildpackage -us -uc -rfakeroot

wird der Kompilier-Vorgang eingeleitet. Der Vorgang selbst kann lange dauern, bei mir waren es über 20 Minuten. Warnings während dieses Prozesses sind normal und können ignoriert werden.
Die fertigen Pakete liegen dann ein Verzeichnis höher zur Installation bereit:

1
2
cd ..
dpkg -i apache2_2.2.14-4_amd64.deb apache2.2-bin_2.2.14-4_amd64.deb apache2.2-common_2.2.14-4_amd64.deb apache2-mpm-event_2.2.14-4_amd64.deb apache2-mpm-itk_2.2.14-4_amd64.deb apache2-mpm-worker_2.2.14-4_amd64.deb apache2-mpm-prefork_2.2.14-4_amd64.deb apache2-suexec_2.2.14-4_amd64.deb apache2-utils_2.2.14-4_amd64.deb

Je nach verwendeter Version und Architektur (x32 oder x64) heißen die Pakete anders, der Namensteil vorn bleibt aber immer gleich. Bei der Installation werden die bestehenden Pakete von Apache durch die neuen 2.2.14er ersetzt, Apache wird dadurch SNI-fähig gemacht.

Jetzt ist es Zeit, den private key und die Zertifikatsanforderung (CSR) zu erzeugen. Das geht ganz einfach und schnell mit openssl:

[Update 2012: StartSSL verlangt nun eine Mindestschlüssellänge von 2048 Bit]

1
2
3
openssl genrsa -des3 2048 > ssl.key.org (hier ist ein Passwort erforderlich)
openssl req -new -key ./ssl.key > ssl.csr
openssl rsa -in ssl.key.org -out ssl.key

Der letzte Befehl schreibt den ungeschützten private key in die Datei ssl.key, das ist notwendig da sonst bei jedem Apache-Start (auch nach Server-Neustart) die Passwortabfrage kommt und der httpd so lange hängt. Daher immer gut auf den ssl.key aufpassen und am besten mittels

1
chmod 400 ssl.key

nur für den root-Account lesbar machen. Den ssl.csr nimmt man dann und übergibt ihn der CA seiner Wahl (bei mir: StartSSL). Diese erzeugt daraus dann die benötigte ssl.crt, welche man ebenfalls auf seinen Server hochladen muss. Damit ist das Zertifikat an sich bereit, nun muss Apache noch konfiguriert werden, es auch zu verwenden.

Falls die Direktive „NameVirtualHost“ noch nicht in der Apache-Config gesetzt ist muss dies nun nachgeholt werden (z.B. in der httpd.conf):

1
NameVirtualHost 196.17.23.52:443

Um zu verhindern, dass nicht-SSI-fähige Browser nur ein 403: forbidden angezeigt bekommen (stattdessen sollte man sie freundlich darauf hinweisen, auch im Interesse ihrer eigenen Sicherheit einen neueren Browser zu verwenden) erstellt man folgenden Eintrag (am besten wieder in der httpd.conf oder in der apache2.conf):

1
SSLStrictSNIVHostCheck off
1
2
3
4
5
6
7
8
<VirtualHost 196.17.23.52:443>
  # Because this virtual host is defined first, it will
  # be used as the default if the hostname is not received
  # in the SSL handshake, e.g. if the browser doesn't support
  # SNI.
  DocumentRoot /www/error/use_new_browser_html
  ServerName www.serverone.tld
</VirtualHost>

Für die einzelnen Adressen, die man jetzt per SSL zugreifbar machen möchte wird nun jeweils ein VirtualHost-Eintrag erstellt:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<VirtualHost 196.17.23.52:443>
   ServerName serverone.tld:443
   DocumentRoot /var/www/vhosts/serverone
   #ErrorLog /usr/local/apache/logs/error_log
   #TransferLog /usr/local/apache/logs/access_log
   SSLEngine on
   SSLProtocol all -SSLv2
   SSLCipherSuite ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM
   SSLCertificateFile /usr/local/apache/conf/ssl.crt
   SSLCertificateKeyFile /usr/local/apache/conf/ssl.key
   SSLCertificateChainFile /usr/local/apache/conf/sub.class1.server.ca.pem
   SSLCACertificateFile /usr/local/apache/conf/ca.pem
   SetEnvIf User-Agent ".*MSIE.*" nokeepalive ssl-unclean-shutdown
   #CustomLog /usr/local/apache/logs/ssl_request_log \
   #   "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
</VirtualHost>

Für die Nutzer von ACP-Oberflächen wie z.B. Plesk ergibt sich hier eine Besonderheit. Die Admin-Systeme überschreiben nämlich gern ihre eigenen Dateien. Bei Plesk werden die Einstellungen für VirtualHosts z.B. in der /var/www/vhosts/serverone/conf/httpd.include liegen. Diese wird aber regelmäßig überschrieben. Als info steht dort, wo man seine Config hinspeichern muss, damit sie dauerhaft erhalten bleibt. Leider kann man dann nur ausgewählte Direktiven setzen (z.B. php-flags), keine vollständigen Einträge (dazu steht übrigens nichts im Handbuch). Daher legt man am besten für jeden zu erstellenden VirtualHost eine nameDesHosts.conf in /etc/apache2/conf.d/ an oder nutzt wieder die httpd.conf/apache2.conf, wobei die erstere Möglichkeit übersichtlicher ist, aber erfordert, dass das Verzeichnis conf.d auch eingelesen wird. Zusätzlich sollte man vorsichtshalber in Plesk das SSL-Hosting für diese Domain ausstellen, damit dessen Settings nicht versehentlich die selbst erstellten überschreiben.

Das SSLCertificateChainFile und das SSLCACertificateFile muss man sich dabei vom Anbieter holen, sofern das erforderlich ist (z.B. bei StartSSL). Anderenfalls lässt man die Einträge einfach weg. Auch der Eintrag für die Logfiles kann natürlich weggelassen werden.

Nach einem Neustart des Apachen kann man jetzt – sofern man alles richtig gemacht hat – alle Domains, für die ein gültiges Zertifikat existiert, ohne Fehlermeldungen aufrufen.

css.php