Project

General

Profile

Actions

HowToSetupFastCgiIndividualPermissions » History » Revision 17

« Previous | Revision 17/39 (diff) | Next »
Anonymous, 2007-02-06 17:20
server.errorlog not working in Conditionals


= Setup FastCGI and PHP with individual user permissions =

''First of all: please notice that this how-to is only a suggestion on how to do this, so please don't blame anybody if you prefer to do things differently, or get mad customers, or whatever....''

''Note: This only works on *nix like operating systems. I don't know how to do this on Windows.''

Introduction

Running a website hosting service for individual users/customers requires some extra brain-work when you set up your web-server.

Basically, you give every user an individual (ordinary) user account on your web-server. The user then uploads her PHP script files to her own virtual host document root.

What we want to do, is to execute all PHP script files with the exact same user permissions as the user that manages the virtual host in question. If this is accomplished, you can be sure that none of your users will be able to browse through other users' PHP scripts.

Consider the following PHP script executed on a web-server without individual user permissions on PHP-scripts (please do not attempt to do this, since you might end up with the police knocking on your door!):

{{{
#!php
$filename = "/path_to_other_users_vhost_root/index.php";
$handle = fopen($filename, "rb");
$contents = fread($handle, filesize($filename));
fclose($handle);

echo $contents;
?>
}}}

This will read (and show) the source code of PHP script of some other user. The source code might contain passwords that gives access to that user's MySQL databases, or other interesting stuff. You could even make a PHP script that writes PHP script files to other user's virtual host directories!

This is the setup we want to get rid of!

What about PHP's built-in safe_mode

I will not say any bad things about PHP here, and you could probably just use PHP's built-in ''safe_mode'' features. (See the [http://www.php.net/manual/en/features.safe-mode.php safe_mode documentation at php.net] for a detailed description.)

However, if you rely on your operating system's build-in user permissions, you will be better off. (You can even combine the two, if you're completely paranoid.)

= Installation =

We assume that you already have Lighttpd installed, and installed PHP with FastCGI support. ([http://trac.lighttpd.net/trac/wiki/TutorialLighttpdAndPHP How to install PHP with FastCGI support])

You need to log in as ''root'' to do this.

1. Add users to the operating system

(This is only needed if you haven't added users yet.)

You must add a user account to the operating system for each user that you want to give separate user permissions, in order to deny access to other users' source code.

Let's assume that we need to create three users (fred, george, and ron):

{{{
#!ShellExample
  1. useradd fred
  2. useradd george
  3. useradd ron
    }}}
2. Add user groups to the operating system

You need to add one user group for each user added above. To keep things simple, we just name the user groups similar:

{{{
#!ShellExample
  1. groupadd fred
  2. groupadd george
  3. groupadd ron
    }}}

Now you need to add users to each of these user groups. For each user group, there must be two members: the corresponding user and the lighttpd daemon user.

You configure the user groups by editing /etc/group with your favourite text editor.

The file must look something like this (group numbers may vary):

{{{
..... [lots of stuff above]
fred:x:441:fred,lighttpd
george:x:442:george,lighttpd
ron:x:443:ron,lighttpd
}}}

3. Set up filesystem structure

Let's assume that you want to keep all files associated with the web-server's virtual hosts under the directory ''/var/www''. (Of course you can choose another location, just make sure that the users created above have read and execute rights to the directory. (I.e. ''chmod 755 /var/www && chown root:root /var/www'').

3.1 Create server root directory

Now, create two directories: One for some start-up scripts that only ''root'' have access to, and another for all your virtual hosts:

{{{
#!ShellExample
  1. cd /var/www
  2. mkdir fastcgi
  3. mkdir vhosts
  4. chown lighttpd:lighttpd *
  5. chmod 755 *
  1. ls -l /var/www

drwxr-xr-x 2 lighttpd lighttpd 4096 Feb 15 12:17 fastcgi
drwxr-xr-x 9 lighttpd lighttpd 4096 Feb 15 11:21 vhosts
}}}

3.2 Create a directory for each virtual host

Now create a directory for each virtual host in the directory ''/var/www/vhosts'', and set up appropriate user rights to them:

{{{
#!ShellExample
  1. cd /var/www/vhosts
  2. mkdir fred-weasley.com
  3. mkdir george-weasley.com
  4. mkdir ron-weasley.com
  5. chown fred:fred fred-weasley.com
  6. chown george:george george-weasley.com
  7. chown ron:ron ron-weasley.com
  8. chmod 750 *
  1. ls -l /var/www/vhosts

drwxr-x--- 7 fred fred 4096 Feb 15 20:18 fred-weasley.com
drwxr-x--- 6 george george 4096 Feb 15 11:02 george-weasley.com
drwxr-x--- 6 ron ron 4096 Feb 15 11:23 ron-weasley.com
}}}

Now we have created three directories where the three users cannot see each others' files; however, the ''lighttpd'' daemon user can see it all.

3.3 Create directory structure for each virtual host

Now, we want to create the directory struture needed for each virtual host:

{{{
#!ShellExample
  1. cd /var/www/vhosts/fred-weasley.com
  2. mkdir html
  3. mkdir includes (optional)
  4. mkdir logs
  5. chown fred:fred *
  6. chown lighttpd:fred logs
  7. chmod 750 *
  1. ls -l /var/www/vhosts/fred-weasley.com

drwxr-x--- 14 fred fred 4096 Feb 17 11:55 html
drwxr-x--- 2 fred fred 4096 Feb 15 12:05 includes
drwxr-x--- 2 lighttpd fred 4096 Feb 15 11:11 logs
}}}

'''You need to repeat this for each virtual host, replacing the user name 'fred' with the appropriate user name.'''

3.4 Create a FastCGI directory for each user

Now we have to do all the fun stuff!

Now, go to the ''/var/www/fastcgi'' directory where we want to create a directory for each user. (When we're finished, these directories will hold the sockets to the FastCGI server processes):

{{{
#!ShellExample
  1. cd /var/www/fastcgi
  2. mkdir fred
  3. mkdir george
  4. mkdir ron
  5. chown fred:fred fred
  6. chown george:george george
  7. chown ron:ron ron
  8. chmod 750 *
  1. ls -l /var/www/fastcgi

drwxr-x--- 7 fred fred 4096 Feb 15 20:18 fred
drwxr-x--- 6 george george 4096 Feb 15 11:02 george
drwxr-x--- 6 ron ron 4096 Feb 15 11:23 ron
}}}

(Note that the lighttpd user can read all directories, while the three users can only access their own directory.)

4. Create a FastCGI start-up script for each user

Create a directory that will hold all your FastCGI start-up scripts:

{{{
#!ShellExample
  1. cd /var/www/fastcgi
  2. mkdir startup
  3. chmod 750 startup
  1. ls -l /var/www/fastcgi

drwxr-x--- 7 fred fred 4096 Feb 15 20:18 fred
drwxr-x--- 6 george george 4096 Feb 15 11:02 george
drwxr-x--- 6 ron ron 4096 Feb 15 11:23 ron
drwxr-x--- 6 root root 4096 Feb 15 11:23 startup
}}}

Now, go to the ''/var/www/fastcgi/startup'' directory, create a start-up script for ''fred'' (let's call it ''fred-startup.sh'', using your favourite text editor:

{{{
#!sh
#!/bin/bash

  1. ABSOLUTE path to the spawn-fcgi binary
    SPAWNFCGI="/usr/bin/spawn-fcgi"
  1. ABSOLUTE path to the PHP binary
    FCGIPROGRAM="/usr/bin/php-cgi"
  1. bind to tcp-port on localhost
    FCGISOCKET="/var/www/fastcgi/fred/fred.socket"
  1. uncomment the PHPRC line, if you want to have an extra php.ini for this user
  2. store your custom php.ini in /var/www/fastcgi/fred/php.ini
  3. with an custom php.ini you can improve your security
  4. just set the open_basedir to the users webfolder
  5. Example: (add this line in you custom php.ini)
  6. open_basedir = /var/www/vhosts/fred/html ##
    #PHPRC="/var/www/fastcgi/fred/"
  1. number of PHP childs to spawn in addition to the default. Minimum of 2.
  2. Actual childs = PHP_FCGI_CHILDREN + 1
    PHP_FCGI_CHILDREN=5
  1. number of request server by a single php-process until is will be restarted
    PHP_FCGI_MAX_REQUESTS=1000
  1. IP adresses where PHP should access server connections from
    FCGI_WEB_SERVER_ADDRS="127.0.0.1"
  1. allowed environment variables sperated by spaces
    ALLOWED_ENV="PATH USER"
  1. if this script is run as root switch to the following user
    USERID=fred
    GROUPID=fred
  1. no config below this line

if test x$PHP_FCGI_CHILDREN = x; then
PHP_FCGI_CHILDREN=5
fi

export PHP_FCGI_MAX_REQUESTS
export FCGI_WEB_SERVER_ADDRS
export PHPRC

ALLOWED_ENV="$ALLOWED_ENV PHP_FCGI_MAX_REQUESTS FCGI_WEB_SERVER_ADDRS PHPRC"

  1. copy the allowed environment variables
    E=

for i in $ALLOWED_ENV; do
E="$E $i=${!i}"
done

  1. clean environment and set up a new one
    env - $E $SPAWNFCGI -s $FCGISOCKET -f $FCGIPROGRAM -u $USERID -g $GROUPID -C $PHP_FCGI_CHILDREN

chmod 770 $FCGISOCKET
}}}

Please be careful with the paths, USERID and GROUPID.

Note that, in this example, the php process runs as the user we created above
('fred'). This means that the php code will have write access to the html and
php files. This can be convenient, but might be a security risk.
Alternatively, you could set USERID to 'nobody' (or any other user without any
specific permissions), to deny write access to the php process.

You need to repeat the process and create a startup-script for each user in the ''/var/www/fastcgi/startup'' directory. (Just copy the file and replace FCGISOCKET, USERID and GROUPID with the correct values).

Remember to set execute permissions on all your startup-scripts:

{{{
#!ShellExample
  1. cd /var/www/fastcgi/startup
  2. chmod 750 *
    }}}
5. Check your PHP configuration

If you're uncertain about the location of your php.ini, just run the following command:

{{{
#!ShellExample
$ php-cgi -i | grep php.ini
}}}

Please check, that you have the following line in your php.ini:

{{{
cgi.fix_pathinfo=1
}}}

6. Execute all FastCGI start-up scripts

Now, fire up all your FastCGI server processes:

{{{
#!ShellExample
  1. /var/www/fastcgi/startup/fred-startup.sh
    spawn-fcgi.c.170: child spawned successfully: PID: xxxxx
  2. /var/www/fastcgi/startup/george-startup.sh
    spawn-fcgi.c.170: child spawned successfully: PID: xxxxx
  3. /var/www/fastcgi/startup/ron-startup.sh
    spawn-fcgi.c.170: child spawned successfully: PID: xxxxx
    }}}

If you get any error messages, please re-check your startup-scripts and the permissions to the ''/var/www/fastcgi'' directory, including all user sub-directories.

7. Configure virtual hosts in the lighttpd server

Edit ''/etc/lighttpd.conf'' in your favourite text-editor:

{{{

.....[lots of configuration stuff above].....

$HTTP["host"] =~ "(^|\.)fred-weasley.com$" {
server.document-root = "/var/www/vhosts/fred-weasley.com/html"
server.errorlog = "/var/www/vhosts/fred-weasley.com/logs/error_log"
accesslog.filename = "/var/www/vhosts/fred-weasley.com/logs/access_log"
fastcgi.server = ( ".php" =>
(
( "socket" => "/var/www/fastcgi/fred/fred.socket",
"check-local" => "disable",
"broken-scriptfilename" => "enable"
)
)
)
}

$HTTP["host"] =~ "(^|\.)george-weasley.com$" {
server.document-root = "/var/www/vhosts/george-weasley.com/html"
server.errorlog = "/var/www/vhosts/george-weasley.com/logs/error_log"
accesslog.filename = "/var/www/vhosts/george-weasley.com/logs/access_log"
fastcgi.server = ( ".php" =>
(
( "socket" => "/var/www/fastcgi/george/george.socket",
"check-local" => "disable",
"broken-scriptfilename" => "enable"
)
)
)
}

$HTTP["host"] =~ "(^|\.)ron-weasley.com$" {
server.document-root = "/var/www/vhosts/ron-weasley.com/html"
server.errorlog = "/var/www/vhosts/ron-weasley.com/logs/error_log"
accesslog.filename = "/var/www/vhosts/ron-weasley.com/logs/access_log"
fastcgi.server = ( ".php" =>
(
( "socket" => "/var/www/fastcgi/ron/ron.socket",
"check-local" => "disable",
"broken-scriptfilename" => "enable"
)
)
)
}

}}}

''Please note the paths to the FastCGI sockets for each virtual host.''
''server.errorlog is NOT working in conditionals, all errors go to the last logfile specified See http://trac.lighttpd.net/trac/ticket/665 ''

8. Restart the lighttpd daemon process

Simply run this command:

{{{
#!ShellExample
  1. /etc/init.d/lighttpd restart
    }}}

If you get any errors, please re-check your ''/etc/lighttpd.conf'' configuration file.

9. Hello World!

Now, log in as the user ''fred'' and create a PHP script file in his virtual host (e.g. ''/var/www/vhosts/fred-weasley.com/html/index.php''):

{{{
#!php
echo "<h1>Hello World!</h1>";
echo "<p>Current User ID is: ". posix_getuid();
echo "<p>Current Group ID is: ". posix_getgid();
?>
}}}

Also, make sure to set the file permissions:

{{{
#!ShellExample
  1. chown fred:fred /var/www/vhosts/fred-weasley.com/html/index.php
  2. chmod 640 /var/www/vhosts/fred-weasley.com/html/index.php
  1. ls -l /var/www/vhosts/fred-weasley.com/html

rw-r---- 1 fred fred 116 Jul 25 2004 index.php
}}}

Now fire up your web-browser and check the output of your PHP script. (Here: http://www.fred-weasley.com/index.php)

If everything went well, you will see an output showing the User ID of the user ''fred'', and the Group ID of the user group ''fred''. (You can see these IDs in the files ''/etc/passwd'' and ''/etc/group'').

10. Automatically start the FastCGI startup scripts

Optionally, you may also create a crontab entry to automatically execute the FastCGI startup scripts when your server boots.

Use the following command to edit your crontab:

{{{
  1. crontab -e
    }}}

Now add the following line:

{{{
@reboot for i in /var/www/fastcgi/startup/*.sh; do $i; done
}}}

And finally type ":x" to save and exit.

This crontab entry will execute all .sh files found in the /var/www/fastcgi/startup directory after the server has booted.

Congratulations! You now have a working fast server configuration with individual (separate) user rights.

Updated by Anonymous about 17 years ago · 17 revisions