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.
Requirements
- Server: Apache 2 Web server.
- Jail directory: /httpdjail.
- User / Group: apache / apache (never ever run chroot using root user).
- Virtual domain directory for all domain inside jail: /home/httpd.
- PHP is configured via default mod_php.
- Instructions are tested under CentOS / RHEL 5.x.
More about Jail directory: /httpdjail
Create a jail directory as follows:
# J=/httpdjail
# mkdir $J
- Do not create /dev directory inside your jail.
- Do not create special device files inside jail.
- Do not copy shell or any other single executable files inside your jail.
- Do not run httpd or php / perl / python as root user.
- 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
- $J/var/run will store PID and other files.
- $J/var/lib/php/session PHP session file path (configured in php.ini).
- $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. HTTPD_LANG=${HTTPD_LANG-"C"}
Add following line (set ROOT to $J):
ROOT=/httpdjail
Find stop() that read as follows:
stop() { echo -n $"Stopping $prog: " killproc -d 10 $httpd RETVAL=$? echo [ $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=$? echo [ $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
Continue reading rest of the Apache 2: RHEL / CentOS Jail Setup series.
Contents
- Red Hat / CentOS: Chroot Apache 2 Web Server
- Apache Chroot Jail: Virtual Hosting
- Test and Troubleshoot Chrooted Apache Jail
- Email this to a friend
- Printable version
- Rss Feed
- Last Updated: Dec/27/2008

{ 27 comments… read them below or add one }
My Fedora 10 mirrors do not include php-par or php-dv:
~ root $ yum install php-par php-dv
Loaded plugins: dellsysidplugin, refresh-packagekit
Setting up Install Process
Parsing package install arguments
No package php-par available.
No package php-dv available.
Nothing to do
My mirrors are:
adobe-linux-i386.repo
livna.repo
rpmfusion-nonfree-rawhide.repo
fedora-rawhide.repo
rpmfusion-free-rawhide.repo
rpmfusion-nonfree.repo
fedora.repo
rpmfusion-free.repo
rpmfusion-nonfree-updates.repo
fedora-updates.repo
rpmfusion-free-updates.repo
rpmfusion-nonfree-updates-testing.repo
fedora-updates-testing.repo
rpmfusion-free-updates-testing.repo
@Bill
It was a typo on my part. I’ve update the instructions.
I got trouble when I typed
apxs -cia mod_chroot.c
notification appeared :
bash: apxs: command not found
I have installed httpd-devel in my system
I’m using fedora 9
please help me
@adhoy,
You are using old apache version 1.3. or older. This is not supported and not tested by me. All information on this page is tested with APACHE 2 and latest Red Hat Linux version 5.
I use new version of apache server.
there is :
[root@aksanfamily algebra25]# yum -y install httpd
Loaded plugins: fastestmirror, refresh-packagekit
Loading mirror speeds from cached hostfile
* livna: livna.cat.pdx.edu
* rpmfusion-free-updates: lordmorgul.net
* rpmfusion-nonfree-updates: lordmorgul.net
* rpmfusion-free: lordmorgul.net
* updates-newkey: mirror.nus.edu.sg
* fusion: repo.kinitron.net
* updates: mirror.nus.edu.sg
* rpmfusion-nonfree: lordmorgul.net
Setting up Install Process
Parsing package install arguments
Package httpd-2.2.9-1.fc9.i386 already installed and latest version
Nothing to do
what wrong?
@adhoy
look for apxs on your system, I used /usr/sbin/apxs on a CentOS5 install
@everyone, some typos to deal with:
vi /etc/init.d/httpd.conf will not work, the correct command is
# vi /etc/init.d/httpd
chatter +i /etc/init.d/httpd.conf is incorrect, the program is chattr. the file is httpd (as above) so the correct command is
# chattr +i /etc/init.d/httpd
otherwise, good informative article which works.
Charlie,
Thanks for the heads up.
No problem, good to spread the word and get more people taking charge of their systems.
You still need to change this:
# chatter +i /etc/init.d/httpd
its chattr, no e
and also this:
PidFile /var/run/httpd.pidM
no M
Embarrassing typos :(
mod_chroot also works on CentOS 4 server.
Thanks, I can now sleep peacefully.
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.
Your instructions were extremely easy to follow–thank you. Unfortunately, I am having a problem starting Apache after running down the list. I’ve created the httpdjail directory exactly as described, and edited the referenced files exactly as described, but I here’s what /var/log/httpd/error_log shows whenever I try to start the service:
[Thu Jan 01 16:21:00 2009] [notice] core dump file size limit raised to 4294967295 bytes
[Thu Jan 01 16:21:00 2009] [notice] suEXEC mechanism enabled (wrapper: /usr/sbin/suexec)
[Thu Jan 01 16:21:01 2009] [notice] Digest: generating secret for digest authentication …
[Thu Jan 01 16:21:01 2009] [notice] Digest: done
[Thu Jan 01 16:21:01 2009] [notice] mod_chroot: changed root to /httpdjail.
[Thu Jan 01 16:21:01 2009] [alert] (2)No such file or directory: Can’t chdir to /httpdjail
(this alert repeats seven times)
[Thu Jan 01 16:21:01 2009] [notice] Apache/2.2.10 (Unix) DAV/2 mod_chroot/0.5 configured — resuming normal operations
[Thu Jan 01 16:21:01 2009] [alert] Child 4620 returned a Fatal error… Apache is exiting!
And that’s it. I’ve verified that the /httpdjail directory does indeed exist, and it’s permission block is “drwxr-xr-x”, which I believe is correct. I’m running Fedora 10. Any suggestions?
This is well known issue with selinux, try disabling SELinux for httpd:
If this failed disable SELinux totally.
Thanks for the suggestion. I already have SELinux disabled (by changing “SELINUX=enforcing” to “SELINUX=disabled” in /etc/selinux/config), so the setsebool command yields a notification that SELinux is disabled. I re-enabled it and rebooted, then tried the setsebool command, which now says “Could not change active booleans: Invalid boolean”.
The only difference running with SELinux enabled vs. disabled is that with it enabled, the following additional line appears in /var/log/httpd/error_log:
[notice] SELinux policy enabled; httpd running as context unconfined_u:unconfined_r:unconfined_t:s0
Every other line is the same as reported earlier. With SELinux disabled, that line isn’t there.
I do appreciate the help. Any other suggestions?
I think is more secure stay apache with selinux enabled than do yours instructions and run apache in chroot. SELinux is a very important software to make the system more secure. I recommend do not do this, and use apache+selinux.
Hello
I’m running into the exact problem as LeeH. I have the message repeated 5 times. I’m using apache-2.2.10 w/mod_chroot-0.5 on a Gentoo System. My set up is a little different, and after upgrading from apache-2.2.9-r1, I noticed this problem.
[Sat Jan 10 09:37:30 2009] [notice] mod_chroot: changed root to /var/chroot/apache.
[Sat Jan 10 09:37:30 2009] [alert] (2)No such file or directory: Can’t chdir to /var/chroot/apache
[Sat Jan 10 09:37:30 2009] [alert] (2)No such file or directory: Can’t chdir to /var/chroot/apache
[Sat Jan 10 09:37:30 2009] [alert] (2)No such file or directory: Can’t chdir to /var/chroot/apache
[Sat Jan 10 09:37:30 2009] [alert] (2)No such file or directory: Can’t chdir to /var/chroot/apache
[Sat Jan 10 09:37:30 2009] [alert] (2)No such file or directory: Can’t chdir to /var/chroot/apache
[Sat Jan 10 09:37:30 2009] [notice] Apache/2.2.10 (Unix) mod_ssl/2.2.10 OpenSSL/0.9.8j mod_chroot/0.5 Apache configured — resuming normal operations
[Sat Jan 10 09:37:30 2009] [alert] Child 24458 returned a Fatal error… Apache is exiting!
It’s almost appears that apache goes into chroot, but is referencing /var/chroot/apache after it’s been jailed, so it can’t reach these. Just like LeeH, I have those folders in place with proper permissions. Rolling back to apache-2.2.9-r1 and the problem goes away. Definitely appears to be related to apache-2.2.10.
Any ideas?? I am not using SELinux
Thanks!
hanji
LeeH / hanjil,
Either you disable SSL support or create required dirs in /httpdjail directory.
Tom
Not sure if I’m following. Like I said, this was working for me in 2.2.9-r1 (with SSL ability). I do have /etc/ssl/certs in the jail as well as /dev/urandom. What else would I need in the jail? Going through my strace, I didn’t see any complaints about SSL, but then again, I didn’t see it really complaining about anything except being unable to chdir.
Thanks!
hanji
Hello Tom
I just rebuilt apache-2.2.10 and removed SSL flags, and it still showed the same problem on start up.
hanji
I’ve tested this with Apache/2.2.3 and have no problem. Look like this is related to Apache version. Do you have SELinux endabled?
Hello Vivek
I do not have SELInux enabled. I agree, it seems like there is a problem with apache-2.2.10 and above with mod_chroot.
hanji
Hello
I have it figured out. You cannot use mod_chroot with apache-2.2.10. Apache-2.2.10 has built in chroot handling now. All you need to do is provide a ChrootDir variable in httpd.conf. The problem is that mod_chroot sets that value as well, I think it was trying to do a “double” chroot. Removing the -D chroot flag from apache startup options and configuring the variable got the server to start, and I verified that the chroot jail was working.
hanji
Hi there, i’ve testet this with CentOS 5, looks like it works but….
in my vhost.conf:
DocumentRoot /home/httpd/.sklep/http points into the httpdjailroot/home….
but
ErrorLog point into the main / not into the httpjail ?????
hello sir actually i want to create a directory in linux by using command line i created a directory by using mkdir but i am unable to create a directory in the name red Hat if any one knows plz help me waiting for your answers plz kindly reply me
Hello soumya,
well what errors are u getting while creating a directory using mkdir?
Hello guys,
I also received the following errors in the apache error logs and it happened two times causing apache to fail
[Mon Apr 20 00:20:02 2009] [emerg] (43)Identifier removed: couldn’t grab the accept mutex
[Mon Apr 20 00:20:02 2009] [emerg] (43)Identifier removed: couldn’t grab the accept mutex
[Mon Apr 20 00:20:02 2009] [emerg] (43)Identifier removed: couldn’t grab the accept mutex
[Mon Apr 20 00:20:02 2009] [emerg] (43)Identifier removed: couldn’t grab the accept mutex
[Mon Apr 20 00:20:02 2009] [emerg] (43)Identifier removed: couldn’t grab the accept mutex
[Mon Apr 20 00:20:02 2009] [emerg] (43)Identifier removed: couldn’t grab the accept mutex
[Mon Apr 20 00:20:02 2009] [emerg] (43)Identifier removed: couldn’t grab the accept mutex
[Mon Apr 20 00:20:02 2009] [emerg] (43)Identifier removed: couldn’t grab the accept mutex
[Mon Apr 20 00:20:02 2009] [alert] Child 13096 returned a Fatal error…\nApache is exiting!
exclog: signal received 15
While digging through some forums i noticed the cause can probably due to extra swap alloted. Can someone throw some light on it?
doing so http, mysql, php work fine but, perl script will not run.
log file show
(2)No such file or directory: exec of ‘/var/www/cgi-bin/test.cgi’ failed
Premature end of script headers: test.cgi
I guess httpdjail needs /usr/lib/perl5 into it
Please advice.