Skip to content

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.