2009-05-10

Sysadmin Sunday: Dealing with OpenBSD's chroot Apache server

Prerequisites
I'm assuming that you have a working OpenBSD/Apache/MySQL/PHP environment working prior to this, or at least have all the packages installed. We will be slightly modifying the MySQL startup process, and changing some stuff on the filesystem to allow the chroot to function properly.

Introduction to chroot
Chroot means "change root", and it's a way to spawn a process so that it has a different apparent root directory. Try as it might, this process cannot get to the "real" root. This has many advantages, especially with web servers.

While an operating system itself might be locked down like Fork Knox, the system's overall security is only as good as the applications that get installed on it. Just take a look at Milw0rm and you'll see that web application vulnerabilities are a dime a dozen. Local file inclusion and other vulnerabilities can sometimes allow an attacker to get to the very heart of the host operating system. With chroot, the attacker is unable to see the real operating system's environment -- in this case, only things within /var/www are visible to Apache and its sub-processes.

Path names that do not have a leading / will automatically use the "ServerRoot" directive (/var/www in OpenBSD) This is why you see lines such as "ErrorLog logs/error_log" in the configuration.

Basically, chroot is awesome. It's also a pain in the ass if you aren't used to dealing with it. Don't worry; I'm here to help.

Why isn't Apache always chrooted then?
There are things in the real operating system environment that the web server relies on. For AMP web applications, PHP needs access to the MySQL Socket (in /var/run/mysql). If you have Sessions enabled and using the filesystem, then /tmp needs to be accessible. Anything else in the "real root" that needs to be accessed must be re-created or hard-linked within /var/www as if it's the root directory.

Anything else in Apache's configuration that calls for path names with a leading / (VirtualHost, UserDir, etc) will be forced to use /var/www as its root as well. I do not recommend creating hard links from inside ServerRoot to external directories for web content. Instead, create subdirectories under /var/www for content (ex: /var/www/virtuals/HiRtest/ ) and grant write permissions for users who need it.

Fixing the broken stuff!
Most AMP packages only need somewhere to store Session information and a way to get to the MySQL socket. Since the real /tmp contains information that is not needed for Apache, we'll just create a new tmp directory specifically for Apache within /var/www and make it world-writable with the "sticky bit" set (exactly like the real /tmp)

sudo mkdir /var/www/tmp
sudo chmod 1777 /var/www/tmp

Next up is the MySQL socket. First, reproduce the directory structure for the MySQL socket under /var/www.
sudo mkdir -p /var/www/var/run/mysql  # -p creates subdirs as needed

Then, make sure the real mysql.sock file gets hard linked into the new directory. If you added the "mysql.server start" line to the end of /etc/rc.local, you can accomplish this pretty easily by adding a hard link command after the mysql server starts. I also added a line to remove the old hard link before starting MySQL. The end of my /etc/rc.local looks like this:
rm /var/www/var/run/mysql/mysql.sock
/usr/local/share/mysql/mysql.server start
ln /var/run/mysql/mysql.sock /var/www/var/run/mysql/mysql.sock

Finally, make sure that you set the httpd line in /etc/rc.conf to look like the line below, unless you really need more options. Just make sure "-u" isn't one of them!
httpd_flags=""  # for normal use: "" (or "-DSSL" after reading ssl(8))

At this point, I would advise rebooting the system. While you can start and stop Apache and MySQL, it's best to make sure that everything will come back up and that the chroot hard link to the MySQL socket will be re-created properly upon reboot. Otherwise, you might find yourself with a problem later on.

After rebooting, Apache web server should have all its components working properly again within its chroot environment. In my case, I installed WordPress 2.7.1 before setting Apache to chroot mode. After simply restarting Apache in chroot mode, WordPress gave only an error message about being unable to connect to the database. After making the other changes above and rebooting, WordPress is back to life. The same should hold true for most other packages.

blog comments powered by Disqus