OpenBSD Journal

Developer Blog - jdixon@: Chrooting Perl CGI Apps with mod_perl

Contributed by jason on from the swarm-of-unreadable-code-has-been-unleashed dept.

A recent thread on the misc@ mailing list reminded me of the obstacles faced when installing Perl CGI applications in the default OpenBSD httpd(8) chroot. Here is a brief guide at how I approached the problem by using mod_perl to import the necessary modules at execution.

Finding any Perl CGIs in the ports/www tree is the proverbial needle in a haystack. The obstacles facing chrooted CGIs are well documented. Simpler alternatives (read: php) have led to a lack of creative solutions, even at the expense of security holes and poor design practices.

I mentioned previously that I'd managed to overcome this problem for some of my own software. I talked with some colleagues about this leading up to c2k8, and we agreed that it should work as long as OpenBSD does privilege separation after mod_perl compiles its libraries and modules. This would mean that any external Perl modules an application needs could be imported when Apache is started. Fortunately for us, this is exactly what happens with OpenBSD's httpd(8).

Setting up Hatchet to use this method was incredibly simple. Beyond a few typical mod_perl directives, I just had to tell it where to find the file containing the list of modules I needed to import (startup.pl). Here is the example I include in README.OpenBSD:

<VirtualHost _default_:80>
    DocumentRoot /var/www/hatchet
    PerlModule Apache::PerlRun
    <Location /cgi/>
        SetHandler perl-script
        PerlHandler Apache::PerlRun
        PerlRequire /var/www/hatchet/cgi/startup.pl
        Options ExecCGI
        PerlSendHeader On
        allow from all
    </Location>
</VirtualHost>

The PerlModule and PerlRequire directives are analogous to Perl's use() and require() functions, respectively. The SetHandler and PerlHandler directives tell Apache that mod_perl should handle the content generation phase for any Perl scripts in this Location.

Because the startup.pl file is PerlRequire'd, it must return true. Any modules that your application use()'s should be included in here for importing:

use CGI ();
use DBI ();
use DBD::SQLite ();
use HTML::Template ();
use Time::Local ();
use Data::Dumper ();

1;

The final line guarantees that the file returns true. Parentheses following each package cause everything in that package's namespace to be imported. Because Perl is an interpreted language and mod_perl compiles your source code at startup, Apache must be restarted whenever there are changes to any affected source code. Once this is complete and configured properly, not only should your application work properly in the httpd chroot, but it will benefit from mod_perl's significant speed increase over native Perl CGI.

This article was not intended to be a comprehensive introduction to mod_perl. Rather, I hope that this motivates others to research mod_perl for themselves and see how it can be used to make their Perl applications shine on OpenBSD. Please refer to the official documentation for more information.

(Comments are closed)


Comments
  1. By Anonymous Coward (88.72.238.247) on

    thank you very much for this

    Comments
    1. By Motley Fool (MotleyFool) on

      > thank you very much for this

      This is helpful. In the past I've loaded the appropriate Perl modules into chroot'd space.

  2. By Anonymous Coward (66.185.204.43) on

    How does this deal with stat::inc?

    Comments
    1. By jason (jason) on http://www.dixongroup.net/

      > How does this deal with stat::inc?

      You mean Apache::StatINC? You might want to read this.

      Comments
      1. By Anonymous Coward (87.62.105.131) on

        > How does this deal with stat::inc?
        >
        > You mean Apache::StatINC? You might want to read this.

        Really like how you put the code examples in white blockquotes, that looks really neat. Way to go!

  3. By Alexander Hall (alexander) alexander@beard.se on

    Does this handle issues with modules like Dynaloader? I've had problems getting all dynamical stuff loaded and parsed before privsep occurs.

    In those cases, I've had to fall back to copying the lot into the chroot...

    Comments
    1. By Simon Bertrang (85.182.75.98) simon@ on

      > Does this handle issues with modules like Dynaloader? I've had problems
      > getting all dynamical stuff loaded and parsed before privsep occurs.
      >
      > In those cases, I've had to fall back to copying the lot into the chroot...

      That can actually hurt quite a lot.
      In any case you need to find out all modules and libraries that will be
      loaded at runtime, which can already be an exhaustive task unless i've
      missed something obvious.
      If you can preload all of them you'll be fine.
      But I have one port that still isn't finished exactly because of that...
      I wasn't able to figure out all modules and shared libraries it's using
      at runtime as they're deeply hidden in the dependency chain.
      In the end I was copying over /usr/libdata/perl5 to get it working in the
      chroot at all.

      I tried to think of simpler ways to solve this issue but didn't find a
      nice solution to it yet.
      If anyone has an idea to this i'd very much welcome a hint about it.

      In general it would be nice to have some kind of tool that solves all
      those problems on it's own but as it's highly language dependent i
      suspect that there will be a unified solution to it (correct me if i'm wrong).

      Comments
      1. By Marc Espie (213.41.185.88) espie@openbsd.org on

        at some point, I'll figure out how to extend pkg_add to deal with this.

        The trick would be to be able to specify to install specific stuff under chroot. What I'm afraid of is that this may require lots of extra information... and since it's a knob, it won't get tested, so it won't work...

        It should be as easy as possible to do, as always.

        Anyways, I'm of the opinion that httpd's chroot doesn't serve any useful purpose, once you reach the point where you run complex code under httpd.

        Think about it. Okay, so httpd is chroot'ed, and things run under www, in the chroot.

        Cool.

        Now, what's important ?

        Usually, it's your webserver. And it's running several applications.
        Some of them are critical.

        oh, oh.

        So you're fucked.

        And apache is ridiculous. It's incredible that there's no actual *working* safe mechanism to run separate applications under apache as separate users in their own space.

  4. By michael (belenus) belenus@bsdmail.de on

    Thanks a lot! Works just great. :-)

    Tested with yabb2 and cvsweb.

Credits

Copyright © - Daniel Hartmeier. All rights reserved. Articles and comments are copyright their respective authors, submission implies license to publish on this web site. Contents of the archive prior to as well as images and HTML templates were copied from the fabulous original deadly.org with Jose's and Jim's kind permission. This journal runs as CGI with httpd(8) on OpenBSD, the source code is BSD licensed. undeadly \Un*dead"ly\, a. Not subject to death; immortal. [Obs.]