A chroot on Red Hat / CentOS / Fedora Linux operating changes the apparent disk root directory for the Apache process and its children. Once this is done attacker or other php / perl / python scripts cannot access or name files outside that directory. This is called a “chroot jail” for Apache. You should never ever run a web server without jail. There should be privilege separation between web server and rest of the system.

In this exclusive series, you will learn more about:

  • Securing an Apache 2 web server under Red Hat Enterprise Linux / CentOS Linux using mod_chroot
  • Virtual hosting configuration under chrooted jail.
  • Troubleshooting Chrooted Apache jail problem.



  1. Server: Apache 2 Web server.
  2. Jail directory: /httpdjail.
  3. User / Group: apache / apache (never ever run chroot using root user).
  4. Virtual domain directory for all domain inside jail: /home/httpd.
  5. PHP is configured via default mod_php.
  6. Instructions are tested under CentOS / RHEL 5.x.

More about Jail directory: /httpdjail

Create a jail directory as follows:
# J=/httpdjail
# mkdir $J

  1. Do not create /dev directory inside your jail.
  2. Do not create special device files inside jail.
  3. Do not copy shell or any other single executable files inside your jail.
  4. Do not run httpd or php / perl / python as root user.
  5. If possible mount $J using a separate partition with nosuid, nodev and noexec options. This will improve security as user will not able to run suid enabled programs and device files inside a jail.

Install Apache, PHP and MySQL

Install required packages using yum command, enter:
# yum install mysql mysql-server httpd php-mysql php-pear php-xml php-mysql php-cli php-imap php-gd php-pdo php-devel php-mbstring php-common php-ldap php httpd-devel
Now, create required directories inside your jail:
# mkdir -p $J/var/run
# chown -R root.root $J/var/run
# mkdir -p $J/home/httpd
# mkdir -p $J/var/www/html
# mkdir -p $J/tmp
# chmod 1777 $J/tmp
# mkdir -p $J/var/lib/php/session
# chown root.apache $J/var/lib/php/session

  1. $J/var/run will store PID and other files.
  2. $J/var/lib/php/session PHP session file path (configured in php.ini).
  3. $J/tmp – Used by many scripts and cms software to upload files.

Install mod_chroot

mod_chroot makes running Apache in a secure chroot environment easy. You don’t need to create a special directory hierarchy containing /dev, /lib, /etc. mod_chroot allows you to run Apache in a chroot jail with no additional files. The chroot() system call is performed at the end of startup procedure – when all libraries are loaded and log files open. Download mod_chroot using wget command:
# cd /opt/
# wget http://core.segfault.pl/~hobbit/mod_chroot/dist/mod_chroot-0.5.tar.gz

Untar it:
# tar -zxvf mod_chroot-0.5.tar.gz
Compile and install mod_chroot for using apxs, enter:
# cd mod_chroot-0.5
# apxs -cia mod_chroot.c

Configure Apache mod_chroot

Open /etc/httpd/conf/httpd.conf file, type:
# C=/etc/httpd/conf/httpd.conf
# vi $C

Set PidFile path in which the server should record its process identification number when it starts. Find line that reads as follows:

PidFile run/httpd.pid

Replace with:
PidFile /var/run/httpd.pid
Next add ChrootDir directive, enter:
ChrootDir /httpdjail
Find line that read as follows:
ServerRoot "/etc/httpd"
Append following lines:

LockFile /var/run/httpd.lock
CoreDumpDirectory /var/run
ScoreBoardFile /var/run/httpd.scoreboard

Make sure mod_chroot.so line exists. For example, 64 bit Linux should have line as follows:

LoadModule chroot_module /usr/lib64/httpd/modules/mod_chroot.so

32 bit Linux config line:

LoadModule chroot_module /usr/lib/httpd/modules/mod_chroot.so

Save and close the file.
Disable SELinux for Apache

You need to disable SELinux for apache, enter:
# setsebool httpd_disable_trans 1
See article “disabling SELinux for only Apache / httpd in Linux” for further details.
Patch up /etc/init.d/httpd

Open /etc/init.d/httpd file, enter:
# vi /etc/init.d/httpd
Find out line that read as follows:

# Start httpd in the C locale by default.

Add following line (set ROOT to $J):


Find stop() that read as follows:

stop() {
echo -n $"Stopping $prog: "
killproc -d 10 $httpd
[ $RETVAL = 0 ] && rm -f ${lockfile} ${pidfile}

Replace it as follows (you need to link /var/run/httpd.pid to $J/var/run/httpd.pid; so that stop operation works):

stop() {
/bin/ln -s $ROOT/var/run/httpd.pid /var/run/httpd.pid
echo -n $"Stopping $prog: "
killproc -d 10 $httpd
[ $RETVAL = 0 ] && rm -f ${lockfile} ${pidfile}

Save and close the file. Set immutable permission on /etc/init.d/httpd so that file cannot be modified, updated by yum, deleted or renamed, no link can be created to this file and no data can be written to the file. Only the superuser or a process possessing the CAP_LINUX_IMMUTABLE capability can set or clear this attribute:
# chattr +i /etc/init.d/httpd
How do I start chrooted httpd?

Type the following command:
# /etc/init.d/httpd start
You should not see any error in /var/log/httpd/error_log file:

[Sun Dec 21 18:43:09 2008] [notice] core dump file size limit raised to 18446744073709551615 bytes
[Sun Dec 21 18:43:09 2008] [notice] SELinux policy enabled; httpd running as context root:system_r:initrc_t
[Sun Dec 21 18:43:09 2008] [notice] suEXEC mechanism enabled (wrapper: /usr/sbin/suexec)
[Sun Dec 21 18:43:09 2008] [notice] Digest: generating secret for digest authentication …
[Sun Dec 21 18:43:09 2008] [notice] Digest: done
[Sun Dec 21 18:43:10 2008] [notice] mod_chroot: changed root to /httpdjail.
[Sun Dec 21 18:43:10 2008] [notice] Apache/2.2.3 (CentOS) configured — resuming normal operations

How do I stop chrooted httpd?

# /etc/init.d/httpd stop

How do I restart chrooted httpd?

# /etc/init.d/httpd restart}