Applications Using lighttpd

Lighttpd works nicely with most web-projects. To get them setup easily some are providing special lighttpd setups.

Using a Perl dispatcher instead of mod_perl

I just received a mail from Alex Shah <>:

I thought you might like to include this in the distribution:


#!perl
#!/usr/bin/perl
use strict;
use CGI::Fast;
use Embed::Persistent;
{
    my $p = Embed::Persistent->new();
    while (new CGI::Fast) {
        my $filename = $ENV{SCRIPT_FILENAME};
        my $package = $p->valid_package_name($filename);
        my $mtime;
        if ($p->cached($filename, $package, \$mtime))
        {
            eval {$package->handler;};
        }
        else
        {
            $p->eval_file($ENV{SCRIPT_FILENAME});
        }
    }
}

Here's the lighttpd.conf:


#!perl
fastcgi.server = ( ".pl" =>
    (( "socket"   => "/tmp/application.fcgi.socket",
       "bin-path" => "/Users/ashah/docroot/dispatch.fcgi",
    ))
)

There is sometime problem to compile ExtUtils from CPAN which provide Embed::Persistent, so you can only download them and copy lib/Embed to your perl path. You can also find some info at http://search.cpan.org/dist/perl/pod/perlembed.pod#Maintaining_a_persistent_interpreter.

If the above does not work nicely for you maybe you can try SpeedyCGI or PPerl which runs Perl scripts persistently through its own persistent Perl interpreter. It runs like an ordinary CGI and does not require any changes to your lighttpd setup (just change the shebang line in your script).

Yet Another Using a Perl dispatcher instead of mod_perl

I am using CGI::Application and lighty 1.4.11. The only module you need to install is CGI::Fast which is a pure perl module. The C::A app runs fine with little change in the instance script. CGI::Application::Dispatch can be used if you use multiple instance scripts. -- Qiang@cgiapp from freenode.

here is my instance script ( or dispatcher ).


#!perl
#!/usr/bin/perl -w
use strict;
use warnings;
use My::App;
use CGI::Fast();

while (my $q = new CGI::Fast){
    my $webapp = My::App->new( QUERY => $q );
    $webapp->run();
}

#### below is what i have for mod_perl. 
#### you can see there isn't much change after migrating to lighty.
#!/usr/bin/perl -w
use strict;
use warnings;
use My::App;
use CGI::Fast;

my $webapp = My::App->new();
$webapp->run();

here is my lighty conf:


#!perl
       ### here is the fastcgi server
       $HTTP["host"] == "dev.example.com" {
            var.root    = "/path/to/app/root/" 
            server.document-root = var.root + "htdocs/" 

            url.rewrite = ( "^/static/.*"        => "$0",
                            "^/([a-zA-Z_]+)$"    => "/index.pl/$1",
                            "^/([a-zA-Z_]+/.*)$" => "/index.pl/$1" 
                           )

            fastcgi.server = ( ".pl" => ((
                                 "bin-path"        => var.root + "htdocs/index.pl",
                                 "bin-environment" => ( "PERL5LIB" => var.root + "lib",
                                                        "CGIAPP_CONFIG_FILE" => var.root + "conf/my.conf" ),
                                 "socket"          => "/tmp/perl.socket",
                                 "check-local"     => "disable",
                                 "min-procs"       => 2,
                                 "max-procs"       => 5,
                                 "idle-timeout"    => 20
                 )))
        }

        # this is for plain cgi
        $HTTP["host"] == "dev1.example.com" {
            alias.url  = ( "/bin/" => "/app/htdocs/bin/" )
            $HTTP["url"] =~ "^/bin" {
                    setenv.add-environment = ( "DEVMODE" => "dev" )
                    cgi.assign = ( ".pl" => "/usr/bin/perl" )
            }
        }

multiple RubyOnRails on one server

http://wiki.rubyonrails.com/rails/show/HowtoDeployMoreThanOneRailsAppOnOneMachine describes this on Apache, here we do it on lighty:


#!perl
$HTTP["url"] =~ "^/appOne" {
  url.rewrite = ( "^/appOne(/.*)$" => "/appOne/public$1" )
  fastcgi.server = ( "dispatch.fcgi" => (( "bin-path" ... )))
  server.error-handler-404 = "/appOne/dispatch.fcgi" 
}
$HTTP["url"] =~ "^/appTwo" {
  url.rewrite = ( "^/appTwo(/.*)$" => "/appTwo/public$1" )
  fastcgi.server = ( "dispatch.fcgi" => (( "bin-path" ... )))
  server.error-handler-404 = "/appTwo/dispatch.fcgi" 
}

RunPhp: Multiple virtualhost php spawning outside from lighttpd

In order to understand how this tool works, i define the problem using php with lighttpd.

Problem description

With lighttpd, you can have two options to run php:

  • let lighttpd to spawn PHP-FastCGI processes for virtualhosts from inside,
  • or you can spawn it for yourself externally, lighttpd will use the socket.

I found lighttpd spawned FastCGI processes not a good idea, because lighttpd is a webserver, not an external process spawner. The other counter argument was handling files in php safemode, which problem also existed in the older used apache by me:

In php safe mode, a code cannot access files not owned by its UID. When a code created a directory, the directory's UID was the webservers's UID (usually www-data on my system), not the user's UID who has uploaded the file. That code even cannot ''chown()'' the file/directory to have it's UID. This made recursive directory creating unavailable, and this made some widely-used opensource codes - which used that method too to handle lots of files - also unusable.

Turning safe_mode off, and letting user uploaded codes reach out of documentroot? I found this a bad idea.

There was also another problem: if you use one php setup (one php.ini) for every virtualhost, how will different virtualhosts have different settings? This was needed too.

Solution

The solution was to use a separate PHP FastCGI process for each virtualhost. But this is hard to handle if you have a lots of virtualhosts, so i created this tool. Now, php runs in the UID of the site's user (in my case who uploads the files to the server using scp). This way, safe_mode directory and file creation problem is solved. For example, Joomla and other free portals, which are managing directories and files under their webroot, can be used with safe_mode turned on!

_'_Question: Could you please explain, what exactly this tool does? If I look into the source it seems to me that it is NOT a kind of suexec wrapper but a kind of config-creator for multiple FastCGI processes? So we have here an automation for the solution described in http://trac.lighttpd.net/trac/wiki/HowToSetupFastCgiIndividualPermissions - right?

RunPhp manages separate and unique virtualhost settings in a configuration file, which format is easily understandable, and it can be handled like an .ini file.

Now let's see how you can setup and use it.

Setup, configuration and command line usage

Setup: Programs needed

  • Python (as far as i know, it works with python2.2.0 and up)
  • ''/usr/bin/spawn-fcgi'', it can be found in the lighttpd distrib, after compiling you have to copy it i'ts place: run ''"cp src/spawn-fcgi /usr/bin"'' from the lighttpd source directory
  • php compiled as fcgi binary (see lighttpd's documentation)
  • lighttpd running, having userid www-data
  • http://trac.lighttpd.net/trac/attachment/wiki/ApplicationsUsingLighttpd/RunPhp.tar.gz, untared onto your system :-)

Configuration: how to configure RunPhp

If you have everything installed mentioned above, edit ''/etc/lighttpd/RunPhp/RunPhp.ini''. What you will find here:


#!ini
[examplehost1.exampledomain.com]
# The FCGI socket on your system. This will be used by lighttpd.
fCgiSocket = /tmp/examplehost1.exampledomain.com.php.sock
# php's safe_mode variable
safeMode = On
# php's open_basedir variable
openBaseDir = /var/www/examplehost1.exampledomain.com/examplehost1.exampledomain.com
# php's doc_root variable
docRoot = /var/www/examplehost1.exampledomain.com/examplehost1.exampledomain.com
# php's upload_tmp_dir variable
uploadTmpDir = /var/www/examplehost1.exampledomain.com/examplehost1.exampledomain.com/admin/tmp
# php's zlib_output_compression variable
zlibOutputCompression = On
# php's disable_functions variable
disableFunctions = escapeshellarg,escapeshellcmd,exec,passthru,proc_close,proc_get_status,proc_nice,proc_open,proc_terminate,shell_exec,system
# The userid, the spawned php will have this userid
userId = exampleuser1
# The group id, the spawned php will have this group id
groupId = examplegroup1
# php's register_globals variable
registerGlobals = On
# php's magic_quotes_gpc variable
magicQuotesGpc = On
# php's magic_quotes_runtim variable
magicQuotesRuntime = Off
# php's magic_quotes_sybase variable
magicQuotesSybase = Off

[examplehost2.exampledomain.com]
fCgiSocket = /tmp/examplehost2.exampledomain.com.php.sock
safeMode = Off
openBaseDir = /var/www/examplehost2.exampledomain.com/examplehost2.exampledomain.com
docRoot = /var/www/examplehost2.exampledomain.com/examplehost2.exampledomain.com
uploadTmpDir = /var/www/examplehost2.exampledomain.com/examplehost2.exampledomain.com/admin/tmp
zlibOutputCompression = Off
disableFunctions = function1,function2,etc
userId = exampleuser2
groupId = examplegroup2
registerGlobals = Off
magicQuotesGpc = Off
magicQuotesRuntime = Off
magicQuotesSybase = Off

This file contains all the settings now can be used with the spawned PHP-FastCGI processes. You can edit them according to your needs. The section name contains the virtualhost's name, this name will be used later for starting/restarting one virtualhost. Read about this below.

You can add more sections for more virtualhosts, they will spawn when you run RunPhp next time.

Command line usage

For easier usage, i symlinked ''/etc/lighttpd/RunPhp/RunPhp.py_ to /usr/bin/runphp'', so you'll have a _runphp command if you untared the tarball correctly.

Command line usage is simple:

A simple runphp command will run all sections it founds in the ''/etc/lighttpd/RunPhp/RunPhp.ini'' configuration file. If you supply a virtualhost's name (e.g. section name), only that one will be started/restarted.

Tweaking RunPhp

If you need more unique variables per virtualhost (a configuration option you can't find in the RunPhp.ini file), you can easily expand RunPhp. I'll show it through an example.

Imagine we need safe_mode_exec_dir to be unique per virtualhost, so we do the following:

  1. Edit ''/etc/lighttpd/RunPhp/php.ini'', this is the skeleton configuration used for every spawned PHP-FastCGI processes. Replace "safe_mode_exec_dir = <something>" to "safe_mode_exec_dir = safeModeExecDir" (case-sensitive is for terminology)
  2. Edit ''/etc/lighttpd/RunPhp/RunPhp.py'', add the following line between line 56 and 57 (keep the indenting):

'safeModeExecDir' : '<default_value_you_want>',
... so this way you will have a default value for safe_mode_exec_dir, even if you haven't added it to your RunPhp.ini sections. (of course, change default_value_you_want to your wanted default value!) # Edit your ''/etc/lighttpd/RunPhp/RunPhp.ini'', and add ''"safeModeExecDir = <your_unique_value>"'' to every section. This is not really necessary because if you did the second step correctly, safe_mode_exec_dir will have a default value specified in the python source. In that case, you have to add this line only into the needed sections.

After that tweak, you have to restart that virtualhost which you want to have that new value to use.

Thats all. :-)

Questions?

You can freely contact the author, Laszlo Karolyi (laszlo-AT-karolyi-DOT-hu).

Have fun!

Laszlo KAROLYI

Changelog

2007-03-03 Update: I changed my kernel from 2.4.x to 2.6.x, and with that kernel generated php inifiles could be deleted earlier as they could be read and parsed up, so i updated the script not to delete them. Now it uses a directory called 'createdConfigs' to hold these files, so php can read them at any time.

RunPhp.tar.gz (16.9 KB) LaszloKAROLYI, 2007-03-03 19:52