BigAdmin System Administration Portal

HowTos

Archived from Sun's Dot-Com Builder Web Site
This content is archived from Sun's Dot-Com Builder Web Site.
These are the Best Practices > How To's archives.

Some of these pages may contain links that are no longer available. If you see these, you can report it through the Suggestions link and we will remove the link and leave the name (for reference).

Back to Dot-Com Builder How-Tos Archive

How to Secure Services by Running in a Chrooted Environment
November 2, 2001

by Eric D. Larson

Firewalls provide network security, and OS hardening limits the amount of external holes exposed on your server, but any host that provides services on a network can still be susceptible to security holes and bugs in the daemons themselves. For example, by exploiting bugs in httpd or ftpd software, hackers can gain access to the server. The result could range from a hacker modifying files on your Web server to crashing the server or even gaining access to other resources on your network. Recently, worms have been found propagating through bugs in httpd software (Code Red, Nimda). Bugs happen, so what's a sys admin to do?

Controlling the damage is the key here. By using security techniques such as "Least Privilege" and "Compartmentalization," you can limit the amount of damage caused by such exploits. To do this, convince yourself that unauthorized users will gain access to your servers. Sure, you have a great firewall, intrusion detection system (IDS), auditing, and log files, and the OS is locked down tighter than Fort Knox. But it just takes one little bug, and before you can say "Security Patch," the bad guys are all over your server.

A Chroot Jail

On an average server, once users gain access -- even just as normal users -- they have access to a wealth of information and other executables that could be used for further exploits. If you contain services on your Web server and use Least Privilege security measures, intruders are severely limited in what information they're able to obtain. The chroot(2) system call and associated command can be used to accomplish this on most UNIX® machines. It is used to restrict a process to a particular directory. Figure 1 illustrates a chroot jail within the file system hierarchy.

Figure 1: A chroot jail in the file system hierarchy
Figure 1: A chroot jail in the file system hierarchy
(Click image to enlarge.)

While most processes can access the entire file system, a chrooted process is restricted to a subset of the file system. This subset is known often as a "jail" or "sandbox." This virtual environment contains only the bare necessities that allow your service to run, isolating it from the rest of the OS, therefore limiting the chances for further exploits. In this article, the term "jail" means the virtual chroot environment that is used to isolate particular services. This term is not to be confused with the FreeBSD jail utility, which is different from just using the standard UNIX chroot command.


Should You Use Chroot Jails?

A chrooted environment can introduce some complexities that may make your server a little harder to maintain. Will the setup and maintenance effort be worth it? Probably. If done correctly, isolating services with chroot will have a tremendous security benefit. For a small site with few users, it may not be worth the extra effort. However, for a large site, or one with sensitive information, you'll want to make every effort to make the site secure. Along with limiting damage caused by bugs in your Web server, you can also protect yourself against security holes introduced by your users' CGI scripts or other server-side apps.

Setting Up the Jail

Although any process can potentially be "jailed " using chroot, HTTP and FTP daemons are the most popular candidates in a Web environment. The goal here is to create a directory structure that contains only the necessary files for your service to run. A network service may need /dev/tcp, for example, so you would need to create that device in your directory structure. Any libraries your daemon needs would have to be copied into your "jailed " directory as well.

A Simple Example: Running a Shell in a Chroot Jail

To illustrate how to create a chroot jail and see how a process behaves in that jail, look at a simple example of running /bin/sh in a chrooted environment. Here's a step-by-step procedure:

  1. Create a directory for your jail:

    # mkdir /chroot

  2. Create a skeleton directory structure:

    # cd /chroot
    # mkdir -p usr/bin usr/lib lib etc tmp dev

  3. Copy /bin/sh into your jail:

    # cp /bin/sh /chroot/bin/sh

  4. Copy in standard shared libraries and those needed by /bin/sh:
    # cp /usr/lib/ld.so.1 /usr/lib/libc.so.1
              /usr/lib/libdl.so.1 \ /chroot/usr/lib
    # ldd /chroot/bin/sh
    	libgen.so.1 =>   /usr/lib/libgen.so.1
            libc.so.1 =>     /usr/lib/libc.so.1
            libdl.so.1 =>    /usr/lib/libdl.so.1  
    # cp /usr/lib/libgen.so.1 /chroot/usr/lib
    
  5. Make required devices:

    # mknod dev/null c 13 2
    # mknod dev/zero c 13 12

  6. That should just about do it. Test it out:

    # chroot /chroot /bin/sh

    If you do a pwd at the prompt, it should return "/" no matter where you started the chrooted /bin/sh from. If you try to execute an ls to see what files are in the directory, it won't work because ls isn't in your environment. Exit the shell by typing exit and copy ls over to your jail:

    # cp /bin/ls /chroot/bin/ls

  7. Now if you start up your chrooted shell, you should at least be able to browse the directories:

    # chroot /chroot /bin/sh
    # ls
    bin  dev  etc  lib  tmp  usr
    # ls -l bin
    total 188
    -r-xr-xr-x   1 0     1    15400 Oct  4 08:42 ls
    -r-xr-xr-x   1 0     1    79380 Oct  4 08:18 sh
    # ls -l dev
    total 0
    crw-r--r--   1 0     1   13,  2 Oct  4 08:30 null
    crw-r--r--   1 0     1   13, 12 Oct  4 08:26 zero
    # ls -l usr/lib
    total 2408
    -rwxr-xr-x   1 2     2   198836 Oct  4 08:29 ld.so.1
    -rwxr-xr-x   1 2     2   959436 Oct  4 08:29 libc.so.1
    -rwxr-xr-x   1 2     2     4484 Oct  4 08:29 libdl.so.1
    -rwxr-xr-x   1 0     1    38944 Oct  4 08:31 libgen.so.1
    # pwd
    /
    	#
    

By experimenting with the shell in this chrooted environment, you can see how restricted it is.

Basic Steps in a Nutshell

Directory Structure
The first task for setting up a service in a chroot jail is to create the directory structure. Ideally this would be on a separate partition, mounted read-only if possible. You can use an NFS-mounted file system, but this would be more complicated and less secure since you would need to include NFS networking commands, libraries, and possibly other devices in your jail -- which would make it less effective. Keep the file system local if possible, and then make the directories very restricted -- even limit directory listing permission if possible.

Libraries
Typically, the hardest part of configuring a service to run in a chroot jail is determining all the dependencies. Many programs will need at least a few shared libraries. You will almost always need ld, the runtime linker, and libdl, the dynamic linking library. Be careful about libc -- also a staple library -- because it usually allows access to functions such as chown(), chmod(), chroot(), mknod(), and other system calls that could allow users to break out of the chroot jail.

Devices
Devices, like libraries, should be kept to a bare minimum. The following should always be created and pose little security risk: /dev/zero and /dev/null. If the program you're chrooting needs network access, you'll need to create /dev/tcp. You should not create /dev/kmem unless absolutely necessary. Having it available, especially writable, is a possible security hole. To create the required devices, use the mknod command.

Other Dependencies
Determining which libraries, devices, and other files or programs your particular application requires can be tricky -- especially if you don't have the source code. Two utilities can make this task a little easier:

  • ldd lists the dependencies of an executable or library. Run it against all the binaries and libraries in your jail to determine their dependencies. Keep in mind that libraries might have other dependencies of their own that you'll need to include.

  • truss traces the system calls of your program as it runs. The first time you try chrooting your app, it may die because it can't find a file it needs or doesn't have permission to access a file/device/directory in your jail. To truss your process, simply run it on your chroot command:

    # truss chroot /chroot /bin/ls

Typically you'll see a failure on an open() call when your app tries to open a particular library or device. A line like this, for example, shows an error #2 "No such file or directory" when opening /dev/zero, so you would simply need to create /dev/zero and try again:

open("/dev/zero", O_RDONLY)         Err#2 ENOENT

Problems and Gotchas

One problem with chroot is that you must be logged in as the root user to run it; yet, running a program as a root user in a chroot jail is not advised. If at all possible, the program you're running should call setuid() to change the effective user ID to a non-root user. A good way to do this is to use a "wrapper" script. You can write your own quite easily or use the runas program that comes with the Titan security package. The runas.c source code illustrates how to write a good wrapper script. It changes the user ID, group ID, and umask of a specified process, and can easily use chroot to set the root directory for the process.

Don't allow binaries to be brought or created in the jail. This means restricting write access to the file system as much as possible, removing any compilers, and restricting file transfer mechanisms. Don't allow ftp or mknod to run in the environment, and even something like cat or an editor could be bad news. In a nutshell, remove all unnecessary files. Don't allow chmoding of files -- if a user can't make a script executable, it'll be hard for he or she to run it. The example in this article used /bin/sh, but normally you would not want any shells in your chroot tree. Set the umask for your process, and if files are created somehow, they won't be executable.

Patches can also be an issue for chrooted programs. If a patch is applied to the original package after the chroot environment is created, it won't affect any files copied to your chroot directories. The originals will get patched, but the copies will need to be manually recopied to remain up-to-date. Admins need to check if a patch they install contains any new/patched library or program binaries that they are also using in their chrooted environment.

Other Resources

Running a service in a chrooted environment is different for every service. The tips presented in this article should get you started, and there are many resources already available for specific daemons. Here are some:


BigAdmin