Build the chroot Directory Structure


The basic establishment of a chroot environment is simple. Create a directory that will server as the new root (in other words, the new / directory). Then assign ownership of that directory to a non-root user and group . Before we explain how to fully deploy a service within a chroot environment, let s take a look at how it works.

The following steps set up a simple chroot base directory and assign its ownership to min (our minimum access user).

 # mkdir /opt/chroot # ls -l /opt/chroot/ total 0 

Now, compare the output of the ls command when run from the user s shell with the output of the ls command executed within a shell in the chroot environment.

First, use ls from the user shell:

 # ls -l / total 52 drwxr-xr-x    2 root     root         4096 Dec  5 10:22 bin/ drwxr-xr-x    3 root     root         4096 Apr 21 16:40 boot/ drwxr-xr-x    1 root     root            0 Dec 31  1969 dev/ drwxr-xr-x   67 root     root         4096 Apr 22 10:11 etc/ drwxr-xr-x    4 root     root         4096 Apr 22 10:11 home/ drwxr-xr-x    2 root     root         4096 Jun 21  2001 initrd/ drwxr-xr-x   10 root     root         4096 Dec  5 10:22 lib/ drwxr-xr-x    5 root     root         4096 May 23  2000 mnt/ drwxr-xr-x    3 root     root         4096 Apr 22 09:59 opt/ dr-xr-xr-x   92 root     root            0 Apr 21 12:39 proc/ drwx------   14 root     root         4096 Apr 22 10:17 root/ drwxr-xr-x    2 root     root         4096 Dec  5 10:23 sbin/ drwxrwxrwt    7 root     root         4096 Apr 22 10:01 tmp/ drwxr-xr-x   12 root     root         4096 Nov 17 10:54 usr/ drwxr-xr-x   21 root     root         4096 Aug 29  2003 var/ 

We need to copy some system libraries in order to make the ls command work correctly in /opt/chroot:

 # chroot /opt/chroot/ /bin/ls -lR / total 8 drwxr-xr-x    2 0        0        4096 Apr 22 14:35 bin drwxr-xr-x    2 0        0        4096 Apr 22 14:35 lib /bin: total 80 -rwxr-xr-x    1 0        0       76620 Apr 22 14:24 ls /lib: total 1312 -rwxr-xr-x    1 0        0       80296 Apr 22 14:34 ld-linux.so.2 -rwxr-xr-x    1 0        0     1237568 Apr 22 14:29 libc.so.6 -rwxr-xr-x    1 0        0       12112 Apr 22 14:28 libtermcap.so.2 

In this example, we executed /bin/ls -lR / to generate a recursive list of all subdirectories of the root (/). The root, of course, is really /opt/chroot, so we need to have a copy of ls and any libraries on which it relies. If we had omitted these libraries and assumed that ls would execute, the result of the chroot command would have been a less-than -helpful error:

 # chroot /opt/chroot/ ls l chroot: ls: No such file or directory 

The error does not mean that there is no such file as ls . The error actually means that ls could not find a dynamic library that it needed in order to execute. Unfortunately, chroot does not have the ability to perform a robust error analysis. If you see an error like No such file or directory, you may have an incomplete directory structure.

The deceptively simple command line usage of chroot actually requires some behind-the-scenes preparation in order for commands to execute smoothly. In the subsequent sections we ll cover some tricks for preparing the new environment and setting up common services with chroot .

Resolve Dynamic Library Dependencies

Usually, the biggest challenge when establishing a chroot environment is determining the libraries required by a binary. This was highlighted in the previous section when we had to copy three system libraries in order for the ls command to work. The natural follow-up question is, How does one figure out which libraries a binary uses? There are a few system utilities that help an administrator identify the libraries and system calls made by a program. One of the simplest is strace .

The strace command has many capabilities, most of which aid users trying to debug a binary or kernel. We need only be concerned with the basic use of strace , which traces the system calls and signals used by a command. Let s return to our /opt/chroot example where we tried to execute /bin/ls . Watch what happens when we use strace to list the system calls used by the command:

Note  

strace is not installed by default for most desktop and server installation profiles. It is likely that you will need to install the strace RPM before continuing this section. strace is a standard package in the Development Tools category for Red Hat.

 # strace /bin/ls   execve("/bin/ls", ["/bin/ls"], [/* 45 vars */]) = 0   uname({sys="Linux", node="worm", ...})  = 0   brk(0)                                  = 0x805ba2c   old_mmap(NULL, 4096, PROT_READPROT_WRITE, MAP_PRIVATEMAP_ANONYMOUS,         -1, 0) = 0x40014000   open("/etc/ld.so.preload", O_RDONLY)    = -1 ENOENT (No such file          or directory) open("/etc/ld.so.cache", O_RDONLY)      = 3   fstat64(3, {st_mode=S_IFREG0644, st_size=39641, ...}) = 0   old_mmap(NULL, 39641, PROT_READ, MAP_PRIVATE, 3, 0) = 0x40015000   close(3)                                = 0   open("/lib/libtermcap.so.2", O_RDONLY)  = 3 

The first line, with execve , represents the command and any of its arguments. strace produces a lot of output. The easiest way to capture these data is to use the -o option to save the information to a file:

 strace o info.txt /bin/ls 

We re looking for open calls because those are used to access libraries (among other things). In the previous example, the output was truncated at the first reference to a library, /lib/libtermcap.so.2. Thus, we have our first system library that must be copied from its current location to the new one:

 # cd /opt/chroot   # mkdir lib   # cp p /lib/libtermcap.so.2 /opt/chroot/lib/libtermcap.so.2 
Tip  

Use the -p option for the cp command to preserve the file s attributes, including mode, ownership, timestamps, and links

Those of you familiar with Unix know there s always more than one way to do something. You can use the -e option with strace to only print open calls. For example, here is the complete output of strace used with ls :

 # cd /opt   # strace -eopens /bin/ls  open("/etc/ld.so.preload", O_RDONLY)    = -1 ENOENT (No such file or        directory) open("/etc/ld.so.cache", O_RDONLY)      = 3   open("/lib/libtermcap.so.2", O_RDONLY)  = 3   open("/lib/i686/libc.so.6", O_RDONLY)   = 3   open("/usr/share/locale/locale-archive", O_RDONLYO_LARGEFILE) = -1        ENOENT No such file or directory) open("/usr/share/locale/locale.alias", O_RDONLY) = 3   open("/usr/share/locale/en_US/LC_IDENTIFICATION", O_RDONLY) = 3   open("/usr/share/locale/en_US/LC_MEASUREMENT", O_RDONLY) = 3   open("/usr/share/locale/en_US/LC_TELEPHONE", O_RDONLY) = 3   open("/usr/share/locale/en_US/LC_ADDRESS", O_RDONLY) = 3   open("/usr/share/locale/en_US/LC_NAME", O_RDONLY) = 3   open("/usr/share/locale/en_US/LC_PAPER", O_RDONLY) = 3   open("/usr/share/locale/en_US/LC_MESSAGES", O_RDONLY) = 3   open("/usr/share/locale/en_US/LC_MESSAGES/SYS_LC_MESSAGES", O_RDONLY)      = 3   open("/usr/share/locale/en_US/LC_MONETARY", O_RDONLY) = 3   open("/usr/share/locale/en_US/LC_COLLATE", O_RDONLY) = 3   open("/usr/share/locale/en_US/LC_TIME", O_RDONLY) = 3   open("/usr/share/locale/en_US/LC_NUMERIC", O_RDONLY) = 3   open("/usr/share/locale/en_US/LC_CTYPE", O_RDONLY) = 3   open(".", O_RDONLYO_NONBLOCKO_LARGEFILEO_DIRECTORY) = 3   chroot/ 

In the preceding example, the -eopen option instructs strace to only print open calls. The final line with the chroot/ entry is the output of the ls command.

Finally, copy the /lib/ld-linux.so.2 library into the chroot directory. Binaries use this file to determine its library dependencies. It is the dynamic loader used by all programs, even though you may have noticed that there was no reference to the ld-linux.so.2 library in any of the open calls. It is required in the chroot environment if any dynamic libraries are present. Since binaries use ld-linux.so.2 to find their own dependencies, you can simply execute this file with the --list option to manually list all library dependencies of a command. This produces a clearer and complete list of libraries to be copied.

 # ./ld-linux.so.2 --list /bin/ls          libtermcap.so.2 => /lib/libtermcap.so.2 (0x4000b000)         libc.so.6 => /lib/i686/libc.so.6 (0x4000f000)         /lib/ld-linux.so.2 => ./ld-linux.so.2 (0x80000000) 

Another system tool, ldd , performs the same function.

 # ldd /bin/ls           libtermcap.so.2 => /lib/libtermcap.so.2 (0x4001f000)         libc.so.6 => /lib/i686/libc.so.6 (0x40023000)         /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000) 

On Red Hat Enterprise Linux AS 3.0, the list of dynamic libraries for /bin/ls is longer:

 # ldd /bin/ls           libacl.so.1 => /lib/libacl.so.1 (0xb75d9000)         libtermcap.so.2 => /lib/libtermcap.so.2 (0xb75d5000)         libc.so.6 => /lib/tls/libc.so.6 (0xb749e000)         libattr.so.1 => /lib/libattr.so.1 (0xb749b000)         /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0xb75eb000) 

Both libacl and libattr provide interfaces to extended file permission settings.

Determine File Dependencies

At this point, you may be wondering why we introduced the more complicated strace at all. Well, more complex programs have file dependencies other than dynamic libraries. The most common examples are configuration files that reside in the /etc/ directory, such as passwd, group, hosts , and nsswitch.

Without getting too far ahead of ourselves , let s take a look at Apache on Red Hat Enterprise Linux AS 3.0:

 $ cp /usr/sbin/httpd . $ strace -f -o info.txt ./httpd k start $ less info.txt 21154 execve("./httpd", ["./httpd", "-k", "start"], [/* 46 vars */])=0 21154 uname({sys="Linux", node="GeidiPrime", ...}) = 0 21154 brk(0)                            = 0x80a8dd4 21154 old_mmap(NULL, 4096, PROT_READPROT_WRITE, MAP_PRIVATE       MAP_ANONYMOUS, -1, 0) = 0x40014000 21154 open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such        file or directory) 21154 open("/etc/ld.so.cache", O_RDONLY) = 3 21154 fstat64(3, {st_mode=S_IFREG0644, st_size=49684, ...}) = 0 21154 old_mmap(NULL, 49684, PROT_READ, MAP_PRIVATE, 3, 0)=0x40015000 21154 close(3)                          = 0 21154 open("/lib/i686/libm.so.6", O_RDONLY) = 3 21154 read(3, "7ELF 
 $ cp /usr/sbin/httpd . $ strace -f -o info.txt ./httpd “k start $ less info.txt 21154 execve("./httpd", ["./httpd", "-k", "start"], [/* 46 vars */])=0 21154 uname({sys="Linux", node="GeidiPrime", ...}) = 0 21154 brk(0) = 0x80a8dd4 21154 old_mmap(NULL, 4096, PROT_READPROT_WRITE, MAP_PRIVATE MAP_ANONYMOUS, -1, 0) = 0x40014000 21154 open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory) 21154 open("/etc/ld.so.cache", O_RDONLY) = 3 21154 fstat64(3, {st_mode=S_IFREG0644, st_size=49684, ...}) = 0 21154 old_mmap(NULL, 49684, PROT_READ, MAP_PRIVATE, 3, 0)=0x40015000 21154 close(3) = 0 21154 open("/lib/i686/libm.so.6", O_RDONLY) = 3 21154 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\0005 \0"..., 512) = 512 
 $ cp /usr/sbin/httpd . $ strace -f -o info.txt ./httpd “k start $ less info.txt 21154 execve("./httpd", ["./httpd", "-k", "start"], [/* 46 vars */])=0 21154 uname({sys="Linux", node="GeidiPrime", ...}) = 0 21154 brk(0) = 0x80a8dd4 21154 old_mmap(NULL, 4096, PROT_READPROT_WRITE, MAP_PRIVATE MAP_ANONYMOUS, -1, 0) = 0x40014000 21154 open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory) 21154 open("/etc/ld.so.cache", O_RDONLY) = 3 21154 fstat64(3, {st_mode=S_IFREG0644, st_size=49684, ...}) = 0 21154 old_mmap(NULL, 49684, PROT_READ, MAP_PRIVATE, 3, 0)=0x40015000 21154 close(3) = 0 21154 open("/lib/i686/libm.so.6", O_RDONLY) = 3 21154 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\0005 \0"..., 512) = 512 
 $ cp /usr/sbin/httpd . $ strace -f -o info.txt ./httpd “k start $ less info.txt 21154 execve("./httpd", ["./httpd", "-k", "start"], [/* 46 vars */])=0 21154 uname({sys="Linux", node="GeidiPrime", ...}) = 0 21154 brk(0) = 0x80a8dd4 21154 old_mmap(NULL, 4096, PROT_READPROT_WRITE, MAP_PRIVATE MAP_ANONYMOUS, -1, 0) = 0x40014000 21154 open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory) 21154 open("/etc/ld.so.cache", O_RDONLY) = 3 21154 fstat64(3, {st_mode=S_IFREG0644, st_size=49684, ...}) = 0 21154 old_mmap(NULL, 49684, PROT_READ, MAP_PRIVATE, 3, 0)=0x40015000 21154 close(3) = 0 21154 open("/lib/i686/libm.so.6", O_RDONLY) = 3 21154 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\0005 \0"..., 512) = 512 
 $ cp /usr/sbin/httpd . $ strace -f -o info.txt ./httpd “k start $ less info.txt 21154 execve("./httpd", ["./httpd", "-k", "start"], [/* 46 vars */])=0 21154 uname({sys="Linux", node="GeidiPrime", ...}) = 0 21154 brk(0) = 0x80a8dd4 21154 old_mmap(NULL, 4096, PROT_READPROT_WRITE, MAP_PRIVATE MAP_ANONYMOUS, -1, 0) = 0x40014000 21154 open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory) 21154 open("/etc/ld.so.cache", O_RDONLY) = 3 21154 fstat64(3, {st_mode=S_IFREG0644, st_size=49684, ...}) = 0 21154 old_mmap(NULL, 49684, PROT_READ, MAP_PRIVATE, 3, 0)=0x40015000 21154 close(3) = 0 21154 open("/lib/i686/libm.so.6", O_RDONLY) = 3 21154 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\0005 \0"..., 512) = 512 
 $ cp /usr/sbin/httpd . $ strace -f -o info.txt ./httpd “k start $ less info.txt 21154 execve("./httpd", ["./httpd", "-k", "start"], [/* 46 vars */])=0 21154 uname({sys="Linux", node="GeidiPrime", ...}) = 0 21154 brk(0) = 0x80a8dd4 21154 old_mmap(NULL, 4096, PROT_READPROT_WRITE, MAP_PRIVATE MAP_ANONYMOUS, -1, 0) = 0x40014000 21154 open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory) 21154 open("/etc/ld.so.cache", O_RDONLY) = 3 21154 fstat64(3, {st_mode=S_IFREG0644, st_size=49684, ...}) = 0 21154 old_mmap(NULL, 49684, PROT_READ, MAP_PRIVATE, 3, 0)=0x40015000 21154 close(3) = 0 21154 open("/lib/i686/libm.so.6", O_RDONLY) = 3 21154 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\0005 \0"..., 512) = 512 
 $ cp /usr/sbin/httpd . $ strace -f -o info.txt ./httpd “k start $ less info.txt 21154 execve("./httpd", ["./httpd", "-k", "start"], [/* 46 vars */])=0 21154 uname({sys="Linux", node="GeidiPrime", ...}) = 0 21154 brk(0) = 0x80a8dd4 21154 old_mmap(NULL, 4096, PROT_READPROT_WRITE, MAP_PRIVATE MAP_ANONYMOUS, -1, 0) = 0x40014000 21154 open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory) 21154 open("/etc/ld.so.cache", O_RDONLY) = 3 21154 fstat64(3, {st_mode=S_IFREG0644, st_size=49684, ...}) = 0 21154 old_mmap(NULL, 49684, PROT_READ, MAP_PRIVATE, 3, 0)=0x40015000 21154 close(3) = 0 21154 open("/lib/i686/libm.so.6", O_RDONLY) = 3 21154 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\0005 \0"..., 512) = 512 
 $ cp /usr/sbin/httpd . $ strace -f -o info.txt ./httpd “k start $ less info.txt 21154 execve("./httpd", ["./httpd", "-k", "start"], [/* 46 vars */])=0 21154 uname({sys="Linux", node="GeidiPrime", ...}) = 0 21154 brk(0) = 0x80a8dd4 21154 old_mmap(NULL, 4096, PROT_READPROT_WRITE, MAP_PRIVATE MAP_ANONYMOUS, -1, 0) = 0x40014000 21154 open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory) 21154 open("/etc/ld.so.cache", O_RDONLY) = 3 21154 fstat64(3, {st_mode=S_IFREG0644, st_size=49684, ...}) = 0 21154 old_mmap(NULL, 49684, PROT_READ, MAP_PRIVATE, 3, 0)=0x40015000 21154 close(3) = 0 21154 open("/lib/i686/libm.so.6", O_RDONLY) = 3 21154 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\0005 \0"..., 512) = 512 
 $ cp /usr/sbin/httpd . $ strace -f -o info.txt ./httpd “k start $ less info.txt 21154 execve("./httpd", ["./httpd", "-k", "start"], [/* 46 vars */])=0 21154 uname({sys="Linux", node="GeidiPrime", ...}) = 0 21154 brk(0) = 0x80a8dd4 21154 old_mmap(NULL, 4096, PROT_READPROT_WRITE, MAP_PRIVATE MAP_ANONYMOUS, -1, 0) = 0x40014000 21154 open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory) 21154 open("/etc/ld.so.cache", O_RDONLY) = 3 21154 fstat64(3, {st_mode=S_IFREG0644, st_size=49684, ...}) = 0 21154 old_mmap(NULL, 49684, PROT_READ, MAP_PRIVATE, 3, 0)=0x40015000 21154 close(3) = 0 21154 open("/lib/i686/libm.so.6", O_RDONLY) = 3 21154 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\0005 \0"..., 512) = 512 
 $ cp /usr/sbin/httpd . $ strace -f -o info.txt ./httpd “k start $ less info.txt 21154 execve("./httpd", ["./httpd", "-k", "start"], [/* 46 vars */])=0 21154 uname({sys="Linux", node="GeidiPrime", ...}) = 0 21154 brk(0) = 0x80a8dd4 21154 old_mmap(NULL, 4096, PROT_READPROT_WRITE, MAP_PRIVATE MAP_ANONYMOUS, -1, 0) = 0x40014000 21154 open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory) 21154 open("/etc/ld.so.cache", O_RDONLY) = 3 21154 fstat64(3, {st_mode=S_IFREG0644, st_size=49684, ...}) = 0 21154 old_mmap(NULL, 49684, PROT_READ, MAP_PRIVATE, 3, 0)=0x40015000 21154 close(3) = 0 21154 open("/lib/i686/libm.so.6", O_RDONLY) = 3 21154 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\0005 \0"..., 512) = 512 
 $ cp /usr/sbin/httpd . $ strace -f -o info.txt ./httpd “k start $ less info.txt 21154 execve("./httpd", ["./httpd", "-k", "start"], [/* 46 vars */])=0 21154 uname({sys="Linux", node="GeidiPrime", ...}) = 0 21154 brk(0) = 0x80a8dd4 21154 old_mmap(NULL, 4096, PROT_READPROT_WRITE, MAP_PRIVATE MAP_ANONYMOUS, -1, 0) = 0x40014000 21154 open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory) 21154 open("/etc/ld.so.cache", O_RDONLY) = 3 21154 fstat64(3, {st_mode=S_IFREG0644, st_size=49684, ...}) = 0 21154 old_mmap(NULL, 49684, PROT_READ, MAP_PRIVATE, 3, 0)=0x40015000 21154 close(3) = 0 21154 open("/lib/i686/libm.so.6", O_RDONLY) = 3 21154 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\0005 \0"..., 512) = 512 
 $ cp /usr/sbin/httpd . $ strace -f -o info.txt ./httpd “k start $ less info.txt 21154 execve("./httpd", ["./httpd", "-k", "start"], [/* 46 vars */])=0 21154 uname({sys="Linux", node="GeidiPrime", ...}) = 0 21154 brk(0) = 0x80a8dd4 21154 old_mmap(NULL, 4096, PROT_READPROT_WRITE, MAP_PRIVATE MAP_ANONYMOUS, -1, 0) = 0x40014000 21154 open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory) 21154 open("/etc/ld.so.cache", O_RDONLY) = 3 21154 fstat64(3, {st_mode=S_IFREG0644, st_size=49684, ...}) = 0 21154 old_mmap(NULL, 49684, PROT_READ, MAP_PRIVATE, 3, 0)=0x40015000 21154 close(3) = 0 21154 open("/lib/i686/libm.so.6", O_RDONLY) = 3 21154 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\0005 \0"..., 512) = 512 
 $ cp /usr/sbin/httpd . $ strace -f -o info.txt ./httpd “k start $ less info.txt 21154 execve("./httpd", ["./httpd", "-k", "start"], [/* 46 vars */])=0 21154 uname({sys="Linux", node="GeidiPrime", ...}) = 0 21154 brk(0) = 0x80a8dd4 21154 old_mmap(NULL, 4096, PROT_READPROT_WRITE, MAP_PRIVATE MAP_ANONYMOUS, -1, 0) = 0x40014000 21154 open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory) 21154 open("/etc/ld.so.cache", O_RDONLY) = 3 21154 fstat64(3, {st_mode=S_IFREG0644, st_size=49684, ...}) = 0 21154 old_mmap(NULL, 49684, PROT_READ, MAP_PRIVATE, 3, 0)=0x40015000 21154 close(3) = 0 21154 open("/lib/i686/libm.so.6", O_RDONLY) = 3 21154 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\0005 \0"..., 512) = 512 
 $ cp /usr/sbin/httpd . $ strace -f -o info.txt ./httpd “k start $ less info.txt 21154 execve("./httpd", ["./httpd", "-k", "start"], [/* 46 vars */])=0 21154 uname({sys="Linux", node="GeidiPrime", ...}) = 0 21154 brk(0) = 0x80a8dd4 21154 old_mmap(NULL, 4096, PROT_READPROT_WRITE, MAP_PRIVATE MAP_ANONYMOUS, -1, 0) = 0x40014000 21154 open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory) 21154 open("/etc/ld.so.cache", O_RDONLY) = 3 21154 fstat64(3, {st_mode=S_IFREG0644, st_size=49684, ...}) = 0 21154 old_mmap(NULL, 49684, PROT_READ, MAP_PRIVATE, 3, 0)=0x40015000 21154 close(3) = 0 21154 open("/lib/i686/libm.so.6", O_RDONLY) = 3 21154 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\0005 \0"..., 512) = 512 
 $ cp /usr/sbin/httpd . $ strace -f -o info.txt ./httpd “k start $ less info.txt 21154 execve("./httpd", ["./httpd", "-k", "start"], [/* 46 vars */])=0 21154 uname({sys="Linux", node="GeidiPrime", ...}) = 0 21154 brk(0) = 0x80a8dd4 21154 old_mmap(NULL, 4096, PROT_READPROT_WRITE, MAP_PRIVATE MAP_ANONYMOUS, -1, 0) = 0x40014000 21154 open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory) 21154 open("/etc/ld.so.cache", O_RDONLY) = 3 21154 fstat64(3, {st_mode=S_IFREG0644, st_size=49684, ...}) = 0 21154 old_mmap(NULL, 49684, PROT_READ, MAP_PRIVATE, 3, 0)=0x40015000 21154 close(3) = 0 21154 open("/lib/i686/libm.so.6", O_RDONLY) = 3 21154 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\0005 \0"..., 512) = 512 
 $ cp /usr/sbin/httpd . $ strace -f -o info.txt ./httpd “k start $ less info.txt 21154 execve("./httpd", ["./httpd", "-k", "start"], [/* 46 vars */])=0 21154 uname({sys="Linux", node="GeidiPrime", ...}) = 0 21154 brk(0) = 0x80a8dd4 21154 old_mmap(NULL, 4096, PROT_READPROT_WRITE, MAP_PRIVATE MAP_ANONYMOUS, -1, 0) = 0x40014000 21154 open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory) 21154 open("/etc/ld.so.cache", O_RDONLY) = 3 21154 fstat64(3, {st_mode=S_IFREG0644, st_size=49684, ...}) = 0 21154 old_mmap(NULL, 49684, PROT_READ, MAP_PRIVATE, 3, 0)=0x40015000 21154 close(3) = 0 21154 open("/lib/i686/libm.so.6", O_RDONLY) = 3 21154 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\0005 \0"..., 512) = 512 
05
 $ cp /usr/sbin/httpd . $ strace -f -o info.txt ./httpd “k start $ less info.txt 21154 execve("./httpd", ["./httpd", "-k", "start"], [/* 46 vars */])=0 21154 uname({sys="Linux", node="GeidiPrime", ...}) = 0 21154 brk(0) = 0x80a8dd4 21154 old_mmap(NULL, 4096, PROT_READPROT_WRITE, MAP_PRIVATE MAP_ANONYMOUS, -1, 0) = 0x40014000 21154 open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory) 21154 open("/etc/ld.so.cache", O_RDONLY) = 3 21154 fstat64(3, {st_mode=S_IFREG0644, st_size=49684, ...}) = 0 21154 old_mmap(NULL, 49684, PROT_READ, MAP_PRIVATE, 3, 0)=0x40015000 21154 close(3) = 0 21154 open("/lib/i686/libm.so.6", O_RDONLY) = 3 21154 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\0005 \0"..., 512) = 512 
"..., 512) = 512

After you ve tracked down library dependencies, the next major file group relates to the /etc/directory. Again, take a look at some selected lines from the strace output of Apache:

 $ grep "open(" info.txt  less 21154 open("/etc/group", O_RDONLY)      = 4 21154 open("/etc/host.conf", O_RDONLY)  = 3 21154 open("/etc/hosts", O_RDONLY)      = 3 21154 open("/etc/ld.so.cache", O_RDONLY) = 3 21154 open("/etc/nsswitch.conf", O_RDONLY) = 4 21154 open("/etc/passwd", O_RDONLY)     = 4 21154 open("/etc/resolv.conf", O_RDONLY) = 3 

These files are referenced during the startup process. Consequently, it will be necessary to recreate the files or at least their relevant content in the chroot environment. Notice that the /etc/group and /etc/passwd files are accessed by Apache. These files contain the user and group information (UID, GID) defined by the User and Group directives in the httpd.conf file. Apache starts with root privileges, but drops to these lower privileges once the daemon binds to a listening TCP port. The account s password is not necessary, which we confirm by noticing that no calls are made to the /etc/shadow file. We ll address Apache in more detail later in this chapter. For now, it is just important to understand the benefit of strace .

Create Devices in the chroot Directory

If an application requires an interface to the device file system, you must create the device in the chroot directory. Unlike adding dynamic libraries and file dependencies, devices cannot be copied with the cp command. Devices are often necessary when dealing with hardware (USB devices, CD-ROM drives, floppy drives , the hard drive) and remote access (console devices). If you know that any of these will be accessed by the service, you must create the /dev file system. If you are unsure, you can still create the /dev file system without introducing a security risk to the chroot environment.

First, create the /opt/chroot/dev directory. For Red Hat Enterprise Linux AS 3.0, use your system s /dev/MAKEDEV script to populate the new directory defined with the -d option:

 # mkdir /opt/chroot/dev # /dev/MAKEDEV -d /opt/chroot/dev/ generic 
Note  

The Red Hat MAKDEV script interprets the -d argument as a target directory. SUSE interprets -d as an instruction to delete a device. Double-check the man page for MAKEDEV before you execute this command.

For SUSE, change to the target dev directory and omit the -d option.

 # mkdir /opt/chroot/dev # cd /opt/chroot/dev # /sbin/MAKEDEV generic 

The generic argument instructs MAKEDEV to create a generic, default set of devices. Red Hat defines the devices for generic in the /etc/makedev.d/generic file. There are several other options available in this same directory. SUSE does not have this directory, but provides generic , std , and local device creation options.

 # ls /etc/makedev.d/ 00macros  cciss   console  ftape    ia64  ida  ipfilter  linux1394 mouse     raid    sound    undocumented   v4l  ataraid   cdrom dac960    generic ibcs     ide      isdn  linux-2.4.x    qic redhat  std    usb 

The generic choice will probably create many devices unnecessary to the application. Simply create a new file in the /etc/makedev.d/ directory. For example, these are generic devices:

 # cat /etc/makedev.d/generic # Support for the older "generic" devices. a generic std a generic fd a generic had a generic hdb a generic hdc a generic hdd a generic tty a generic ptyp a generic mouse a generic lp a generic parport a generic sound 

Any application that requires pseudo-terminals needs a special device. Such applications typically provide console access to the server. The /dev/pts device is created like this:

 # mkdir p /opt/chroot/dev/pts # mount -t devpts devpts /opt/chroot/dev/pts 

The man page for MAKEDEV provides detailed information for specific types of devices and alternate command line options.

Establish Shells and User Environments

This step is often unnecessary for most services that do not provide an interactive shell.

A chroot environment works best as a combination of a restricted file system and a service running with non-root privileges. When you execute chroot with root privileges, you want to be sure that the daemon assigned to the chroot executes with some other account ID. Apache is a good example of a service that is executed as root, but switches to a low-privilege account once the server is bound to a port. The account must be defined in the chroot environment when the service does this type of privilege dropping action.

When a service only switches from root to another account, you just need to provide an /etc/passwd and /etc/group entry for the service. No shadow file is necessary. The steps necessary for this are simple; just copy the relevant lines from each file into their chroot equivalent:

 # touch /opt/chroot/etc/passwd  # grep apache /etc/passwd apache:x:48:48:Apache:/var/www:/sbin/nologin # grep apache /etc/passwd >> /opt/chroot/etc/passwd 

Red Hat Enterprise Linux AS 3.0 provides configuration options for setting up chroot environments for users who authenticate via SSH. Check out the pam_chroot section later in this chapter.

Use BusyBox to Manage Shells and Command Line Tools

An alternative to copying a large number of shells, system tools, and dynamic libraries is to use the BusyBox (http://www.busybox.net/) suite of utilities. This is a collection of GNU utilities (coreutils, fileutils, and others) that have been placed into a monolithic package. Still, BusyBox enables you to compile only a particular subset of tools or tool options, which lets you create a customized toolset and maintain a minimal environment. This is the quickest way to add command line utilities to a chroot environment that is intended for a service that will provide users with remote shell access.

Once you ve downloaded BusyBox, untar the file and set your desired configuration options. Use the make menuconfig command to access a curses -based configuration menu. If you ve ever compiled a custom Linux kernel, then you ll be at home with this menu system. Figure 9-1 shows the top-level menu.

click to expand
Figure 9-1: The make menuconfig step for BusyBox

You should build the static library version, so make sure to install the glibc-static- devel RPM for your system. Otherwise, you ll receive the error Cannot find -lc. The static option is under the Build Options menu. Also, set the chroot target in the Installation Options menu and check the option for Don t use /usr. Figure 9-2 shows this menu.

click to expand
Figure 9-2: Set the BusyBox installation target to the chroot environment.

The steps to install and use BusyBox are easy. Start with a fresh /opt/chroot directory in which nothing has been copied or installed:

 # mkdir /opt/chroot # cd /usr/local/src/busybox-src # make menuconfig # make dep # make # make install 

Now, give the ls command a try in the new chroot environment:

 # chroot /opt/chroot /bin/ls bin      linuxrc  sbin 

It works! The tools have been compiled statically, so you don t even need to worry about finding dynamic libraries. There s another advantage. Take a look at how the ls command is created:

 # ls -l /opt/chroot/bin/ls lrwxrwxrwx  1 root root 7 Apr 22 13:45 /opt/chroot/bin/ls -> busybox* 

Each BusyBox command is actually a soft-link to a single binary. Instead of trying to match file permissions and ownership for several command line utilities, you only need to modify and monitor the busybox binary:

 # ls -l /opt/chroot/bin/busybox  -rwxr-xr-x  1 root root 784888 Apr 22 13:45 /opt/chroot/bin/busybox* 

BusyBox has most of the tools you ll need for daily administration of the chroot environment. In fact, the Red Hat and Mandrake distributions use the current stable branch (0.60) as part of their installer.




Hardening Linux
Hardening Linux
ISBN: 0072254971
EAN: 2147483647
Year: 2004
Pages: 113

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net