Here is a thorough howto on how I built a chrooted apache using mod_chroot. Along with chroot, I'm using mod_security for basic filtering and the suhosin PHP extension for adding additional security to the core of PHP on the server.
As some of you know, adding additional security, you often take away functionality and usability. These techniques won't be for everyone, and the learning curve is slight steep for debugging the initial problems. Once you learn what to look for and how to correct them, thing become much easier.
There are many ways to skin the chroot cat, and my way is just one of those ways. I've used mod_security's chroot in the past and how mod_chroot and mod_security apply the chroot are very similar, so you could either one. I want to still maintain mod_security due to it's great handling of automated script attacks and comment spam handling.
This howto is focused to Gentoo linux, but could easily be applied to other distributions, please view mod_chroot's site for their documentation and caveats.
Issues to contend with on chrooting apache. The first issue is the placement of Mysql's socket. It needs to be in the jail for Apache to use it. This causes additional problems. When you execute mysql from shell, you need to provide the new socket location. Simple solution is to add alias to your .bashrc. The other issue are relating to other programs needing mysql (ie: snort, etc)
Another common problem is PHP's mail function no longer works in chroot. You'll need to compile a small binary (ie: mini_sendmail or nbsmtp. I went with mini_sendmail since I was familiar with it. There are a couple of problems with this, mini_sendmail will need a shell to run, so you're going to need to bring in a shell in the jail. Not ideal, but not sure what to do for all those apps using PHP's mail. The second problem with mini_sendmail is that it needs user accounts, so I brought over a stripped down /etc/passwd into the jail
You'll also notice that I created a jail in /var/chroot/apache instead of the common /chroot. This is due to the way I originally set up partitions. /var is the largest available partition for me, and didn't want to create another bind mount to create this. You may want to locate your jail in another location.
I mentioned that I'm using mod_security and suhosin. These configurations will not be discussed here, just the configuration of mod_chroot. Stay tuned for the others.
emerge -v mod_chroot
# make jail environment
mkdir -p /var/chroot/apache/var/www
mkdir bin dev etc lib tmp usr var
mount -t auto -o bind /var/www /var/chroot/apache/var/www
mount -t auto -o bind /tmp /var/chroot/apache/tmp/
mkdir bin lib sbin share
mkdir curl misc php
mount -t auto -o bind /usr/share/php /var/chroot/apache/usr/share/php
vi /etc/fstab and add mount points
/var/www /var/chroot/apache/var/www auto bind 0 0
/usr/share/php /var/chroot/apache/usr/share/php auto bind 0 0
/tmp /var/chroot/apache/tmp auto bind 0 0
# build var
mkdir run cache
chown apache:root /var/chroot/apache/var/cache/apache2
chown mysql:mysql /var/chroot/apache/var/run/mysqld
# build usr
cp /usr/share/curl/curl-ca-bundle.crt .
cp /usr/share/misc/file/magic* .
tar xjvf mini_sendmail-1.3.6.tar.gz
cp /usr/local/src/mini_sendmail-1.3.6/mini_sendmail .
# add the following to php.ini
sendmail_path = /usr/sbin/mini_sendmail -t -i
cp /usr/lib/libmagic.so.1 .
cp /usr/lib/libnss_nis.so .
cp /usr/bin/file .
# build lib
cp /lib/ld-linux.so.2 .
cp /lib/libc.so.6 .
cp /lib/libdl.so.2 .
cp /lib/libncurses.so.5 .
cp /lib/libnsl.so.1 .
cp /lib/libnss_compat.so.2 .
cp /lib/libnss_dns.so.2 .
cp /lib/libnss_files.so.2 .
cp /lib/libnss_nis.so.2 .
cp /lib/libz.so.1 .
# build etc
cp /etc/hosts .
cp /etc/ld.so.cache .
cp /etc/localtime .
cp /etc/nsswitch.conf .
cp /etc/passwd .
vi passwd (leave bin, mail, postmaster, nobody and apache)
cp /etc/resolv.conf .
cp /etc/services .
# build dev
mknod -m 444 urandom c 1 9
mknod -m 666 null c 1 3
# build bin
cp /bin/sh .
# configure mysql to drop socket so apache can use it now
socket = /var/chroot/apache/var/run/mysqld/mysqld.sock
socket = /var/chroot/apache/var/run/mysqld/mysqld.sock
# make sure you're env knows to where it's at.. simple create alias
alias mysql="mysql -S /var/chroot/apache/var/run/mysqld/mysqld.sock"
# now test
ps aux | grep apache (check to see if it's up)
tail /var/log/apache/error_log (look for chroot)
[Fri Apr 20 15:49:09 2007] [notice] mod_chroot: changed root to /var/chroot/apache.
[Fri Apr 20 15:49:09 2007] [notice] Apache configured -- resuming normal operations
Restart again to make sure all is good. Test your PHP mail script.
Two programs that will be absolutely invaluable are strace and ldd. When things go wrong, run apache with strace and view the output. Look for (No such file or directory) messages. These are libraries or files that are missing in the chroot. I ran into an interesting problem with fsockopen trying to connect to a host. It would fail due to getaddrname in PHP, after running strace, I discovered that it was missing DNS library (libnss_dns.so.2). Strace is to trace system calls and signals. Simple usage would be this:
strace -o mytrace -fF apache2 -D DEFAULT_VHOST -D PHP4 -D SSL -D SSL_DEFAULT_VHOST -D CHROOT
ldd shows shared libraries. When you bring in binaries into the jail and they don't work, look at their shared dependencies. For example, I wanted file to do approver look ups. I wanted to know what I need to bring in:
linux-gate.so.1 => (0xb7ee9000)
libmagic.so.1 => /usr/lib/libmagic.so.1 (0xb7ed1000)
libz.so.1 => /lib/libz.so.1 (0xb7ebd000)
libc.so.6 => /lib/libc.so.6 (0xb7d96000)
For `file` to work, I need these libraries in the jail. Using ldd is great!
Now while apache is running in the jail, it won't be able to 'reload'. Update your logrotate script to restart or it will die in the middle of the night!
#/etc/init.d/apache2 reload > /dev/null 2>&1 || true
/etc/init.d/apache2 restart > /dev/null 2>&1 || true