PHP-FPM mit Debian Jessie
Statische Webseiten sind nett (und durchaus wieder im Kommen), aber zumindest für manche Zwecke sind dynamisch generierte Seiten und die auf diese Weise ermöglichte Interaktivität doch dann besser oder gar notwendig. Facebook, bspw., würde sich als statische Seite dann doch eher schwierig gestalten.
Die Welt dynamischer Webseiten
Scriptsprachen
Dynamisch generierte Seiten bedingen, dass beim Aufruf einer Webseite nicht nur eine auf dem Server abgelegte Datei angezeigt wird, sondern dass ein Programm dort läuft, das die Ausgabe mehr oder weniger live generiert. Das hat ganz andere Implikationen für die Sicherheit des Servers, denn nunmehr läuft dort von außen erreichbarer Code, der schlimmstenfalls noch durch die Nutzer selbst aufgespielt werden kann. Nicht jeder, der seine ersten Gehversuche mit Scripts macht, hat dabei auch Fragen der IT-Sicherheit ausreichend im Blick - um das Problem einmal stark verharmlosend zu beschreiben. Gerade PHP hat nicht den Ruf, zumindest in den ersten Jahren seiner Entwicklung großes Gewicht auf das Fördern oder gar Erzwingen sicherer Praktiken gelegt zu haben, und viele lern(t)en es vor allem als eine Art Templating-Sprache kennen, die man irgendwie in seine HTML-Seiten einbettet, um ihnen damit eine erweiterte Funktionalität zu verleihen, ohne dabei groß an Konsequenzen zu denken (schuldig, euer Ehren!).
Rechteprobleme
Besondere Schwierigkeiten kommen hinzu, wenn mehr als ein Benutzer auf dem Server Scripts nutzen will. Denn wenn der Webserver bzw. der von diesem gestartete Interpreter das Script ausführen will, muss er es lesen können. Dann muss er aber - logischerweise - auch die Scripts aller anderen nutzer lesen können, was bedeutet, dass Nutzer A ein Sript schreiben kann, mit dem er sich die Scripts von Benutzer B anzeigen lassen kann, samt aller dort gespeicherten Informationen (Datenbankpassworte usw.), ebenso wie Dateien, die Benutzer B anlegt. Und wenn das Script irgendwelche Dateien anlegt (man denke an hochgeladene Bilder), dann werden diese Dateien unter der Nutzerkennung des Webservers angelegt, so dass der Benutzer dann keinen vollen Zugriff darauf hat. Das ist alles etwas unschön, weshalb man schon bald die Möglichkeit geschaffen hat, Scripts (sei es Perl, sei es Python, sei es PHP) unter der Benutzerkennung des jeweiligen Nutzers laufen zu lassen, dem das Script “gehört”.
suexec und suphp
Das schafft zwar andere Probleme, insbesondere im Bereich der Performance, denn nun kann ein Webserverprozess nicht mehr beliebig viele Scripts in parallelen Threads abarbeiten, weil diese ja vielleicht verschiedenen Nutzern gehören, so dass für jedes Script ein neuer Interpreter-Prozess gestartet werden muss, aber es ist doch in der Regel vorzugswürdig.
Für Scripts, die über die CGI-Schnittstelle (ja, ich weiß, das “I” steht schon für “Interface”) gestartet werden, stellt suexec die entsprechende Funktionalität bereit. Für PHP tat das traditionell suphp. suphp gibt es aber in Debian Jessie nicht mehr. Was also tun?
Als Lösung bietet sich PHP-FPM an.
PHP-FPM
FPM steht dabei für FastCGI Process Manager und ist eine sehr angenehme Lösung, um PHP als FastCGI einzubinden. FastCGI ist ein Mittelding zwischen der Einbindung per mod_php, also als Modul im Webserver, und als CGI, so dass für jeden Aufruf eines PHP-Scripts ein eigener Prozess gestartet werden muss: es werden einige FastCGI-Prozesse gestartet, die in einem Pool vorgehalten werden, und wenn ein PHP-Script aufgerufen wird, wird der Aufruf vom Webserver an den schon laufenden Prozess weitergereicht. Das erspart den sonst für jeden Aufruf notwendigen Start des Interpreters - und es bietet die Möglichkeit, pro Pool festzulegen, unter welcher Benutzerkennung die Prozesse dieses Pools laufen sollen.
Hinweis: Der folgende Vorschlag ist nicht der einzige Weg, PHP-FPM zu installieren. Eine bessere Lösung unter Verwendung von mod_proxy_fcgi, auf die ich in den Kommentaren hingewiesen wurde, habe ich mittlerweile auch beschrieben. Dieser andere Weg setzt Apache 2.4.x voraus, benötigt aber kein Paket aus dem nicht-freien Debian-Repository. Insgesamt erscheint mir dieser andere Weg vorzugswürdig.
PHP-FPM installieren
Unter Debian Jessie ist die Installation der Pakete hinreichend einfach:
apt-get install libapache2-mod-fastcgi php5-fpm php5
libapache2-mod-fastcgi
kommt hier aus dem non-free
-Repository, das dementsprechend eingebunden sein muss.
Debian hinterlegt dabei die php.ini
für die FastCGI-Prozesse unter /etc/php5/fpm/php.ini
und die Konfiguration für FPM unter /etc/php5/fpm/php-fpm.conf
, ergänzt u.a. um die Konfiguration installierter Module in /etc/php5/fpm/conf.d/
, wie man das so kennt.
Im Verzeichnis /etc/php5/fpm/pool.d/
sind dann die einzelnen Pools definiert, die ebenfalls in die /etc/php5/fpm/php-fpm.conf
inkludiert werden, standardmäßig nur der Pool des Benutzers www-data
, unter dem der Webserver läuft.
FastCGI für PHP-FPM konfigurieren
Wenn man möchte, kann man die bestehende Konfigurationsdatei sichern:
cp /etc/apache2/mods-available/fastcgi.conf /etc/apache2/mods-available/fastcgi.conf.backup
und danach sie danach mittels vim /etc/apache2/mods-available/fastcgi.conf
neu befüllen wie folgt:
<IfModule mod_fastcgi.c>
AddType application/x-httpd-fastphp5 .php
Action application/x-httpd-fastphp5 /php5-fcgi
Alias /php5-fcgi /usr/lib/cgi-bin/php5-fcgi
FastCgiExternalServer /usr/lib/cgi-bin/php5-fcgi -socket /var/run/php5-fpm.sock -pass-header Authorization
<Directory /usr/lib/cgi-bin>
Require all granted
</Directory>
</IfModule>
Auf diese Weise bekommen auf .php
endende Dateien den Typ x-httpd-fastphp5
zugewiesen, der über den Handler /php5-fcgi
aufgerufen wird, der wiederum auf /usr/lib/cgi-bin/php5-fcgi
zeigt, was aber ebenfalls nicht existiert, so dass Apache stattdessen alles dem externen FastCGI-Server zuwirft, der über den Socket /var/run/php5-fpm.sock
kommuniziert.
Nach Aktivieren der notwendigen Module mittels a2enmod fastcgi actions alias
und einem Restart des Webservers (service apache2 restart
) sollte PHP zur Verfügung stehen.
Pools für einzelne Benutzer einrichten
Um nun verschiedene Pools für verschiedene Benutzer einzurichten, bedarf es neben einer passenden Konfiguration des jeweiligen Pools in /etc/php5/fpm/pool.d/
und eines geänderten Aufrufs in jedem virtual host, der diesem Benutzer zugeordnet ist.
Am einfachsten kopiert man sich die bestehende Konfiguration für den Pool www
und wandelt sie ab:
cd /etc/php5/fpm/pool.d
cp www.conf user1.conf
vim user1.conf
Folgende Teile der Konfiguration müssen ersetzt werden: der Name des Pools, user
, group
und listen
:
; Start a new pool named 'user1'.
; the variable $pool can we used in any directive and will be replaced by the
; pool name ('user1' here)
[user1]
[...]
user = user1
group = users
[...]
listen = /var/run/php5-fpm-user1.sock
Selbstverständlich können auch andere, für diesen Pool spezifische Änderungen vorgenommen werden
Und danach bekommt der bspw. in /etc/apache2/sites-available/user1-domain.example
konfigurierte virtual host noch folgende Ergänzung (zwischen <VirtualHost ...>
und </VirtualHost>
):
<IfModule mod_fastcgi.c>
AddType application/x-httpd-fastphp5 .php
Action application/x-httpd-fastphp5 /php5-fcgi
Alias /php5-fcgi /usr/lib/cgi-bin/php5-fcgi-user1
FastCgiExternalServer /usr/lib/cgi-bin/php5-fcgi-user1 -socket /var/run/php5-fpm-user1.sock -pass-header Authorization
</IfModule>
In diesem virtual host kommuniziert der Apache also nicht mit dem FastCGI-Server über /var/run/php5-fpm.sock
, sondern über /var/run/php5-fpm-user1.sock
, und den haben wir ja anders konfiguriert. Ein service apache2 restart
bzw. service php5-fpm restart
später sollte die geänderte Konfiguration zur Verfügung stehen.
Soll PHP in mehreren virtual hosts unter derselben Benutzerkennung laufen, empfiehlt es sich, die o.g. Konfiguration in eine eigene Datei (bspw. /etc/apache2/sites-available/user1.fcgi
) auszulagern und diese Datei dann in die virtual host-Konfiguration einzubinden:
Include /etc/apache2/sites-available/user1.fcg
Das spart nicht nur Tipparbeit, sondern erleichtert auch spätere Änderungen sehr.
Wichtig: Der Eintrag FastCgiExternalServer
darf dabei nur einmal erfolgen, also nicht pro vhost wiederholt werden.
Die Installation testen
Am einfachsten gestaltet sich der Test, wenn man im Webroot des Servers (Debian Jessie: /var/www/html/
)
und der einzelnen virtual hosts jeweils eine Datei test.php
speichert, die nur phpinfo()
aufruft:
<?php phpinfo(); ?>
In der Ausgabe von phpinfo()
wird angezeigt, unter welchem Benutzer das Script ausgeführt wird.
Weiterer Lesestoff
- Alex Fornuto bei linode: Install PHP-FPM and Apache on Debian 8 (Jessie)
- Apache httpd Wiki: High-performance PHP on apache httpd 2.4.x using mod_proxy_fcgi and php-fpm
Kommentare
Ansicht der Kommentare: Linear | Verschachtelt
Sven Hartge am :
Das mit fastcgi ist die uralte und nicht mehr wirklich präferierte Variante. Vor allem, weil Apache2.4 in Debian Jessie via mod_proxy direkt fcgi sprechen kann.
Besser ist also folgendes:
(aus meiner /etc/apache2/conf-enabled/php5-fpm.conf)
(via http://float64.uk/blog/2014/08/20/php-fpm-sockets-apache-mod-proxy-fcgi-ubuntu/ und http://httpd.apache.org/docs/current/mod/mod_proxy.html#handler)
Damit kann man sich das ganze Gehampel mit libapache2-mod-fastcgi aus non-free und der Alias-Geschichte für den Dummy-Pfad, etc. sparen.
Sven Hartge am :
Nürx, was hat denn da die <> Zeichen im Markdown verwurxelt?
Thomas Hochstein am :
Ach, es ist ein Kreuz, da kommen sich die Plugins in die Haar - Markdown, BBCode und der Filter, der HTML verhindern soll. Alles historisch gewachsen, und wenn man jetzt irgendetwas deaktiviert, sehen alte Kommentare komisch aus.
Ich gucke mal, ob ich an der Reihenfolge was tun kann. Deinen Kommentar habe ich leicht abgewandelt, damit es auf jeden Fall erst einmal ordentlich aussieht. Sorry.
Thomas Hochstein am :
Ich glaube, uralt ist das, was ich vorher getrieben habe.
Aber ich merke mir das gerne mal zum Testen vor. — Rest der Config bleibt sonst gleich, und mod_proxy muss dann noch aktiviert werden, richtig?
Sven Hartge am :
mod_proxy_fcgi muss eingeschaltet werden, richtig.
Dann obige Zeilen, entweder global oder pro vHost, mit abgewandeltem Socket-Pfad natürlich, wenn man die vHosts via Benutzer voneinander trennen will.
Wichtig ist, ist dass meine obige Lösung die in http://wiki.apache.org/httpd/PHP-FPM am Ende angesprochenen Probleme nicht hat, weil FilesMatch nur auf wirklich existierende Dateien matcht, während die in der Wiki-Seite besprochene ProxyPassMatch-Lösung ein Sicherheitsalbtraum ist.
Thomas Hochstein am :
Sehr schön. Funktioniert hier prima.
Danke!