Chrooting perl for OpenHTTPd
Category: perl
By default, OpenHTTPd runs inside a
chroot in the path /var/www
. In order to run
perl(1) applications, you will need to copy
perl and its dependencies into this chroot.
Configuring httpd
First, configure httpd.conf(5). We will use slowcgi(8) to execute CGI scripts. Here is our sample /etc/httpd.conf:
server "www.example.com" { listen on * port 80 root "/htdocs/perl" location "*.pl" { fastcgi } location "/.well-known/acme-challenge/*" { root "/acme" request strip 2 } }
We specify our web document root in /htdocs/perl
. Remember that because
httpd(1) is chrooted, the path of your
actual web documents must be /var/www/htdocs/perl/
. The fastcgi
directive tells the server to interpret files that end with the extension .pl
to be interpreted as CGI scripts.
Reload the web server so it reloads the conf file:
# rcctl reload httpd
Copying perl
To figure out what files need to be copied into the chroot:
$ ldd /usr/bin/perl /usr/bin/perl: Start End Type Open Ref GrpRef Name 00000c0f12e8d000 00000c0f12e92000 exe 1 0 0 /usr/bin/perl 00000c12054ca000 00000c1205877000 rlib 0 1 0 /usr/lib/libperl.so.24.0 00000c118de26000 00000c118de58000 rlib 0 2 0 /usr/lib/libm.so.10.1 00000c11cc985000 00000c11cca8c000 rlib 0 2 0 /usr/lib/libc.so.100.3 00000c113a3b6000 00000c113a3b6000 ld.so 0 1 0 /usr/libexec/ld.so
Start with this initial guess to build the chroot:
# mkdir -p /var/www/usr/{bin,lib,libexec} # cp -p /usr/bin/perl /var/www/usr/bin/ # cp -p /usr/lib/lib{c,m,perl}.so* /var/www/usr/lib/ # cp -p /usr/libexec/ld.so /var/www/usr/libexec/
Next, turn on slowcgi(8):
# rcctl enable slowcgi # rcctl start slowcgi
Use chroot(8) to see if perl can run inside the chroot:
# chroot -u www -g daemon /var/www perl
Run a test command with perl:
print "All users are created equal\n";
Type ctrl+d to escape; if perl successfully echoes @@All users are created equal@@, we can confirm that this perl command can run inside the chroot.
Next, create the directory to hold web documents:
# mkdir -p /var/www/htdocs/perl/
Create /var/www/htdocs/perl/index.pl
to see if the web server loads it:
#!/usr/bin/perl -w use strict; print "Content-Type:text/html\n\n"; print "All users are created equal: 17".int(rand(100))."\n";
Make the script executable:
# chmod +x /var/www/htdocs/perl/index.pl
Now, try running this command:
$ ftp -o - http://example.com/index.pl Trying 192.168.1.2... Requesting http://example.com/index.pl ftp: Error retrieving http://example.com/index.pl: 500 Internal Server Error
A 500 Internal Error is often a clue that the web server is unable to properly execute a CGI script. In this case, it means that perl was not set up properly inside the chroot.
Search httpd(1)'s error logs for further
clues. By default, errors go to /var/www/logs/error.log
:
server example.com, client 1 (1 active), 10.0.1.2:37391 -> 192.168.1.2, empty stdout (500 Internal Server Error) Can't locate strict.pm in @INC (you may need to install the strict module) (@INC entries checked: /usr/local/libdata/perl5/site_perl/amd64-openbsd /usr/local/libdata/perl5/site_perl /usr/libdata/perl5/amd64-openbsd /usr/libdata/perl5) at /htdocs/perl/index.pl line 2. BEGIN failed--compilation aborted at /htdocs/perl/index.pl line 2.
This error message tells us that the strict.pm
dependency cannot be found
inside the chroot. So, we search for which directories on our filesystem
contain perl libraries:
# find / -iname '*perl*'
In this case, the missing folder is /usr/libdata/perl5. We definitely need to copy these libraries over:
# mkdir -p /var/www/usr/libdata # cp -pR /usr/libdata/perl5 /var/www/usr/libdata/
If we continue this trial and error debugging process, we end up with all the dependencies we will need. Here is the complete list:
# mkdir -p /var/www/usr/{bin,lib,libexec,libdata} # cp -p /usr/bin/perl /var/www/usr/bin/ # cp -p /usr/lib/lib{c,m,perl,pthread,util,z}.so* /var/www/usr/lib/ # cp -p /usr/libexec/ld.so /var/www/usr/libexec/ # cp -pR /usr/libdata/perl5 /var/www/usr/libdata/ # mkdir -p /var/www/{tmp,dev} # touch /var/www/dev/null
Now the web request should show proper output:
$ ftp -o - http://example.com/index.pl Trying 192.168.1.2... Requesting http://example.com/index.pl All users are created equal: 1774 28 bytes received in 0.00 seconds (13.49 KB/s)
Adding TLS for serving your perl scripts is left as an exercise.