BenV's notes

Apache 2.4 and PHP

by on May.01, 2013, under Software

Those of you running Slackware 14 or current probably have noticed the move to the Apache httpd v2.4 already.
On my webservers I haven’t yet dared to upgrade from the stable 2.2 version, but I figured it might be a good time to test out an upgrade procedure.

Notable problems / changes

This new version of the Apache httpd brings a few things that you should really pay attention to before attempting to upgrade. Well, you could ignore it, but it would surprise me if you wouldn’t end up with a broken webserver πŸ˜‰
So you might want to look into these things:

  1. Configuration syntax changes
  2. Handling the Pleuris-Hoeren-Paaltjes aka PHP disaster
  3. Switching to the MPM-event module

Of course you’ve already done your homework and read upgrading to 2.4 from 2.2, right? πŸ˜‰

Configuration changes

Apache 2.4 changed the hard to read ‘Order deny,allow‘ and ‘Allow from all‘ statements into a much easier to comprehend ‘Require all granted‘ or ‘Require all denied
This means that if you don’t want to load mod_compatibility (which I don’t, I hate configuration files that have deprecated syntax) you’ll need to change your configuration files to reflect these changes. Fine with me, it’s a good time to review my current virtualhost configurations, aka vim /etc/apache/vhosts/*.conf πŸ™‚

Another thing that changed are the modules. Some modules were split up / renamed / removed. This means you’ll get to have another good look at what modules you’re actually using. Personally I disabled all modules and enabled them one by one until the configurationtest (httpd -DSSL -t -S) didn’t complain anymore and my sites were fully functional (including rewrites, gzip encoding, etc).
If you can’t be bothered to test them one by one, either enable them all and deal with the memory usage and possible configuration/security issues that arise (bad idea)… OR just enable the basics: authn_file_module, authn_core_module, authz_host_module, authz_core_module, socache_shmcb_module (for SSL), deflate_module, mime_module, log_config_module, logio_module, mime_magic_module, expires_module, setenvif_module, proxy_module (for PHP-FPM), proxy_fcgi_module (for PHP-FPM), ssl_module, mpm_event_module, unixd_module, suexec_module, negotiation_module, dir_module, alias_module, rewrite_module. (at least, this works for me πŸ˜‰

The PHP problem

There are several ways to run PHP, and most of the suck in some way.

  • By default there’s mod_php, which sucks because it runs all php scripts as user apache. That’s a security nightmare, especially for shared hosting.
  • suPHP, which allows php to run under a specified uid/gid through suexec (with extra goodies such as specifying a different php.ini per vhost). Slower because suexec is used and opcode caches like APC etc don’t function properly using suphp. However, it works very well for shared hosting for sites that don’t get tons of hits.
  • Running PHP through mod_fastcgi(d). Does run under the specified uid/gid, AND allows APC etc to function properly. Downsides are that you’ll be running the php instances even if the site is not being accessed, so it takes up more memory. Also you’ll have a bunch of wrapper scripts lying around.
  • New: php-fpm with some kind of proxy module. PHP-FPM creates process ‘pools‘ that run under a specified uid/gid and listen on a specified ip/port combo. PHP-FPM can also listen on a unix socket, but the failboats at Apache didn’t implement that yet. This method of running PHP gives the advantages of suphp with the speed of fastcgi, without the memory usage of fastcgi. Sounds great, right? One downside (if you don’t want scary patches at least) is that you’ll need a recent version of PHP, for instance PHP 5.4(.14). Next you’ll have to create a seperate PHP-FPM pool per vhost (or at least per uid/gid you want to run php under). Another downside is the fumbling with ProxyMatch or Rewrite [P] rules to make PHP-FPM handle php files at all. Sigh πŸ˜‰

suPHP

Because I’m currently running suPHP for most vhosts I figured I’d keep that part the same, and then upgrade vhosts to PHP-FPM for sites that could use some more speed.
Of course, that’s easier said than done, because compiling suPHP for Apache 2.4 introduced a problem or two.
First problem: suPHP hasn’t had an update for a few years and tries to compile an apache 1 module no matter what:

# configure [options] ; make
make[3]: Entering directory `/usr/src/suphp-0.7.1/src/apache'
/bin/sh ../../libtool --tag=CC --mode=compile gcc -DHAVE_CONFIG_H -I. -I../../src -I/usr/local/apache2/include -DSUPHP_PATH_TO_SUPHP=\"/usr/sbin/suphp\" -DSUPHP_USE_USERGROUP -w -MT mod_suphp.lo -MD -MP -MF .deps/mod_suphp.Tpo -c -o mod_suphp.lo mod_suphp.c
gcc -DHAVE_CONFIG_H -I. -I../../src -I/usr/local/apache2/include -DSUPHP_PATH_TO_SUPHP=\"/usr/sbin/suphp\" -DSUPHP_USE_USERGROUP -w -MT mod_suphp.lo -MD -MP -MF .deps/mod_suphp.Tpo -c mod_suphp.c -fPIC -DPIC -o .libs/mod_suphp.o
In file included from /usr/local/apache2/include/ap_hooks.h:39:0,
from /usr/local/apache2/include/ap_config.h:25,
from /usr/local/apache2/include/httpd.h:44,
from mod_suphp.c:24:
/usr/local/apache2/include/apr.h:358:1: error: unknown type name β€˜off64_t’
typedef off64_t apr_off_t;
^
mod_suphp.c:47:26: error: expected β€˜=’, β€˜,’, β€˜;’, β€˜asm’ or β€˜__attribute__’ before β€˜suphp_module’
module MODULE_VAR_EXPORT suphp_module;
^
mod_suphp.c:59:5: error: unknown type name β€˜table’
table *handlers;
^
mod_suphp.c:66:38: error: unknown type name β€˜pool’
# a few more pages about the same

Joe Gillotti posted a patch on the suPHP mailing list that works for me (it removes the compilation of the old apache garbage). I’ve mirrored the patch here:
[Download not found]

However, the first error it barfed remains even after the patch:

make[3]: Entering directory `/usr/src/suphp-0.7.1-patched/src/apache2'
/bin/sh ../../libtool --tag=CC --mode=compile gcc -DHAVE_CONFIG_H -I. -I../../src -I/usr/local/apache2/include -I -DSUPHP_PATH_TO_SUPHP=\"/usr/sbin/suphp\" -DSUPHP_USE_USERGROUP -g -O2 -MT mod_suphp.lo -MD -MP -MF .deps/mod_suphp.Tpo -c -o mod_suphp.lo mod_suphp.c
libtool: compile: gcc -DHAVE_CONFIG_H -I. -I../../src -I/usr/local/apache2/include -I -DSUPHP_PATH_TO_SUPHP=\"/usr/sbin/suphp\" -DSUPHP_USE_USERGROUP -g -O2 -MT mod_suphp.lo -MD -MP -MF .deps/mod_suphp.Tpo -c mod_suphp.c -fPIC -DPIC -o .libs/mod_suphp.o
In file included from mod_suphp.c:21:0:
/usr/local/apache2/include/apr.h:358:1: error: unknown type name 'off64_t'
typedef off64_t apr_off_t;
^
mod_suphp.c: In function 'suphp_brigade_read':
mod_suphp.c:85:9: warning: passing argument 2 of 'b->type->read' from incompatible pointer type [enabled by default]
if (apr_bucket_read(b, &buf, &size, APR_BLOCK_READ) == APR_SUCCESS) {
^
mod_suphp.c:85:9: note: expected 'const char **' but argument is of type 'char **'
make[3]: *** [mod_suphp.lo] Error 1
make[3]: Leaving directory `/usr/src/suphp-0.7.1-patched/src/apache2'
make[2]: *** [all-recursive] Error 1
make[2]: Leaving directory `/usr/src/suphp-0.7.1-patched/src'
make[1]: *** [all] Error 2
make[1]: Leaving directory `/usr/src/suphp-0.7.1-patched/src'
make: *** [all-recursive] Error 1

This can be fixed by giving configure/make an extra compile flag: CFLAGS=”-D_LARGEFILE64_SOURCE=1″. That fixes suPHP, at least for me πŸ˜‰

PHP-FPM

Once you recompile and reinstall PHP using configure –enable-fpm you’ll notice a /etc/php-fpm.conf in which you can define some global options and then configure pools for php to work with. It’s probably smart to add the include=/etc/php-fpm/*.conf directive and define your vhost pools there. One noteworthy config directive is the chroot = /path directive. This makes the pools somewhat safer in my opinion, but the downside is that the error_log etc also has to be under this chroot. Something to play with πŸ˜‰
Also note that security.limit_extensions makes sure that php-fpm only processes files that end with .php by default.

After molesting this config file and starting the php-fpm daemon (it might be a good to put this sucker in daemontools) the only thing left is the virtualhost configuration in apache that you want to handle with PHP-FPM.
There are several ways to configure Apache to use PHP-FPM, the supposedly fastest new apache 2.4 method is to use the mod_proxy_fcgi module, and then add a statement like this in your virtualhost configuration:

ProxyPassMatch ^/(.*\.php(/.*)?)$ fcgi://127.0.0.1:9000/www/vhosts/notes.benv.junerules.com/$1

Obviously you’ll have to replace the path with something that makes sense for your vhost. Pay special attention to the IP:PORT combination, in this case 127.0.0.1:9000. Every pool you define in the PHP-FPM configuration should get a different port number that matches the port number you specify in your apache vhost configuration.

Alternatively you can use the rewrite engine to proxy PHP-FPM:

RewriteRule ^/(.*\.php(/.*)?)$ fcgi://127.0.0.1:9000/www/vhosts/notes.benv.junerules.com/$1 [P,L]

There’s a third alternative using mod_fastcgi (which I haven’t tested, because it’s a bit silly — mod_proxy_fcgi is the preferred method) which should look a bit like this:


FastCGIExternalServer /usr/sbin/php-fpm -host 127.0.0.1:9000
AddHandler php-fastcgi .php
Action php-fastcgi /usr/sbin/php-fpm.fcgi
ScriptAlias /usr/sbin/php-fpm.fcgi /usr/sbin/php-fpm


Options ExecCGI FollowSymLinks
SetHandler fastcgi-script
Require all granted


WordPress and PHP 5.4

While I’m typing this I noticed an update for one of my WordPress modules. This used to be ‘clickety’ *clack*, auto-update, done. Somehow today it asks me for my FTP details. This means that WordPress somehow failed to realize that “YES, WordPress can write files and is running under my uid/gid instead of apache because of suPHP“, next it failed to mention the reason for this failure, and then skips to asking for FTP details. Gee, thanks WordPress. Now I have to dig through the code and figure out why you are too stupid to autoupdate today. The forums say nothing useful obviously, so we’ll dig through the code.
Of course this only broke after I upgraded to PHP 5.4 (from 5.3), so it probably uses a function that no longer exists or has been disabled in php.ini.

Buried in wp-admin/file.php there’s a function called get_filesystem_method, which returns one of: ‘direct’, ‘ssh2’, ‘ftpext’, ‘ftpsockets’. Obviously we want it to be ‘direct’. In order to be ‘direct’ it has to run through:

$method = defined('FS_METHOD') ? FS_METHOD : false; //Please ensure that this is either 'direct', 'ssh', 'ftpext' or 'ftpsockets'

if ( ! $method && function_exists('getmyuid') && function_exists('fileowner') ){
if ( !$context )
$context = WP_CONTENT_DIR;
$context = trailingslashit($context);
$temp_file_name = $context . 'temp-write-test-' . time();
$temp_handle = @fopen($temp_file_name, 'w');
if ( $temp_handle ) {
if ( getmyuid() == @fileowner($temp_file_name) )
$method = 'direct';
@fclose($temp_handle);
@unlink($temp_file_name);
}
}

So basically it wants PHP to have the function ‘getmyuid’ and ‘fileowner’, and then it tries to create a temporary file that has to have the same uid as that of the running php script (as opposed to PHP’s process!!!!). Note that they assume that some other function simply work, such as fopen, fclose and unlink.
Running this piece of test code in your WordPress installation should tell you the problem:



For me, it told me that apparently I don’t have the getmyuid() — further testing revealed PHP claiming it has been disabled for security reasons. Which is interesting, because I’m quite sure that I run WordPress with a very specific php.ini that does NOT have the getmyuid function listed in disable_functions (also: before the upgrade it worked).
After messing with php.ini so I could run phpinfo() I noticed that PHP was being retarded again as usual. My apache vhost config specified a suphp_configpath /etc/php/wordpress. With that I want to override the default /etc/php/php.ini so I can relax or tighten security measures and other settings per vhost.
What PHP was doing however, was loading the php.ini from /etc/php/wordpress, followed by loading /etc/php/php.ini. (AKA “Scan this dir for additional .ini files => /etc/php“). NO YOU DUMB PIECE OF GARBAGE, THAT’S NOT WHAT I WANT. *rage*
However, this was my own doing as usual, because I handed PHP’s configure the option “–with-config-file-scan-dir=/etc/php“, assuming it would look for the default php.ini there instead of an additional php.ini. (why one would want an additional php.ini that’s always loaded is beyond me — we already have PHPRC for this).
After a PHP recompile with the config-file-scan-dir removed fixed the problem, and now wordpress auto-updates plugins again without FTP garabge. Yay.

MPM Event module

This is a small but nice improvement (hopefully) over Apache 2.2, a new MPM module. This MPM module is based on the Apache 2.2 default ‘MPM-Worker’ module. Note that for SSL connections it hasn’t improved, so if your website is always server over SSL you probably won’t see any improvement (and might even prefer MPM-Prefork for speed).
Using this is a simple matter of enabling the module mpm_event_module and disabling any other mpm_XXX_module you might have.

Well, that’s enough about this for now. I might be back with some rant on mod_spdy soon πŸ˜‰




:, , , , , ,

Leave a Reply

You must be logged in to post a comment.

Archives

  • 2018 (1)
  • 2016 (1)
  • 2015 (7)
  • 2014 (4)
  • 2013 (11)
  • 2012 (27)
  • 2011 (26)
  • 2010 (25)
  • 2009 (68)