Using Openbsd's Chrooted HTTPD
Using Openbsd's Chrooted HTTPD
Contents
1 Apache httpd in the OpenBSD Environment
2 chroot
3 Chrooting httpd
7 Conclusion
The Apache HTTP daemon has been part of OpenBSD for quite some time
now. Until the recent change of operation mode from non-chrooted to chrooted
Apache behaved on OpenBSD as it does on most other operating systems. The
server runs as a normal process, usually under its own user id. Modules loaded
into the server or programs invoked by it run under the same security restrictions
as the server itself.
This mode of operation, however, puts the machine running the webserver
at risk unnecessarely, by the way. If you allow the installation of CGI scripts
1 It is arguable whether CGI-programs should be considered good or evil. Running the
webserver chrooted at least takes away one of the greater security risks of CGI-programs,
namely their unrestricted access to the file system of the machine running the webserver.
nothing prevents your user from introducing code that tries to spy on system
or user files. If the Apache daemon itself is vulnerable to a remote exploit the
complete filesystem is open to the intruder. Certainly this is not what most
system administrators want.
The new chrooted mode of operation confines the Apache webserver to a
user defined directory, usually /var/www, by changing the filesystem root to
this location. The operating system does this by a special kernel call, chroot(),
which effectively shifts the filesystem root for the calling process to the directory
given as the argument to the chroot() system call. This system call can not be
reversed, i.e. once a process is chrooted it will stay there forever. The chrooted
filesystem becomes the new filesystem hierarchy not only for the chrooted process but also for all of its child processes. If a webserver is chrooted this means
that all programs forked by it are confined to the so called chroot jail, be it a
CGI-program or a shell executed by some remote exploitable code in httpd.
chroot
A UNIX filesystems always starts a the root directory, /. Every process has
a pointer to its root directory within in the kernel. Whenever a new process
is started it inherits this information from its parent process. The chroot()
system call can change this information to any directory below the current root,
making this directory the new filesystem root,/, for this process and all its child
processes. Calling chroot() is restricted to the super-user and the process can
not be reversed.
The following figure shows a simple partial UNIX filesystem on the left. On
the right side you see the same filesystem chrooted to /var/www:
your new / and then there is no /bin/ksh (which would have to be installed
in /var/www/bin/ksh for this example to work). This is the first rule when
using chroot environments: They must be populated with all files and programs
necessary for their operation.
Chrooting httpd
library). Loading additional shared libraries fortunately is very easy: The environment variable LD PRELOAD can contain a list of shared libraries that are
mapped in a process address space before the executable (and its other shared
libraries) is loaded. The pre-loaded code can then be used by the executable,
whether chrooted or not. Most shells have a special syntax to specify environment variables for a single process only by prepending the variable definition to
the command itself. The following command will start httpd with a pre-loaded
PostgreSQL library:
# LD_PRELOAD=/usr/local/lib/libpq.so.2.2 /usr/sbin/httpd -DPHP4
A httpd process wich is started in this way can now chroot to /var/www, use
PHP4 and use the PostgreSQL client library. PHP4 will not try to dynamically
load the library from the filesystem because it is already in memory.
For the following example lets assume that you run a website that uses PHP4
to access a PostgreSQL database. I have already shown how httpd can be
started so that PHP4 has access to the PostgreSQL client library. As the normal
OpenBSD httpd starting mechanism knows nothing (yet?) about shared library
preloading, we simply turn off httpd in /etc/rc.conf:
4
httpd_flags=NO
and we start it whith LD PRELOAD set in /etc/rc.local
echo Starting local services
# Clean up left-over httpd locks
rm -f /var/www/logs/{ssl_mutex,httpd.lock,accept.lock}.*
echo -n httpd
LD_PRELOAD=/usr/local/lib/libpq.so.2.2 /usr/sbin/httpd -DPHP4
There is another issue with PostgreSQL besides the loading of the shared
library: For safety reasons you do not want it to use a TCP/IP socket but rather
use a UNIX socket which is usually located in /tmp. The chrooted httpd has
no access to this UNIX socket, of course. It will look for it in /var/www/tmp/.
To solve this issue you must configure your PostgreSQL server to use the socket
at /var/www/tmp/ with the following entry in the PostgreSQL configuration
file:
unix_socket_directory = /var/www/tmp
The directory /var/www/tmp needs to be created first. It does not matter
where your PostgreSQL server is installed but it should be outside the httpd
chroot jail (I use /var/pgwww). Your PostgreSQL server will now create its
UNIX socket in /var/www/tmp and the PHP4/PostgreSQL client after the
chroot call will find it in its standard location /tmp.
The last thing to do is to indicate the location of the PostgreSQL UNIX
socket to programs like psql. psql will by default look for the UNIX socket in
/tmp and, in this case, fail. Fortunately the psql program allows to specify
a different location with the -h option (which is normally used to indicate a
TCP/IP hostname when using the TCP/IP method to connect to the server).
psql needs to be called with -h /var/www/tmp in the chrooted environment.
Conclusion