Esta pagina se ve mejor con JavaScript habilitado

Apache con soporte php bajo Gentoo

 ·  🎃 kr0m

Apache es uno de los servidores web mas conocidos del mundo, destaca principalmente por su estabilidad, rendiemiento y un gran abanico de módulos disponibles. En este artículo explicaré los diferentes gestores de hilos y los diferentes modos de ejecución de código en php y las implicaciones que conlleva hacerlo de una forma u otra.

Es posible habilitar el soporte de PHP en Apache de varias maneras:

  • mod_php(mejor opción para entornos mono-usuario): El modo mas rápido siempre que NO se atiendan peticiones de forma concurrente, si se comparte hosting y alguien consigue esplotar alguna vulnerabilidad todos los ficheros de todos los clientes serán vulnerables ya que los ficheros generados por Apache pertenecen al mismo usuario del sistema, si ejecuta código se ejecutará con los permisos del usuario de Apache, el interprete de php está embebido dentro de Apache por lo tanto ese binario se cargará tanto para servir contenido estático como dinámico, por lo tanto consumirá mas recursos.
  • CGI: Se ejecuta el php como un software externo, cada ejecución del cgi abre y cierra el entorno de ejecución entero. Mediante este tipo de ejecución tenemos el problema de mod_php de un solo usuario para todo pero podemos utilizar el módulo suexec de Apache para que se ejecute con los permisos del propietario del cgi.
  • FastCGI(mejor opción para entornos multi-usuario): El nivel de rendimiento es muy similar al de mod_php, pero consume bastante RAM.
  • php-fpm(FastCGI Process Manager): Versión mejorada de fastcgi, permite recargar la config de php en caliente, reinicio de los procesos sin destruir la caché del opcode entre otras funcionalidades, no soporta suexec pero mediante varias instancias de fpm podremos ejecutar cada vhost con un usuario diferente.

NOTA: CGI vs FastCGI –> En vez de crear procesos nuevos por cada petición, FastCGI puede usar un solo proceso persistente el cual maneja cualquier petición durante su periodo de vida.

Modificaciones en la ejecución:

  • SuPHP: Igual que CGI pero permite ejecutar el cgi con los permisos del propietario del cgi sin utilizar ningún módulo de Apache como suexec. Por contra consume una cantidad considerable de CPU y no se pueden utilizar sistemas de opcode caching(en cada petición se recompila el código en PHP, no cachea), debido a esta limitación se ha convertido en una opción descartable en la mayoría de escenarios.
  • SuExec: Módulo de Apache que nos permite ejecutar php con el usuario indicado, sí que permite opcode caching, solo es compatible con la ejecución de phps mediante cgi. Suexec isn’t really required to run cgi-scripts, but it’s a really good idea if you have multiple users serving websites…

SuExec posee varios parámetros que nos permiten afinar el comportamiento de los phps:

  • SUEXEC_SAFEPATH: Default PATH for suexec (default: /usr/local/bin:/usr/bin:/bin)
  • SUEXEC_LOGFILE: Path to the suexec logfile (default: /var/log/Apache2/suexec_log)
  • SUEXEC_CALLER: Name of the user Apache is running as (default: Apache)
  • SUEXEC_DOCROOT: Directory in which suexec will run scripts (default: /var/www)
  • SUEXEC_MINUID: Minimum UID, which is allowed to run scripts via suexec (default: 1000)
  • SUEXEC_MINGID: Minimum GID, which is allowed to run scripts via suexec (default: 100)
  • SUEXEC_USERDIR: User subdirectories (like /home/user/html) (default: public_html)
  • SUEXEC_UMASK: Umask for the suexec process (default: 077)

Por ejemplo si necesitamos un determinado umask sería tan sencillo como definirlo en la compialción de Apache:

vi /etc/make.conf
SUEXEC_UMASK="022"
emerge -av Apache

NOTA: Mediante cgi/fastcgi se puede indicar un manejador de php-cgi diferente para cada vhost permitiendonos así tener una versión específica de php o php.ini en cada vhost.

Recursos mod_php CGI FastCGI suPHP
Memory usage Low Low High Low
CPU Usage Low High Low High
Security Low Low High High
Run as file owner Mo No Yes Yes
Overall Performance Fast Slow Fast Slow

Además del método de ejecución de php debemos tener en cuenta el gestor de hilos de Apache( mpms ):

  • worker –> Escalabilidad y concurrencia, además recomendado si se utiliza SSL. La única pega es que necesita mantener un thread por cada conexión establecida hasta que se cumpla el timeout, aunque no se genere tráfico. Arranca varios procesos y cada uno de ellos varios threads.
  • prefork(default Unix) –> Estabilidad y retrocompatibilidad, adecuado cuando se utilizan librerías que no soportan threading(como mod_php). Arranca un nuevo proceso por petición.
  • itk –> Es experimental y muy parecido a prefork, hace un chroot en cada fork, mas seguro pero mas pesado, cada vhost se ejecuta con un uid y gid determinado.
  • peruser(abandonado desde el 2004) –> Cada hijo de Apache corre con su UID y GID, chrootea procesos de Apache, por contra no se recomienda utilizarlo en producción, rompe SSL y no es muy escalable.
  • event –> Como worker pero un solo thread se encarga de todas las conexiones que no generan tráfico, recomendado cuando el tráfico de los clientes es puntual, es considerado inestable.

NOTA: Los únicos mpms con soporte para threads es worker/event por lo tanto si queremos ejecutar php como cgi/fastcgi no dispondremos de otra opción mas que compilar Apache con mpm –> worker ya que event es muy inestable.

Por lo tanto las opciones disponibles quedan así:

MPM Modo ejecución
worker CGI/FastCGI con o sin suexec
prefork mod_php
itk mod_php
peruser mod_php
event CGI/FastCGI con o sin suexec

Podemos ver los diferentes mpms a la hora de compilar Apache:

emerge -av www-servers/Apache

Apache2_MPMS="-event -itk -peruser -prefork worker"

La diferencia entre un proceso hijo (fork) y un hilo de ejecución (thread) es que los procesos no comparten ciertos recursos entre sí, son independientes entre sí y se comunican mediante IPC , normalmente pasando por el núcleo, mientras que los threads si comparten recursos como la memoria del proceso que los genera, un proceso multihilo genera threads y hace que compartan estos recursos y se comuniquen entre sí, normalmente sin pasar por el núcleo. Los threads o hilos de ejecución son más rápidos que los procesos independientes pero, la ventaja de los procesos hijo es esa, que son independientes y están aislados los unos de los otros.

NOTA: Para poder ejecutar cgi, Apache debe ser compilado con soporte para threading, el único mpm estable que lo soporta es worker, así que:

vi /etc/make.conf
Apache2_MPMS="worker"

Un aspecto importante a la hora de compilar Apache es decidir que funcionalidades debe soportar, de este modo además de obtener un binario mas liviando y rápido tendremos un Apache mas seguro ya que si existe alguna vulnerabilidad de un módulo que no hemos incluido no nos afectará.

Podemos ver la lista completa de módulos a la hora de compilar:

emerge -av Apache
Apache2_MODULES="actions alias auth_basic authn_alias authn_anon authn_dbm authn_default authn_file authz_dbm authz_default authz_groupfile authz_host authz_owner authz_user autoindex cache cgi cgid dav dav_fs dav_lock deflate dir disk_cache env expires ext_filter file_cache filter headers include info log_config logio mem_cache mime mime_magic negotiation rewrite setenvif speling status unique_id userdir usertrack vhost_alias -asis -auth_digest -authn_dbd -cern_meta -charset_lite -dbd -dumpio -ident -imagemap -log_forensic -proxy -proxy_ajp -proxy_balancer -proxy_connect -proxy_ftp -proxy_http -proxy_scgi -reqtimeout -substitute -version"

NOTA: Según el tipo de mpm que vayamos a utilizar deberemos compilar Apache con diferentes opciones.

En la propia compilación de Apache emerge ya nos advierte:

Attention: cgi and cgid modules are now handled via Apache2_MODULES flags in make.conf. Make sure to enable those in order to compile them.
In general, you should use 'cgid' with threaded MPMs and 'cgi' otherwise.

Si el mpm soporta threading:

vi /etc/portage/package.use/Apache

www-servers/Apache threads
vi /etc/make.conf
Apache2_MODULES="... cgid ..."
vi /etc/portage/package.use/php
dev-lang/php cgi gd sockets mysql mysqli threads

Si no lo soporta:

vi /etc/portage/package.use/Apache

www-servers/Apache -threads
vi /etc/make.conf
Apache2_MODULES="... cgi ..."
vi /etc/portage/package.use/php
dev-lang/php Apache2 gd sockets mysql mysqli -threads

En mis pruebas he optado por abandonar métodos obsoletos o inestables, por lo tanto se van a chequear:


Apache prefork - mod_php:

vi /etc/make.conf
Apache2_MPMS="prefork"
Apache2_MODULES="actions alias auth_basic authn_alias authn_anon authn_dbm authn_default authn_file authz_dbm authz_default authz_groupfile authz_host authz_owner authz_user autoindex cache dav dav_fs dav_lock deflate dir disk_cache env expires ext_filter file_cache filter headers include info log_config logio mem_cache mime mime_magic negotiation rewrite setenvif speling status unique_id userdir usertrack vhost_alias"
vi /etc/portage/package.use/Apache
www-servers/Apache -threads

Si indicamos un mpm que no soporta threads como es el caso de prefork, pero intentamos compilar Apache con la use flag de threads nos aparecerá el siguiente error:

You have selected a non-threaded MPM but USE=threads is enabled
vi /etc/portage/package.use/php
dev-lang/php Apache2 gd sockets mysql mysqli -threads

NOTA: El parámetro que indica que los phps se ejecutarán con mod_php es la use Apache2

Compilamos Apache:

emerge -av Apache

Compilamos php:

emerge -av php

Indicamos a Apache que debe utilizar el módulo interno:

vi /etc/conf.d/Apache2

Apache2_OPTS="............ -D PHP5"

Configuramos el vhost:

Listen 80
<VirtualHost *:80>
        ServerAdmin kr0m@alfaexploit.com
        DocumentRoot /var/www/WEB
        ServerName alfaexploit.com
        ServerAlias www.alfaexploit.com
        ErrorLog /var/log/apache2/alfaexploit.error_log
        CustomLog /var/log/apache2/alfaexploit.access_log combined
        DirectoryIndex index.php index.htm index.html

        <Directory "/var/www/WEB">
            options -Indexes FollowSymLinks
            AllowOverride All
            order allow,deny
            Allow from all
        </Directory>
</VirtualHost>
chown -R Apache:Apache /var/www/WEB

Reiniciamos el servicio:

/etc/init.d/Apache2 restart

Podemos comprobar como Apache está siendo ejecutado por el usuario Apache:

vi /var/www/WEB/w.php
<?php system('whoami'); ?>

NOTA: Todos los vhosts configurados correrrán con este mismo user, así que una vulnerabilidad en una de las aplicaciones web puede afectar a los demás vhosts!!

Un usuario malicioso podría hacer algo de este estilo:

<?php
        system("cp /var/www/users/victim/public_html/blog/wp-config.php /var/www/users/pwnerer/hohoho.txt");
?>

Eliminamos el fichero:

rm /var/www/WEB/w.php


Apache ITK - mod_php:

vi /etc/make.conf
Apache2_MPMS="itk"
Apache2_MODULES="actions alias auth_basic authn_alias authn_anon authn_dbm authn_default authn_file authz_dbm authz_default authz_groupfile authz_host authz_owner authz_user autoindex cache dav dav_fs dav_lock deflate dir disk_cache env expires ext_filter file_cache filter headers include info log_config logio mem_cache mime mime_magic negotiation rewrite setenvif speling status unique_id userdir usertrack vhost_alias"

Compilamos Apache:

emerge -av Apache

Configuramos el vhost:

Listen 80
<VirtualHost *:80>

     ServerAdmin kr0m@alfaexploit.com
     DocumentRoot /var/www/WEB
     ServerName alfaexploit.com
     ServerAlias www.alfaexploit.com
     ErrorLog /var/log/apache2/alfaexploit.error_log
     CustomLog /var/log/apache2/alfaexploit.access_log combined
     DirectoryIndex index.php index.htm index.html

     AssignUserId USER USER

     <Directory "/var/www/WEB">
         options -Indexes FollowSymLinks
         AllowOverride All
         order allow,deny
         Allow from all
     </Directory>
</VirtualHost>

Indicamos a Apache que debe utilizar el módulo interno:

vi /etc/conf.d/Apache2

Apache2_OPTS="............ -D PHP5"
useradd USER
chown -R USER:USER /var/www/WEB

Reiniciamos el servicio:

/etc/init.d/Apache restart

Una forma de comprobar que el php se está ejecutando con el usuario correcto es mediante este sencillo php:

vi /var/www/WEB/w.php

<?php system('whoami'); ?>
chown USER:USER /var/www/WEB/w.php

Debería de responder con el nombre del usuario con el que configurásteis el vhost.

Eliminamos el fichero:

rm /var/www/WEB/w.php


Apache worker-cgi:

Para poder ejecutar php con un cgi externo necesitamos un mpm con soporte para threads(worker).

vi /etc/make.conf

Apache2_MPMS="worker"
Apache2_MODULES="actions alias auth_basic authn_alias authn_anon authn_dbm authn_default authn_file authz_dbm authz_default authz_groupfile authz_host authz_owner authz_user autoindex cache cgid dav dav_fs dav_lock deflate dir disk_cache env expires ext_filter file_cache filter headers include info log_config logio mem_cache mime mime_magic negotiation rewrite setenvif speling status unique_id userdir usertrack vhost_alias"

Compilamos Apache con soporte para threads:

vi /etc/portage/package.use/Apache

www-servers/Apache threads

Compilamos php con soporte para la ejecución a través de cgi:

vi /etc/portage/package.use/php

dev-lang/php berkdb bzip2 cli crypt ctype curl curlwrappers exif fileinfo filter ftp gd gdbm hash iconv imap intl json mysql mysqli nls odbc pdo phar posix readline session simplexml soap sockets sqlite3 ssl sysvipc threads tokenizer unicode xml xmlreader xmlrpc xmlwriter zip zlib threads cgi

Compilamos Apache y php:

emerge -av Apache php

Configuramos Apache para que utilice cgi y suexec:

vi /etc/conf.d/Apache2

Apache2_OPTS=".... -D CGI"

Creamos el directorio donde estará el interprete de php y en caso de ser necesario el php.ini:

mkdir -p /var/www/html/cgi-bin/USER
find / -iname php-cgi
cp /usr/bin/php-cgi /var/www/html/cgi-bin/WEB/
chown -R Apache:Apache /var/www/html/cgi-bin/WEB

Configuramos el vhost indicando que debe hacer cuando se le solicite un fichero php:

Listen 80
<VirtualHost *:80>
        ServerAdmin kr0m@alfaexploit.com
        DocumentRoot /var/www/WEB
        ServerName alfaexploit.com
        ServerAlias www.alfaexploit.com
        ErrorLog /var/log/apache2/alfaexploit.error_log
        CustomLog /var/log/apache2/alfaexploit.access_log combined
        DirectoryIndex index.php index.htm index.html
        ScriptAlias /local-bin /var/www/html/cgi-bin/WEB
        AddHandler application/x-httpd-php5 php
        Action application/x-httpd-php5 /local-bin/php-cgi

        <Directory "/var/www/html/cgi-bin/WEB">
            Options -Indexes ExecCGI
            Order allow,deny
            Allow from all
        </Directory>

        <Directory "/var/www/WEB">
            options -Indexes FollowSymLinks
            AllowOverride All
            order allow,deny
            Allow from all
        </Directory>
</VirtualHost>
chown -R Apache:Apache /var/www/WEB

Configuramos Apache para que cargue el módulo cgi:

vi /etc/Apache2/httpd.conf

#LoadModule cgi_module modules/mod_cgi.so
<IfDefine CGI>
LoadModule cgid_module modules/mod_cgid.so
</IfDefine

Reiniciamos el servicio:

/etc/init.d/Apache2 restart

Una forma de comprobar que el php se está ejecutando con el usuario correcto es mediante este sencillo php:

vi /var/www/WEB/w.php

<?php system('whoami'); ?>

Esto debería de responder:
Apache


Apache worker-cgi y suexec:

Para poder ejecutar php con un cgi externo necesitamos un mpm con soporte para threads(worker).

vi /etc/make.conf

Apache2_MPMS="worker"
Apache2_MODULES="actions alias auth_basic authn_alias authn_anon authn_dbm authn_default authn_file authz_dbm authz_default authz_groupfile authz_host authz_owner authz_user autoindex cache cgid dav dav_fs dav_lock deflate dir disk_cache env expires ext_filter file_cache filter headers include info log_config logio mem_cache mime mime_magic negotiation rewrite setenvif speling status unique_id userdir usertrack vhost_alias"

Compilamos Apache con soporte para threads:

vi /etc/portage/package.use/Apache

www-servers/Apache threads suexec

Compilamos php con soporte para la ejecución a través de cgi:

vi /etc/portage/package.use/php

dev-lang/php berkdb bzip2 cli crypt ctype curl curlwrappers exif fileinfo filter ftp gd gdbm hash iconv imap intl json mysql mysqli nls odbc pdo phar posix readline session simplexml soap sockets sqlite3 ssl sysvipc threads tokenizer unicode xml xmlreader xmlrpc xmlwriter zip zlib threads cgi

Compilamos Apache y php:

emerge -av Apache php

Configuramos Apache para que utilice cgi y suexec:

vi /etc/conf.d/Apache2

Apache2_OPTS=".... -D SUEXEC -D CGI"

Creamos el directorio donde estará el interprete de php y en caso de ser necesario el php.ini:

mkdir -p /var/www/html/cgi-bin/WEB
cp /usr/bin/php-cgi /var/www/html/cgi-bin/WEB/
useradd USER
chown -R USER:USER /var/www/html/cgi-bin/WEB

Configuramos el vhost indicando que debe hacer cuando se le solicite un fichero php:

Listen 80
<VirtualHost *:80>
        ServerAdmin kr0m@alfaexploit.com
        DocumentRoot /var/www/WEB
        ServerName alfaexploit.com
        ServerAlias www.alfaexploit.com
        ErrorLog /var/log/apache2/alfaexploit.error_log
        CustomLog /var/log/apache2/alfaexploit.access_log combined
        DirectoryIndex index.php index.htm index.html

        SuexecUserGroup USER USER
        ScriptAlias /local-bin /var/www/html/cgi-bin/WEB
        AddHandler application/x-httpd-php5 php
        Action application/x-httpd-php5 /local-bin/php-cgi

        <Directory "/var/www/html/cgi-bin/WEB">
            Options -Indexes ExecCGI
            Order allow,deny
            Allow from all
        </Directory>

        <Directory "/var/www/WEB">
            options -Indexes FollowSymLinks
            AllowOverride All
            order allow,deny
            Allow from all
        </Directory>
</VirtualHost>
chown -R USER:USER /var/www/WEB

Configuramos Apache para que cargue el módulo cgi:

vi /etc/Apache2/httpd.conf

#LoadModule cgi_module modules/mod_cgi.so
<IfDefine CGI>
LoadModule cgid_module modules/mod_cgid.so
</IfDefine

Reiniciamos el servicio:

/etc/init.d/Apache2 restart

Una forma de comprobar que el php se está ejecutando con el usuario correcto es mediante este sencillo php:

vi /var/www/WEB/w.php

<?php system('whoami'); ?>

Esto debería de responder con el nombre del usuario configurado en el vhost.


Apache worker-fastcgi:

Para poder ejecutar php con un cgi externo necesitamos un mpm con soporte para threads(worker).

vi /etc/make.conf

Apache2_MPMS="worker"
Apache2_MODULES="actions alias auth_basic authn_alias authn_anon authn_dbm authn_default authn_file authz_dbm authz_default authz_groupfile authz_host authz_owner authz_user autoindex cache cgid dav dav_fs dav_lock deflate dir disk_cache env expires ext_filter file_cache filter headers include info log_config logio mem_cache mime mime_magic negotiation rewrite setenvif speling status unique_id userdir usertrack vhost_alias"

Compilamos Apache con soporte para threads:

vi /etc/portage/package.use/Apache

www-servers/Apache threads

Compilamos php con soporte para la ejecución a través de fastcgi:

vi /etc/portage/package.use/php

dev-lang/php berkdb bzip2 cli crypt ctype curl curlwrappers exif fileinfo filter ftp gd gdbm hash iconv imap intl json mysql mysqli nls odbc pdo phar posix readline session simplexml soap sockets sqlite3 ssl sysvipc threads tokenizer unicode xml xmlreader xmlrpc xmlwriter zip zlib threads fpm cgi

Compilamos Apache, php y el módulo de fastcgi para Apache:

emerge -av Apache php www-Apache/mod_fastcgi

Configuramos Apache para que utilice fastcgi:

vi /etc/conf.d/Apache2

Apache2_OPTS=".... -D FASTCGI"
vi /etc/Apache2/httpd.conf
LoadModule fastcgi_module modules/mod_fastcgi.so
vi /etc/Apache2/modules.d/20_mod_fastcgi.conf
<IfDefine FASTCGI>
AddHandler fastcgi-script fcg fcgi fpl
</IfDefine>

Creamos el directorio donde estará el interprete de php con soporte para fastcgi y en caso de ser necesario el php.ini:

mkdir -p /var/www/html/cgi-bin/WEB
find / -iname php-fpm
cp /usr/bin/php-fpm /var/www/html/cgi-bin/WEB/
chown -R Apache:Apache /var/www/html/cgi-bin/WEB

Configuramos el vhost indicando que debe hacer cuando se le solicite un fichero php:

Listen 80
<VirtualHost *:80>
        ServerAdmin kr0m@alfaexploit.com
        DocumentRoot /var/www/WEB
        ServerName alfaexploit.com
        ServerAlias www.alfaexploit.com
        ErrorLog /var/log/apache2/alfaexploit.error_log
        CustomLog /var/log/apache2/alfaexploit.access_log combined
        DirectoryIndex index.php index.htm index.html
        ScriptAlias /local-bin /var/www/html/cgi-bin/WEB
        AddHandler application/x-httpd-php5 php
        Action application/x-httpd-php5 /local-bin/php-fpm
        FastCgiExternalServer /var/www/html/cgi-bin/WEB/php-fpm -socket /var/run/php-fpm.sock

        <Directory "/var/www/html/cgi-bin/WEB">
            Options -Indexes ExecCGI
            Order allow,deny
            Allow from all
        </Directory>

        <Directory "/var/www/WEB">
            options -Indexes FollowSymLinks
            AllowOverride All
            order allow,deny
            Allow from all
        </Directory>
</VirtualHost>
vi /etc/php/fpm-php5.5/php-fpm.conf
listen = /var/run/php-fpm.sock
listen.owner = Apache
listen.group = Apache
user = Apache
group = Apache

En caso de ser necesario modificamos el umask:

vi /etc/init.d/php-fpm

start-stop-daemon --umask 0002 --start --pidfile ${PHP_FPM_PID} --exec /usr/lib/${PHPSLOT}/bin/php-fpm -- -y "${PHP_FPM_CONF}" -g "${PHP_FPM_PID}"

NOTA: Mediante socket Unix además de ganar en redimiento también lo haremos en seguridad ya que solo se permitirá conexiones desde la máquina local.

chown -R Apache:Apache /var/www/WEB

Reiniciamos el servicio:

/etc/init.d/Apache2 restart
/etc/init.d/php-fpm restart

Una forma de comprobar que el php se está ejecutando con el usuario correcto es mediante este sencillo php:

vi /var/www/WEB/w.php

<?php system('whoami'); ?>

Esto debería de responder el usuario con el que hemos configurado php-fpm, en mi caso nobody.


Apache worker-fastcgi-multiinstancia:

La configuración es muy similar a la anterior pero en este caso lanzaremos varias instancias de php-fpm de este modo cada una de ellas correrá con un usuario distinto.

vi /etc/make.conf
Apache2_MPMS="worker"
Apache2_MODULES="actions alias auth_basic authn_alias authn_anon authn_dbm authn_default authn_file authz_dbm authz_default authz_groupfile authz_host authz_owner authz_user autoindex cache cgid dav dav_fs dav_lock deflate dir disk_cache env expires ext_filter file_cache filter headers include info log_config logio mem_cache mime mime_magic negotiation rewrite setenvif speling status unique_id userdir usertrack vhost_alias"

Compilamos Apache con soporte para threads:

vi /etc/portage/package.use/Apache

www-servers/Apache threads

Compilamos php con soporte para la ejecución a través de fastcgi:

vi /etc/portage/package.use/php

dev-lang/php berkdb bzip2 cli crypt ctype curl curlwrappers exif fileinfo filter ftp gd gdbm hash iconv imap intl json mysql mysqli nls odbc pdo phar posix readline session simplexml soap sockets sqlite3 ssl sysvipc threads tokenizer unicode xml xmlreader xmlrpc xmlwriter zip zlib threads fpm cgi

Compilamos Apache, php y el módulo de fastcgi para Apache:

emerge -av Apache php www-Apache/mod_fastcgi

Configuramos Apache para que utilice fastcgi:

vi /etc/conf.d/Apache2

Apache2_OPTS=".... -D FASTCGI"
vi /etc/Apache2/httpd.conf
LoadModule fastcgi_module modules/mod_fastcgi.so
vi /etc/Apache2/modules.d/20_mod_fastcgi.conf
<IfDefine FASTCGI>
AddHandler fastcgi-script fcg fcgi fpl
</IfDefine>

Creamos el directorio donde estará el interprete de php con soporte para fastcgi y en caso de ser necesario el php.ini:

useradd user00
mkdir -p /var/www/html/cgi-bin/user00
find / -iname php-fpm
cp /usr/bin/php-fpm /var/www/html/cgi-bin/user00/
chown -R user00:user00 /var/www/html/cgi-bin/user00
mkdir -p /var/www/user00
chown -R user00:user00 /var/www/user00

useradd user01
mkdir -p /var/www/html/cgi-bin/user01
find / -iname php-fpm
cp /usr/bin/php-fpm /var/www/html/cgi-bin/user01/
chown -R user01:user01 /var/www/html/cgi-bin/user01
mkdir -p /var/www/user01
chown -R user01:user01 /var/www/user01

Configuramos el vhost indicando que debe hacer cuando se le solicite un fichero php:

Listen 80
<VirtualHost *:80>
        ServerAdmin kr0m@alfaexploit.com
        DocumentRoot /var/www/user00
        ServerName alfaexploit.com
        ServerAlias www.alfaexploit.com
        ErrorLog /var/log/apache2/alfaexploit.error_log
        CustomLog /var/log/apache2/alfaexploit.access_log combined
        DirectoryIndex index.php index.htm index.html
        ScriptAlias /local-bin /var/www/html/cgi-bin/user00
        AddHandler application/x-httpd-php5 php
        Action application/x-httpd-php5 /local-bin/php-fpm
        FastCgiExternalServer /var/www/html/cgi-bin/user00/php-fpm -socket /var/run/php-fpm_user00.sock

        <Directory "/var/www/html/cgi-bin/user00">
            Options -Indexes ExecCGI
            Order allow,deny
            Allow from all
        </Directory>

        <Directory "/var/www/user00">
            options -Indexes FollowSymLinks
            AllowOverride All
            order allow,deny
            Allow from all
        </Directory>

        # Monitoring status:
        <LocationMatch "/(ping|status)">
            SetHandler php-fastcgi-virt
            Action php-fastcgi-virt /local-bin/php-fpm virtual
        </LocationMatch>
</VirtualHost>
<VirtualHost *:80>
        ServerAdmin kr0m@alfaexploit.com
        DocumentRoot /var/www/user01
        ServerName alfaexploit2.com
        ServerAlias www.alfaexploit2.com
        ErrorLog /var/log/apache2/alfaexploit.error_log
        CustomLog /var/log/apache2/alfaexploit.access_log combined
        DirectoryIndex index.php index.htm index.html
        ScriptAlias /local-bin /var/www/html/cgi-bin/user01
        AddHandler application/x-httpd-php5 php
        Action application/x-httpd-php5 /local-bin/php-fpm
        FastCgiExternalServer /var/www/html/cgi-bin/user01/php-fpm -socket /var/run/php-fpm_user01.sock

        <Directory "/var/www/html/cgi-bin/user01">
            Options -Indexes ExecCGI
            Order allow,deny
            Allow from all
        </Directory>

        <Directory "/var/www/user01">
            options -Indexes FollowSymLinks
            AllowOverride All
            order allow,deny
            Allow from all
        </Directory>

        # Monitoring status:
        <LocationMatch "/(ping|status)">
            SetHandler php-fastcgi-virt
            Action php-fastcgi-virt /local-bin/php-fpm virtual
        </LocationMatch>
</VirtualHost>
vi /etc/php/fpm-php5.5/php-fpm.conf
[global]
error_log = /var/log/php-fpm.log
include=/etc/php/fpm-php5.5/pool.d/*.conf

Configuramos los pools FPM:

vi /etc/php/fpm-php5.5/pool.d/user00.conf

[user00]
user = user00
group = user00
listen = /var/run/php5-fpm_user00.sock
listen.owner = Apache
listen.group = Apache
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
pm.status_path = /status
ping.path = /ping
ping.response = pong
vi /etc/php/fpm-php5.5/pool.d/user01.conf
[user01]
user = user01
group = user01
listen = /var/run/php5-fpm_user01.sock
listen.owner = Apache
listen.group = Apache
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
pm.status_path = /status
ping.path = /ping
ping.response = pong

En caso de ser necesario modificamos el umask:

vi /etc/init.d/php-fpm

start-stop-daemon --umask 0002 --start --pidfile ${PHP_FPM_PID} --exec /usr/lib/${PHPSLOT}/bin/php-fpm -- -y "${PHP_FPM_CONF}" -g "${PHP_FPM_PID}"

Reiniciamos el servicio:

/etc/init.d/Apache2 restart
/etc/init.d/php-fpm restart

Una forma de comprobar que el php se está ejecutando con el usuario correcto es mediante este sencillo php:

vi /var/www/user00/w.php

<?php system('whoami'); ?>
vi /var/www/user01/w.php
<?php system('whoami'); ?>

Esto debería de responder el usuario con el que hemos configurado en cada instancia php-fpm.

FPM nos proporciona cierta información de cada uno de los pools:

URL/status
URL/ping


Con esto ya tendríamos los diferentes modos de configuración de Apache, sus MPMs y sus formas de ejecución de PHP, en cada escenario se han realizado unas pruebas orientativas sobre rendimiento en cada escenario, se debe tener en cuenta que el servidor Apache ha sido instalado y testado en un portátil con las X arrancadas, firefox con diversas pestañas y varias aplicaciones de escritorio.

Los benchmarks se han realizado sobre un core 2 duo a 2.26GHz con 4Gb de RAM y el software empleado para las pruebas ha sido Apache benchmark:

ab -n100000 -c50 -g NAME.csv http://WEB/ –> 100000 peticiones y 50 peticiones concurrentes.

Para plotear se ha optado por gnuplot:

vi /etc/portage/package.use/gnuplot

sci-visualization/gnuplot X cairo gd qt4 readline wxwidgets -aqua bitmap -doc -examples -ggi -latex -lua plotutils svga -thin-splines
emerge -av sci-visualization/gnuplot

Generamos los ficheros de gráficas:

gnuplot> set terminal png
gnuplot> set output “NAME.png”
gnuplot> plot “NAME.csv” using 7 w lines title ‘CTime’, “NAME.csv” using 8 w lines title ‘DTime’, “NAME.csv” using 9 w lines title ‘TTime’, “NAME.csv” using 10 w lines title ‘Wait’

Estos son los resultados:

itk_modphp prefork_modphp worker_cgi
worker_cgi_suexec worker_fastcgi comparativa

Como podemos observar la combinación que mas estable permanece cuando hay varias peticiones simultáneas es la que utiliza fast-cgi.

Si te ha gustado el artículo puedes invitarme a un RedBull aquí