Book Excerpt: Practical Guide to Ubuntu Linux, A, 3rd Edition

Excerpt from Practical Guide to Ubuntu Linux, A, 3rd Edition.
By Mark G. Sobell
Published by Prentice Hall
ISBN-10: 0-13-254248-X
ISBN-13: 978-0-13-254248-7

Securing a Server

Two ways you can secure a server are by using TCP wrappers and by setting up a chroot jail. This section describes both techniques.

TCP Wrappers: Secure a Server (hosts.allow and hosts.deny)

Follow these guidelines when you open a local system to access from remote systems:

  • Open the local system only to systems you want to allow to access it.

  • Allow each remote system to access only the data you want it to access.

  • Allow each remote system to access data only in the appropriate manner (readonly, read/write, write only).

libwrap

As part of the client/server model, TCP wrappers, which can be used for any daemon that is linked against libwrap, rely on the /etc/hosts.allow and /etc/hosts.deny files as the basis of a simple access control language (ACL). This access control language defines rules that selectively allow clients to access server daemons on a local system based on the client’s address and the daemon the client tries to access. The output of ldd shows that one of the shared library dependencies of sshd is libwrap:

$ ldd /usr/sbin/sshd | grep libwrap
		libwrap.so.0 => /lib/libwrap.so.0 (0xb7ec7000)

hosts.allow and hosts.deny

Each line in the hosts.allow and hosts.deny files has the following format:

daemon_list : client_list [: command]

where daemon_list is a comma-separated list of one or more server daemons (such as portmap, vsftpd, and sshd), client_list is a comma-separated list of one or more clients, and the optional command is the command that is executed when a client from client_list tries to access a server daemon from daemon_list.

When a client requests a connection to a server, the hosts.allow and hosts.deny files on the server system are consulted in the following order until a match is found:

  1. If the daemon/client pair matches a line in hosts.allow, access is granted.

  2. If the daemon/client pair matches a line in hosts.deny, access is denied.

  3. If there is no match in the hosts.allow or hosts.deny file, access is granted.

The first match determines whether the client is allowed to access the server. When either hosts.allow or hosts.deny does not exist, it is as though that file was empty. Although it is not recommended, you can allow access to all daemons for all clients by removing both files.

Examples

For a more secure system, put the following line in hosts.deny to block all access:

$ cat /etc/hosts.deny
...
ALL : ALL : echo '%c tried to connect to %d and was blocked' >> /var/log/tcpwrappers.log

This line prevents any client from connecting to any service, unless specifically permitted to do so in hosts.allow. When this rule is matched, it adds a line to the file named /var/log/tcpwrappers.log. The %c expands to client information and the %d expands to the name of the daemon the client attempted to connect to.

With the preceding hosts.deny file in place, you can include lines in hosts.allow that explicitly allow access to certain services and systems. For example, the following hosts.allow file allows anyone to connect to the OpenSSH daemon (ssh, scp, sftp) but allows telnet connections only from the same network as the local system and users on the 192.168. subnet:

$ cat /etc/hosts.allow
sshd: ALL
in.telnet: LOCAL
in.telnet: 192.168. 127.0.0.1
...

The first line allows connection from any system (ALL) to sshd. The second line allows connection from any system in the same domain as the server (LOCAL). The third line matches any system whose IP address starts with 192.168. as well as the local system.

Setting Up a chroot Jail

On early UNIX systems, the root directory was a fixed point in the filesystem. On modern UNIX variants, including Linux, you can define the root directory on a per-process basis. The chroot utility allows you to run a process with a root directory other than /.

The root directory appears at the top of the directory hierarchy and has no parent. Thus a process cannot access files above the root directory because none exists. If, for example, you run a program (process) and specify its root directory as /tmp/jail, the program would have no concept of any files in /tmp or above: jail is the program’s root directory and is labeled / (not jail).

By creating an artificial root directory, frequently called a (chroot) jail, you prevent a program from accessing, executing, or modifying—possibly maliciously—files outside the directory hierarchy starting at its root. You must set up a chroot jail properly to increase security: If you do not set up the chroot jail correctly, you can make it easier for a malicious user to gain access to a system than if there were no chroot jail.

Using chroot

Creating a chroot jail is simple: Working with root privileges, give the command /usr/sbin/chroot directory. The directory becomes the root directory and the process attempts to run the default shell. The following command sets up a chroot jail in the (existing) /tmp/jail directory:

$ sudo /usr/sbin/chroot /tmp/jail
/usr/sbin/chroot: cannot run command '/bin/bash': No such file or directory

This example sets up a chroot jail, but when the system attempts to run the bash shell, the operation fails. Once the jail is set up, the directory that was named jail takes on the name of the root directory, /. As a consequence, chroot cannot find the file identified by the pathname /bin/bash. In this situation the chroot jail works correctly but is not useful.

Getting a chroot jail to work the way you want is more complicated. To have the preceding example run bash in a chroot jail, create a bin directory in jail (/tmp/jail/bin) and copy /bin/bash to this directory. Because the bash binary is dynamically linked to shared libraries, you need to copy these libraries into jail as well. The libraries go in lib.

The next example creates the necessary directories, copies bash, uses ldd to display the shared library dependencies of bash, and copies the necessary libraries to lib. The linux-gate.so.1 file is a dynamically shared object (DSO) provided by the kernel to speed system calls; you do not need to copy it.

$ pwd
/tmp/jail
$ mkdir bin lib
$ cp /bin/bash bin
$ ldd bin/bash
        linux-gate.so.1 =>  (0x0032c000)
        libncurses.so.5 => /lib/libncurses.so.5 (0x00d4d000)
        libdl.so.2 => /lib/tls/i686/cmov/libdl.so.2 (0x0091d000)
        libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0x00110000)
        /lib/ld-linux.so.2 (0x0026a000)
$ cp /lib/{libncurses.so.5,ld-linux.so.2} lib
$ cp /lib/tls/i686/cmov/{libdl.so.2,libc.so.6} lib

Now start the chroot jail again. Although all the setup can be done by an ordinary user, you must be working with root privileges to run chroot:

$ sudo /usr/sbin/chroot /tmp/jail
bash-4.1# pwd
/
bash-4.1# ls
bash: ls: command not found
bash-4.1# exit
exit
$

This time chroot finds and starts bash, which displays its default prompt (bash-4.1#). The pwd command works because it is a shell builtin. However, bash cannot find the ls utility because it is not in the chroot jail. You can copy /bin/ls and its libraries into the jail if you want users in the jail to be able to use ls. An exit command allows you to escape from the jail.

If you provide chroot with a second argument, it takes that argument as the name of the program to run inside the jail. The following command is equivalent to the preceding one:

$ sudo /usr/sbin/chroot /tmp/jail /bin/bash

To set up a useful chroot jail, first determine which utilities the users of the chroot jail need. Then copy the appropriate binaries and their libraries into the jail. Alternatively, you can build static copies of the binaries and put them in the jail without installing separate libraries. (The statically linked binaries are considerably larger than their dynamic counterparts. The size of the base system with bash and the core utilities exceeds 50 megabytes.) You can find the source code for most common utilities in the bash and coreutils source packages.

The chroot utility fails unless you run it with root privileges—the preceding examples used sudo to gain these privileges. The result of running chroot with root privileges is a root shell (a shell with root privileges) running inside a chroot jail. Because a user with root privileges can break out of a chroot jail, it is imperative that you run a program in the chroot jail with reduced privileges (i.e., privileges other than those of root).

There are several ways to reduce the privileges of a user. For example, you can put su or sudo in the jail and then start a shell or a daemon inside the jail, using one of these programs to reduce the privileges of the user working in the jail. A command such as the following starts a shell with reduced privileges inside the jail:

$ sudo /usr/sbin/chroot jailpath /usr/bin/sudo -u user /bin/bash &

where jailpath is the pathname of the jail directory, and user is the username under whose privileges the shell runs. The problem with this scenario is that sudo and su, as compiled for Ubuntu, call PAM. To run one of these utilities you need to put all of PAM, including its libraries and configuration files, in the jail, along with sudo (or su) and the /etc/passwd file. Alternatively, you can recompile su or sudo. The source code calls PAM, however, so you would need to modify the source so it does not call PAM. Either one of these techniques is time-consuming and introduces complexities that can lead to an insecure jail.

The following C program[1] runs a program with reduced privileges in a chroot jail. Because this program obtains the UID and GID of the user you specify on the command line before calling chroot(), you do not need to put /etc/passwd in the jail. The program reduces the privileges of the specified program to those of the specified user. This program is presented as a simple solution to the preceding issues so you can experiment with a chroot jail and better understand how it works.

$ cat uchroot.c

/ See svn.gna.org/viewcvs/etoile/trunk/Etoile/LiveCD/uchroot.c for terms of use. /

#include <stdio.h>
#include <stdlib.h>
#include <pwd.h>

int main(int argc, char  argv[])
{
	if(argc < 4)
	{
		printf("Usage: %s {username} {directory} {program} [arguments]\n", argv[0]);
		return 1;
	}
	/ Parse arguments /
	struct passwd  pass = getpwnam(argv[1]);
	if(pass == NULL)
	{
		printf("Unknown user %s\n", argv[1]);
		return 2;
	}
	/ Set the required UID /
	chdir(argv[2]);
	if(chroot(argv[2])
		||
		setgid(pass->pw_gid)
		||
		setuid(pass->pw_uid))
	{
		printf("%s must be run as root.  Current uid=%d, euid=%d\n", 
				argv[0],
				(int)getuid(),
				(int)geteuid()
				);
		return 3;
	}
	char buf[100];
	return execv(argv[3], argv + 3);
}

The first of the following commands compiles uchroot.c, creating an executable file named uchroot. Subsequent commands move uchroot to /usr/local/bin and give it appropriate ownership.

$ cc -o uchroot uchroot.c
$ sudo mv uchroot /usr/local/bin
$ sudo chown root:root /usr/local/bin/uchroot
$ ls -l /usr/local/bin/uchroot
-rwxr-xr-x 1 root root 7922 2010-07-17 08:26 /usr/local/bin/uchroot

Using the setup from earlier in this section, give the following command to run a shell with the privileges of the user sam inside a chroot jail:

$ sudo /usr/local/bin/uchroot sam /tmp/jail /bin/bash

Keeping multiple chroot jails - If you plan to deploy multiple chroot jails, it is a good idea to keep a clean copy of the bin and lib directories somewhere other than one of the active jails.

Running a Service in a chroot Jail

Running a shell inside a jail has limited usefulness. In reality, you are more likely to want to run a specific service inside the jail. To run a service inside a jail, make sure all files needed by that service are inside the jail. Using uchroot, the format of a command to start a service in a chroot jail is

$ sudo /usr/local/bin/uchroot user jailpath daemonname

where jailpath is the pathname of the jail directory, user is the username that runs the daemon, and daemonname is the pathname (inside the jail) of the daemon that provides the service.

Some servers are already set up to take advantage of chroot jails. For example, you can set up DNS so that named runs in a jail , and the vsftpd FTP server can automatically start chroot jails for clients.

Security Considerations

Some services need to be run by a user or process with root privileges but release their root privileges once started (Apache, Procmail, and vsftpd are examples). If you are running such a service, you do not need to use uchroot or put su or sudo inside the jail.

A process run with root privileges can potentially escape from a chroot jail. For this reason, you should reduce privileges before starting a program running inside the jail. Also, be careful about which setuid binaries you allow inside a jail—a security hole in one of them could compromise the security of the jail. In addition, make sure the user cannot access executable files that he uploads to the jail.

Footnote

[1]Thanks to David Chisnall and the Étoilé Project (etoileos.com) for the uchroot.c program.

 

This excerpt is from the 3rd Ed. of Mark Sobell's "A Practical Guide to Ubuntu Linux", published by Pearson/Prentice Hall Professional, Aug. 2010, ISBN 013254248X, Copyright 2011 Mark G. Sobell, for a full Table of Contents, please visit: www.informit.com/title/013254248X

Author Contact:

 

www.sobell.com
twitter.com/marksobell

© Copyright Mark G. Sobell. All rights reserved.

Load Disqus comments