May 27, 2004

Chrooting Apache

Author: Mike Peters

The chroot daemon allows you to run a program and have it see a given
directory as the root (/) directory. This effectively locks the process
into its very own filesystem ("chroot jail") isolated from the real /
filesystem. In this article we will look at how to install the Apache Web server in such an environment.

Installing Apache in a chroot jail does not make Apache itself any more
secure. Rather, it serves to restrict the access of Apache and its child processes to a small subset of the filesystem. The advantage in chrooting a process is not in
preventing a breakin, but rather in containing a potential threat.

Before deciding whether you need to chroot your Web server you should consider
the advantages and disadvantages of such a setup.

Advantages

If Apache is compromised, an intruder will have access only to the files
within the chroot jail.

Potentially dangerous CGI scripts do not have access to your server's filesystem.

Your Web tree is contained in one area that's easy to back up and move.

Disadvantages

A chroot environment is more difficult to set up than a traditional install,
especially if you run external software such as Perl, PHP, MySQL, or Python.

The process is only viable if your entire Web tree can exist on a single filesystem.

Compiling and installing the Apache binary

There are no special steps needed to build the Apache binary in order to
install it in a chroot jail. The following steps apply equally to a precompiled binary (such as an RPM) or one you have compiled yourself. Starting with a working binary helps with debugging later, if necessary. Just make sure that you are using the latest patched version of the server, install Apache normally, and ensure that it is working as
expected.

Finally, make sure you configure Apache to run with its own user and
group IDs. Create a user and group with the commands:


# groupadd apache
# useradd -c "Apache Server" -d /dev/null -g apache -s /bin/false apache

These commands create the regular user apache and the apache group. Apache runs as
nobody by default. User nobody may be used
by many processes, and if it is compromised an intruder will gain access to all
processes on your system running under that UID.

Creating the chroot tree

Our chroot jail is a mini-version of the Linux filesystem. I prefer to use a
seperate partition mounted as /chroot, with Apache under a
directory named httpd on my chroot partition.

# mkdir /chroot/httpd
# mkdir /chroot/httpd/dev
# mkdir /chroot/httpd/lib
# mkdir /chroot/httpd/etc
# mkdir -p /chroot/httpd/usr/sbin
# mkdir /chroot/httpd/usr/lib
# mkdir /chroot/httpd/usr/libexec
# mkdir -p /chroot/httpd/var/run
# mkdir -p /chroot/httpd/var/log/apache
# mkdir -p /chroot/httpd/home/httpd

Now set the permissions on your directory structure:

# chown -R root /chroot/httpd
# chmod -R 0755 /chroot/httpd
# chmod 750 /chroot/httpd/var/log/apache/

Your exact structure may vary slightly depending upon what features of Apache
you are using and where the nescessary libraries live on your main file
system.

Once you have created the nescessary directories you need to create the
null device.

# mknod  /chroot/httpd/dev/null c 1 3
# chown root.sys /chroot/httpd/dev/null 
# chmod 666 /chroot/httpd/dev/null

You need the null device and
/chroot/httpd/var/log/httpd/ because, when run in chroot jail, Apache
sees the /chroot/httpd directory as the equivalent of /. This means that it cannot access /dev/null or /var/log on the normal filesystem.

Copying the nescessary files

Now shut down Apache, run killall httpd, and you're ready to start
copying across the necessary files. Note that some directory names may be
different in your case depending upon how you originally installed Apache.
First, copy your configuration files:


# cp -r /etc/apache /chroot/httpd/etc/

Next, copy your Apache DocumentRoot and CGI scripts:


# cp -r /home/httpd/html /chroot/httpd/home/httpd/
# cp -r /home/httpd/cgi-bin /chroot/httpd/home/httpd/

Now copy your httpd binary (and, if you use them, the Apache
scripts) from /usr/sbin:


# cp /usr/sbin/httpd /chroot/usr/sbin/
# cp /usr/sbin/apache* /chroot/usr/sbin/

If you use mod_ssl you need to copy the /etc/ssl directory and
its contents too:


# cp -a /etc/ssl /chroot/httpd/etc/

You should also copy any modules from your original install:


cp -r /usr/libexec/apache /chroot/httpd/usr/libexec/

Once you have copied Apache itself (and ssl if needed) you need to copy all of
the shared libraries Apache relies on to run. To find out which libraries you
need, execute # ldd /chroot/httpd/usr/sbin/httpd. This should give output something like:

        /lib/libsafe.so.2 => /lib/libsafe.so.2 (0x40017000)
        libm.so.6 => /lib/libm.so.6 (0x40037000)
        libcrypt.so.1 => /lib/libcrypt.so.1 (0x40059000)
        libdb.so.2 => /lib/libdb.so.2 (0x40086000)
        libexpat.so.0 => /usr/lib/libexpat.so.0 (0x40096000)
        libdl.so.2 => /lib/libdl.so.2 (0x400b6000)
        libc.so.6 => /lib/libc.so.6 (0x400b9000)
        /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)

The exact output will depend upon how your httpd binary was built in the first
place. Copy the required files to their respective directories in your
chroot:

# cp /lib/libsafe* /chroot/httpd/lib/
# cp /lib/libm* /chroot/httpd/lib/
# cp /lib/libcrypt* /chroot/httpd/lib/
# cp /lib/libdb* /chroot/httpd/lib/
# cp /usr/lib/libexpat* /chroot/httpd/usr/lib/
# cp /lib/libdl* /chroot/httpd/lib/
# cp /lib/libc* /chroot/httpd/lib/
# cp /lib/ld-* /chroot/httpd/lib/

You need certain libraries for some standard networking functionality:

# cp /lib/libnss_compat* /chroot/httpd/lib/
# cp /lib/libnss_dns* /chroot/httpd/lib/
# cp /lib/libnss_files* /chroot/httpd/lib/
# cp /lib/libnsl* /chroot/httpd/lib/

The /chroot/httpd/etc configuration files

For Apache to function properly you also need several configuration files from
/etc. First, edit the /etc/passwd and /etc/group files. These should contain only entries for the Apache user and group you created earlier. For example:

/etc/passwd:
apache:x:12347:12348:Apache Server:/dev/null:/bin/false

/etc/group:
apache:x:12347:

You also need several network configuration files:

# cp /etc/hosts /chroot/httpd/etc/
# cp /etc/host.conf /chroot/httpd/etc/
# cp /etc/resolv.conf /chroot/httpd/etc/
# cp /etc/nsswitch.conf /chroot/httpd/etc/

For extra security you can set the immutable bit on these configuration files.
When the immutable bit is set, root has to specifically
unset it before a file can be modified, making it much harder for an intruder to tamper with the files:

# chattr +i /chroot/httpd/etc/hosts
# chattr +i /chroot/httpd/etc/host.conf
# chattr +i /chroot/httpd/etc/resolv.conf
# chattr +i /chroot/httpd/etc/nsswitch.conf
# chattr +i /chroot/httpd/etc/passwd
# chattr +i /chroot/httpd/etc/group

In order that the log files be written with the correct time, you need to check
/etc/localtime. localtime is a symlink to a file in
/usr/share/zoneinfo. To find out which file, run
ls -l /etc/localtime and copy the appropriate file to
/chroot/httpd/etc/localtime.

By default, syslogd monitors log files only in
/var/log. The chrooted httpd daemon will write its logs to
/chroot/httpd/var/log, however, so you need to tell syslogd to
monitor this directory too. To change this you need to edit the appropriate
startup script, /etc/rc.d/rc.syslog or
/etc/rc.d/init.d/syslog, depending upon your distro.

For /etc/rc.d/rc.syslog change daemon syslogd -m 0 to
daemon syslogd -m 0 -a /chroot/httpd/dev/log.

For /etc/rc.d/rc.syslog change:

    echo -n " /usr/sbin/syslogd"
    /usr/sbin/syslogd

to:

    echo -n " /usr/sbin/syslogd"
    /usr/sbin/syslogd -m 0 -a /chroot/httpd/dev/log

It is a good idea to create the nescessary log files and set the appendable
bit on them too.

# touch /chroot/httpd/var/log/apache/access_log
# touch /chroot/httpd/var/log/apache/error_log
# chmod 600 /chroot/httpd/var/log/apache/*
# chattr +a /chroot/httpd/var/log/apache/*

Finally, you need to change the httpd startup script to run the chrooted
httpd. Depending on your distro, open up /etc/rc.d/rc.httpd or
/etc/rc.d/init.d/httpd and change the command that starts the
httpd daemon to read /usr/sbin/chroot /chroot/httpd/ /usr/sbin/httpd.

Testing the server

If you have not already done so you should shut down the httpd daemon now.
Next, restart the syslog daemon: /etc/rc.d/rc.syslog
restart
(or /etc/rc.d/init.d/syslog restart accordingly).
Now start the chrooted version of Apache with /etc/rc.d/rc.httpd
start
(or /etc/rc.d/init.d/httpd start).

If there are no errors, check the daemon is running with the command
ps -aux | grep httpd. You should see several entries indicating a
running httpd process. Taking the process number from the output of ps and running ls -l /proc/PROC_NUMBER/root/ should show the structure of
your /chroot/httpd rather than your server's / filesystem.

If something has gone wrong, you should try running your chrooted httpd with
strace. The command # strace chroot /chroot/httpd /usr/sbin/httpd 2> httpd.strace redirects the output of strace to a file named
httpd.strace which should give you an idea where the problem lies.

Once everything is running you can remove your original Apache install.

Summary

Although chroot can be used to help create a more secure environment, it is not
perfect. You still need to keep your Web server patched up to date and monitor your logs. Your
chroot environment should help to contain a potential breakin and protect your system's
main filesystem from unseen vulnerabilities in your Web server.

Category:

  • Security