Last update: 2006/08/17 15:13h CEST
NEW: You can hire me for (security related) Apache consultancy and security audits, through my company Madison Gurkha.
Back when I was a graduate student at Eindhoven University of Technology I discovered the World Wide Web, Mosaic and NCSA httpd (somewhere in 1993 I guess). I wanted to run my own http daemon and asked one of our system administrators, Wietse Venema (of tcp-wrappers, Satan and Postfix fame), whether that was allowed. Wietse immediately saw the dangers of running an http daemon so the next day he came up with the chrootuid program so I could run NCSA httpd in a chrooted and least privilege environment.
Later I founded an ISP (IAE) and converted the chroot method used in chrootuid into built-in functionality for NCSA httpd, and later of course Apache. That's when the first version of this patch emerged (July 1994).
Since then I've been running Apache with this chroot(2) patch at all places where I have been employed since 1994. It only took some 7 years before I wrote this document...
This chroot(2) patch performs a chroot(2) call in the child processes when using the standalone mode of Apache. This means that after succesful completion of that chroot(2) call the child process is limited to a very small part of the filesystem.
If correctly setup, the file set in the chroot(2) 'jail' consists of only those files that may be viewed by untrusted anonymous visitors and nothing more.
That means that the Apache binary, loadable modules, configuration files and even log files are outside the chroot(2) 'jail'. When a malicious users succeeds in exploiting a bug in Apache or a CGI/whatever script running in that chroot(2) 'jail', the only information (s)he can get at is the data and programs (s)he would have had already access to anyway.
$ tar zxvf apache_1.3.31.tar.gz
$ patch < /some/where/apache_1.3.31.chroot.patch
Use the normal configure command to configure Apache:
$ cd apache_1.3.31
$ ./configure --prefix=/home/www --enable-module=most --enable-shared=max
$ make
$ make install
$ make distclean
The patch adds a new 'core' Apache directive: ChrootDir. Example:
ChrootDir "/home/www/htdocs"
It must be set before any DocumentRoot or <Directory "name"> settings in the httpd.conf file and must start with a "/". When using ChrootDir all further file references in DocumentRoot, <Directory> and <Files> should be relative to the ChrootDir.
Example:
ServerRoot "/home/www"
DocumentRoot "/home/www/htdocs"
<Directory "/home/www/htdocs">
Options Indexes
AllowOverride None
</Directory>
becomes:
ServerRoot "/home/www"
ChrootDir "/home/www/htdocs"
DocumentRoot "/"
<Directory "/">
Options Indexes
AllowOverride None
</Directory>
when using ChrootDir. Note that the "/" in DocumentRoot "/" and <Directory "/"> correspond with /home/www/htdocs/ on the real file system.
With this configuration the Apache child processes (that handle the requests) are restricted to the file system subtree starting at /home/www/htdocs/. This means that, e.g., /home/www/bin/ and /home/www/logs/ are not accessible by the child processes. In case of a buffer overflow it means that the exploit code has only access to the file system subtree starting at /home/www/htdocs.
Normally Apache creates an icons directory outside htdocs and uses an Alias to make them available:
Alias /icons/ "/home/www/icons"
When using ChrootDir those icons are not accessible anymore and the icons directory should be moved into the htdocs directory.
A more complete example:
ServerRoot "/home/www"
ChrootDir "/home/www/htdocs"
DocumentRoot "/"
<Directory "/">
Options Indexes
AllowOverride None
</Directory>
ScriptAlias /cgi-bin/ "/cgi-bin/"
<Directory "/cgi-bin">
Options None
AllowOverride None
</Directory>
Some general rules when setting up a chroot(2) environment for Apache:
Apache must be started as root in order to be able to use ChrootDir. It will report in error_log when it has been started chroot'ed and to which directory it has chroot'ed to.
Note that after succesful startup file names logged in error_log are relative to the ChrootDir.
After chroot'ing the Apache child processes still have 2 file descriptors with write access open (see lsof(8)), one to the access_log and one to the error_log. Malicious exploit code could therefore still do nasty things to these files even when these files are outside the chroot environment. On BSD based systems the append-only mode can be used to prevent this (see chflags(1)).
Malicious code can send signals to other processes 'outside' the chroot environment running under the same userid as Apache. Therefore make sure that Apache has its own userid, preferably not 'nobody'.
The patch is not #ifdef'ed to enable it solely on platforms that have a chroot(2) system call. Make sure your platform supports chroot(2) before trying this patch.
For FreeBSD I should consider making a jail(2) version of this patch (unless somebody else already did it). jail(2) is an improved version of chroot(2).
This patch assumes there are no security issues with chroot(2) on your system.
Chroot'ing Apache is not a solution that gives you near 100% security on its own, directory/file permissions, resource limits and many other things are needed as well.
This patch also assumes you are aware of the limitations of chroot(2), such as the root userid being able to step out of a chroot(2) jail.
Although I've successfully survived several NCSA httpd, Apache and other security problems with this patch since 1994, I cannot give any guarantees for this code. Inspect the code, use lsof(1) to check cwd/rtd and open files of the httpd processes, etc. and decide for yourself whether you trust this code.
Download the patch (for Apache 1.3.37, other versions may work).
The same patch suitable for putting it into the /usr/ports/www/apache13/files directory on a FreeBSD system for chroot support in the FreeBSD port or package.