OpenBSD Blog #12: Multiple OpenBSD httpd instances with multiple chroots
OpenBSD’s httpd web server calls chroot when it runs so that it
cannot access files outside of the specified directory. This is
awesome for security, but annoying for my weirdo internal home
setup where I might want to serve media from a large
capacity drive mounted outside of /var/www/ or serve a local test copy
of my website straight out of the source directory.
I tried to get around this, but chroot is quite effective. That’s good!
As near as I can tell, the correct (and only) way to deal with this
strange situation is to go ahead and run an entirely separate instance
of httpd listening on an alternative port (8080 is a popular choice).
In the next two sections, I have an example that I created as my intial test.
At the bottom of this page, I describe the setup I actually ended up with for serving my local test instance of ratfactor.com (this website).
A separate httpd.conf example
I made a copy of /etc/httpd.conf called
/etc/httpd-media.conf and invoked it with:
$ httpd -f /etc/httpd-media.conf
Here’s httpd-media.conf:
# web server cannot access anything outside this directory
chroot "/big-drive/media"
server "phobos2" {
listen on * port 8080
# web server will look here for files (default is '/htdocs')
root "/"
directory index "index.php"
location "*.php" {
fastcgi socket "/run/php-fpm.sock"
}
}
I manually created the following directories as well:
-
/big-drive/media/logs/ -
/big-drive/media/run/
The logs directory will contain the access and error logs.
The run directory is for the PHP-FPM socket, which I’ll explain next.
PHP-FPM
You’ll notice I’m also running PHP. Feel free to skip this section if you aren’t.
PHP-FPM also restricts itself to a chroot setting.
So I also made a copy of /etc/php-fpm.conf called
/etc/php-fpm-media.conf and invoked it with:
$ php-fpm -y /etc/php-fpm-media.conf
The relevant portions of php-fpm-media.conf:
...
listen = /big-drive/media/run/php-fpm.sock
...
chroot = /big-drive/media
You will also likely have noticed that the socket
used to connect from httpd to PHP_FPM must be under
the chroot and is traditionally at run/php-fpm.sock.
Script it
Here’s what I actually have for my local test copy of
ratfactor.com. You’ll notice that I moved both .conf
files into the root of the "project" area to keep /etc/
clean.
Files:
~/wiki/ratf/ <-- set as chroot in both confs ~/wiki/ratf/src/ <-- the web content root directory ~/wiki/ratf/logs/ <-- create directory for logs ~/wiki/ratf/run/ <-- create directory fpm socket ~/wiki/ratf/httpd.conf ~/wiki/ratf/php-fpm.conf
I also write a script, ratf-restart, which stops and
starts these special httpd and php-fpm instances while
leaving the main instances started by the main rc.d(8) scripts
alone!
#!/bin/ksh
# pkill args:
# f = match full args list!
# q = quiet
# l = print long format (debugging)
# I = prompt to kill (debugging)
#
# note: you can see what was matched by the pkills below by
# running the same thing with "pgrep -l -f ..."
printf "Stopping httpd..."
doas pkill -qf 'httpd.*ratf'
if [ $? == 0 ]
then
echo "Killed"
else
echo "Not running"
fi
printf "Stopping php-fpm..."
doas pkill -qf 'php-fpm.*ratf'
if [ $? == 0 ]
then
echo "Killed"
else
echo "Not running"
fi
echo "Starting php-fpm..."
doas php-fpm-8.4 -y /home/dave/wiki/ratf/php-fpm.conf
echo "Starting httpd..."
doas httpd -f /home/dave/wiki/ratf/httpd.conf
I can run that after starting the computer (though the uptime is epic, so this
is rare) or any time I make changes to either .conf file.
It looks like this when I run it:
Stopping httpd...Killed Stopping php-fpm...Killed Starting php-fpm... Starting httpd...
Really happy with this. Just automated enough to take the pain out of it without being too automated so I forget how things work.