PHP as a CGI
Apr 21st, 2008 by abbot
“He who would learn to fly one day must first learn to stand and walk and run and climb and dance; one cannot fly into flying.” — Friedrich Nietzsche
In the initial post, “PHP Implementation” three ways to implement PHP were outlined. In the previous post, PHP as a Module, PHP configured to run as an Apache module was examined. Today’s post follows that up with a discussion of configuring PHP as a CGI. There are many options in configuring PHP as a CGI. This post is only meant as an introduction and an example to get you started. Please see previously mentioned source for a more in depth discussion. Configuring PHP as a CGI is not much different from that of a module. There are two methods:
- Treat PHP scripts like other CGI scripts. Execution is carried out by the operating system. The file must be executable to the Apache user. The Apache configuration file must have ExecCGI set. The PHP –enable-discard-path configuration option is used.
- Use the –enable-force-cgi-redirect option and have Apache post-process the CGI scripts.
On most Unix systems, the first line in the script tells the system how to execute the script. For example: #!/bin/sh, #!/usr/bin/perl, or #!/usr/local/apache/php/bin/php. As an Apache module, PHP scripts do not require this first line so many software packages do not write PHP code in this manner. Therefor, you would have to modify every code currently installed and many PHP code you would install in the future.
We are going to use the second method. Please see previous section, “Configuring PHP as a Module” as to the meaning of the additional configuration options.
/usr/local/src root# cd /usr/local/src/php-5.2.5 /usr/local/src/php-5.2.5 root# CC=gcc CFLAGS="-O3 -fno-omit-frame-pointer" \ CXX=gcc CXXFLAGS="-O3 -fno-omit-frame-pointer -felide-constructors \ -fno-exceptions -fno-rtti" ./configure --enable-force-cgi-redirect \ --prefix=/usr/local/apache/php --enable-sockets --with-gd --with-mysql=/usr/local/mysql \ --with-zlib-dir=/usr --with-jpeg-dir=/usr --with-png-dir=/usr /usr/local/src/php-5.2.5 root# make /usr/local/src/php-5.2.5 root# make install |
To avoid the possibility of anyone exploiting the PHP interpreter by calling it directory, create a separate folder php-cgi-bin. Only the php interpreter will go there. Copy /usr/local/apache/php/bin/php-cgi into new php-cgi-bin directory.
/usr/local/src/php-5.2.5 root# mkdir /var/www/php-cgi-bin /usr/local/src/php-5.2.5 root# chmod uog+rx /var/www/php-cgi-bin /usr/local/src/php-5.2.5 root# cp /usr/local/apache/php/bin/php-cgi /var/www/php-cgi-bin /usr/local/src/php-5.2.5 root# chmod uog+rx /var/www/php-cgi-bin/php-cgi |
In the Apache configuration file deny all access to the php-cgi-bin directory by using “Deny from all.” Modify the Apache configuration file adding the AddHandler directives. This is similar to what was done in the previous section, except we no longer have the line “LoadModule php5_module modules/libphp5.so”. Instead we are going to tell Apache what action to take with the line “Action application/x-httpd-php /php-cgi-bin/php-cgi”. The following lines get added to /usr/local/apache/conf/httpd.conf
# ScriptAlias: This controls which directories contain server scripts.
ScriptAlias /php-cgi-bin/ "/var/www/php-cgi-bin/"
# Associate file extensions with PHP
AddHandler application/x-httpd-php .php
AddHandler application/x-httpd-php .php3
AddHandler application/x-httpd-php .inc
AddHandler application/x-httpd-php .class
AddHandler application/x-httpd-php .module
# Activates a CGI script for a particular handler or content-type
Action application/x-httpd-php /php-cgi-bin/php-cgi
#
DirectoryIndex index.html index.php
# "/usr/local/apache/cgi-bin" should be changed to whatever your ScriptAliased
# CGI directory exists, if you have that configured.
<Directory "/var/www/php-cgi-bin">
AllowOverride None
Options None
Order allow,deny
Allow from all
</Directory>
|
Test that PHP scripts are now running under the Apache web server.
/usr/local/src/php-5.2.5 root# echo "<?php phpinfo(); ?>" > /var/www/htdocs/index.php /usr/local/src/php-5.2.5 root# chmod uog+rx /var/www/htdocs/index.php /usr/local/src/php-5.2.5 root# /usr/local/apache/bin/apachectl start |
Access the index.php file by going to “http://127.0.0.1/index.php” with your browser. Once you see PHP is working, stop the web server, remove the index.php info.
/usr/local/src/php-5.2.5 root# /usr/local/apache/bin/apachectl stop /usr/local/src/php-5.2.5 root# rm /var/www/htdocs/index.php |
The Apache server can now serve up PHP files. The problem is the PHP is still running as the Apache user. Apache running as a non-root user creates a problem in switching user identities. This is where the use execution wrappers becomes important. To quote from the suEXEC Support page, “The suEXEC feature provides Apache users the ability to run CGI and SSI programs under user IDs different from the user ID of the calling web server. Normally, when a CGI or SSI program executes, it runs as the same user who is running the web server.”
Configuring Apache below, all possible configuration options for suEXEC are included, in addition to the previous configuration options we used for Apache. While this is unnecessary, it is helpful to see what is being set. Please see the suEXEC Support for explanation of the options. In the example below, we will create a specific area, /var/www/htdocs/public_area where we are going to later configure CGIs to run under a certain user. This is just an example and should be adjusted to your environment and needs.
/usr/local/src/ root# cd /usr/local/src/httpd-2.2.8 /usr/local/src/httpd-2.2.8 root# make clean /usr/local/src/httpd-2.2.8 root# ./configure --prefix=/usr/local/apache \ --enable-rewrite \ --enable-so \ --disable-imap \ --disable-userdir --with-mpm=worker \ --enable-suexec \ --with-suexec-bin=/usr/local/apache/bin/suexec \ --with-suexec-caller=httpd \ --with-suexec-userdir=public_html \ --with-suexec-docroot=/var/www/htdocs \ --with-suexec-uidmin=100 \ --with-suexec-gidmin=100 \ --with-suexec-logfile=/var/www/logs/suexec_log \ --with-suexec-safepath=/usr/local/bin:/usr/bin:bin \ --with-suexec-umask-=022 /usr/local/src/httpd-2.2.8 root# make /usr/local/src/httpd-2.2.8 root# make install /usr/local/src/httpd-2.2.8 root# /usr/local/apache/bin/apachectl start |
Look in the Apache error log to confirm that suEXEC mechanism is enabled. There will be a log entry in /var/www/logs/error_log:
[notice] suEXEC mechanism enabled (wrapper: /usr/local/apache/bin/suexec)
Check the parameters used to compiled suEXEC:
/usr/local/src/httpd-2.2.8 root# /usr/local/apache/bin/suexec -V |
No let us create a script under /var/www/htdocs/public_html/cgi_bin to run as user jbond. We will set this up to run under the virtual host 127.0.0.1. The following is a minimal example of using suEXEC in the Apache configuration file (/usr/local/apache/conf/httpd.conf):
<VirtualHost 127.0.0.1:80> # Execute all scripts as user as user jbond, group spy SuexecUserGroup jbond spy # Maximum 1 CPU second to be used by a process RLimitCPU 1 1 # Maximum of 25 processes at any one time RLimitNPROC 25 25 # Allow 10 MB to be used per-process RLimitMEM 10000000 10000000 <directory /home/jbond/public_html/cgi_bin> Options +ExecCGI SetHandler cgi-script </directory> </VirtualHost 127.0.0.1:80> |
Create a simple “hello script” that tells you who is running it in the /var/www/htdocs/public_html/cgi_bin folder. Call the script test.sh
#!/bin/sh echo "Content-Type: text/html" echo echo "Hello world from user <b>`whoami`</b>! " |
Adjust the owner and group permissions.
/usr/local/src/httpd-2.2.8 root# chown -R jbond /var/www/htdocs/public_html /usr/local/src/httpd-2.2.8 root# chgrp -R spy /var/www/htdocs/public_html |
You can now get the Apache web server to execute the script /var/www/htdocs/public_htm/cgi_bin/test.sh as user jbond by going to “http://127.0.0.1/public_html/cgi_bin/test.sh”.
To provide PHP access is not supported by suEXEC except through the use of Apache module mod_rewrite. A copy of the PHP binary will need to be in each account’s cgi-bin folder. Each account can have its own php.ini file for tailoring to specific account requirements. This will be left as an exercise to the reader, at least until I have time to come back and fill specifics in.
Final Words
In the next post, we will examine running PHP over FastCGI.
[...] Comments « PHP as a CGI [...]