Apache Tomcat is a server of web applications primarily used in commercial environment not only as an application platform, but also as a component of large projects related with providing of a web-interface. In corporate sector, security of information systems has the highest priority, while infrastructure stability ensures failure-free operation. Let us test a vaunted stability and security of UNIX daemons taking Tomcat as an example.
Historically, it has become a conventional practice to perform settings with root privileges, to work — with non-root, and if these activities are mixed we get a rap on the knuckles. Everyone who has read even one book on *nix remembers by heart the author’s instruction — not to work with root rights and to do away with the habit to configure the system immediately upon installation, prior to creating an account, at least, with access to wheel group. This is really a bad habit with far-reaching consequences. Having looked into the world of MS Windows, we chuckled at admins and regular users with unlimited rights. As if they were experts sitting in front of unlocked doors that lead to the very core of the system.
As time passes, applications become larger but appetite grows, too. Hardware gets more portable and sleek, holding not these applications only but also various means of protection against malware and other threats, which are no less various. Look! The development, implementation, and operation have become an industry, an extremely profitable industry. It’s hard to imagine an enterprise not using commercial proposals. These proposals keep getting more complicated, more reliable and smart. The whole niches of corporate developments have emerged, and it has become incredibly difficult to do without them. Try, e.g., to give up remote access to corporate mail, or leave your laptop at home, with so convenient calendar and a tunnel to your work files.
Probably, httpd is the most popular product of Apache community, the product’s name being the same, apache, until version 2. At present, web server working in the background and dealing with thousands of parallel sessions is only used academically, for backward compatibility, and simply because of nostalgia. The Apache community keeps living and demonstrating ever new technologies, applications, and solutions to the world. We are going to deal with one of these miracles now.
Find yourself a cat
Developers of Java applications are familiar with Tomcat server, and appropriate qualification in dealing with it has become a requirement for many work positions. An admin knowing this server can find a fairly good position and earn his/her bread and butter without much effort. Developers of very large software systems also keep the cat. Sometimes, these software systems work hard for many thousands (and maybe even millions) of people while the cat silently provides a web interface of management. In fine, cats are useful animals.
But has everyone looked into the cat’s environment? If many of us ever pay attention to the area where it is allowed to walk, or the food left for it to smell, and if the noble animal is allowed to let drop all pots with plants?
But we will leave metaphors and study the privileges assigned to our container with servlets. I have never happened to see more than starting server through /bin/su tomcat $CATALINA_HOME/bin/startup.sh. Yes, a tomcat user has been created; yes, a directory has been assigned owner rights, but are these the things that Linux/UNIX world is capable of providing to us? Not for sure. Consider a special case of starting, when a process and hence, all its children and threads are started with root privileges. What are the dangers here? Here ‘s the list:
- A hypothetical intruder can make use of any vulnerability of library, class, or program code. With root privileges, a trouble is inevitable.
- A similar trouble but now with respect to code, libraries, and classes of the server. It can be traced by changelog of the release.
- A bug in a code or algorithm may prove to be quite expensive, if performed with root privileges.
- An error in the application design can block a shared resource.
People do make mistakes and we are going to eliminate them through the mechanisms which lay the foundation of modern multi-user operating systems. We shall begin with the meaning of isolation of a server process and transforming it into a real daemon.
How does it function?
We have been remembering potential dangers of starting the daemon with root privileges since the time of the above-mentioned httpd: these are: unauthorized access to files (especially with Perl) and transition one-two levels higher than DocumentRoot, with SQL injections being a separate branch of science, even with their professors. And now, two questions and two answers:
- What way has been selected by the developers of httpd, MySQL, PostgreSQL, and other products? The answer: they have disabled root privileges of the above-listed processes.
- What shall be done if we really need these privileges for system calls, direct access to the core and its resources? We do need an open port. The answer: there are several mechanisms, e.g., notorious SUID or weakening privileges through
Let’s have a look at process status display (Fig. 1).
These are the processes at the state of execution. The ‘master’ process has initiated five processes, made them the owners of a system owner ‘nobody’, unstuck them from a terminal by redirecting output to log, and opened port for them (by default, port 80). With this, we can be sure that a directory where the process is being performed is not a root directory (
/), and no one will be able to log in as user ‘nobody’. You see that the basics of isolation in the system are met.
Now, we examine a modified output (Fig.2).
How do I know that these are child processes? By PPID. Everything is in compliance with the rules: master process has PPID 1; so, it is clear that it has been initiated by init, while all others have PPID = PID of master process.
Most daemons not only create child processes but also distribute load over them, transfer logs to syslogd daemon, and perform other useful work. In general, we can transfer any signals to daemon similar to any other process, e.g., SIGHUP, via reload in initialization script. This is useful under heavy loads when restarting the process, in order to apply a new config file, can take too much time, thus interrupting a lot of sessions. We can use signal SIGSTOP to pause, then continue again (SIGCONT).
Working with commercial applications, I have noticed that even large companies — industry leaders do not pay attention to decrease in privileges and leave root privileges to a Tomcat owner. Let us discuss how to eliminate these problems.
Let me introduce — jsvc
Apache Commons project has developed jsvc utility, its source code being delivered together with Tomcat distribution software. The purpose of this development: performing a number of actions which would result in transformation of Tomcat into a UNIX daemon. Jsvc can change UID of a child process via fork(), hibernate a parent process, divide standard output and error flows, accept standard Java options, accept Java machine options, process code and system assertions, and do many other things, the whole list being accessible by
In order to have a complete experience, we will assemble the project manually, though, e.g., in CentOS base there is an implementation (no the latest one) with a code name jakarta-commons-daemon-jsvc, described as Java daemon launcher. Now, to the point. We’ll need ‘make’ and GCC. Go down to
$CATALINA_HOME/bin/ and unpack commons-daemon-native.tar.gz archive, going to unix directory:
$ tar xzvf commons-daemon-native.tar.gz ; cd commons-daemon-1.0.10-native-src/unix/
Installation docs say that for compilation it is sufficient to make a simple link
./configure ; make ; cp jsvc ../.. ; cd ../.., but I’d like to supplement the information with my own findings upon thorough study of
./configure file. The information is useful and gentoo professionals will appreciate it.
First, make sure to specify, when assembling,
JAVA_HOME is path to the directory with JDK. As far as I could understand, this option statically fixes path to JAVA; if you are going to assemble for a specific server, the path will be taken from a constant in the code, instead of applying an environment variable. Second, it is desirable to specify
-with-os-type=linux; this option could have been left undescribed, its word-for-word meaning is clear. The value is the name of directory
JAVA_HOME/include/<OS_for_which_JDK_was_built>; in the compilation process, it is possible to eliminate the Sun-specific data types. Well, third, the values of GCC options
CFLAGS=-m64 LDFLAGS=-m64; here, I noticed that the executable file had become about two times smaller. Be careful: these keys make machine-dependent x86-instructions unavailable for the application; so, the software will not start on a 32-bit machine.
In the whole, my configuration string looked as follows:
./configure -with-java=/usr/java/latest -with-os-type=/include/linux CFLAGS=-m64 LDFLAGS=-m64; if you are interested in boosting the application for a specific processor, welcome to Wiki Gentoo. However, it seemed to me that the performance was not so great, taking into account modern capacities.
I’d like to specially note a bug, which I have caught when assembling on CentOS 5, the application version delivered with Tomcat 6. If you do not make clean before make, the assembly barfs to libservice.a. As far as I know, Malformed archive is understood by an earlier version of ar archiver. Using version 2.20, the archive is successfully modified.
As an output, we’ll get a small executable file which will demonize our Thomas (Tomcat). We move it to bin directory and prepare a script to start it up and initialize. In release 7.x, everything turned out to be very easy: Java options are transferred via setenv.sh file, which you must just create and have these options assigned to it (
JAVA_OPTS=”-Xmx2G -Xms1G …”). In the same directory, in bin, there is daemon.sh file; we open it and assign to TOMCAT_USER variable the name of the user to whom the process, e.g., ‘daemon’ will belong to. What is important — this user already exists in the system, and it has /sbin/nologin shell — the same as all special users have who have been created to own background processes. Then, we make a symbolic reference to this file:
$ ll /etc/init.d/tomcatd
lrwxrwxrwx 1 root root 30 Jun 26 13:38 /etc/init.d/tomcatd -> /opt/tomcatd/bin/daemon.sh
For validation, we have used CentOS; therefore, for chkconfig to correctly work with the initialization script, it is necessary to enter, at the beginning of the script, the description of initialization levels and priorities used during start-up and shutdown:
$ head /etc/init.d/tomcatd
# chkconfig: 345 73 21
# description: Tomcat super daemon
Let’s try to start it (Fig. 3).
There is always a chance of deviations from what has been planned. Server starting log is written to catalina-daemon.out. As I have mentioned, errors are written to another file: catalina-daemon.err.
There is a parent process in proc list with root privileges, and also a child one, owned by
daemon. Default shell for this process is /sbin/nologin, so there’s no way to execute anything without direct access to OS core through system calls.
$ su daemon
This account is currently not available.
Eventually, we have obtained a full-featured server of Java applications, launched according to UNIX concept, with the required and sufficient privileges. In the general case, the server follows the scenario:
- It downloads to memory, makes fork(), thus changing the owner of a child process.
- The parent waits for the child response — something like ‘I’m ready’, whereupon it falls waiting through wait_child(), which is classics of the genre.
- The child process is a master similar to that described for nginx. It is this process that activates a Java machine, applies options to it, and performs everything provided by the logic of Tomcat’s operation; then it falls into threads and receives clients’ connections.