ClueNet is looking for a new Chief Technical Admin. More information here.
Shellsnet:Chroot Apache
From ClueWiki
Important Note: This is a backed up copy of what existed on Shellsnet's old wiki.
This article is a part of the ShellsNet Old Wiki Backup index
Do not modify this page!
Contents |
[edit] Overview
Apache runs as an unprivileged user on most systems. However, it also has access to almost all of the filesystem. If a malicious user is able to to gain control of Apache, he has nearly unlimited access to your system. By using a chroot jail, you effectively change the root directory of the program you are running. Therefore, if an attacker does manage to gain control of Apache, he only has access to certain files, rather than the whole system.
This document is a guide to assist in getting Apache chroot, and also making PHP and suPHP run inside the chroot. This example is based on a FreeBSD 6.X system. Your file locations and install methods may vary.
[edit] File System Considerations
Because Apache will now be chroot, this means that your user directories will no longer be accessible to Apache. We have to move all user directories to the chroot filesystem in order for them to work correctly. This means we'll need extra space to hold the extra files. I would suggest that you create a seperate partition, with enough capacity to hold your system website, a few libraries, and also your user websites. If you have, or plan on having a lot of users, make sure you make your new partition big enough to hold all the files.
On my system, my chroot directory is a seperate partition, mounted at /www. Throughout the rest of this document, I will refer to the chroot directory as /www.
After you have your new partition or directory set up, add two new directories to it: /www/data and /www/users. We will use /www/data as the document root directory, and /www/users as the users' public_html directories.
[edit] Installing Apache
I prefer to have SSL+Apache. On the BSD's, this is easy enough:
cd /usr/ports/www/apache13-modssl && make install
After compilation completes you will need to put apache_enable="YES" in your /etc/rc.conf in order for Apache to work. Also , don't forget to edit your /usr/local/etc/apache/httpd.conf to suit your needs. Fire up Apache:
/usr/local/etc/rc.d/apache.sh start
Check to make sure everything is working by visiting your domain. You should see an "It Worked!" page. Let's move on.
[edit] Getting Apache chroot
Now that Apache is installed, we need to get it chroot. This used to be a very long and complex process, but is now much easier thanks to mod_security. mod_security will open up all the files and modules that Apache needs, and THEN chroot Apache. This is almost too easy:
cd /usr/ports/www/mod_security && make install
After mod_security is installed, edit your httpd.conf again and add the following:
SecFilterEngine On
SecFilterCheckURLEncoding On
SecFilterCheckUnicodeEncoding Off
SecFilterForceByteRange 0 255
SecAuditEngine RelevantOnly
SecAuditLog /www/logs/audit_log
SecFilterDebugLog /www/logs/modsec_debug_log
SecFilterDebugLevel 0
SecFilterScanPOST On
SecFilterDefaultAction "deny,log,status:500"
SecChrootDir /www
Note that these are very basic rules for the mod_security filter. More detailed setups can be found by searching google. The one that you should note is the SecChrootDir as it is the directive that tells where to chroot Apache. Adjust it to suit your needs.
You will also need to adjust the DocumentRoot directive to point to /data and the UserDir directive to point to /users/. You may be saying "But the path is /www/data and /www/users". Yes, this is true, but remember, Apache chroots to /www so it thinks /www is actually /.
Now just restart apache via apachectl restart. Check your logs, you should see something like this if mod_security actually chroots Apache:
[Sun Feb 26 11:25:16 2006] [notice] mod_security: chroot checkpoint #1 (pid=97671 ppid=97669) [Sun Feb 26 11:25:16 2006] [notice] mod_security/1.9.2 configured [Sun Feb 26 11:25:17 2006] [notice] mod_security: chroot checkpoint #2 (pid=97673 ppid=1) [Sun Feb 26 11:25:17 2006] [notice] mod_security: chroot successful, path=/www [Sun Feb 26 11:25:18 2006] [notice] Apache/1.3.34 (Unix) mod_gzip/1.3.26.1a configured -- resuming normal operations [Sun Feb 26 11:25:18 2006] [notice] Accept mutex: flock (Default: flock)
If you see that, then you have successfully gotten Apache to chroot. Pat yourself on the back. That was easy, now we move on to more meaty details.
[edit] User Directories
Since Apache is now chroot, it no longer has access to /home/user/public_html. How do we solve this? Well, each user will now require a directory in /www/users/. You can create each directory by hand, or write scripts to move all the files over to their proper directories. Why don't we just symlink /www/users/username to /home/username/public_html? It won't work. By definition, the chroot cannot follow symlinks out of the chroot. It CAN follow hardlinks, but since /www is on a seperate partition, hardlinks won't work. However, you can symlink INTO the chroot, by making /home/username/public_html point to /www/users/username, therefore making this a transparent process to your users. Don't forget to enable quota's on /www!
[edit] PHP
Now comes the fun part, throwing PHP into the mix. This is probably the most difficult part of the equation. We will be installing PHP from ports and using the CGI version.
cd /usr/ports/www/php5-cgi/ && make install
This should also enable everything in your httpd.conf file. Next, install any PHP modules that you may want. My current list (from my /usr/local/etc/php/extensions.ini) includes:
extension=session.so extension=pcre.so extension=gd.so extension=mysql.so extension=mhash.so extension=gettext.so extension=mbstring.so extension=openssl.so extension=xml.so
The rest of the document will cover getting these modules to work correctly in the chroot. If you have more (or less) modules, the following instructions will require more (or less) work, depending on your module needs.
After you have all of your modules installed, you will need to start adding the proper directories to your /www partition. At a minimum, you will need:
/www/bin /www/etc /www/lib /www/libexec /www/tmp /www/var /www/usr/ /www/usr/local /www/usr/local/bin /www/usr/local/sbin /www/usr/local/etc /www/usr/local/lib /www/usr/local/lib/php /www/usr/local/lib/mysql /www/usr/local/etc/php
Copy your /usr/local/etc/php.ini to /www/usr/local/etc/php.ini and your /usr/local/etc/php/extensions.ini to /www/usr/local/etc/php/extensions.ini. You will also need to copy your PHP executable (usually in /usr/local/bin/php).
The idea is now that if PHP needs a library in /usr/local/lib/ it will look for that file in the same spot. This means that because it is now chroot, it will look in /usr/local/lib which *REALLY* means it will look in /www/usr/local/lib. We need to get all those files into the chroot area so that PHP can use them. The following is a complete listing of my /www partition. You will need ALL of the libraries and files if you are using the same PHP extensions as I am, and more if you have more extensions. Remember, if I show a listing for /www/lib, that means you can copy the files from /lib to /www/lib.
/www/lib: libc.so.6 libcrypt.so.3 libedit.so.5 libiconv.so.3 libm.so.4 libncurses.so.6 libz.so.3 /www/libexec: ld-elf.so.1 /www/usr/local/lib: libcrypto.so.4 libiconv.so.3 libmhash.so.2 libssl.so.4 mysql libexpat.so.6 libintl.so.6 libmm.so.14 libt1.so.5 php libfreetype.so.9 libjpeg.so.9 libpng.so.5 libxml2.so.5 /www/usr/local/lib/php/20050922: gd.so mbstring.so mysql.so pcre.so xml.so gettext.so mhash.so openssl.so session.so (these are the actual PHP modules. 20050922 maybe different on your system) /www/usr/local/lib/mysql: libmysqlclient.so.15
That should do it for required libraries for PHP.
[edit] PHP mail()
PHP's mail() function uses /bin/sh to send mail. It also, by default uses sendmail. What we need to do is setup this stuff in the chroot so that PHP can use it. We will use a very simple MTA named mini_sendmail inside the chroot that hands off mail to your real MTA.
cp /bin/sh /www/bin cd /usr/ports/mail/mini_sendmail && make install
After mini_sendmail compiles, copy it to your chroot:
cp /usr/local/bin/mini_sendmail /www/bin
Next, edit your /www/usr/local/etc/php.ini file to tell PHP to use mini_sendmail to send mail:
Find the line that says: ;sendmail_path = and change it to sendmail_path = /bin/mini_sendmail -t -i
DON'T FORGET TO REMOVE THE ; FROM THE FRONT OF THE LINE!
We have already copied the necessary libraries over, so that is all we need to get mail() to work. At this point, PHP should be fully functional inside the chroot, however, we still need to tell Apache how to handle .php files.
[edit] suPHP
suPHP is an Apache module that allows PHP scripts to be executed by the owner of the script. This is another safety mechanism to prevent users from abusing PHP. It allows you to see who actually ran scripts, and makes mail messages sent via PHP to show the actual user that sent the message, rather than showing Apache as the sender. Let's get it installed:
cd /usr/ports/www/suphp && make WITHOUT_CHECKPATH=yes install
Using the WITHOUT_CHECKPATH option let's the user directories work correctly. Since we are chroot now, this shouldn't be much of a security concern.
After compilation completes, copy the necessary files:
cp /usr/local/sbin/suphp /www/usr/local/sbin
Edit your /usr/local/etc/apache/httpd.conf and add the following:
suPHP_Engine on suPHP_ConfigPath /usr/local/etc AddHandler x-httpd-php .php
Next, edit/create /www/usr/local/etc/suphp.conf to look like this:
[global] ;Path to logfile logfile=/var/log/suphp.log ;Loglevel loglevel=info ;User Apache is running as webserver_user=www ;Path all scripts have to be in docroot=/ ;Path to chroot() to before executing script chroot=/ ; Security options allow_file_group_writeable=false allow_file_others_writeable=false allow_directory_group_writeable=false allow_directory_others_writeable=false ;Check wheter script is within DOCUMENT_ROOT check_vhost_docroot=false ;Send minor error messages to browser errors_to_browser=false ;PATH environment variable env_path=/bin:/usr/bin ;Umask to set, specify in octal notation umask=0077 ; Minimum UID min_uid=100 ; Minimum GID min_gid=100 [handlers] ;Handler for php-scripts x-httpd-php=php:/usr/bin/php ;Handler for CGI-scripts x-suphp-cgi=execute:!self
You can edit the file to suit your needs.
Now we need to tell suPHP what users and groups are allowed to execute. We need to copy the /etc/group and /etc/password files over
cp /etc/group /www/etc cp /etc/master.passwd /www/etc cd /www pwd_mkdb -d /www/etc /www/etc/master.passwd rm /www/etc/master.passwd
Each time you add a new user, you will need to rebuild the master.passwd, otherwise that user will not be allowed to execute PHP scripts. Consider removing root,toor, and other users you don't want running PHP scripts from your /www/etc/master.passwd before you do the pwd_mkdb command.
[edit] Finishing Up
Restart Apache and check your log files, most notably your httpd-error.log. If anything is missing, it will let you know. Copy the files that Apache says are missing to your chroot.
Congratulations! You have now chroot'ed Apache and PHP, and gotten suPHP to execute PHP scripts as a normal user!
[edit] Notes
[edit] MySQL
PHP will no longer be able to access MySQL through the local socket, unless you *HARDLINK* /tmp/mysql.sock to /www/tmp/mysql.sock. Symlinks won't work. You can rebuild MySQL and tell it to put the socket in /www/tmp, or you can code your scripts to connect via TCP/IP. I find doing it via TCP/IP the easier solution, although not as efficient. In PHP code, this would look like:
<?
mysql_connect('127.0.0.1');
?>
[edit] PHP system(), exec(), passthru()
These functions will also not work, because we are now in a chroot. If you need a certain external program, copy it to the chroot. Don't forget any libraries that the program may require!

