aaron-spettl.de

Debian Etch: apache2-mpm-worker mit PHP5 via FCGI und suexec

Datum: 17. November 2007 - Zuletzt aktualisiert: 03. März 2010 - Kategorie Ubuntu/Debian

Ziel:

Als Alternative zu PHP5 mit mod_php bietet sich die FastCGI-Schnittstelle an, da dort die Ausführung von PHP im Kontext des Users möglich ist, nicht nur als www-data. So kann man alle Projekte gut trennen, pro VirtualHost kann man einen Benutzer festlegen. Im Gegensatz zu suphp ist diese Lösung performanter, aber nicht ganz so einfach zu konfigurieren. Noch ein Vorteil: Verschiedene PHP-Versionen sind auch kein Problem.
Ein Vergleich der verschiedenen Arten PHP einzubinden: rootforum.de: mod_php vs PHP-CGI

Zu diesem Howto:

Diese Anleitung hier schreibe ich, weil es viele meist zu komplizierte Tutorials gibt - sie sind einfach zu alt, oder wollen bestimmte Dinge durch selbstkompilieren umgehen... gerade letzteres wollte ich nicht, um die Sicherheitsupdates von Debian nutzen zu können.

Anmerkung: Ich verwende hier mod_fcgid statt mod_fastcgi - einen ausführlichen Vergleich zwischen den Alternativen habe ich nicht vorgenommen.

Benutzer und Gruppen:

Im Folgenden wird immer als Beispiel myuser und mygroup verwendet. myuser muss natürlich Mitglied der Gruppe mygroup sein - weiterhin setze ich aber voraus, dass auch www-data Mitglied dieser Gruppe ist: Der Apache muss die statischen Seiten/Grafiken/... ausliefern können, dafür benötigt er die Leserechte.
Hinzufügen von www-data zur Gruppe:

adduser www-data mygroup

Aus Sicherheitsgründen sollte man deshalb für jeden User eine eigene Gruppe anlegen! Nur so ist gesichert, dass ein Benutzer nicht auf die Daten eines anderen zugreifen kann.

Die ID des Benutzers und die ID der Gruppe dürfen außerdem nicht kleiner als 1000 sein, dies ist wegen suexec notwendig.

Anleitung:

Alle Befehle müssen als root ausgeführt werden. Zuerst müssen die benötigten Pakete installiert werden:

aptitude install php5-cgi apache2-mpm-worker libapache2-mod-fcgid

(Am besten sollten vorher Apache und PHP komplett deinstalliert werden, inklusive Konfigurationsdateien.)
(Anmerkung: In Debian Lenny muss auch apache2-suexec oder apache2-suexec-custom (für z.B. abweichendes DocumentRoot) installiert werden.)

Es sollte sichergestellt werden, dass fcgid und suexec aktiviert sind:

a2enmod suexec
a2enmod fcgid

Unsere Verzeichnisstruktur muss zwingend unterhalb von /var/www liegen (die Debian-Binary fordert dies). Ich erstelle dort ein Verzeichnis vhosts:

mkdir /var/www/vhosts

Sicherstellen, dass die Rechte richtig sind (müsste schon so sein):

chmod 755 /var/www /var/www/vhosts

In diesem Verzeichnis wird für jedes Projekt (ein oder mehrere VirtualHosts) ein Verzeichnis angelegt, das root gehört, die Gruppe soll die des Benutzers sein:

mkdir /var/www/vhosts/projekt
chgrp mygroup /var/www/vhosts/projekt
chmod 750 /var/www/vhosts/projekt

In diesem Projektverzeichnis soll es 4 Ordner geben: conf, log, php-fcgi und web. Unter "conf" kommt die vollständige php.ini des Projekts, unter "log" sollen die Logs angelegt werden, in "php-fcgi" kommt das PHP-Startskript. "web" enthält die eigentlichen Daten, es wird das DocumentRoot des VirtualHost. Wichtig ist, dass Eigentümer und Gruppe sowie Rechte richtig gesetzt werden:

mkdir /var/www/vhosts/projekt/conf
mkdir /var/www/vhosts/projekt/log
mkdir /var/www/vhosts/projekt/php-fcgi
mkdir /var/www/vhosts/projekt/php-tmp
mkdir /var/www/vhosts/projekt/web
chown myuser /var/www/vhosts/projekt/php-fcgi /var/www/vhosts/projekt/php-tmp /var/www/vhosts/projekt/web
chgrp mygroup /var/www/vhosts/projekt/*
chmod 750 /var/www/vhosts/projekt/*

Nun wird in "conf" die php.ini angelegt:

cp /etc/php5/cli/php.ini /var/www/vhosts/projekt/conf/php.ini

Die Einstellungen in der php.ini anpassen:

open_basedir = /var/www/vhosts/projekt/web/:/var/www/vhosts/projekt/php-tmp/
upload_tmp_dir = /var/www/vhosts/projekt/php-tmp/
session.save_path = /var/www/vhosts/projekt/php-tmp

Aus Sicherheitsgründen sind noch empfehlenswert:

disable_functions = show_source, system, shell_exec, passthru, exec, phpinfo, popen, proc_open
display_errors = Off
log_errors = On
allow_url_fopen = Off

Es fehlt noch das Startskript für PHP:

vi /var/www/vhosts/projekt/php-fcgi/php-fcgi-starter

Der Inhalt soll sein:

#!/bin/sh
PHPRC="/var/www/vhosts/projekt/conf/"
export PHPRC
exec /usr/bin/php5-cgi

(Später kann man bei Bedarf mit der Variable PHP_FCGI_CHILDREN in diesem Skript noch eine Maximalanzahl von Prozessen festlegen. Das php5-cgi-Binary von Debian hat glücklicherweise FastCGI-Unterstützung, nicht von dem fehlenden f verwirren lassen.)

Nun muss das Skript noch den User als Eigentümer sowie die richtige Gruppe haben, sonst funktioniert es nicht:

chown myuser:mygroup /var/www/vhosts/projekt/php-fcgi/php-fcgi-starter
chmod 750 /var/www/vhosts/projekt/php-fcgi/php-fcgi-starter

Da der User nun alle Rechte an der Datei hat (und an deren Verzeichnis, beides wieder unbedingt notwendig), müssen wir Änderungen trotzdem verhindern - das geht mit dem immutable-Bit (muss vom Dateisystem unterstützt sein, auch root kann die Datei dann nicht mehr bearbeiten):

chattr +i /var/www/vhosts/projekt/php-fcgi/php-fcgi-starter

Zu beachten ist hier: Beim Kopieren wird das immutable-Bit nie mitkopiert, es muss immer neu gesetzt werden!

Damit wäre dies soweit abgeschlossen, es fehlt nur noch der VirtualHost in Apache:

<VirtualHost *>
        ServerName www.domain-des-projekts.xy

        SuexecUserGroup myuser mygroup
        AddHandler fcgid-script .php

        DocumentRoot "/var/www/vhosts/projekt/web/"
        <Directory "/var/www/vhosts/projekt/web/">
                FCGIWrapper /var/www/vhosts/projekt/php-fcgi/php-fcgi-starter .php
                Options ExecCGI
        </Directory>

        ErrorLog /var/www/vhosts/projekt/log/error.log
        LogLevel warn
        CustomLog /var/www/vhosts/projekt/log/access.log combined
        ServerSignature On
</VirtualHost>

Wie man sieht, kann man pro VirtualHost den User mit Gruppe verändern, und pro Verzeichnis sogar das PHP-Startskript.

In "web" sollte man jetzt zum Test als User eine PHP-Datei anlegen. Eigentlich überflüssig zu erwähnen, aber natürlich muss der Apache neu gestartet werden. Der erste Aufruf eines PHP-Skriptes wird spürbar dauern, da erst dann der PHP-Prozess gestartet wird. Danach geht es schneller.

In den Logdateien sieht es dann z.B. so aus:

$ cat /var/log/apache2/suexec.log
[2007-11-17 09:45:34]: uid: (1000/myuser) gid: (1000/1000) cmd: php-fcgi-starter
$ tail -n2 /var/log/apache2/error.log
[Sat Nov 17 09:45:34 2007] [notice] mod_fcgid: call /var/www/vhosts/projekt/web/index.php with wrapper
                                               /var/www/vhosts/projekt/php-fcgi/php-fcgi-starter
[Sat Nov 17 09:45:34 2007] [notice] mod_fcgid: server /var/www/vhosts/projekt/web/index.php(9495) started
Moped Tuning Bei Mofa-Power gibts alles was das Herz begehrt
Stromanbieter vergleichen hier schnell und sicher
stromverbrauch einfach Kosten senken