All Tech Php

Chroot Apache

Introduction

Chrooting makes a process believe that the root of the file system is higher up in the hierarchy. For example, if a process was chrooted in /home/www, the process would see it's root, /, as whatever was in /home/www. Everything else in the file hierarchy is nonexistent as far as the chrooted process is concerned. Chrooting is not a magic bullet, solving all your security needs. It instead puts up a road block from the hacked and exploited process from taking over an entire system. If anything, it slows down an attack and keeps an administrator from having to blow out an entire server when a hack occurs. This is important for apache running modules like PHP where a bad coding could compromise the entire server. Chrooting apache or other programs are just small steps to having a more secure server. It is not impossible to break out, just like with road blocks, there is always some way around them. In addition to chroot, FreeBSD's jail should also be looked at as an option. A jailed process has certain system calls restricted and can only bind to a single IP address.

Planning

In order to chroot apache, you will need to decide where everything will reside. For this example I will place apache into /home/www/ which will look like / to apache. In addition I must create a /home/www/sbin, /home/www/tmp, /home/www/var, /home/www/usr, /home/www/usr/lib, /home/www/dev,etc... I will also need my own device nodes just like /dev has. With current versions of FreeBSD you should use devfs, but if you are using old version you will have to stick to using mknod. I will need to create null, random, urandom, and crypto.

# devfs_domount /home/www/dev devfsrules_hide_all
# devfs -m /home/www/dev rule apply path null unhide
# devfs -m /home/www/dev rule apply path random unhide
# devfs -m /home/www/dev rule apply path urandom unhide
# devfs -m /home/www/dev rule apply path crypto unhide

These are just some basic ones that you will needed, you will find out later if any others are needed. I will also need to add a user "www" to our chroot so that apache can hand off the process to this user. In /home/www/etc I created the file passwd with only the single www user and also create the group file and add the single "www" group to it.

# echo "www:*:80:80::::::" > /home/www/etc/passwd
# echo "www:*:80:" > /home/www/etc/group

Now I just need to create a password database using pwd_mkdb a such:

# pwd_mkdb -d /home/www/etc /home/www/etc/passwd

Configuration

To make things easier I append my own "chroot" layout to config.layout in the main directory of the apache source as such:


prefix: /home/www
exec_prefix: ${prefix}
bindir: ${exec_prefix}/bin
sbindir: ${exec_prefix}/sbin
libdir: ${exec_prefix}/usr/lib
libexecdir: ${exec_prefix}/usr/libexec/apache2
mandir: ${prefix}/usr/man
sysconfdir: ${prefix}/etc/apache2
datadir: ${prefix}/websites
installbuilddir: ${prefix}/usr/share/apache2
errordir: ${datadir}/error
iconsdir: ${datadir}/icons
htdocsdir: ${datadir}/htdocs
manualdir: ${prefix}/usr/share/doc/apache2
cgidir: ${datadir}/cgi-bin
includedir: ${prefix}/usr/include/apache2
localstatedir: /var
runtimedir: ${localstatedir}/run
logfiledir: ${localstatedir}/log
proxycachedir: ${datadir}/proxy
infodir: ${exec_prefix}/usr/share/info

I compile and install as usual but add the --with-layout=chroot option to configure. I then move /etc/apache2 to /home/www/etc/apache2 and create a symbolic link from it's current location to its old.

# ln -s /home/www/etc/apache2 /etc/apache2

Libraries

To see which libraries httpd need I will use ldd:

# ldd /home/www/sbin/httpd

This will give me a nice starting point to copy libs into our chroot. For further testing, I will start up httpd with the -t option:
# chroot /home/www/ /sbin/httpd -t

Apache is nice enough to print out what libraries it needs. I will also use truss to see that files and devices httpd is trying to access and what failures occur. Linux users can try strace

# truss chroot /home/www /sbin/httpd -k start -d /

Truss will give me exactly what httpd is looking for and where it's looking. Copy those files over into /home/www/lib and httpd should find them. My httpd.conf needed to be edited to reflect the new file hierarchy. Anywhere there was a "/home/www", I replaced it with a "/". Truss will again come in handy for telling me where httpd is looking especially for files and directories in the chrooted /var that I always forget about. With everything in place, we just need to start and stop apache.

Starting & Stopping

To start and stop the chrooted apache server I created a shell script at /usr/local/etc/rc.d/apache22 which contains the following:

#!/bin/sh

# PROVIDE: apache22
# REQUIRE: NETWORKING SERVERS
# BEFORE: DAEMON
# KEYWORD: FreeBSD shutdown

#
# Add the following lines to /etc/rc.conf to enable apache22:
# apache22_enable (bool): Set to "NO" by default.
# Set it to "YES" to enable apache22
# apache22limits_enable (bool):Set to "NO" by default.
# Set it to yes to run `limits $limits_args`
# just before apache starts.
# apache22_flags (str): Set to "" by default.
# Extra flags passed to start command.
# apache22limits_args (str): Default to "-e -C daemon"
# Arguments of pre-start limits run.
# apache22chroot (str): Set chroot directory.
. /etc/rc.subr

name="apache22"
rcvar=`set_rcvar`

start_precmd="apache22_precmd"
stop_postcmd="apache22_postcmd"
restart_precmd="apache22_checkconfig"
reload_precmd="apache22_checkconfig"
reload_cmd="apache22_graceful"
graceful_cmd="apache22_graceful"
gracefulstop_cmd="apache22_gracefulstop"
start_cmd="apache22_start"
command="/sbin/httpd"

[ -z "$apache22_enable" ] && apache22_enable="NO"
[ -z "$apache22_flags" ] && apache22_flags=""
[ -z "$apache22limits_enable" ] && apache22limits_enable="NO"
[ -z "$apache22limits_args" ] && apache22limits_args="-e -C daemon"
[ -z "$apache22_chrootdir" ] && apache22_chrootdir="/home/www"

pidfile="$apache22_chrootdir/var/run/httpd.pid"
required_dirs="$apache22_chrootdir"

load_rc_config $name

apache22_checkconfig()
{
echo "Performing sanity check on apache22 configuration:"
chroot ${apache22_chrootdir} ${command} ${apache22_flags} -t
}

apache22_graceful() {
echo "Performing a graceful restart"
chroot ${apache22_chrootdir} ${command} -k graceful
}

apache22_gracefulstop() {
echo "Performing a graceful stop"
chroot ${apache22_chrootdir} ${command} -k graceful-stop
if [ -n "${apache22_chrootdir}" -a -c ${apache22_chrootdir}/dev/null ]; then
umount ${apache22_chrootdir}/dev 2>/dev/null || true
fi
}

apache22_precmd()
{
if checkyesno apache22limits_enable
then
eval `/usr/bin/limits ${apache22limits_args}` 2>/dev/null
else
return 0
fi
if [ -f /etc/localtime ]; then
cmp -s /etc/localtime "${apache22_chrootdir}/etc/localtime" ||
cp -p /etc/localtime "${apache22_chrootdir}/etc/localtime"
fi
umount ${apache22_chrootdir}/dev 2>/dev/null
devfs_domount ${apache22_chrootdir}/dev devfsrules_hide_all
devfs -m ${apache22_chrootdir}/dev rule apply path null unhide
devfs -m ${apache22_chrootdir}/dev rule apply path random unhide
devfs -m ${apache22_chrootdir}/dev rule apply path urandom unhide
devfs -m ${apache22_chrootdir}/dev rule apply path crypto unhide
}

apache22_start()
{
umount ${apache22_chrootdir}/dev 2>/dev/null
devfs_domount ${apache22_chrootdir}/dev devfsrules_hide_all
devfs -m ${apache22_chrootdir}/dev rule apply path null unhide
devfs -m ${apache22_chrootdir}/dev rule apply path random unhide
devfs -m ${apache22_chrootdir}/dev rule apply path urandom unhide
devfs -m ${apache22_chrootdir}/dev rule apply path crypto unhide
chroot ${apache22_chrootdir} ${command} ${apache22_flags} -k start
}

apache22_postcmd()
{
if [ -n "${apache22_chrootdir}" -a -c ${apache22_chrootdir}/dev/null ]; then
umount ${apache22_chrootdir}/dev 2>/dev/null || true
fi
}

extra_commands="reload graceful gracefulstop"
run_rc_command "$1"

This startup script only works on FreeBSD 5+ servers.

Modules

The apache modules such as php can be installed just as they would otherwise. Just use apxs installed at /home/www/sbin/apxs and you are all set. The only difference is that you will need to use truss once again to see what the new module needs. Php for example has its own modules that all can have their own requirements as far as libraries are concerned. Follow the same procedure as before to get the system libraries into the chroot.

MH @ Sep 05th 2007 12:35PM
I have updated this page to reflect changes with newer versions of FreeBSD. Mostly the changes show how to use devfs rather then mknod. devfs showed up in FreeBSD 5, and I haven't used mknod since. --MH
 
Add your comments:
Name:
Verify: verify