Apache2 php fast cgi suexec dynamic

Un article de Le wiki de 2 noisettes - noisette.ch.


Ce petit article à pour but d'illustrer une procédure d'installation de Apache2, PHP5 et FastCGI, le tout dynamic avec suexec.

L'installation se repose sur Gentoo, dont vous pourrez en passant admirer la simplicité d'utilisation.

Sommaire

Installation

On commence par spécifier les options que l'on souhaite à la compilation de nos paquets :

echo "dev-lang/php -apache2 -cli cgi discard-path force-cgi-redirect" >> /etc/portage/package.use
echo "net-www/apache mpm-worker" >> /etc/portage/package.use

Le flag -apache2 et -cli de PHP est simplement pour ne builder que la version CGI/FastCGI, mais on peut bien entendu adapter selon les besoins.

Et hop on compile et on installe ces 3 paquets et toutes leurs dépendances.

USE=" threads" emerge -av apache2 php mod_fastcgi

Configuration

mod_fastcgi

La configuration repose entièrement sur 2 fichiers :

/etc/conf.d/apache2 dans lequel il faut ajouter -D FASTCGI et /etc/apache2/modules.d/20_mod_fastcgi.conf qu'on va modifier comme suit :

<IfDefine FASTCGI>
      <IfModule !mod_fastcgi.c>
              LoadModule fastcgi_module    modules/mod_fastcgi.so
      </IfModule>

      <IfModule mod_fastcgi.c>

              FastCgiWrapper On
              FastCgiIpcDir /var/tmp/fastcgi/
              FastCgiConfig -autoUpdate -singleThreshold 100 \
                  -killInterval 300 -idle-timeout 10 \
                  -maxClassProcesses 5 -maxProcesses 20 -pass-header none

              AddHandler fastcgi-script .fcgi
              
              <Location />
                      Options +ExecCGI       
              </Location>
              
              #<IfDefine PHP>
                      Action application/x-httpd-fastphp "/cgi-bin/php.fcgi"
                      AddType application/x-httpd-fastphp .php
                      AddDirectoryIndex index.php index.phtml
              #<IfDefine PHP>
      </IfModule>
</IfDefine>

La directive FastCgiWrapper On spéficie l'utilisation du Suexec. FastCgiConfig permet l'utilisation dynamic du serveur FastCGI, et FastCgiIpcDir spécifie le chemin où seront placés les sockets de connexion aux serveurs. Il ne faut donc pas oublier de :

mkdir -p /var/tmp/fastcgi/dynamic
chmod 777 -R /var/tmp/fastcgi/ 

(bien qu'il semblerait qu'un chown apache:apache -R /var/tmp/fastcgi/ suffirait)

/cgi-bin/php.fcgi

Il faut ensuite ajouter dans chaque virtual host le fichier /cgi-bin/php.fcgi qui contient les lignes suivantes :

#!/bin/sh
PHP_RC="/etc/php/cgi-php5/
export PHP_RC
PHP_FCGI_CHILDREN=2
export PHP_FCGI_CHILDREN
PHP_FCGI_MAX_REQUESTS=500
export PHP_FCGI_MAX_REQUESTS
exec /usr/bin/php-cgi

Sans oublier de donner les droite d'exécution à ce fichier, ainsi que le même propriétaire que le virtual host, on redémarre Apache et le tour est joué.

Problèmes connus

Plusieurs points posent problème lors de l'installation, notemment les suivants :

Suexec uid et umask

Avec l'ebuild par défaut de Gentoo, suexec est trop restrictif pour une utilisation simple avec fastcgi.

  • suexec-uidmin=1000 alors que l'utilisateur apache est 81
  • suexec-gidmin=100 alors que le groupe apache est aussi 81
  • suexec-umask=077 veut dire que si php fait un mkdir("rep"), ce rép aura les persissions 700, donc si un fichier .html y est placé (par des gestionnaires de cache par exemple) on ne pourra pas y accéder par le navigateur

apache-suexec.patch :

--- apache-2.0.58-r2.ebuild-orig        2006-09-26 15:34:45.000000000 +0200
+++ apache-2.0.58-r2.ebuild     2006-09-26 15:34:20.000000000 +0200
@@ -141,9 +141,9 @@
                               --with-suexec-userdir=${USERDIR} \
                               --with-suexec-caller=apache \
                               --with-suexec-docroot=/var/www \
-                               --with-suexec-uidmin=1000 \
-                               --with-suexec-gidmin=100 \
-                               --with-suexec-umask=077 \
+                               --with-suexec-uidmin=81 \
+                               --with-suexec-gidmin=81 \
+                               --with-suexec-umask=072 \
                               --enable-suexec=shared"
       fi

Pour palier à ce problème il faut modifier l'ebuild d'apache 2.0 et mettre les valeurs souhaitées pour les 3 champs cités ci-dessus (et plus si affinités) Puis il faut regénérer le fichier Manifest

ebuild /path/to/apache.2.0.*.ebuild digest

Et finalement relancer la compilation d'apache.

Fastcgi header output

Patch mod_fastcgi

Un autre problème est que fastCGI ne supporte pas qu'on envoie plusieur fois des headers, ce que PHP fait allègrement. Il faut donc patcher fastCGI avant que PHP ne corrige le problème (parce que c'est plus simple de patch fastCGI que PHP)

ebuild /path/to/mod_fastcgi.*.ebuild unpack
cd /path/to/mod_fastcgi/work
patch mod_fastcgi.c < php-status-header-output.patch
ebuild /path/to/mod_fastcgi.*.ebuild merge

php-status-header-output.patch from http://bugs.php.net/bug.php?id=36705 :

--- mod_fastcgi.c-orig  2006-09-25 22:57:38.000000000 +0200
+++ mod_fastcgi.c       2006-09-25 22:57:58.000000000 +0200
@@ -718,7 +718,7 @@
            int statusValue = strtol(value, NULL, 10);

            if (hasStatus) {
-                goto DuplicateNotAllowed;
+                /* goto DuplicateNotAllowed; */
            }
            if (statusValue < 0) {
                fr->parseHeader = SCAN_CGI_BAD_HEADER;

Et on peut redémarrer Apache.

Patch php

php-status-header-output.patch from http://bugs.php.net/bug.php?id=38369 :

--- SAPI.c-orig 2006-09-26 22:18:40.000000000 +0200
+++ SAPI.c      2006-09-26 22:16:15.000000000 +0200
@@ -638,6 +638,13 @@
                                               sapi_update_response_code(302 TSRMLS_CC);
                                       }
                               }
+                       } else if (!STRCASECMP(header_line, "Status")) {
+                               int code;
+                               if (1 == sscanf(colon_offset + 1, "%d", &code)
+                                       && code >= 100 && code < 1000) {
+                                       sapi_update_response_code(code TSRMLS_CC);
+                                       return SUCCESS;
+                               } /* else error? */
                       } else if (!STRCASECMP(header_line, "WWW-Authenticate")) { /* HTTP Authentication */

                               sapi_update_response_code(401 TSRMLS_CC); /* authentication-required */

Permissions

 chmod o+x /usr/sbin/suexec2

Conclusion

Trop de problèmes génant persistent avec ce type de config, notemment au niveau de la gestion des headers. Cette configuration, bien qu'optimal en terme de consommation de ressources, devient vite un calvaire pour les administrateurs.

--> Je la déconseille donc pour un environnement en production.