Setting up httpd on OpenBSD 5.7

N.B. chroot.sh updated for OpenBSD 6.0.

OpenBSD's httpd runs chrooted, so to support the python CGI scripts used by my websites, all the required python binaries, libraries, and supporting shared libraries had to be copied into the chroot environment.

The chroot location was changed from the default of /var/www to /home/www. Flags to slowcgi (which acts as the interface to httpd's fastcgi implementation, were changed to match, thus:

  slowcgi_flags="-p /home/www -s /home/www/run/slowcgi.sock"

The web site organisation I'd adopted for apache put the cgi-bin directory outside of the document root. This is supported by httpd, but the DOCUMENT_ROOT variable passed in by httpd to the cgi program contains the location of cgi-bin, so it becomes impossible to access the document files (e.g. the system.template) without setting up soft links or having the cgi-bin programs know the layout conventions.

The solution I chose was to change the site building script (make publish) to create the appropriate soft link when on OpenBSD. The link has to be relative, as we are in a chroot environment.

The next difficulty was caused by a bug in the python library file posixpath.py, which is called by setup.py. This produces the following error, when run in a chroot environment:

  KeyError: 'getpwuid(): uid not found: 67'

Python is attempting to look up the www user from somewhere (I assumed /etc/passwd). This didn't exist in the chroot environment, but even when I copied in /etc/passwd to the chroot enrionment, the error still occured. The other workaround described was to specify the HOME environment variable. Since I didn't want to install sh in the chroot, I tried to set it via os.environ. That didn't work either, because setup.py is run before the python script is processed. Finally, I patched the chrooted library file. That worked, but is ugly.

A better fix was to specify the -S argument to the python shebang:

 -S     Disable the import of the module site and the site-dependent
              manipulations of sys.path that it entails.

Or, even better, copy pwd.db to the chrooted etc. After experimentation, that one file appears to satisfy python's getpwuid().

A script to create a chrooted environment for python (or at least for my requirements) is shown below.

  #!/bin/sh
  #
  # Enable python to operate for a chrooted environment (for httpd)
  #
  export CHROOT=/home/www
  rm -rf ${CHROOT}/usr ${CHROOT}/var ${CHROOT}/etc ${CHROOT}/sbin \
         ${CHROOT}/run ${CHROOT}/logs
  mkdir -p ${CHROOT}/usr/local/bin ${CHROOT}/usr/lib ${CHROOT}/usr/libexec \
           ${CHROOT}/sbin ${CHROOT}/var/run ${CHROOT}/etc \
           ${CHROOT}/usr/local/lib ${CHROOT}/run ${CHROOT}/logs
  cp -p /sbin/ldconfig ${CHROOT}/sbin 
  cp -p /usr/local/bin/python2.7 ${CHROOT}/usr/local/bin/python
  cp -p /usr/local/lib/libpython2.7.so.*  ${CHROOT}/usr/local/lib
  cp -p /usr/lib/libpthread.so.* ${CHROOT}/usr/lib
  cp -p /usr/lib/libutil.so.* ${CHROOT}/usr/lib
  cp -p /usr/lib/libstdc++.so.* ${CHROOT}/usr/lib
  cp -p /usr/lib/libm.so.* ${CHROOT}/usr/lib
  cp -p /usr/lib/libc.so.* ${CHROOT}/usr/lib
  cp -p /usr/libexec/ld.so ${CHROOT}/usr/libexec
  cp -p /usr/lib/libz.so.* ${CHROOT}/usr/lib
  cp -p /usr/lib/libpthread.so.* ${CHROOT}/usr/lib
  cp -p /usr/lib/libutil.so.* ${CHROOT}/usr/lib
  cp -p /usr/lib/libm.so.* ${CHROOT}/usr/lib
  cp -p /usr/lib/libssl.so* ${CHROOT}/usr/lib
  # libcrypto required for OpenBSD 6.0
  cp -p /usr/lib/libcrypto.so* ${CHROOT}/usr/lib
  cp -p /etc/pwd.db ${CHROOT}/etc
  cp -pr /usr/local/lib/python2.7 ${CHROOT}/usr/local/lib
  # build ld.hints.so file so python can find its libraries
  chroot ${CHROOT} /sbin/ldconfig /usr/local/lib

Internet Resources

The following resources helped:

getuid error

python in chroot environment

blog on httpd and cgi

httpd configuration file

# magenta httpd.conf

# Macros
ext_addr="*"

#
# Global Options
#
# prefork 3
chroot "/home/www"

#
# Servers
#

# A name-based "virtual" server
server "magenta.hydrus.org.uk" {
    alias "magenta"
    listen on $ext_addr port 80

    root "/hydrus/data"
    log access "hydrus-access.log"
    log error "hydrus-error.log"

    location "/cgi-bin/*" {
        fastcgi
    }

}