Apache2 mit PHP7 + PHP8 als php-fpm und suexec einrichten
Ich möchte auf dem Server 2 unterschiedliche PHP Versionen (php-7.4
und php-8.2
) mit fcgid und suexec installieren/einrichten. Die PHP Prozesse sollen dabei unter verschiedenen Benutzer (user1, user2) laufen. Für dieses Beispiel verwende ich Debian 11 Bullseye. Diese Dokumentation sollte aber auch auf anderen Debian/Ubuntu Versionen so funktionieren.
Zuerst erstellen wir uns die beiden Benutzer wie folgt.
root:~# adduser --quiet --disabled-password --gecos "WWW User 1" user1
root:~# adduser --quiet --disabled-password --gecos "WWW User 2" user2
Unter Debian Bullseye ist die PHP Version 7.4 die aktuellste Version. Wir werden uns das Repository für PHP8 hinzufügen und dann beide Versionen und den Apache Webserver installieren.
Wenn das Paket software-properties-common
bereits installiert ist, können die ersten beiden Zeilen weggelassen werden und wir beginnen mit der dritten Zeile.
root:~# apt-get update -y
root:~# apt-get install software-properties-common -y
root:~# echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" | tee /etc/apt/sources.list.d/sury-php.list
root:~# wget -O /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg
root:~# apt-get update -y
Nun installieren wir PHP7.
root:~# apt-get install php7.4 php7.4-fpm php7.4-mysql php7.4-cli php7.4-common -y
Als Nächstes installieren wir uns PHP8 wie folgt.
root:~# apt-get install php8.2 php8.2-fpm php8.2-mysql php8.2-cli php8.2-common -y
Als letztes installieren wir noch den Apache Webserver mit suexec.
root:~# apt-get install apache2 apache2-utils apache2-suexec-custom -y
Nun müssen wir noch die installierten Apache Module aktivieren (laden).
root:~# a2enmod actions alias proxy_fcgi suexec
Nun haben wir alle benötigten Services installiert. Wir prüfen ob diese bereits laufen und (re)starten diese ggf.
root:~# systemctl status apache2
root:~# systemctl status php7.4-fpm
root:~# systemctl status php8.2-fpm
Wir erstellen für unsere beiden Benutzer die Verzeichnis Struktur. Im Verzeichnis /var/www/
erstellen wir jeweils ein Benutzerverzeichnis user1
und user2
und darin jeweils ein Verzeichnis page1
und page2
für unterschiedliche Seiten (Vhosts).
Verzeichnisse erstellen
root:~# mkdir -p /var/www/user{1,2}/page{1,2}
Index Seite mit phpinfo() erstellen
root:~# echo -e "<?php\nphpinfo();\n?>" >/var/www/user1/page1/index.php
root:~# echo -e "<?php\nphpinfo();\n?>" >/var/www/user1/page2/index.php
root:~# echo -e "<?php\nphpinfo();\n?>" >/var/www/user2/page1/index.php
root:~# echo -e "<?php\nphpinfo();\n?>" >/var/www/user2/page2/index.php
Berechtigungen setzen
root:~# chown -R user1.user1 /var/www/user1
root:~# chown -R user2.user2 /var/www/user2
Nun werden wir PHP für die beiden Benutzer anpassen. Das lässt sich bei php-fpm über Pools regeln. Wir definieren uns die Variable user
und setzen diese erst auf user1
, danach wiederholgen wir die Schritte für user2
.
Variable auf user1 setzen.
root:~# user="user1"
php7.4-fpm Pool mit dem Namen ${user}.conf
für den Benutzer $user
erstellen.
root:~# cat << PHP7 >/etc/php/7.4/fpm/pool.d/${user}.conf
[${user}]
user = ${user}
group = ${user}
listen = /run/php/php7.4-fpm-${user}.sock
listen.owner = www-data
listen.group = www-data
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
; php_value/php_flag - you can set classic ini defines which can
; be overwritten from PHP call 'ini_set'.
; php_admin_value/php_admin_flag - these directives won't be overwritten by
; PHP call 'ini_set'
php_admin_value[upload_max_filesize] = 1024M
php_admin_value[post_max_size] = 1024M
PHP7
php8.2-fpm Pool mit dem Namen ${user}.conf
für den Benutzer $user
erstellen.
root:~# cat << PHP8 >/etc/php/8.2/fpm/pool.d/${user}.conf
[${user}]
user = ${user}
group = ${user}
listen = /run/php/php8.2-fpm-${user}.sock
listen.owner = www-data
listen.group = www-data
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
; php_value/php_flag - you can set classic ini defines which can
; be overwritten from PHP call 'ini_set'.
; php_admin_value/php_admin_flag - these directives won't be overwritten by
; PHP call 'ini_set'
php_admin_value[upload_max_filesize] = 1024M
php_admin_value[post_max_size] = 1024M
PHP8
Um nicht die Komplette PHP Konfiguration im jeweiligen VirtualHost File zu definieren, sondern diese dort nur zu includieren, erstellen wir uns die Konfiguration als separate Dateien. Dies könnte man unter /etc/apache2/conf-available
speichern, ich verwende für solche Dinge ein eigenes Verzeichnis mit dem Namen /etc/apache2/custom.d
. Dieses Verzeichnis erstellen wir uns nun.
root:~# mkdir -p /etc/apache2/custom.d
Nun erstellen wir uns die PHP Konfigurationen der jeweiligen PHP Version und des jeweiligen Benutzers. Auch die oben definierte Variable $user
nutzen wir dazu.
PHP Konfiguration für php7.4-fpm für den Benutzer $user
erstellen.
root:~# cat << PHP7CONF >/etc/apache2/custom.d/php7-fpm-${user}.conf
# File: custom.d/php7-fpm-${user}.conf
# Created: $(date +%Y-%m-%d\ %H:%M:%S)
# Description: PHP configuration for php7-fpm for user ${user}
<IfModule !mod_php7.c>
<IfModule proxy_fcgi_module>
# Enable http authorization headers
<IfModule setenvif_module>
SetEnvIfNoCase ^Authorization$ "(.+)" HTTP_AUTHORIZATION=$1
</IfModule>
<FilesMatch ".+\.ph(ar|p|tml)$">
SetHandler "proxy:unix:/run/php/php7.4-fpm-${user}.sock|fcgi://localhost"
</FilesMatch>
<Files ".user.ini">
Require all denied
</Files>
</IfModule>
</IfModule>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
PHP7CONF
PHP Konfiguration für php8.2-fpm für den Benutzer $user
erstellen.
root:~# cat << PHP8CONF >/etc/apache2/custom.d/php8-fpm-${user}.conf
# File: custom.d/php8-fpm-${user}.conf
# Created: $(date +%Y-%m-%d\ %H:%M:%S)
# Description: PHP configuration for php8-fpm for user ${user}
<IfModule !mod_php8.c>
<IfModule proxy_fcgi_module>
# Enable http authorization headers
<IfModule setenvif_module>
SetEnvIfNoCase ^Authorization$ "(.+)" HTTP_AUTHORIZATION=$1
</IfModule>
<FilesMatch ".+\.ph(ar|p|tml)$">
SetHandler "proxy:unix:/run/php/php8.2-fpm-${user}.sock|fcgi://localhost"
</FilesMatch>
<Files ".user.ini">
Require all denied
</Files>
</IfModule>
</IfModule>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
PHP8CONF
Damit der VirtualHost unter dem Benutzer $user
läuft, richten wir für diesen eine eigene suexec Konfiguration ein. Auch diese speichern wir in /etc/apache2/custom.d/
um diese einfach in dem VirtualHost zu includen.
root:~# cat << SUEXECCONF >/etc/apache2/custom.d/suexec_${user}.conf
# Suexec definition for user/group ${user}
<IfModule mod_suexec.c>
SuexecUserGroup ${user} ${user}
</IfModule>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
SUEXECCONF
Nun fehlen nur noch die VirtualHost definitionen. Der Einfachheit halber, habe ich nur den VirtualHost für Port 80 verwendet. Normalerweise wird dieser mit Rewrite zum VirtualHost 443 weitergeleitet, aber für diese Dokumentation möchte ich das nicht so komplex machen. Mit folgendem Befehl erstellen wir und eine VirtualHost Konfiguration von page1-${user}.example.com
mit php7.4-fpm für Benutzer $user
.
root:~# cat << VHOSTCONF1 >/etc/apache2/sites-available/page1-${user}.example.com.conf
# File: sites-available/page1-${user}.example.com.conf
# Created: $(date +%Y-%m-%d\ %H:%M:%S)
# Description: VirtualHost file for page1-${user}.example.com
<VirtualHost *:80>
ServerAdmin webmaster@example.com
ServerName page1-${user}.example.com
DocumentRoot /var/www/${user}/page1
Include custom.d/suexec_${user}.conf
Include custom.d/php7-fpm-${user}.conf
<Directory />
Options +FollowSymLinks -Indexes
AllowOverride None
</Directory>
<Directory /var/www/${user}/page1>
Options +MultiViews
AllowOverride None
DirectoryIndex index.php index.html index.htm
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/page1-${user}.example.com_err.log
CustomLog ${APACHE_LOG_DIR}/page1-${user}.example.com_acc.log combined
LogLevel warn
</VirtualHost>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
VHOSTCONF1
VirtualHost Konfiguration von page2-${user}.example.com
mit php8.2-fpm für Benutzer $user
erstellen.
root:~# cat << VHOSTCONF2 >/etc/apache2/sites-available/page2-${user}.example.com.conf
# File: sites-available/page2-${user}.example.com.conf
# Created: $(date +%Y-%m-%d\ %H:%M:%S)
# Description: VirtualHost file for page2-${user}.example.com
<VirtualHost *:80>
ServerAdmin webmaster@example.com
ServerName page2-${user}.example.com
DocumentRoot /var/www/${user}/page2
Include custom.d/suexec_${user}.conf
Include custom.d/php8-fpm-${user}.conf
<Directory />
Options +FollowSymLinks -Indexes
AllowOverride None
</Directory>
<Directory /var/www/${user}/page2>
Options +MultiViews
AllowOverride None
DirectoryIndex index.php index.html index.htm
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/page2-${user}.example.com_err.log
CustomLog ${APACHE_LOG_DIR}/page2-${user}.example.com_acc.log combined
LogLevel warn
</VirtualHost>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
VHOSTCONF2
Wenn wir nun mit diesen Schritten durch sind, ändern wir die Variable $user
in user2
und wiederholen die obigen Schritte.
Nun restarten wir die Services apache2
, php7.4-fpm
und php8.2-fpm
damit die Änderungen übernommen werden.
root:~# systemctl restart php7.4-fpm
root:~# systemctl restart php8.2-fpm
root:~# systemctl restart apache2
Als nächsten Schritt aktivieren wir die erstellten VitualHost Dateien und laden den Apache Webserver neu.
root:~# a2ensite page1-user1.example.com.conf
root:~# a2ensite page2-user1.example.com.conf
root:~# a2ensite page1-user2.example.com.conf
root:~# a2ensite page2-user2.example.com.conf
root:~# systemctl reload apache2
Wenn wir alles richtig gemacht haben, sollten wir im Browser unter http://page1-user1.example.com/ die Seite mit der PHP7 Konfiguration des Benutzer user1 sehen, die PHP8 Version des Benutzers user1 sollten wir dann unter http://page2-user1.example.com/ finden. Dies setzt natürlich ein korrektes DNS vorraus, was zum Webserver zeigt. Mann müsste im diesen Fall vier DNS CNAME page1-user1.example.com
, page2-user1.example.com
, page1-user2.example.com
und page2-user2.example.com
erstellt haben. Die Domain example.com
dient natürlich nur als Platzhalter für eure Domain.
Somit haben wir nun 2 unterschiedliche PHP Versionen auf unseren Apache Webserver eingerichtet. Mit php-fpm und suexec haben wir auch die Möglichkeit, die PHP Prozesse unter unterschiedlichen Benutzer laufen zu lassen. Somit ist ein Setzen der Schreibberechtigungen von Verzeichnissen nicht mehr nötig und je nach belieben können wir PHP7 oder aber PHP8 verwenden.