Putting Apache in Jail

In this conclusion to a six-part series on Apache configuration and installation, you will learn how to use chroot to put Apache in jail, how to prepare PHP to work in jail, and more. This article is excerpted from chapter two of Apache Security, written by Ivan Ristic (O’Reilly; ISBN: 0596007248). Copyright © 2006 O’Reilly Media, Inc. All rights reserved. Used with permission from the publisher. Available from booksellers or direct from O’Reilly Media.

Using chroot to Put Apache in Jail

Now that you know the basics of using chroot to put a process in jail and you are familiar with tools required to facilitate the process, we can take the steps required to put Apache in jail. Start by creating a new home for Apache and move the version installed (shown in the “Installation Instructions” section) to the new location:

  # mkdir -p /chroot/apache/usr/local
  # mv /usr/local/apache /chroot/apache/usr/ local
  # ln
 -s /chroot/apache/usr/local/apache / usr/local/apache
  # mkdir -p /chroot/apache/var
  # mv /var/www /chroot/apache/var/
  # ln
-s /chroot/apache/var/www /var/www

The symbolic link from the old location to the new one allows the web server to be used with or without being jailed as needed and allows for easy web server upgrades.

Like other programs, Apache depends on many shared libraries. The ldd tool gives their names (this ldd output comes from an Apache that has all default modules built-in statically):

  # ldd /chroot/apache/usr/local/apache/bin/httpd
 
      libm.so.6 => /lib/tls/libm.so.6 (0x005e7000)
       libcrypt.so.1 => /lib/libcrypt.so.1 (0×00623000)
       libgdbm.so.2 => /usr/lib/libgdbm.so.2 (0×00902000)
       libexpat.so.0 => /usr/lib/libexpat.so.0 (0×00930000)
       libdl.so.2 => /lib/libdl.so.2 (0x0060b000)
       libc.so.6 => /lib/tls/libc.so.6 (0x004ac000)
       /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0×00494000)

This is a long list; we make copies of these libraries in the jail:

  # mkdir /chroot/apache/lib
 
# cp /lib/tls/libm.so.6 /chroot/apache/lib
 
# cp /lib/libcrypt.so.1 /chroot/apache/lib
 
# cp /usr/lib/libgdbm.so.2 /chroot/apache/lib
 
# cp /usr/lib/libexpat.so.0 /chroot/apache/lib
 
# cp /lib/libdl.so.2 /chroot/apache/lib
 
# cp /lib/tls/libc.so.6 /chroot/apache/lib
 
#
cp /lib/ld-linux.so.2 /chroot/apache/lib

Putting user, group, and name resolution files in jail

Though the httpd user exists on the system (you created it as part of the installation earlier); there is nothing about this user in the jail. The jail must contain the basic user authentication facilities:

  # mkdir /chroot/apache/etc
  # cp /etc/nsswitch.conf /chroot/apache/etc/
  # cp /lib/libnss_files.so.2 /chroot/apache/lib

The jail user database needs to contain at least one user and one group. Use the same name as before and use the identical user and group numbers inside and outside the jail. The filesystem stores user and group numbers to keep track of ownership. It is a job of the ls binary to get the usernames from the user list and show them on the screen. If there is one user list on the system and another in the jail with different user numbers, directory listings will not make much sense.

  # echo "httpd:x:500:500:Apache:/:/sbin/nologin" > /chroot/apache/etc/passwd
  # echo "httpd:x:500:" > /chroot/apache/etc/group

At this point, Apache is almost ready to run and would run and serve pages happily. A few more files are needed to enable domain name resolution:

  # cp /lib/libnss_dns.so.2 /chroot/apache/lib
 
# cp /etc/hosts /chroot/apache/etc
 
#
cp /etc/resolv.conf /chroot/apache/etc

{mospagebreak title=Finishing touches for Apache jail preparation}

The walls of the jail are now up. Though the following files are not necessary, experience shows that many scripts require them. Add them now to avoid having to debug mysterious problems later.

Construct special devices after using ls to examine the existing /dev folder to learn what numbers should be used:

  # mkdir /chroot/apache/dev
  # mknod -m 666 /chroot/apache/dev/null c 1 3
  # mknod -m 666 /chroot/apache/dev/zero c 1 5
  # mknod -m 644 /chroot/apache/dev/random c 1 8

Then, add a temporary folder:

  # mkdir /chroot/apache/tmp
  # chmod +t /chroot/apache/tmp
  # chmod 777 /chroot/apache/tmp

Finally, configure the time zone and the locale (we could have copied the whole /usr/ share/locale folder but we will not because of its size):

  # cp /usr/share/zoneinfo/MET /chroot/apache/
etc/localtime
 
# mkdir
-p /chroot/apache/usr/lib/locale
 
# set | grep LANG
 
LANG=en_US.UTF- 8
  LANGVAR=en_US.UTF-8
  #
cp -dpR /usr/lib/locale/en_US.utf8 /chroot/ apache/usr/lib/locale  

{mospagebreak title=Preparing PHP to work in jail}

To make PHP work in jail, you should install it as normal. Establish a list of shared libraries required and copy them into the jail: 

  # ldd /chroot/apache/usr/local/apache/ libexec/libphp4.so
     
libcrypt.so.1 => /lib/libcrypt.so.1 (0x006ef000)
       libresolv.so.2 => /lib/libresolv.so.2 (0x00b28000)
       libm.so.6 => /lib/tls/libm.so.6 (0×00111000)
       libdl.so.2 => /lib/libdl.so.2 (0×00472000)
      
libnsl.so.1 => /lib/libnsl.so.1 (0x00f67000)
      
libc.so.6 => /lib/tls/libc.so.6 (0x001df000)
       /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0×00494000)

Some of the libraries are already in the jail, so skip them and copy the remaining libraries (shown in bold in the previous output):

  # cp /lib/libresolv.so.2 /chroot/apache/lib
  #
cp /lib/libnsl.so.1 /chroot/apache/lib

One problem you may encounter with a jailed PHP is that scripts will not be able to send email because the sendmail binary is missing. To solve this, change the PHP configuration to make it send email using the SMTP protocol (to localhost or some other SMTP server). Place the following in the php.ini configuration file:

  SMTP = localhost

Preparing Perl to work in jail

To make Perl work, copy the files into the jail:

  # cp -dpR /usr/lib/perl5 /chroot/apache/usr/lib
 
# mkdir /chroot/apache/bin
 
#
cp /usr/bin/perl /chroot/apache/bin

Determine the missing libraries:

  # ldd /chroot/apache/bin/perl 
           libperl.so => /usr/lib/perl5/5.8.1/i386-linux-thread-mult i
 
/CORE/libperl.so (0x0067b000)
           libnsl.so.1 => /lib/libnsl.so.1 (0×00664000)
           libdl.so.2 => /lib/libdl.so.2 (0x0060b000)
           libm.so.6 => /lib/tls/libm.so.6 (0x005e7000)
           libcrypt.so.1 => /lib/libcrypt.so.1 (0×00623000)
          libutil.so.1 => /lib/libutil.so.1 (0×00868000)
          libpthread.so.0 => /lib/tls/libpthread.so.0 (0×00652000) 
           libc.so.6 => /lib/tls/libc.so.6 (0x004ac000)
           /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0×00494000)

Then add them to the libraries that are inside:

  # cp /lib/libutil.so.1 /chroot/apache/lib
 
#
cp /lib/tls/libpthread.so.0 /chroot/ apache/lib

{mospagebreak title=Taking care of small jail problems}

Most CGI scripts send email using the sendmail binary. That will not work in our jail since the sendmail binary isn’t there. Adding the complete sendmail installation to the jail would defy the very purpose of having a jail in the first place. If you encounter this problem, consider installing mini_sendmail (http://www.acme.com/ software/mini_sendmail/), a sendmail replacement specifically designed for jails. Most programming languages come with libraries that allow email to be sent directly to an SMTP server. PHP can send email directly, and from Perl you can use the Mail::Sendmail library. Using these libraries reduces the number of packages that are installed in a jail.

You will probably encounter database connectivity problems when scripts in jail try to connect to a database engine running outside the jail. This happens if the program is using localhost as the host name of the database server. When a database client library sees localhost, it tries to connect to the database using a Unix domain socket. This socket is a special file usually located in /tmp, /var/run,or /var/lib, all outside the jail. One way to get around this is to use 127.0.0.1 as the host name and force the database client library to use TCP/IP. However, since a performance penalty is involved with that solution (Unix domain socket communication is much faster than communication over TCP/IP), a better way would be to have the socket file in the jail.

For PostgreSQL, find the file postgresql.conf (usually in /var/lib/pgsql/data) and change the line containing the unix_socket_directory directive to read:

  unix_socket_directory = ‘/chroot/apache/tmp’

Create a symbolic link from the previous location to the new one:

  # ln -s /chroot/apache/tmp/.s.PGSQL.5432 /tmp

MySQL keeps its configuration options in a file called my.cnf, usually located in /etc. In the same file, you can add a client section (if one is not there already) and tell clients where to look for a socket:

  [mysqld ]
  datadir=/var/lib/mysql 

  socket=/chroot/apache/var/lib/mysql/ mysql.sock

  [client] 
  socket=/chroot/apache/var/lib/mysql/ mysql.sock

Or, just as you did with PostgreSQL, create a symbolic link:

  # mkdir -p /chroot/apache/var/lib/mysql
  # chown mysql /chroot/apache/var/lib/mysql/
  # ln -s /chroot/apache/var/lib/mysql/mysql.sock
/var/lib/mysql

Using the chroot(2) Patch

Now that I have explained the manual chroot process, you are wondering if an easier way exists. The answer is, conditionally, yes.

The approach so far was to create the jail before the main process was started. For this approach to work, the jail must contain all shared libraries and files the process requires. This approach is also known as an external chroot.

With an internal chroot, the jail is established from within the process after the process initialization is completed. In the case of Apache, the jail must be created before request processing begins, at the latest. The process is born free and then jailed. Since the process has full access to the filesystem during the initialization phase, it is free to access any files it needs. Because of the way chrooting works, descriptors to the files opened before the call remain valid after. Therefore, we do not have to create a copy of the filesystem and we can have a “perfect” jail, the one that contains only files needed for web serving, the files in the web server tree.

Internal chroot can be dangerous. In external chroot approaches, the process is born in jail, so it has no opportunity to interact with the outside filesystem. With the internal chroot, however, the process has full access to the filesystem in the beginning and this allows it to open files outside the jail and continue to use them even after the jail is created. This opens up interesting opportunities, such as being able to keep the logs and the binaries outside jail, but is a potential problem. Some people are not comfortable with leaving open file descriptors outside jail. You can use the lsof utility to see which file descriptors Apache has open and determine whether any of them point outside jail. My recommendation is the following: If you can justify a high level of security for your installation, go for a proper external chroot approach. For installations of less importance, spending all that time is not feasible. In such cases, use the internal chroot approach.

It is obvious that internal chrooting is not a universal solution. It works only if the following is true:

  1. The only functionality needed is that of Apache and its modules.
  2. There will be no processes (such as CGI scripts) started at runtime. Alternatively, if CGI scripts are used, they will be statically compiled.
  3. Access to files outside the web server root will be not be required at runtime. (For example, if you intend to use the piped logging mechanism, Apache must be able to access the logging binary at runtime to restart logging in case the original logging process dies for some reason. Piped logging is discussed in Chapter 8.)

Now that I have lured you into thinking you can get away from the hard labor of chrooting, I will have to disappoint you: Apache does not support internal chrooting natively. But the help comes from Arjan de Vet in the form of a chroot(2) patch. It is available for download from http://www.devet.org/apache/chroot/. After the patch is applied to the source code, Apache will support a new directive, ChrootDir . Chrooting Apache can be as easy as supplying the new root of the filesystem as the ChrootDir first parameter. The record of a successful chroot(2) call will be in the error log.

As a downside, you will have to apply the patch every time you install Apache. And there is the problem of finding the patch for the version of Apache you want to install. At the time of this writing only the patch for Apache 1.3.31 is available. But not everything is lost.

{mospagebreak title=Using mod_security or mod_chroot}

In a saga with more twists than a soap opera, I will describe a third way to jail Apache. Provided the limitations described in the previous section are acceptable to you, this method is the simplest: chrooting using mod_security (http://www. modsecurity.org) or mod_chroot (http://core.segfault.pl/~hobbit/mod_chroot/). Both modules use the same method to do their work (at the time of this writing) so I will cover them in this section together. Which module you will use depends on your circumstances. Use mod_security if you have a need for its other features. Otherwise, mod_chroot is likely to be a better choice because it only contains code to deal with this one feature and is, therefore, less likely to have a fault.

The method these two modules use to perform chrooting is a variation of the chroot(2) patch. Thus, the discussion about the usefulness of the chroot(2) patch applies to this case. The difference is that here the chroot(2) call is made from within the Apache module (mod_security or mod_chroot), avoiding a need to patch the Apache source code. And it works for 1.x and 2.x branches of the server. As in the previous case, there is only one new directive to learn: SecChrootDir for mod_security or ChrootDir for mod_chroot. Their syntaxes are the same, and they accept the name of the root directory as the only parameter:

  SecChrootDir /chroot/apache

The drawback of working from inside the module is that it is not possible to control exactly when the chroot call will be executed. But, as it turns out, it is possible to successfully perform a chroot(2) call if the module is configured to initialize last.

Apache 1

For Apache 1, this means manually configuring the module loading order to make sure the chroot module initializes last. To find a list of compiled-in modules, execute the httpd binary with the -l switch:

  # ./httpd -l
 
Compiled-in modules:
   
http_core.c
   
mod_env.c
   
mod_log_config.c
    mod_mime.c
   
mod_negotiation.c
   
mod_status.c
   
mod_include.c
   
mod_autoindex.c
   
mod_dir.c
   
mod_cgi.c
   
mod_asis.c
   
mod_imap.c
   
mod_actions.c
   
mod_userdir.c
   
mod_alias.c
   
mod_rewrite.c
   
mod_access.c
   
mod_auth.c
   
mod_so.c
   
mod_setenvif.c

To this list, add modules you want to load dynamically. The core module, http_core, should not appear on your list. Modules will be loaded in the reverse order from the one in which they are listed in the configuration file, so mod_security (or mod_chroot) should be the first on the list:

  ClearModuleLis t
  AddModule mod_security.c
  AddModule …
  AddModule …

Apache 2

With Apache 2, there is no need to fiddle with the order of modules since the new API allows module programmers to choose module position in advance. However, the changes in the architecture are causing other potential problems to appear:

  • Unlike in Apache 1, in Apache 2 some of the initialization happens after the last module initializes. This causes problems if you attempt to create a jail in which the logs directory stays outside jail. The solution is to create another logs direc tory inside jail, which will be used to store the files Apache 2 needs (e.g., the pid file). Many of the modules that create temporary files have configuration directives that change the paths to those files, so you can use those directives to have temporary files created somewhere else (but still within the jail).
  • On some platforms, internal Apache 2 chroot does not work if the AcceptMutex directive is set to pthread . If you encounter a problem related to mutexes change the setting to something else (e.g., posixsem , fcntl , or flock ). 
[gp-comments width="770" linklove="off" ]
antalya escort bayan antalya escort bayan