Apache chroot(2) patch

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.

History

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...

Overview

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.

Applying the patch

     $ tar zxvf apache_1.3.31.tar.gz
     $ patch < /some/where/apache_1.3.31.chroot.patch

Compiling and installing Apache

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

Configuring Apache to use chroot

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>

Setting up the chroot environment

Some general rules when setting up a chroot(2) environment for Apache:

  1. Keep the chroot environment as empty as possible, the best one would be one with only a /index.html file.
  2. Make sure that the directories and files in the chroot environment cannot be written to by the userid and groupid under which Apache is running. This means using different userids/groupids for running Apache and owning the content, and make sure files are not world writable.
  3. If, for example, a CGI script needs to write data somewhere then create a special sub directory and make only that directory writable for the Apache userid/groupid. Never use the top of the chroot environment for this.
  4. There should be no, I repeat no, setuid-root programs in the chroot environment. Even better, avoid anything that is setuid or setgid. A process in a chroot environment that can become root can escape from the chroot environment.
  5. In case binaries are needed (as CGI scripts) it is suggested you link them statically. It is however also possible to use dynamically linked binaries but then you need to add shared libraries to the chroot environment (how to do this differs per platform).
  6. CGI scripts using Perl or another scripting language require the interpreter and support files to be present in the chroot environment. Note that adding scripting languages like Perl severely violates rule 1.
  7. Because most buffer overflow exploit code tries to do funny things with /bin/sh, you better avoid having a /bin/sh in the chroot environment.

Running 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.

Limitations

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'.

TODO

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).

Disclaimer

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

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.


Arjan.deVet@adv.iae.nl, last modified 2006/08/17 15:13h CEST